This commit is contained in:
CismonX 2020-10-24 23:50:20 +08:00
parent 4cbbcdfd4d
commit c52c7b5924
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
9 changed files with 56 additions and 71 deletions

View File

@ -35,7 +35,7 @@ See `File -> Preferences -> Settings -> Extensions -> Texinfo` for details. The
## Notes ## Notes
* If syntax highlighting is not satisfactory, try another color theme where keyword/operator colors are distinct (e.g. Solarized Light/Dark, Monokai). * If syntax highlighting is not satisfactory, try another color theme (e.g. Solarized Light/Dark, Monokai) where keyword/operator colors are distinct.
* Preview content is updated on document save rather than document change. * Preview content is updated on document save rather than document change.
* For macOS users: Preinstalled GNU Texinfo distribution is very old. Use a latest one instead. This can be easily done by `brew install texinfo` and change extension setting `texinfo.makeinfo` value. * For macOS users: Preinstalled GNU Texinfo distribution is very old. Use a latest one instead. This can be easily done by `brew install texinfo` and change extension setting `texinfo.makeinfo` value.

26
package-lock.json generated
View File

@ -44,9 +44,9 @@
} }
}, },
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "0.1.3", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.0.tgz",
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", "integrity": "sha512-+cIGPCBdLCzqxdtwppswP+zTsH9BOIGzAeKfBIbtb4gW/giMlfMwP0HUSFfhzh20f9u8uZ8hOp62+4GPquTbwQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
@ -74,9 +74,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "14.11.10", "version": "14.14.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.2.tgz",
"integrity": "sha512-yV1nWZPlMFpoXyoknm4S56y2nlTAuFYaJuQtYRAOU7xA/FJ9RY0Xm7QOkaYMMmr8ESdHIuUb6oQgR/0+2NqlyA==", "integrity": "sha512-jeYJU2kl7hL9U5xuI/BhKPZ4vqGM/OmK6whiFAXVhlstzZhVamWhDSmHyGLIp+RVyuF9/d0dqr2P85aFj4BvJg==",
"dev": true "dev": true
}, },
"@types/vscode": { "@types/vscode": {
@ -169,9 +169,9 @@
"dev": true "dev": true
}, },
"ajv": { "ajv": {
"version": "6.12.5", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@ -403,13 +403,13 @@
"dev": true "dev": true
}, },
"eslint": { "eslint": {
"version": "7.11.0", "version": "7.12.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.11.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.0.tgz",
"integrity": "sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==", "integrity": "sha512-n5pEU27DRxCSlOhJ2rO57GDLcNsxO0LPpAbpFdh7xmcDmjmlGUfoyrsB3I7yYdQXO5N3gkSTiDrPSPNFiiirXA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.1.3", "@eslint/eslintrc": "^0.2.0",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.2", "cross-spawn": "^7.0.2",

View File

@ -1,7 +1,7 @@
{ {
"name": "texinfo", "name": "texinfo",
"displayName": "Texinfo Language Support", "displayName": "Texinfo Language Support",
"description": "Syntax highlighting, code completion and preview support for Texinfo.", "description": "Syntax highlighting, code completion, folding and preview support for Texinfo.",
"version": "0.1.0", "version": "0.1.0",
"author": { "author": {
"name": "CismonX", "name": "CismonX",
@ -15,12 +15,12 @@
}, },
"icon": "assets/texinfo.png", "icon": "assets/texinfo.png",
"devDependencies": { "devDependencies": {
"@types/node": "^14.11.10", "@types/node": "^14.14.2",
"@types/vscode": "^1.50.0", "@types/vscode": "^1.50.0",
"@typescript-eslint/eslint-plugin": "^3.8.0", "@typescript-eslint/eslint-plugin": "^3.8.0",
"@typescript-eslint/parser": "^3.8.0", "@typescript-eslint/parser": "^3.8.0",
"cson": "^7.20.0", "cson": "^7.20.0",
"eslint": "^7.11.0", "eslint": "^7.12.0",
"language-texinfo": "^1.0.0", "language-texinfo": "^1.0.0",
"typescript": "^4.0.3" "typescript": "^4.0.3"
}, },
@ -128,7 +128,7 @@
"type": "integer", "type": "integer",
"default": 100, "default": 100,
"minimum": 0, "minimum": 0,
"markdownDescription": "Max tolerated number of errors when trying to display preview.\n\nThis corresponds to the `--error-limit=NUM` option of `makeinfo`." "markdownDescription": "Max number of errors before quit when trying to display preview.\n\nThis corresponds to the `--error-limit=NUM` option of `makeinfo`."
}, },
"texinfo.preview.force": { "texinfo.preview.force": {
"type": "boolean", "type": "boolean",

View File

@ -17,7 +17,6 @@ export default class Converter {
* Convert a Texinfo document to HTML. * Convert a Texinfo document to HTML.
* *
* @param path Path to the Texinfo document. * @param path Path to the Texinfo document.
* @returns HTML code, or `undefined` if conversion fails.
*/ */
static async convertToHtml(path: string) { static async convertToHtml(path: string) {
return await new Converter().convert(path); return await new Converter().convert(path);

View File

@ -32,7 +32,7 @@ export default class Document {
static update(event: vscode.TextDocumentChangeEvent) { static update(event: vscode.TextDocumentChangeEvent) {
const documentContext = Document.get(event.document); const documentContext = Document.get(event.document);
if (documentContext?.foldingRange.update(event.contentChanges)) { if (documentContext?.foldingRange.update(event.contentChanges)) {
documentContext.symbol.update(documentContext.foldingRange.values); documentContext.symbol.clear();
} }
} }

View File

@ -14,18 +14,10 @@ export default class Logger {
private static singleton?: Logger; private static singleton?: Logger;
private static get instance() { static get instance() {
return Logger.singleton ??= new Logger(); return Logger.singleton ??= new Logger();
} }
static log(message: string) {
Logger.instance.log(message);
}
static show() {
Logger.instance.show();
}
static destroy() { static destroy() {
Logger.instance.outputChannel.dispose(); Logger.instance.outputChannel.dispose();
Logger.singleton = undefined; Logger.singleton = undefined;
@ -37,12 +29,12 @@ export default class Logger {
this.outputChannel = vscode.window.createOutputChannel('Texinfo'); this.outputChannel = vscode.window.createOutputChannel('Texinfo');
} }
private log(message: string) { log(message: string) {
const dateTime = new Date().toLocaleString(undefined, { hour12: false }); const dateTime = new Date().toLocaleString(undefined, { hour12: false });
this.outputChannel.appendLine(`[ ${dateTime} ]:\n${message}`); this.outputChannel.appendLine(`[ ${dateTime} ]\n${message}`);
} }
private show() { show() {
this.outputChannel.show(true); this.outputChannel.show(true);
} }
} }

View File

@ -53,7 +53,8 @@ export default class Preview {
private pendingUpdate = false; private pendingUpdate = false;
constructor(private readonly documentContext: Document) { constructor(private readonly documentContext: Document) {
this.panel = vscode.window.createWebviewPanel('texinfo.preview', '', vscode.ViewColumn.Beside); this.panel = vscode.window.createWebviewPanel('texinfo.preview', '', vscode.ViewColumn.Beside,
{ enableFindWidget: true, retainContextWhenHidden: true });
this.disposables.push(this.panel.onDidDispose(() => this.close())); this.disposables.push(this.panel.onDidDispose(() => this.close()));
this.updateTitle(); this.updateTitle();
this.updateWebview(); this.updateWebview();

View File

@ -29,53 +29,47 @@ export class DocumentSymbolContext {
private symbols?: vscode.DocumentSymbol[]; private symbols?: vscode.DocumentSymbol[];
private foldingRanges?: FoldingRange[]; private foldingRanges?: readonly FoldingRange[];
get values() { get values() {
return this.symbols ??= this.calculcateDocumentSymbols(); return this.symbols ??= this.calculcateDocumentSymbols();
} }
update(foldingRanges: FoldingRange[]) { clear() {
this.foldingRanges = foldingRanges; this.foldingRanges = undefined;
this.symbols = undefined; this.symbols = undefined;
} }
/** /**
* Calculate (very limited) document symbols based on folding ranges. * Calculate document symbols based on folding ranges.
*/ */
private calculcateDocumentSymbols() { private calculcateDocumentSymbols() {
this.symbols = [];
if (this.foldingRanges === undefined) {
this.foldingRanges = this.documentContext.foldingRange.values;
}
const ranges = Array<RangeNode>(this.document.lineCount); const ranges = Array<RangeNode>(this.document.lineCount);
this.foldingRanges.forEach(range => { (this.foldingRanges ??= this.documentContext.foldingRange.values)
if (range.kind !== undefined) return; .forEach(range => range.kind ?? (ranges[range.start] = range));
ranges[range.start] = range; return this.symbols = foldingRangeToSymbols(ranges, 0, ranges.length);
});
return this.symbols = this.rangeToSymbols(ranges, 0, ranges.length);
}
private rangeToSymbols(ranges: RangeNode[], start: number, end: number) {
const symbols = <vscode.DocumentSymbol[]>[];
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 = this.rangeToSymbols(ranges, idx + 1, node.end);
symbols.push(symbol);
idx = node.end;
}
return symbols;
} }
constructor(private readonly documentContext: Document) {} constructor(private readonly documentContext: Document) {}
} }
type RangeNode = Optional<FoldingRange>; type RangeNode = Optional<FoldingRange>;
function foldingRangeToSymbols(ranges: readonly RangeNode[], start: number, end: number) {
const symbols = <vscode.DocumentSymbol[]>[];
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;
}

View File

@ -8,7 +8,6 @@
import * as child_process from 'child_process'; import * as child_process from 'child_process';
import * as htmlparser from 'node-html-parser'; import * as htmlparser from 'node-html-parser';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import Logger from './logger';
/** /**
* Open a prompt with two buttons, "Confirm" and "Cancel", and wait for user action. * Open a prompt with two buttons, "Confirm" and "Cancel", and wait for user action.
@ -32,14 +31,12 @@ export async function prompt(message: string, confirm: string, error = false) {
* @returns The output data, or `undefined` if execution fails. * @returns The output data, or `undefined` if execution fails.
*/ */
export function exec(path: string, args: string[], maxBuffer: number) { export function exec(path: string, args: string[], maxBuffer: number) {
return new Promise<Optional<string>>(resolve => { return new Promise<ExecResult>(resolve => {
child_process.execFile(path, args, { maxBuffer: maxBuffer }, (error, stdout, stderr) => { child_process.execFile(path, args, { maxBuffer: maxBuffer }, (error, stdout, stderr) => {
if (error) { if (error) {
Logger.log(stderr ? stderr : error.message); resolve({ error: stderr ? stderr : error.message });
resolve(undefined);
} else { } else {
stderr && Logger.log(stderr); resolve({ data: stdout, error: stderr });
resolve(stdout);
} }
}); });
}); });
@ -65,4 +62,6 @@ export function transformHtmlImageUri(htmlCode: string, transformer: (src: strin
export type Optional<T> = T | undefined; export type Optional<T> = T | undefined;
export type ExecResult = { data?: string, error: string };
export type Range = { start: number, end: number }; export type Range = { start: number, end: number };