/** * symbol.ts * * @author CismonX * @license MIT */ import * as vscode from 'vscode'; import Document from './document'; import { FoldingRange } from './folding'; import { Optional } from './utils'; /** * Provide document symbol information for Texinfo documents. */ export class DocumentSymbolProvider implements vscode.DocumentSymbolProvider { provideDocumentSymbols(document: vscode.TextDocument) { return Document.of(document).symbol.values; } } /** * Context for symbols in a Texinfo document. */ export class DocumentSymbolContext { private document = this.documentContext.document; private symbols?: vscode.DocumentSymbol[]; private foldingRanges?: readonly FoldingRange[]; get values() { return this.symbols ??= this.calculcateDocumentSymbols(); } clear() { this.foldingRanges = undefined; this.symbols = undefined; } /** * Calculate document symbols based on folding ranges. */ private calculcateDocumentSymbols() { 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); } constructor(private readonly documentContext: Document) {} } type RangeNode = Optional; function foldingRangeToSymbols(ranges: readonly RangeNode[], start: number, end: number) { const symbols = []; for (let idx = start; idx < end; ++idx) { const node = ranges[idx]; if (node === undefined) continue; const startPosition = new vscode.Position(idx, 0); const endFirstLine = new vscode.Position(idx, Number.MAX_SAFE_INTEGER); const endLastLine = new vscode.Position(node.end, Number.MAX_SAFE_INTEGER); const range = new vscode.Range(startPosition, endLastLine); const selectionRange = new vscode.Range(startPosition, endFirstLine); const symbol = new vscode.DocumentSymbol('@' + node.name, node.detail, vscode.SymbolKind.String, range, selectionRange); symbol.children = foldingRangeToSymbols(ranges, idx + 1, node.end); symbols.push(symbol); idx = node.end; } return symbols; }