diff --git a/src/context/document.ts b/src/context/document.ts index 6da727d..7c95c74 100644 --- a/src/context/document.ts +++ b/src/context/document.ts @@ -10,6 +10,9 @@ import DocumentSymbolContext from './document_symbol'; import FoldingRangeContext from './folding_range'; import PreviewContext from './preview'; +/** + * Holds all contexts for a Texinfo document. + */ export default class DocumentContext { readonly foldingRange = new FoldingRangeContext(this.document); diff --git a/src/context/document_symbol.ts b/src/context/document_symbol.ts index f0b3333..42b040a 100644 --- a/src/context/document_symbol.ts +++ b/src/context/document_symbol.ts @@ -17,17 +17,17 @@ export default class DocumentSymbolContext { private document = this.documentContext.document; - private symbols?: vscode.DocumentSymbol[]; + private documentSymbols?: vscode.DocumentSymbol[]; private foldingRanges?: readonly FoldingRange[]; get values() { - return this.symbols ??= this.calculcateDocumentSymbols(); + return this.documentSymbols ??= this.calculcateDocumentSymbols(); } clear() { this.foldingRanges = undefined; - this.symbols = undefined; + this.documentSymbols = undefined; } constructor(private readonly documentContext: DocumentContext) {} @@ -39,7 +39,7 @@ export default class DocumentSymbolContext { const ranges = Array(this.document.lineCount); (this.foldingRanges ??= this.documentContext.foldingRange.values) .forEach(range => range.kind ?? (ranges[range.start] = range)); - return this.symbols = foldingRangeToSymbols(ranges, 0, ranges.length); + return this.documentSymbols = foldingRangeToSymbols(ranges, 0, ranges.length); } } diff --git a/src/context/folding_range.ts b/src/context/folding_range.ts index 3b7ef5f..d6761a9 100644 --- a/src/context/folding_range.ts +++ b/src/context/folding_range.ts @@ -13,6 +13,12 @@ import { FoldingRange, Range } from '../utils/types'; */ export default class FoldingRangeContext { + /** + * Regex for matching subsection/section/chapter (-like) commands. + */ + private static nodeMatcher = new RegExp('^@(?:(subsection|unnumberedsubsec|appendixsubsec|subheading)|' + + '(section|unnumberedsec|appendixsec|heading)|(chapter|unnumbered|appendix|majorheading|chapheading)) (.*)$'); + /** * Get VSCode folding ranges from the context. */ @@ -58,8 +64,8 @@ export default class FoldingRangeContext { */ private calculateFoldingRanges() { this.foldingRanges = []; - this.headerStart = undefined; - const closingBlocks = <{ name: string, line: number }[]>[]; + this.clearTemporaries(); + let closingBlocks = []; let lastLine = this.document.lineCount - 1; let verbatim = false; for (let idx = lastLine; idx >= 0; --idx) { @@ -70,8 +76,8 @@ export default class FoldingRangeContext { lastLine = idx; // Abort anything after `@bye`. this.foldingRanges = []; - this.commentRange = undefined; - this.headerStart = undefined; + closingBlocks = []; + this.clearTemporaries(); continue; } if (this.processComment(line, idx)) continue; @@ -97,7 +103,6 @@ export default class FoldingRangeContext { } if (this.commentRange !== undefined) { this.addRange(this.commentRange.start, this.commentRange.end, { kind: vscode.FoldingRangeKind.Comment }); - this.commentRange = undefined; } return this.foldingRanges; } @@ -129,19 +134,23 @@ export default class FoldingRangeContext { constructor(private readonly document: vscode.TextDocument) {} private processNode(lineText: string, lineNum: number, lastLineNum: number) { - if (lineText.startsWith('@subsection ')) { - const detail = lineText.substring(12); - this.addRange(lineNum, this.closingSubsection ?? lastLineNum, { name: 'subsection', detail: detail }); + const result = lineText.match(FoldingRangeContext.nodeMatcher); + if (result === null) return false; + // Subsection level node. + if (result[1] !== undefined) { + this.addRange(lineNum, this.closingSubsection ?? lastLineNum, { name: result[1], detail: result[4] }); this.closingSubsection = this.getLastTextLine(lineNum - 1); return true; - } else if (lineText.startsWith('@section ')) { - const detail = lineText.substring(9); - this.addRange(lineNum, this.closingSection ?? lastLineNum, { name: 'section', detail: detail }); + } + // Section level node. + if (result[2] !== undefined) { + this.addRange(lineNum, this.closingSection ?? lastLineNum, { name: result[2], detail: result[4] }); this.closingSubsection = this.closingSection = this.getLastTextLine(lineNum - 1); return true; - } else if (lineText.startsWith('@chapter ')) { - const detail = lineText.substring(9); - this.addRange(lineNum, this.closingChapter ?? lastLineNum, { name: 'chapter', detail: detail }); + } + // Chapter level node. + if (result[3] !== undefined) { + this.addRange(lineNum, this.closingChapter ?? lastLineNum, { name: result[3], detail: result[4] }); this.closingSubsection = this.closingSection = this.closingChapter = this.getLastTextLine(lineNum - 1); return true; } @@ -165,4 +174,12 @@ export default class FoldingRangeContext { (this.foldingRanges ??= []) .push(new FoldingRange(extraArgs.name ?? '', extraArgs.detail ?? '', start, end, extraArgs.kind)); } + + private clearTemporaries() { + this.commentRange = undefined; + this.headerStart = undefined; + this.closingSubsection = this.closingSection = this.closingChapter = undefined; + } } + +type ClosingBlock = { name: string, line: number }; diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 0927723..f7d516a 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -30,16 +30,16 @@ export function exec(path: string, args: string[], maxBuffer: number) { } /** - * Open a prompt with two buttons, "Confirm" and "Cancel", and wait for user action. + * Open a prompt with a button, and wait for user action. * * @param message The message to be displayed on the prompt. - * @param confirm Text to be displayed on the "Confirm" button. + * @param label Text to be displayed on the button. * @param error Whether the prompt is shown as an error message. Default false. - * @returns Whether the user clicked the "Confirm" button. + * @returns Whether the user clicked the button. */ -export async function prompt(message: string, confirm: string, error = false) { +export async function prompt(message: string, label: string, error = false) { const func = error ? vscode.window.showErrorMessage : vscode.window.showInformationMessage; - return confirm === await func(message, confirm, 'Cancel'); + return label === await func(message, label); } /**