11import { FoldingRange , FoldingRangeKind } from "vscode-languageserver/node"
2+ import { TextDocument } from "vscode-languageserver-textdocument"
3+
24import { Visitor } from "@herb-tools/core"
5+ import { ParserService } from "./parser_service"
6+
7+ import { isERBIfNode } from "@herb-tools/core"
8+ import { lspLine } from "./range_utils"
39
410import type {
511 Node ,
612 ERBNode ,
13+ ERBContentNode ,
714 HTMLElementNode ,
815 HTMLOpenTagNode ,
916 HTMLAttributeValueNode ,
1017 HTMLCommentNode ,
18+ HTMLConditionalElementNode ,
19+ CDATANode ,
1120 ERBIfNode ,
12- DocumentNode ,
21+ ERBUnlessNode ,
22+ ERBCaseNode ,
23+ ERBCaseMatchNode ,
24+ ERBBeginNode ,
25+ SerializedPosition ,
1326} from "@herb-tools/core"
1427
1528export class FoldingRangeService {
16- getFoldingRanges ( document : DocumentNode ) : FoldingRange [ ] {
29+ private parserService : ParserService
30+
31+ constructor ( parserService : ParserService ) {
32+ this . parserService = parserService
33+ }
34+
35+ getFoldingRanges ( textDocument : TextDocument ) : FoldingRange [ ] {
36+ const parseResult = this . parserService . parseDocument ( textDocument )
1737 const collector = new FoldingRangeCollector ( )
18- collector . visit ( document )
38+
39+ collector . visit ( parseResult . document )
40+
1941 return collector . ranges
2042 }
2143}
2244
23- class FoldingRangeCollector extends Visitor {
24- ranges : FoldingRange [ ] = [ ]
45+ export class FoldingRangeCollector extends Visitor {
46+ public ranges : FoldingRange [ ] = [ ]
47+ private processedIfNodes : Set < ERBIfNode > = new Set ( )
2548
2649 visitHTMLElementNode ( node : HTMLElementNode ) : void {
2750 this . addRangeForNode ( node , node . body )
@@ -34,9 +57,7 @@ class FoldingRangeCollector extends Visitor {
3457 }
3558
3659 visitHTMLCommentNode ( node : HTMLCommentNode ) : void {
37- const startLine = this . toZeroBased ( node . location . start . line )
38- const endLine = this . toZeroBased ( node . location . end . line )
39- this . addRange ( startLine , endLine , FoldingRangeKind . Comment )
60+ this . addRange ( node . location . start , node . location . end , FoldingRangeKind . Comment )
4061 this . visitChildNodes ( node )
4162 }
4263
@@ -45,43 +66,107 @@ class FoldingRangeCollector extends Visitor {
4566 this . visitChildNodes ( node )
4667 }
4768
69+ visitCDATANode ( node : CDATANode ) : void {
70+ this . addRange ( node . location . start , node . location . end )
71+ this . visitChildNodes ( node )
72+ }
73+
74+ visitHTMLConditionalElementNode ( node : HTMLConditionalElementNode ) : void {
75+ this . addRange ( node . location . start , node . location . end )
76+ this . visitChildNodes ( node )
77+ }
78+
4879 visitERBNode ( node : ERBNode ) : void {
49- const startLine = this . toZeroBased ( node . location . start . line )
50- const endLine = this . toZeroBased ( node . location . end . line )
51- this . addRange ( startLine , endLine )
80+ this . addRange ( node . location . start , node . location . end )
81+ }
82+
83+ visitERBContentNode ( node : ERBContentNode ) : void {
84+ this . addRange ( node . location . start , node . location . end )
85+ this . visitChildNodes ( node )
5286 }
5387
5488 visitERBIfNode ( node : ERBIfNode ) : void {
55- const startLine = this . toZeroBased ( node . location . start . line )
56- const endLine = this . toZeroBased ( node . location . end . line )
57- this . addRange ( startLine , endLine )
89+ if ( this . processedIfNodes . has ( node ) ) {
90+ this . visitChildNodes ( node )
91+ return
92+ }
93+
94+ this . markIfChainAsProcessed ( node )
95+ this . addRange ( node . location . start , node . location . end )
5896
5997 if ( node . statements . length > 0 ) {
6098 const firstStatement = node . statements [ 0 ]
6199 const lastStatement = node . statements [ node . statements . length - 1 ]
62- const statementsStartLine = this . toZeroBased ( firstStatement . location . start . line )
63- const statementsEndLine = this . toZeroBased ( lastStatement . location . end . line )
64- this . addRange ( statementsStartLine , statementsEndLine )
100+
101+ this . addRange ( firstStatement . location . start , lastStatement . location . end )
65102 }
66103
104+ let current : Node | null = node . subsequent
105+
106+ while ( current ) {
107+ if ( isERBIfNode ( current ) ) {
108+ if ( current . statements . length > 0 ) {
109+ const firstStatement = current . statements [ 0 ]
110+ const lastStatement = current . statements [ current . statements . length - 1 ]
111+ this . addRange ( firstStatement . location . start , lastStatement . location . end )
112+ }
113+
114+ current = current . subsequent
115+ } else {
116+ break
117+ }
118+ }
119+
120+ this . visitChildNodes ( node )
121+ }
122+
123+ visitERBUnlessNode ( node : ERBUnlessNode ) : void {
124+ this . addRange ( node . location . start , node . location . end )
67125 this . visitChildNodes ( node )
68126 }
69127
70- private addRange ( startLine : number , endLine : number , kind ?: FoldingRangeKind ) : void {
128+ visitERBCaseNode ( node : ERBCaseNode ) : void {
129+ this . addRange ( node . location . start , node . location . end )
130+ this . visitChildNodes ( node )
131+ }
132+
133+ visitERBCaseMatchNode ( node : ERBCaseMatchNode ) : void {
134+ this . addRange ( node . location . start , node . location . end )
135+ this . visitChildNodes ( node )
136+ }
137+
138+ visitERBBeginNode ( node : ERBBeginNode ) : void {
139+ this . addRange ( node . location . start , node . location . end )
140+ this . visitChildNodes ( node )
141+ }
142+
143+ private markIfChainAsProcessed ( node : ERBIfNode ) : void {
144+ this . processedIfNodes . add ( node )
145+
146+ let current : Node | null = node . subsequent
147+
148+ while ( current ) {
149+ if ( isERBIfNode ( current ) ) {
150+ this . processedIfNodes . add ( current )
151+ current = current . subsequent
152+ } else {
153+ break
154+ }
155+ }
156+ }
157+
158+ private addRange ( start : SerializedPosition , end : SerializedPosition , kind ?: FoldingRangeKind ) : void {
159+ const startLine = lspLine ( start )
160+ const endLine = lspLine ( end )
161+
71162 if ( endLine > startLine ) {
72163 this . ranges . push ( { startLine, endLine, kind } )
73164 }
74165 }
75166
76167 private addRangeForNode ( node : Node , children : Node [ ] ) {
77168 if ( children . length > 0 ) {
78- const startLine = this . toZeroBased ( node . location . start . line )
79- const endLine = this . toZeroBased ( node . location . end . line )
80- this . addRange ( startLine , endLine )
169+ this . addRange ( node . location . start , node . location . end )
81170 }
82171 }
83-
84- private toZeroBased ( line : number ) : number {
85- return line - 1
86- }
87172}
0 commit comments