diff --git a/src/language/folding.ts b/src/language/folding.ts index ae792106b..8e82c98bf 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -5,7 +5,7 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { constructor() { const sections = vscode.workspace.getConfiguration('latex-workshop').get('view.outline.sections') as string[] - this.sectionRegex = sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + this.sectionRegex = this.buildSectionRegex(sections) } public provideFoldingRanges( @@ -16,7 +16,11 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return [...this.getSectionFoldingRanges(document), ...this.getEnvironmentFoldingRanges(document)] } - private getSectionFoldingRanges(document: vscode.TextDocument) { + protected buildSectionRegex(sections: string[]) { + return sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + } + + protected getSectionFoldingRanges(document: vscode.TextDocument) { const startingIndices: number[] = this.sectionRegex.map(_ => -1) const lines = document.getText().split(/\r?\n/g) let documentClassLine = -1 @@ -78,7 +82,7 @@ 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() @@ -126,6 +130,73 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { } } +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 + } + //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' + //DocTeX folding support + //for '%<*abc>': match[9] contains '%<*abc>', keyword is '%' + //for '%': match[10] contains '%', keyword is '%' + //for '% \iffalse meta-comment': match[11] contains '% \iffalse meta-comment', keyword is '%\\iffalse meta-comment' + //for '% \fi': match[12] contains '% \fi', keyword is '%\\iffalse meta-comment' + 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' + } else if (match[9]) { + keyword = '%<' + match[9] + '>' + } else if (match[10]) { + keyword = '%<' + match[10] + '>' + } else if (match[11] || match[12]) { + keyword = '%\\iffalse meta-comment' + } + + const item = { + keyword, + index: match.index + } + const lastItem = opStack[opStack.length - 1] + + if ((match[4] || match[6] || match[8] || match[10] || match[12]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' + const lastLineTune: number = match[10] || match[12] ? 0 : -1 + opStack.pop() + ranges.push(new vscode.FoldingRange( + document.positionAt(lastItem.index).line, + document.positionAt(item.index).line + lastLineTune + )) + } else { + opStack.push(item) + } + } + } +} + + export class WeaveFoldingProvider implements vscode.FoldingRangeProvider { public provideFoldingRanges( document: vscode.TextDocument, diff --git a/src/language/index.ts b/src/language/index.ts index b4e8c847c..e3a995448 100644 --- a/src/language/index.ts +++ b/src/language/index.ts @@ -1,7 +1,7 @@ import { DocSymbolProvider } from './symbol-document' import { ProjectSymbolProvider } from './symbol-project' import { DefinitionProvider } from './definition' -import { FoldingProvider, WeaveFoldingProvider } from './folding' +import { FoldingProvider, DoctexFoldingProvider, WeaveFoldingProvider } from './folding' import { SelectionRangeProvider } from './selection' import { getLocaleString } from './l10n' @@ -10,6 +10,7 @@ export const language = { projectSymbol: new ProjectSymbolProvider(), definition: new DefinitionProvider(), folding: new FoldingProvider(), + doctexFolding: new DoctexFoldingProvider(), weaveFolding: new WeaveFoldingProvider(), selectionRage: new SelectionRangeProvider(), getLocaleString diff --git a/src/main.ts b/src/main.ts index 3391ec0dd..02478cb7f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -311,7 +311,8 @@ function registerProviders(extensionContext: vscode.ExtensionContext) { extensionContext.subscriptions.push( vscode.languages.registerCodeActionsProvider(latexSelector, lw.lint.latex.actionprovider), vscode.languages.registerFoldingRangeProvider(latexSelector, lw.language.folding), - vscode.languages.registerFoldingRangeProvider(weaveSelector, lw.language.weaveFolding) + vscode.languages.registerFoldingRangeProvider(weaveSelector, lw.language.weaveFolding), + vscode.languages.registerFoldingRangeProvider(latexDoctexSelector, lw.language.doctexFolding) ) const selectionLatex = configuration.get('selection.smart.latex.enabled', true)