From 79146f7f92f48fe2452c4420b3b004e73ffb28e9 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Mon, 7 Oct 2024 00:01:36 +0800 Subject: [PATCH 1/6] support code folding for DocTeX --- src/language/folding.ts | 21 +++++++++++++++++---- src/language/index.ts | 1 + src/main.ts | 3 ++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/language/folding.ts b/src/language/folding.ts index ae792106b..3c4c16dca 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -2,10 +2,17 @@ import * as vscode from 'vscode' export class FoldingProvider implements vscode.FoldingRangeProvider { private readonly sectionRegex: RegExp[] = [] + private readonly doctex: boolean - constructor() { + constructor(doctex: boolean = false) { const sections = vscode.workspace.getConfiguration('latex-workshop').get('view.outline.sections') as string[] - this.sectionRegex = sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + this.doctex = doctex + if (this.doctex) { + this.sectionRegex = sections.map(section => RegExp(`%\\s*\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + } + else { + this.sectionRegex = sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) + } } public provideFoldingRanges( @@ -82,7 +89,7 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { 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' + const envRegex = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|_()\-a-zA-Z0-9]+)>|^%\s*<\/([|_()\-a-zA-Z0-9]+)>/gm //to match one 'begin' OR 'end' while (true) { const match = envRegex.exec(text) @@ -96,6 +103,8 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { //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' + //for '%<*abc>': match[9] contains '%<*>', keyword is 'abc' + //for '%': match[10] contains '%', keyword is 'abc' let keyword: string = '' if (match[1]) { keyword = match[2] @@ -105,6 +114,10 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { keyword = 'group' } else if (match[7] || match[8]) { keyword = 'region' + } else if (match[9]) { + keyword = match[9] + } else if (match[10]) { + keyword = match[10] } const item = { @@ -113,7 +126,7 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { } const lastItem = opStack[opStack.length - 1] - if ((match[4] || match[6] || match[8]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' + if ((match[4] || match[6] || match[8] || match[10]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' opStack.pop() ranges.push(new vscode.FoldingRange( document.positionAt(lastItem.index).line, diff --git a/src/language/index.ts b/src/language/index.ts index b4e8c847c..5550fe1c3 100644 --- a/src/language/index.ts +++ b/src/language/index.ts @@ -10,6 +10,7 @@ export const language = { projectSymbol: new ProjectSymbolProvider(), definition: new DefinitionProvider(), folding: new FoldingProvider(), + doctexFolding: new FoldingProvider(true), 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) From 9e1b5f8e75f472d5b5b880ee8557a1411448e813 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Mon, 7 Oct 2024 10:43:48 +0800 Subject: [PATCH 2/6] DoctexFoldingProvider extends FoldingProvider --- src/language/folding.ts | 74 ++++++++++++++++++++++++++++++++++------- src/language/index.ts | 4 +-- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/language/folding.ts b/src/language/folding.ts index 3c4c16dca..02157696b 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -2,17 +2,10 @@ import * as vscode from 'vscode' export class FoldingProvider implements vscode.FoldingRangeProvider { private readonly sectionRegex: RegExp[] = [] - private readonly doctex: boolean - constructor(doctex: boolean = false) { + constructor() { const sections = vscode.workspace.getConfiguration('latex-workshop').get('view.outline.sections') as string[] - this.doctex = doctex - if (this.doctex) { - this.sectionRegex = sections.map(section => RegExp(`%\\s*\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) - } - else { - this.sectionRegex = sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) - } + this.sectionRegex = this.buildSectionRegex(sections) } public provideFoldingRanges( @@ -23,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 @@ -85,11 +82,64 @@ 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: RegExp = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)/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' + 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]+)>/gm //to match one 'begin' OR 'end' + const envRegex: RegExp = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|_()\-a-zA-Z0-9]+)>|^%\s*<\/([|_()\-a-zA-Z0-9]+)>/gm while (true) { const match = envRegex.exec(text) diff --git a/src/language/index.ts b/src/language/index.ts index 5550fe1c3..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,7 +10,7 @@ export const language = { projectSymbol: new ProjectSymbolProvider(), definition: new DefinitionProvider(), folding: new FoldingProvider(), - doctexFolding: new FoldingProvider(true), + doctexFolding: new DoctexFoldingProvider(), weaveFolding: new WeaveFoldingProvider(), selectionRage: new SelectionRangeProvider(), getLocaleString From e04da9a9b30feb835f83767834304aa3af774893 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Mon, 7 Oct 2024 11:00:49 +0800 Subject: [PATCH 3/6] support meta-comment folding --- src/language/folding.ts | 76 ++++++++++------------------------------- 1 file changed, 18 insertions(+), 58 deletions(-) diff --git a/src/language/folding.ts b/src/language/folding.ts index 02157696b..0dd89f9cc 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -2,6 +2,7 @@ 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[] @@ -20,7 +21,7 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return sections.map(section => RegExp(`\\\\(?:${section})(?:\\*)?(?:\\[[^\\[\\]\\{\\}]*\\])?{(.*)}`, 'm')) } - protected getSectionFoldingRanges(document: vscode.TextDocument) { + private getSectionFoldingRanges(document: vscode.TextDocument) { const startingIndices: number[] = this.sectionRegex.map(_ => -1) const lines = document.getText().split(/\r?\n/g) let documentClassLine = -1 @@ -82,67 +83,13 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { return sections.map(section => new vscode.FoldingRange(section.from, section.to)) } - protected getEnvironmentFoldingRanges(document: vscode.TextDocument) { + private getEnvironmentFoldingRanges(document: vscode.TextDocument) { const ranges: vscode.FoldingRange[] = [] const opStack: { keyword: string, index: number }[] = [] const text: string = document.getText() - const envRegex: RegExp = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)/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' - 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: RegExp = /\\(begin){(.*?)}|\\(begingroup)[%\s\\]|\\(end){(.*?)}|\\(endgroup)[%\s\\]|^%\s*#?([rR]egion)|^%\s*#?([eE]ndregion)|^%\s*<\*([|_()\-a-zA-Z0-9]+)>|^%\s*<\/([|_()\-a-zA-Z0-9]+)>/gm while (true) { - const match = envRegex.exec(text) + const match = this.envRegex.exec(text) if (match === null) { //TODO: if opStack still not empty return ranges @@ -153,8 +100,11 @@ export class DoctexFoldingProvider extends FoldingProvider { //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 '%<*>', keyword is 'abc' //for '%': match[10] contains '%', keyword is 'abc' + //for '% \iffalse meta-comment': match[11] contains '\iffalse meta-comment', keyword is 'meta-comment' + //for '% \fi': match[12] contains '\fi', keyword is 'meta-comment' let keyword: string = '' if (match[1]) { keyword = match[2] @@ -168,6 +118,8 @@ export class DoctexFoldingProvider extends FoldingProvider { keyword = match[9] } else if (match[10]) { keyword = match[10] + } else if (match[11] || match[12]) { + keyword = 'meta-comment' } const item = { @@ -176,7 +128,7 @@ export class DoctexFoldingProvider extends FoldingProvider { } const lastItem = opStack[opStack.length - 1] - if ((match[4] || match[6] || match[8] || match[10]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' + if ((match[4] || match[6] || match[8] || match[10] || match[12]) && lastItem && lastItem.keyword === item.keyword) { // match 'end' with its 'begin' opStack.pop() ranges.push(new vscode.FoldingRange( document.positionAt(lastItem.index).line, @@ -189,6 +141,14 @@ export class DoctexFoldingProvider extends FoldingProvider { } } +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( document: vscode.TextDocument, From 6bc34203be40b9018378d53b987cfaa52fe06ee0 Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Mon, 7 Oct 2024 11:29:39 +0800 Subject: [PATCH 4/6] avoid potential keyword collision --- src/language/folding.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/language/folding.ts b/src/language/folding.ts index 0dd89f9cc..8f2b070b0 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -101,10 +101,10 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { //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 '%<*>', keyword is 'abc' - //for '%': match[10] contains '%', keyword is 'abc' - //for '% \iffalse meta-comment': match[11] contains '\iffalse meta-comment', keyword is 'meta-comment' - //for '% \fi': match[12] contains '\fi', keyword is 'meta-comment' + //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] @@ -115,11 +115,11 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { } else if (match[7] || match[8]) { keyword = 'region' } else if (match[9]) { - keyword = match[9] + keyword = '%<' + match[9] + '>' } else if (match[10]) { - keyword = match[10] + keyword = '%<' + match[10] + '>' } else if (match[11] || match[12]) { - keyword = 'meta-comment' + keyword = '%\\iffalse meta-comment' } const item = { From b86048dfec92dadd86430ffc7f9bbb88ac7c14dd Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Mon, 7 Oct 2024 11:58:12 +0800 Subject: [PATCH 5/6] finetune folding range --- src/language/folding.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/language/folding.ts b/src/language/folding.ts index 8f2b070b0..e10839c85 100644 --- a/src/language/folding.ts +++ b/src/language/folding.ts @@ -129,10 +129,11 @@ export class FoldingProvider implements vscode.FoldingRangeProvider { 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 - 1 + document.positionAt(item.index).line + lastLineTune )) } else { opStack.push(item) From c66dc1ad09d54eb59072631db32da9d688402d6f Mon Sep 17 00:00:00 2001 From: Mark Huang Date: Wed, 16 Oct 2024 19:03:37 +0800 Subject: [PATCH 6/6] fork getEnvironmentFoldingRanges --- src/language/folding.ts | 69 ++++++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 11 deletions(-) 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(