Implement folding
This commit is contained in:
parent
f5723615e7
commit
b67b37343b
11
package.json
11
package.json
|
@ -46,7 +46,16 @@
|
||||||
"plugin:@typescript-eslint/recommended"
|
"plugin:@typescript-eslint/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"comma-dangle": ["warn", "always-multiline"],
|
"comma-dangle": [
|
||||||
|
"warn",
|
||||||
|
"always-multiline"
|
||||||
|
],
|
||||||
|
"max-len": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"code": 120
|
||||||
|
}
|
||||||
|
],
|
||||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||||
"@typescript-eslint/semi": "warn"
|
"@typescript-eslint/semi": "warn"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,25 @@ import * as vscode from 'vscode';
|
||||||
import { Options } from './options';
|
import { Options } from './options';
|
||||||
import { Preview } from './preview';
|
import { Preview } from './preview';
|
||||||
import { CompletionItemProvider } from './completion';
|
import { CompletionItemProvider } from './completion';
|
||||||
|
import { FoldingRangeProvider, FoldingRangeContext } from './folding';
|
||||||
|
|
||||||
export function activate(context: vscode.ExtensionContext) {
|
export function activate(context: vscode.ExtensionContext) {
|
||||||
context.subscriptions.push(
|
context.subscriptions.push(
|
||||||
vscode.workspace.onDidSaveTextDocument(Preview.update),
|
vscode.workspace.onDidSaveTextDocument(Preview.update),
|
||||||
vscode.workspace.onDidCloseTextDocument(Preview.close),
|
vscode.workspace.onDidCloseTextDocument((document) => {
|
||||||
|
Preview.close(document);
|
||||||
|
FoldingRangeContext.close(document);
|
||||||
|
}),
|
||||||
|
vscode.workspace.onDidOpenTextDocument(FoldingRangeContext.open),
|
||||||
|
vscode.workspace.onDidChangeTextDocument(FoldingRangeContext.update),
|
||||||
vscode.commands.registerTextEditorCommand('texinfo.showPreview', Preview.show),
|
vscode.commands.registerTextEditorCommand('texinfo.showPreview', Preview.show),
|
||||||
vscode.languages.registerCompletionItemProvider('texinfo', new CompletionItemProvider(), '@'),
|
vscode.languages.registerCompletionItemProvider('texinfo', new CompletionItemProvider(), '@'),
|
||||||
|
vscode.languages.registerFoldingRangeProvider('texinfo', new FoldingRangeProvider()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate() {
|
export function deactivate() {
|
||||||
Preview.destroyAll();
|
Preview.destroyAll();
|
||||||
Options.clear();
|
Options.clear();
|
||||||
|
FoldingRangeContext.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* folding.ts
|
||||||
|
*
|
||||||
|
* @author CismonX <admin@cismon.net>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide folding range info for Texinfo source code.
|
||||||
|
*/
|
||||||
|
export class FoldingRangeProvider implements vscode.FoldingRangeProvider {
|
||||||
|
|
||||||
|
provideFoldingRanges(document: vscode.TextDocument) {
|
||||||
|
return FoldingRangeContext.get(document).foldingRanges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FoldingRangeContext {
|
||||||
|
|
||||||
|
private static readonly map = new Map<vscode.TextDocument, FoldingRangeContext>();
|
||||||
|
|
||||||
|
static open(document: vscode.TextDocument) {
|
||||||
|
if (document.languageId === 'texinfo') {
|
||||||
|
new FoldingRangeContext(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(document: vscode.TextDocument) {
|
||||||
|
return FoldingRangeContext.map.get(document) ?? new FoldingRangeContext(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
static update(event: vscode.TextDocumentChangeEvent) {
|
||||||
|
if (event.document.languageId !== 'texinfo') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FoldingRangeContext.get(event.document)?.update(event.contentChanges);
|
||||||
|
}
|
||||||
|
|
||||||
|
static close(document: vscode.TextDocument) {
|
||||||
|
FoldingRangeContext.map.delete(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
static clear() {
|
||||||
|
FoldingRangeContext.map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
foldingRanges = <vscode.FoldingRange[]>[];
|
||||||
|
|
||||||
|
private commentRange?: vscode.FoldingRange;
|
||||||
|
|
||||||
|
private headerStart?: number;
|
||||||
|
|
||||||
|
private closingBlocks = <ClosingBlock[]>[];
|
||||||
|
|
||||||
|
private constructor(private readonly document: vscode.TextDocument) {
|
||||||
|
FoldingRangeContext.map.set(document, this);
|
||||||
|
this.calculateFoldingRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateFoldingRanges() {
|
||||||
|
for (let idx = this.document.lineCount - 1; idx >= 0; --idx) {
|
||||||
|
const line = this.document.lineAt(idx);
|
||||||
|
const lineText = line.text;
|
||||||
|
const lineNum = line.lineNumber;
|
||||||
|
if (!lineText.startsWith('@')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (this.processComment(lineText, lineNum)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.processBlock(lineText, lineNum);
|
||||||
|
}
|
||||||
|
if (this.commentRange !== undefined) {
|
||||||
|
if (this.commentRange.end - this.commentRange.start > 1) {
|
||||||
|
this.foldingRanges.push(this.commentRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private processComment(lineText: string, lineNum: number) {
|
||||||
|
if (lineText.startsWith('c', 1)) {
|
||||||
|
if (!lineText.startsWith(' ', 2) && !lineText.startsWith('omment ', 2)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check for opening/closing header.
|
||||||
|
if (lineText.startsWith('%**', lineText[2] === ' ' ? 3 : 9)) {
|
||||||
|
if (this.headerStart === undefined) {
|
||||||
|
this.headerStart = lineNum;
|
||||||
|
} else {
|
||||||
|
this.foldingRanges.push(new vscode.FoldingRange(lineNum, this.headerStart));
|
||||||
|
this.headerStart = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.commentRange === undefined) {
|
||||||
|
this.commentRange = new vscode.FoldingRange(lineNum, lineNum, vscode.FoldingRangeKind.Comment);
|
||||||
|
} else if (this.commentRange.start - 1 === lineNum) {
|
||||||
|
this.commentRange.start = lineNum;
|
||||||
|
} else {
|
||||||
|
this.foldingRanges.push(this.commentRange);
|
||||||
|
this.commentRange = new vscode.FoldingRange(lineNum, lineNum, vscode.FoldingRangeKind.Comment);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private processBlock(lineText: string, lineNum: number) {
|
||||||
|
if (lineText.startsWith('end ', 1)) {
|
||||||
|
this.closingBlocks.push({ name: lineText.substring(5), line: lineNum });
|
||||||
|
} else {
|
||||||
|
const closingBlock = this.closingBlocks.pop();
|
||||||
|
if (closingBlock === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (lineText.substring(1, closingBlock.name.length + 2).trim() === closingBlock.name) {
|
||||||
|
this.foldingRanges.push(new vscode.FoldingRange(lineNum, closingBlock.line));
|
||||||
|
} else {
|
||||||
|
this.closingBlocks.push(closingBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private update(events: readonly vscode.TextDocumentContentChangeEvent[]) {
|
||||||
|
// console.log(events);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a Texinfo block marked "closing" by `@end` command.
|
||||||
|
*/
|
||||||
|
interface ClosingBlock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the block.
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The terminating line number of the block.
|
||||||
|
*/
|
||||||
|
line: number;
|
||||||
|
}
|
Loading…
Reference in New Issue