@@ -40,6 +40,38 @@ interface IExtensionPackage {
40
40
}
41
41
}
42
42
43
+ // Need to reproduce the IToken interface from vscode-textmate due to
44
+ // the odd way it has to be required
45
+ export interface IToken {
46
+ startIndex : number ;
47
+ readonly endIndex : number ;
48
+ readonly scopes : string [ ] ;
49
+ }
50
+
51
+ class MatchedToken {
52
+ public start : IToken ;
53
+ public startline : number ;
54
+ public end : IToken ;
55
+ public endline : number ;
56
+
57
+ constructor ( start , end , document : vscode . TextDocument ) {
58
+ this . start = start ;
59
+ this . end = end ;
60
+ this . startline = document . positionAt ( start . startIndex ) . line ;
61
+ this . endline = document . positionAt ( end . startIndex ) . line ;
62
+ }
63
+
64
+ public isValidRange ( ) : boolean {
65
+ return ( this . endline - this . startline >= 2 ) ;
66
+ }
67
+
68
+ public toFoldingRange ( ) : vscode . FoldingRange {
69
+ return new vscode . FoldingRange ( this . startline , this . endline , vscode . FoldingRangeKind . Region ) ;
70
+ }
71
+ }
72
+
73
+ interface IMatchedTokenList extends Array < MatchedToken > { }
74
+
43
75
export class FoldingProvider implements vscode . FoldingRangeProvider {
44
76
private powershellGrammar ;
45
77
@@ -54,46 +86,65 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
54
86
55
87
const foldingRanges = [ ] ;
56
88
57
- console . log ( document ) ;
58
- console . log ( context ) ;
59
- console . log ( token ) ;
89
+ // Convert the document text into a series of grammar tokens
90
+
91
+ const tokens = this . grammar ( ) . tokenizeLine ( document . getText ( ) ) . tokens ;
92
+ // Parse the token list looking for matching tokens and return
93
+ // a list of <start, end>. Then filter the list and only return token matches
94
+ // that are a valid text range e.g. Needs to span at least 3 lines
95
+ const foldableTokens = this . matchGrammarTokens ( tokens , document )
96
+ . filter ( ( item ) => item . isValidRange ( ) ) ;
97
+
98
+ // Sort the list of matched tokens to start at the top of the document,
99
+ // and ensure that in the case of multiple ranges starting the same line,
100
+ // that the largest range (i.e. most number of lines spanned) is sorted
101
+ // first. This is needed as vscode will just ignore any duplicate folding
102
+ // ranges
103
+ foldableTokens . sort ( ( a : MatchedToken , b : MatchedToken ) => {
104
+ // Initially look at the start line
105
+ if ( a . startline > b . startline ) { return 1 ; }
106
+ if ( a . startline < b . startline ) { return - 1 ; }
107
+ // They have the same start line so now consider the end line.
108
+ // The biggest line range is sorted first
109
+ if ( a . endline > b . endline ) { return - 1 ; }
110
+ if ( a . endline < b . endline ) { return 1 ; }
111
+ // They're the same
112
+ return 0 ;
113
+ } ) ;
114
+
115
+ // Convert the matched token list into a FoldingRange[]
116
+ foldableTokens . forEach ( ( item ) => { foldingRanges . push ( item . toFoldingRange ( ) ) ; } ) ;
117
+
118
+ // console.log(foldableTokens);
60
119
61
- const content = document . getText ( ) ;
120
+ return foldingRanges ;
121
+ }
122
+
123
+ private matchScopeElements ( tokens , startScopeName , endScopeName , document : vscode . TextDocument ) : IMatchedTokenList {
124
+ const result = [ ] ;
125
+ const tokenStack = [ ] ;
62
126
63
- let tokens = this . grammar ( ) . tokenizeLine ( content ) . tokens ;
64
- for ( var i = 0 ; i < tokens . length ; i ++ ) {
65
- tokens [ i ] [ 'value' ] = content . substring ( tokens [ i ] [ 'startIndex' ] , tokens [ i ] [ 'endIndex' ] ) ;
66
- tokens [ i ] [ 'startLine' ] = document . positionAt ( tokens [ i ] [ "startIndex" ] ) . line ;
67
- tokens [ i ] [ 'endLine' ] = document . positionAt ( tokens [ i ] [ "endIndex" ] ) . line ;
127
+ tokens . forEach ( ( token ) => {
128
+ if ( token . scopes . includes ( startScopeName ) ) {
129
+ tokenStack . push ( token ) ;
68
130
}
131
+ if ( token . scopes . includes ( endScopeName ) ) {
132
+ result . push ( new MatchedToken ( tokenStack . pop ( ) , token , document ) ) ;
133
+ }
134
+ } ) ;
69
135
70
- // Just force a fixed folding range to see what happens
71
- foldingRanges . push ( new vscode . FoldingRange ( 1 , 3 , vscode . FoldingRangeKind . Region ) ) ;
72
-
73
- console . log ( tokens ) ;
74
- // const tocProvider = new TableOfContentsProvider(this.engine, document);
75
- // let toc = await tocProvider.getToc();
76
- // if (toc.length > rangeLimit) {
77
- // toc = toc.slice(0, rangeLimit);
78
- // }
79
-
80
- // const foldingRanges = toc.map((entry, startIndex) => {
81
- // const start = entry.line;
82
- // let end: number | undefined = undefined;
83
- // for (let i = startIndex + 1; i < toc.length; ++i) {
84
- // if (toc[i].level <= entry.level) {
85
- // end = toc[i].line - 1;
86
- // if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) {
87
- // end = end - 1;
88
- // }
89
- // break;
90
- // }
91
- // }
92
- // return new vscode.FoldingRange(
93
- // start,
94
- // typeof end === 'number' ? end : document.lineCount - 1);
95
- // });
96
- return foldingRanges ;
136
+ return result . reverse ( ) ;
137
+ }
138
+
139
+ private matchGrammarTokens ( tokens , document : vscode . TextDocument ) : IMatchedTokenList {
140
+ const matchedTokens = [ ] ;
141
+
142
+ // Find matching Braces { -> }
143
+ this . matchScopeElements ( tokens , "punctuation.section.braces.begin.powershell" , "punctuation.section.braces.end.powershell" , document ) . forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
144
+ // Find matching brackets ( -> )
145
+ this . matchScopeElements ( tokens , "punctuation.section.group.begin.powershell" , "punctuation.section.group.end.powershell" , document ) . forEach ( ( x ) => { matchedTokens . push ( x ) ; } ) ;
146
+
147
+ return matchedTokens ;
97
148
}
98
149
99
150
private powerShellGrammarPath ( ) : string {
0 commit comments