Skip to content

Commit a339bc6

Browse files
committed
(todo) Brackets are working
1 parent aeb4a11 commit a339bc6

File tree

1 file changed

+87
-36
lines changed

1 file changed

+87
-36
lines changed

src/features/Folding.ts

+87-36
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,38 @@ interface IExtensionPackage {
4040
}
4141
}
4242

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+
4375
export class FoldingProvider implements vscode.FoldingRangeProvider {
4476
private powershellGrammar;
4577

@@ -54,46 +86,65 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
5486

5587
const foldingRanges = [];
5688

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);
60119

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 = [];
62126

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);
68130
}
131+
if (token.scopes.includes(endScopeName)) {
132+
result.push(new MatchedToken(tokenStack.pop(), token, document));
133+
}
134+
});
69135

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;
97148
}
98149

99150
private powerShellGrammarPath(): string {

0 commit comments

Comments
 (0)