Refactor code.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
CismonX 2021-08-12 01:23:04 +08:00
parent b3b9644af0
commit 3ecb2a5b03
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
16 changed files with 156 additions and 111 deletions

View File

@ -28,8 +28,8 @@ import { prompt } from './utils/misc';
* Manage mappings between Texinfo documents and corresponding * Manage mappings between Texinfo documents and corresponding
* document-specific contexts. * document-specific contexts.
*/ */
export default class ContextMapping implements vscode.Disposable { export default class ContextMapping implements vscode.Disposable
{
/** /**
* Get context of a Texinfo document. Create one if not exists. * Get context of a Texinfo document. Create one if not exists.
* *

View File

@ -28,8 +28,8 @@ import PreviewContext from './preview';
/** /**
* Holds all contexts for a Texinfo document. * Holds all contexts for a Texinfo document.
*/ */
export default class DocumentContext { export default class DocumentContext
{
readonly foldingRange = new FoldingRangeContext(this); readonly foldingRange = new FoldingRangeContext(this);
readonly documentSymbol = new DocumentSymbolContext(this); readonly documentSymbol = new DocumentSymbolContext(this);

View File

@ -27,8 +27,8 @@ import { FoldingRange, Optional } from '../utils/types';
/** /**
* Context for symbols in a Texinfo document. * Context for symbols in a Texinfo document.
*/ */
export default class DocumentSymbolContext { export default class DocumentSymbolContext
{
get documentSymbols() { get documentSymbols() {
return this._documentSymbols ??= this._calculcateDocumentSymbols(); return this._documentSymbols ??= this._calculcateDocumentSymbols();
} }
@ -63,11 +63,16 @@ function foldingRangeToSymbols(
const symbols = <vscode.DocumentSymbol[]>[]; const symbols = <vscode.DocumentSymbol[]>[];
for (let idx = start; idx < end; ++idx) { for (let idx = start; idx < end; ++idx) {
const node = ranges[idx]; const node = ranges[idx];
if (node === undefined) continue; if (node === undefined) {
const range = lineNumToRange(idx, node.end); continue;
const selectionRange = lineNumToRange(idx); }
const symbol = new vscode.DocumentSymbol('@' + node.name, node.detail, const symbol = new vscode.DocumentSymbol(
vscode.SymbolKind.String, range, selectionRange); '@' + node.name,
node.detail,
vscode.SymbolKind.String,
lineNumToRange(idx, node.end), // full range
lineNumToRange(idx), // selection range
);
symbol.children = foldingRangeToSymbols(ranges, idx + 1, node.end); symbol.children = foldingRangeToSymbols(ranges, idx + 1, node.end);
symbols.push(symbol); symbols.push(symbol);
idx = node.end; idx = node.end;

View File

@ -30,8 +30,8 @@ import DocumentContext from './document';
* Actually, more than folding ranges (e.g. code lens) is handled within * Actually, more than folding ranges (e.g. code lens) is handled within
* this context, so perhaps we should use another name... * this context, so perhaps we should use another name...
*/ */
export default class FoldingRangeContext { export default class FoldingRangeContext
{
/** /**
* Get VSCode folding ranges from the context. * Get VSCode folding ranges from the context.
*/ */
@ -129,7 +129,9 @@ export default class FoldingRangeContext {
let verbatim = false; let verbatim = false;
for (let idx = lastLine; idx >= 0; --idx) { for (let idx = lastLine; idx >= 0; --idx) {
const line = this._document.lineAt(idx).text.trimLeft(); const line = this._document.lineAt(idx).text.trimLeft();
if (!line.startsWith('@')) continue; if (!line.startsWith('@')) {
continue;
}
if (!verbatim) { if (!verbatim) {
if (line === '@bye') { if (line === '@bye') {
lastLine = idx; lastLine = idx;
@ -139,7 +141,9 @@ export default class FoldingRangeContext {
this._clearTemporaries(); this._clearTemporaries();
continue; continue;
} }
if (this._processComment(line, idx)) continue; if (this._processComment(line, idx)) {
continue;
}
} }
// Process block. // Process block.
if (line.startsWith('@end ')) { if (line.startsWith('@end ')) {
@ -151,9 +155,13 @@ export default class FoldingRangeContext {
closingBlocks.push({ name: name, line: idx }); closingBlocks.push({ name: name, line: idx });
continue; continue;
} }
if (!verbatim && this._processNode(line, idx, lastLine)) continue; if (!verbatim && this._processNode(line, idx, lastLine)) {
continue;
}
const closingBlock = closingBlocks.pop(); const closingBlock = closingBlocks.pop();
if (closingBlock === undefined) continue; if (closingBlock === undefined) {
continue;
}
const name = closingBlock.name; const name = closingBlock.name;
if (line.substring(1, name.length + 2).trim() === name) { if (line.substring(1, name.length + 2).trim() === name) {
this._addRange(idx, closingBlock.line, { name: name }); this._addRange(idx, closingBlock.line, { name: name });
@ -165,8 +173,11 @@ export default class FoldingRangeContext {
} }
} }
if (this._commentRange !== undefined) { if (this._commentRange !== undefined) {
this._addRange(this._commentRange.start, this._commentRange.end, this._addRange(
{ kind: vscode.FoldingRangeKind.Comment }); this._commentRange.start,
this._commentRange.end,
{ kind: vscode.FoldingRangeKind.Comment },
);
} }
return this._foldingRanges; return this._foldingRanges;
} }

View File

@ -28,8 +28,8 @@ import { getNodeHtmlRef, prompt } from '../utils/misc';
/** /**
* Stores information of a Texinfo document preview. * Stores information of a Texinfo document preview.
*/ */
export default class PreviewContext { export default class PreviewContext
{
close() { close() {
this._disposables.forEach(event => event.dispose()); this._disposables.forEach(event => event.dispose());
this._panel.dispose(); this._panel.dispose();
@ -57,11 +57,16 @@ export default class PreviewContext {
// Inform the user that the preview is updating, when `makeinfo` // Inform the user that the preview is updating, when `makeinfo`
// takes too long. // takes too long.
setTimeout(() => this._updating && this._updateTitle(), 500); setTimeout(() => this._updating && this._updateTitle(), 500);
const initFile = this._globalContext.path + '/ext/html-preview.pm'; const converter = new Converter(
const converter = new Converter(this._document.fileName, initFile, this._document.fileName,
this._globalContext.options, this._logger); this._globalContext.path + '/ext/html-preview.pm',
const { data, error } = await converter this._globalContext.options,
.toHTML(path => this._webview.asWebviewUri(path), this._script); this._logger,
);
const { data, error } = await converter.toHTML(
path => this._webview.asWebviewUri(path),
this._script,
);
if (error) { if (error) {
this._logger.log(error); this._logger.log(error);
this._diagnosis.update(this._document, error); this._diagnosis.update(this._document, error);
@ -86,8 +91,11 @@ export default class PreviewContext {
retainContextWhenHidden: true, retainContextWhenHidden: true,
enableScripts: true, enableScripts: true,
}; };
this._panel = vscode.window.createWebviewPanel('texinfo.preview', '', this._panel = vscode.window.createWebviewPanel(
vscode.ViewColumn.Beside, options); 'texinfo.preview', '',
vscode.ViewColumn.Beside,
options,
);
this._webview = this._panel.webview; this._webview = this._panel.webview;
this._disposables.push(this._panel.onDidDispose(() => this.close())); this._disposables.push(this._panel.onDidDispose(() => this.close()));
this._updateTitle(); this._updateTitle();

View File

@ -26,8 +26,8 @@ import { isDefined } from './utils/types';
/** /**
* Manage diagnostic information of Texinfo documents. * Manage diagnostic information of Texinfo documents.
*/ */
export default class Diagnosis implements vscode.Disposable { export default class Diagnosis implements vscode.Disposable
{
/** /**
* Remove a document's diagnostic entry from the collection. * Remove a document's diagnostic entry from the collection.
* *
@ -68,10 +68,13 @@ export default class Diagnosis implements vscode.Disposable {
*/ */
function logToDiagnostic(lineText: string) { function logToDiagnostic(lineText: string) {
const lineNum = parseInt(lineText) - 1; const lineNum = parseInt(lineText) - 1;
// Ignore error that does not correspond a line in document. // Ignore error that does not correspond to a line in document.
if (isNaN(lineNum)) return undefined; if (isNaN(lineNum)) {
return undefined;
}
const message = lineText.substring(lineNum.toString().length + 2); const message = lineText.substring(lineNum.toString().length + 2);
const severity = message.startsWith('warning:') const severity = message.startsWith('warning:')
? vscode.DiagnosticSeverity.Warning : undefined; ? vscode.DiagnosticSeverity.Warning
: undefined;
return new vscode.Diagnostic(lineNumToRange(lineNum), message, severity); return new vscode.Diagnostic(lineNumToRange(lineNum), message, severity);
} }

View File

@ -33,14 +33,14 @@ import FoldingRangeProvider from './providers/folding_range';
/** /**
* Manage extension-level global-scope contexts. * Manage extension-level global-scope contexts.
*/ */
export default class GlobalContext { export default class GlobalContext
{
readonly contextMapping = new ContextMapping(this); readonly contextMapping = new ContextMapping(this);
readonly diagnosis = new Diagnosis; readonly diagnosis = new Diagnosis;
readonly indicator = new Indicator(this); readonly indicator = new Indicator(this);
readonly logger = new Logger; readonly logger = new Logger;
readonly path = this.context.extensionPath; readonly path = this._context.extensionPath;
/** /**
* Note: `Options`' no singleton. * Note: `Options`' no singleton.
@ -52,22 +52,35 @@ export default class GlobalContext {
} }
subscribe(...items: vscode.Disposable[]) { subscribe(...items: vscode.Disposable[]) {
this.context.subscriptions.push(...items); this._context.subscriptions.push(...items);
} }
constructor(private readonly context: vscode.ExtensionContext) { constructor(private readonly _context: vscode.ExtensionContext) {
this.subscribe( this.subscribe(
this.contextMapping, this.diagnosis, this.indicator, this.logger, this.contextMapping,
vscode.languages.registerCodeLensProvider('texinfo', this.diagnosis,
new CodeLensProvider(this)), this.indicator,
vscode.languages.registerCompletionItemProvider('texinfo', this.logger,
new CompletionItemProvider(this), '@'), vscode.languages.registerCodeLensProvider(
vscode.languages.registerDocumentSymbolProvider('texinfo', 'texinfo',
new DocumentSymbolProvider(this)), new CodeLensProvider(this),
vscode.languages.registerFoldingRangeProvider('texinfo', ),
new FoldingRangeProvider(this)), vscode.languages.registerCompletionItemProvider(
'texinfo',
new CompletionItemProvider(this),
'@',
),
vscode.languages.registerDocumentSymbolProvider(
'texinfo',
new DocumentSymbolProvider(this),
),
vscode.languages.registerFoldingRangeProvider(
'texinfo',
new FoldingRangeProvider(this),
),
vscode.workspace.onDidChangeConfiguration( vscode.workspace.onDidChangeConfiguration(
() => this._options = undefined), () => this._options = undefined,
),
); );
} }

View File

@ -26,8 +26,8 @@ import { exec } from './utils/misc';
/** /**
* Shows whether GNU Texinfo is properly installed and configured. * Shows whether GNU Texinfo is properly installed and configured.
*/ */
export default class Indicator implements vscode.Disposable { export default class Indicator implements vscode.Disposable
{
get canDisplayPreview() { get canDisplayPreview() {
return this._canDisplayPreview; return this._canDisplayPreview;
} }
@ -36,12 +36,15 @@ export default class Indicator implements vscode.Disposable {
this._statusBarItem.dispose(); this._statusBarItem.dispose();
} }
constructor(private readonly globalContext: GlobalContext) { constructor(private readonly _globalContext: GlobalContext) {
globalContext.subscribe( _globalContext.subscribe(
vscode.commands.registerCommand('texinfo.indicator.click', vscode.commands.registerCommand(
this._click.bind(this)), 'texinfo.indicator.click',
this._click.bind(this),
),
vscode.window.onDidChangeActiveTextEditor( vscode.window.onDidChangeActiveTextEditor(
this._refresh.bind(this)), this._refresh.bind(this),
),
); );
this._updateStatus() this._updateStatus()
.then(() => this._refresh(vscode.window.activeTextEditor)); .then(() => this._refresh(vscode.window.activeTextEditor));
@ -78,9 +81,9 @@ export default class Indicator implements vscode.Disposable {
* by checking `makeinfo --version`. * by checking `makeinfo --version`.
*/ */
private async _updateStatus() { private async _updateStatus() {
const options = this.globalContext.options; const options = this._globalContext.options;
const output = await exec(options.makeinfo, ['--version'], const output
options.maxSize); = await exec(options.makeinfo, ['--version'], options.maxSize);
const result = output.data?.match(/\(GNU texinfo\) (.*)\n/); const result = output.data?.match(/\(GNU texinfo\) (.*)\n/);
let tooltip = '', icon: string, version = ''; let tooltip = '', icon: string, version = '';
if (result && result[1]) { if (result && result[1]) {

View File

@ -24,11 +24,13 @@ import * as vscode from 'vscode';
/** /**
* Logger which prints message to VSCode output channel. * Logger which prints message to VSCode output channel.
*/ */
export default class Logger implements vscode.Disposable { export default class Logger implements vscode.Disposable
{
log(message: string) { log(message: string) {
const dateTime = new Date() const dateTime = new Date().toLocaleString(
.toLocaleString(undefined, { hour12: false }); undefined,
{ hour12: false }
);
this._outputChannel.appendLine(`[ ${dateTime} ]\n${message}`); this._outputChannel.appendLine(`[ ${dateTime} ]\n${message}`);
} }

View File

@ -26,8 +26,8 @@ import * as vscode from 'vscode';
* *
* See the `contributes.configuration` entry in package.json for details. * See the `contributes.configuration` entry in package.json for details.
*/ */
export default class Options { export default class Options
{
get enableSnippets() { get enableSnippets() {
return this._getBoolean('completion.enableSnippets'); return this._getBoolean('completion.enableSnippets');
} }

View File

@ -25,13 +25,17 @@ import GlobalContext from '../global_context';
/** /**
* Provide code lenses for Texinfo document. * Provide code lenses for Texinfo document.
*/ */
export default class CodeLensProvider implements vscode.CodeLensProvider { export default class CodeLensProvider implements vscode.CodeLensProvider
{
provideCodeLenses(document: vscode.TextDocument) { provideCodeLenses(document: vscode.TextDocument) {
if (!this._globalContext.options.enableCodeLens) return undefined; if (!this._globalContext.options.enableCodeLens) {
if (!this._globalContext.indicator.canDisplayPreview) return undefined; return undefined;
return this._globalContext.contextMapping.getDocumentContext(document) }
.foldingRange.nodeValues; if (!this._globalContext.indicator.canDisplayPreview) {
return undefined;
}
return this._globalContext.contextMapping
.getDocumentContext(document).foldingRange.nodeValues;
} }
constructor(private readonly _globalContext: GlobalContext) {} constructor(private readonly _globalContext: GlobalContext) {}

View File

@ -32,15 +32,16 @@ export default class CompletionItemProvider
/** /**
* Full list of completion items. * Full list of completion items.
* *
* Note: Descriptions of completion items for @-commands are excerpted from * Note: Descriptions of completion items for @-commands
* the GNU Texinfo manual * are excerpted from the [GNU Texinfo manual], which is licensed under the
* ({@link https://www.gnu.org/software/texinfo/manual/texinfo }), * GNU Free Documentation License, version 1.3.
* which is licensed under the GNU Free Documentation License, version 1.3.
* *
* According to GFDL, this usage is considered "aggregation with * According to GFDL, this usage is considered "aggregation with
* independent work", which means that GFDL applies to lines 48-367 of this * independent work", which means that GFDL applies to lines 50-1117 of
* file, while the remainder is under GPL like other source code files of * this file, while the remainder is under GPL like other source code files
* the project. * of the project.
*
* [GNU Texinfo manual]: https://gnu.org/software/texinfo/manual/texinfo
*/ */
private _getCompletionItems() { private _getCompletionItems() {
const enableSnippets = this._oldOptions.enableSnippets; const enableSnippets = this._oldOptions.enableSnippets;
@ -159,10 +160,6 @@ export default class CompletionItemProvider
'Center the line of text following the command', 'Center the line of text following the command',
'text-line', 'text-line',
), ),
...lineCommand('centerchap',
'Like @chapter, but centers the chapter title',
'text-line',
),
...lineCommand('chapheading', ...lineCommand('chapheading',
'Print an unnumbered chapter-like heading', 'Print an unnumbered chapter-like heading',
'title', 'title',
@ -245,11 +242,6 @@ export default class CompletionItemProvider
'Define a new index, print entries in a roman font', 'Define a new index, print entries in a roman font',
'index-name', 'index-name',
), ),
...lineCommand('definfoenclose',
'Create a new command for Info that marks text by enclosing ' +
'it in strings that precede and follow the text.',
'newcmd', 'before', 'after',
),
...lineCommandX('defivar', ...lineCommandX('defivar',
'Format a description for an instance variable ' + 'Format a description for an instance variable ' +
'in object-oriented programming', 'in object-oriented programming',
@ -612,10 +604,6 @@ export default class CompletionItemProvider
'Indicate text that is a URL', 'Indicate text that is a URL',
1, 'URL', 1, 'URL',
), ),
...braceCommand('inforef',
'Make a cross-reference to an Info file',
3, 'node-name', 'entry-name', 'info-file-name',
),
...braceCommand('inlinefmt', ...braceCommand('inlinefmt',
'Insert text only if the output format is fmt', 'Insert text only if the output format is fmt',
2, 'fmt', 'text', 2, 'fmt', 'text',
@ -820,10 +808,6 @@ export default class CompletionItemProvider
'any special text', 'any special text',
1, 'node', 'entry', 'node-title', 'info-node', 'manual', 1, 'node', 'entry', 'node-title', 'info-node', 'manual',
), ),
command('refill',
'Refill and indent the paragraph after all the ' +
'other processing has been done',
),
command('registeredsymbol', command('registeredsymbol',
'Generate the legal symbol, "®"', 'Generate the legal symbol, "®"',
{ hasEmptyBrace: true }, { hasEmptyBrace: true },
@ -1153,12 +1137,18 @@ export default class CompletionItemProvider
// Triggered in the middle of a word. // Triggered in the middle of a word.
if (context.triggerKind === vscode.CompletionTriggerKind.Invoke) { if (context.triggerKind === vscode.CompletionTriggerKind.Invoke) {
const wordRange = document.getWordRangeAtPosition(position); const wordRange = document.getWordRangeAtPosition(position);
if (wordRange === undefined) return undefined; if (wordRange === undefined) {
return undefined;
}
// Normalize position so that it can be treated as // Normalize position so that it can be treated as
// triggered by '@' character. // triggered by '@' character.
position = wordRange.start; const charBeforeWord = new vscode.Range(
const pos = new vscode.Range(position.translate(0, -1), position); wordRange.start.translate(0, -1),
if (document.getText(pos) !== '@') return undefined; wordRange.start,
);
if (document.getText(charBeforeWord) !== '@') {
return undefined;
}
} }
// Check whether options has changed. // Check whether options has changed.
const newOptions = this._globalContext.options; const newOptions = this._globalContext.options;
@ -1168,9 +1158,11 @@ export default class CompletionItemProvider
} }
if (position.character === 1) return this._getCompletionItems(); if (position.character === 1) return this._getCompletionItems();
// Check whether the '@' character is escaped. // Check whether the '@' character is escaped.
const pos = new vscode.Range( const secondCharBeforeWord = new vscode.Range(
position.translate(0, -2), position.translate(0, -1)); position.translate(0, -2),
if (document.getText(pos) === '@') { position.translate(0, -1)
);
if (document.getText(secondCharBeforeWord) === '@') {
return undefined; return undefined;
} else { } else {
return this._getCompletionItems(); return this._getCompletionItems();

View File

@ -29,8 +29,8 @@ export default class DocumentSymbolProvider
implements vscode.DocumentSymbolProvider implements vscode.DocumentSymbolProvider
{ {
provideDocumentSymbols(document: vscode.TextDocument) { provideDocumentSymbols(document: vscode.TextDocument) {
return this._globalContext.contextMapping.getDocumentContext(document) return this._globalContext.contextMapping
.documentSymbol.documentSymbols; .getDocumentContext(document).documentSymbol.documentSymbols;
} }
constructor(private readonly _globalContext: GlobalContext) {} constructor(private readonly _globalContext: GlobalContext) {}

View File

@ -29,8 +29,8 @@ export default class FoldingRangeProvider
implements vscode.FoldingRangeProvider implements vscode.FoldingRangeProvider
{ {
provideFoldingRanges(document: vscode.TextDocument) { provideFoldingRanges(document: vscode.TextDocument) {
return this._globalContext.contextMapping.getDocumentContext(document) return this._globalContext.contextMapping
.foldingRange.foldingRanges; .getDocumentContext(document).foldingRange.foldingRanges;
} }
constructor(private readonly _globalContext: GlobalContext) {} constructor(private readonly _globalContext: GlobalContext) {}

View File

@ -29,8 +29,8 @@ import { Operator } from './types';
/** /**
* Converter which converts file from Texinfo to other formats. * Converter which converts file from Texinfo to other formats.
*/ */
export default class Converter { export default class Converter
{
async toHTML(imgTransformer: Operator<vscode.Uri>, insertScript?: string) { async toHTML(imgTransformer: Operator<vscode.Uri>, insertScript?: string) {
const pathUri = vscode.Uri.file(path.dirname(this._path)); const pathUri = vscode.Uri.file(path.dirname(this._path));
const newPath = imgTransformer(pathUri).toString() + '/'; const newPath = imgTransformer(pathUri).toString() + '/';
@ -49,8 +49,11 @@ export default class Converter {
this._addIncludePaths(this._options.includePaths, options); this._addIncludePaths(this._options.includePaths, options);
this._defineVariables(this._options.variables, options); this._defineVariables(this._options.variables, options);
this._includeCustomCSS(this._options.customCSS, options); this._includeCustomCSS(this._options.customCSS, options);
return await exec(this._options.makeinfo, options.concat(this._path), return await exec(
this._options.maxSize); this._options.makeinfo,
options.concat(this._path),
this._options.maxSize,
);
} }
constructor( constructor(

View File

@ -112,6 +112,7 @@ export function getNodeHtmlRef(nodeName: string) {
.join('')) .join(''))
.join('-'); .join('-');
const firstCharCode = result.charCodeAt(0); const firstCharCode = result.charCodeAt(0);
return isAlpha(firstCharCode) ? result : 'g_t_00' return isAlpha(firstCharCode)
+ firstCharCode.toString(16) + result.substring(1); ? result
: 'g_t_00' + firstCharCode.toString(16) + result.substring(1);
} }