Implement diagnosis.
This commit is contained in:
parent
c52c7b5924
commit
2c5aec48ed
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* diagnosis.ts
|
||||||
|
*
|
||||||
|
* @author CismonX <admin@cismon.net>
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import { isDefined, lineNumToRange } from './utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage diagnostic information of Texinfo documents.
|
||||||
|
*/
|
||||||
|
export default class Diagnosis implements vscode.Disposable {
|
||||||
|
|
||||||
|
private static singleton?: Diagnosis;
|
||||||
|
|
||||||
|
static get instance() {
|
||||||
|
return Diagnosis.singleton ??= new Diagnosis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly diagnostics = vscode.languages.createDiagnosticCollection('texinfo');
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this.diagnostics.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate diagnostic information based on error log from `makeinfo`.
|
||||||
|
*
|
||||||
|
* @param document
|
||||||
|
* @param logText
|
||||||
|
*/
|
||||||
|
update(document: vscode.TextDocument, logText: string) {
|
||||||
|
const fileName = document.uri.path;
|
||||||
|
const diagnostics = logText.split('\n').filter(line => line.startsWith(fileName))
|
||||||
|
.map(line => logLineToDiagnostic(line.substring(fileName.length + 1))).filter(isDefined);
|
||||||
|
this.diagnostics.set(document.uri, diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(document: vscode.TextDocument) {
|
||||||
|
this.diagnostics.delete(document.uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function logLineToDiagnostic(lineText: string) {
|
||||||
|
const lineNum = Number.parseInt(lineText) - 1;
|
||||||
|
// Ignore error that does not correspond a line.
|
||||||
|
if (Number.isNaN(lineNum)) return undefined;
|
||||||
|
const message = lineText.substring(lineNum.toString().length + 2);
|
||||||
|
const severity = message.startsWith('warning:') ? vscode.DiagnosticSeverity.Warning : undefined;
|
||||||
|
return new vscode.Diagnostic(lineNumToRange(lineNum), message, severity);
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import Diagnosis from './diagnosis';
|
||||||
import Document from './document';
|
import Document from './document';
|
||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
import Options from './options';
|
import Options from './options';
|
||||||
|
@ -25,6 +26,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||||
vscode.languages.registerCompletionItemProvider('texinfo', new CompletionItemProvider(), '@'),
|
vscode.languages.registerCompletionItemProvider('texinfo', new CompletionItemProvider(), '@'),
|
||||||
vscode.languages.registerFoldingRangeProvider('texinfo', new FoldingRangeProvider()),
|
vscode.languages.registerFoldingRangeProvider('texinfo', new FoldingRangeProvider()),
|
||||||
vscode.languages.registerDocumentSymbolProvider('texinfo', new DocumentSymbolProvider()),
|
vscode.languages.registerDocumentSymbolProvider('texinfo', new DocumentSymbolProvider()),
|
||||||
|
Diagnosis.instance,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import Converter from './converter';
|
import Converter from './converter';
|
||||||
|
import Diagnosis from './diagnosis';
|
||||||
import Document from './document';
|
import Document from './document';
|
||||||
import Logger from './logger';
|
import Logger from './logger';
|
||||||
import Options from './options';
|
import Options from './options';
|
||||||
|
@ -70,6 +71,8 @@ export default class Preview {
|
||||||
this.disposables.forEach(event => event.dispose());
|
this.disposables.forEach(event => event.dispose());
|
||||||
this.panel.dispose();
|
this.panel.dispose();
|
||||||
this.documentContext.closePreview();
|
this.documentContext.closePreview();
|
||||||
|
// Only show diagnostic information when the preview is active.
|
||||||
|
Diagnosis.instance.delete(this.document);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateWebview() {
|
async updateWebview() {
|
||||||
|
@ -81,20 +84,23 @@ export default class Preview {
|
||||||
this.pendingUpdate = false;
|
this.pendingUpdate = false;
|
||||||
// Inform the user that the preview is updating if `makeinfo` takes too long.
|
// Inform the user that the preview is updating if `makeinfo` takes too long.
|
||||||
setTimeout(() => this.updating && this.updateTitle(), 500);
|
setTimeout(() => this.updating && this.updateTitle(), 500);
|
||||||
let htmlCode = await Converter.convertToHtml(this.document.fileName);
|
const { data, error } = await Converter.convertToHtml(this.document.fileName);
|
||||||
if (htmlCode === undefined) {
|
if (error) {
|
||||||
|
Logger.instance.log(error);
|
||||||
|
Diagnosis.instance.update(this.document, error);
|
||||||
|
}
|
||||||
|
if (data === undefined) {
|
||||||
prompt(`Failed to show preview for ${this.document.fileName}.`, 'Show log', true)
|
prompt(`Failed to show preview for ${this.document.fileName}.`, 'Show log', true)
|
||||||
.then(result => result && Logger.show());
|
.then(result => result && Logger.instance.show());
|
||||||
|
} else if (Options.displayImage) {
|
||||||
|
const pathName = path.dirname(this.document.fileName);
|
||||||
|
// To display images in webviews, image URIs in HTML should be converted to VSCode-recognizable ones.
|
||||||
|
this.panel.webview.html = transformHtmlImageUri(data, src => {
|
||||||
|
const srcUri = vscode.Uri.file(pathName + '/' + src);
|
||||||
|
return this.panel.webview.asWebviewUri(srcUri).toString();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
if (Options.displayImage) {
|
this.panel.webview.html = data;
|
||||||
const pathName = path.dirname(this.document.fileName);
|
|
||||||
// To display images in webviews, image URIs in HTML should be converted to VSCode-recognizable ones.
|
|
||||||
htmlCode = transformHtmlImageUri(htmlCode, src => {
|
|
||||||
const srcUri = vscode.Uri.file(pathName + '/' + src);
|
|
||||||
return this.panel.webview.asWebviewUri(srcUri).toString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.panel.webview.html = htmlCode;
|
|
||||||
}
|
}
|
||||||
this.updating = false;
|
this.updating = false;
|
||||||
this.updateTitle();
|
this.updateTitle();
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import Document from './document';
|
import Document from './document';
|
||||||
import { FoldingRange } from './folding';
|
import { FoldingRange } from './folding';
|
||||||
import { Optional } from './utils';
|
import { lineNumToRange, Optional } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide document symbol information for Texinfo documents.
|
* Provide document symbol information for Texinfo documents.
|
||||||
|
@ -60,11 +60,8 @@ function foldingRangeToSymbols(ranges: readonly RangeNode[], start: number, end:
|
||||||
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) continue;
|
||||||
const startPosition = new vscode.Position(idx, 0);
|
const range = lineNumToRange(idx, node.end);
|
||||||
const endFirstLine = new vscode.Position(idx, Number.MAX_SAFE_INTEGER);
|
const selectionRange = lineNumToRange(idx);
|
||||||
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,
|
const symbol = new vscode.DocumentSymbol('@' + node.name, node.detail,
|
||||||
vscode.SymbolKind.String, range, selectionRange);
|
vscode.SymbolKind.String, range, selectionRange);
|
||||||
symbol.children = foldingRangeToSymbols(ranges, idx + 1, node.end);
|
symbol.children = foldingRangeToSymbols(ranges, idx + 1, node.end);
|
||||||
|
|
16
src/utils.ts
16
src/utils.ts
|
@ -60,6 +60,22 @@ export function transformHtmlImageUri(htmlCode: string, transformer: (src: strin
|
||||||
return elements.length === 0 ? htmlCode : dom.outerHTML;
|
return elements.length === 0 ? htmlCode : dom.outerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert line numbers to VSCode range.
|
||||||
|
*
|
||||||
|
* @param startLine
|
||||||
|
* @param endLine Default to `startLine`.
|
||||||
|
*/
|
||||||
|
export function lineNumToRange(startLine: number, endLine = startLine) {
|
||||||
|
const startPosition = new vscode.Position(startLine, 0);
|
||||||
|
const endPosition = new vscode.Position(endLine, Number.MAX_SAFE_INTEGER);
|
||||||
|
return new vscode.Range(startPosition, endPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDefined<T>(value: T | undefined): value is T {
|
||||||
|
return value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export type Optional<T> = T | undefined;
|
export type Optional<T> = T | undefined;
|
||||||
|
|
||||||
export type ExecResult = { data?: string, error: string };
|
export type ExecResult = { data?: string, error: string };
|
||||||
|
|
Loading…
Reference in New Issue