diff --git a/src/language/folding.ts b/src/language/folding.ts index e10839c85..8e82c98bf 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -2,7 +2,6 @@ import * as vscode from 'vscode' export class FoldingProvider implements vscode.FoldingRangeProvider { private readonly sectionRegex: RegExp[] = [] - protected readonly envRegex = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|_()\-a-zA-Z0-9]+)>|^%\s*<\/([|_()\-a-zA-Z0-9]+)>|^%\s*\\iffalse\s*(meta-comment)|^%\s*\\(fi)/gm constructor() { const sections = vscode.workspace.getConfiguration('latex-workshop').get('view.outline.sections') as string[] @@ -21,7 +20,7 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) } - private getSectionFoldingRanges(document: vscode.TextDocument) { + protected getSectionFoldingRanges(document: vscode.TextDocument) { const startingIndices: number[] = this.sectionRegex.map(_ => -1) const lines = document.getText().split(/\r?\n/g) let documentClassLine = -1 @@ -83,13 +82,68 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return sections.map(section => new vscode.FoldingRange(section.from, section.to)) } - private getEnvironmentFoldingRanges(document: vscode.TextDocument) { + protected getEnvironmentFoldingRanges(document: vscode.TextDocument) { const ranges: vscode.FoldingRange[] = [] const opStack: { keyword: string, index: number }[] = [] const text: string = document.getText() + const envRegex = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)/gm //to match one 'begin' OR 'end' while (true) { - const match = this.envRegex.exec(text) + const match = envRegex.exec(text) + if (match === null) { + //TODO: if opStack still not empty + return ranges + } + //for 'begin': match[1] contains 'begin', match[2] contains keyword + //for 'end': match[4] contains 'end', match[5] contains keyword + //for 'begingroup': match[3] contains 'begingroup', keyword is 'group' + //for 'endgroup': match[6] contains 'endgroup', keyword is 'group' + //for '% region': match[7] contains 'region', keyword is 'region' + //for '% endregion': match[8] contains 'endregion', keyword is 'region' + let keyword: string = '' + if (match[1]) { + keyword = match[2] + } else if (match[4]) { + keyword = match[5] + } else if (match[3] || match[6]) { + keyword = 'group' + } else if (match[7] || match[8]) { + keyword = 'region' + } + + const item = { + keyword, + index: match.index + } + const lastItem = opStack[opStack.length - 1] + + if ((match[4] || match[6] || match[8]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' + opStack.pop() + ranges.push(new vscode.FoldingRange( + document.positionAt(lastItem.index).line, + document.positionAt(item.index).line - 1 + )) + } else { + opStack.push(item) + } + } + } +} + +export class DoctexFoldingProvider extends FoldingProvider { + + protected buildSectionRegex(sections: string[]) { + return sections.map(section => RegExp(`%\\s*\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + } + + protected getEnvironmentFoldingRanges(document: vscode.TextDocument) { + const ranges: vscode.FoldingRange[] = [] + const opStack: { keyword: string, index: number }[] = [] + const text: string = document.getText() + const envRegex = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|,&!()_\-a-zA-Z0-9]+)>|^%\s*<\/([|,&!()_\-a-zA-Z0-9]+)>|^%\s*\\iffalse\s*(meta-comment)|^%\s*\\(fi)/gm //to match one 'begin' OR 'end' + + while (true) { + const match = envRegex.exec(text) if (match === null) { //TODO: if opStack still not empty return ranges @@ -142,13 +196,6 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { } } -export class DoctexFoldingProvider extends FoldingProvider { - protected readonly envRegex: RegExp = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|_()\-a-zA-Z0-9]+)>|^%\s*<\/([|_()\-a-zA-Z0-9]+)>|^%\s*\\iffalse\s*(meta-comment)|^%\s*\\(fi)/gm - - protected buildSectionRegex(sections: string[]) { - return sections.map(section => RegExp(`%\\s*\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) - } -} export class WeaveFoldingProvider implements vscode.FoldingRangeProvider { public provideFoldingRanges(