Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | 6x 6x 6x 6x 2x 1x 6x 4x 4x 1x 1x 3x 3x 1x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import * as vscode from 'vscode';
import { compile, isTemplate, TriplateError } from 'triplate';
import { TRIPLATE_LANGUAGE_IDS } from '@src/services/document/document-languages';
import { KeyedDebouncer } from '@src/utilities/debounce';
/**
* Publishes diagnostics for triplate template syntax errors by running the triplate
* compiler over template documents. The language server strips triplate frontmatter
* before validating the underlying SPARQL/Turtle, so triplate's own parser errors
* would otherwise go unreported.
*/
export class TriplateDiagnosticProvider implements vscode.Disposable {
private readonly _collection: vscode.DiagnosticCollection;
private readonly _subscriptions: vscode.Disposable[] = [];
/**
* Debounces per-edit validation: `compile()` runs a full template parse and
* would otherwise execute on every keystroke in every open document.
*/
private readonly _changeDebouncer = new KeyedDebouncer<string>(300);
constructor() {
this._collection = vscode.languages.createDiagnosticCollection('triplate');
this._subscriptions.push(
this._collection,
this._changeDebouncer,
vscode.workspace.onDidOpenTextDocument(doc => this._validate(doc)),
vscode.workspace.onDidChangeTextDocument(e => {
// Every document change is reported here, including those of output channels
// and large files in other languages; only template languages are validated.
if (TRIPLATE_LANGUAGE_IDS.has(e.document.languageId)) {
this._changeDebouncer.schedule(e.document.uri.toString(), () => this._validate(e.document));
}
}),
vscode.workspace.onDidCloseTextDocument(doc => {
this._changeDebouncer.cancel(doc.uri.toString());
this._collection.delete(doc.uri);
}),
);
for (const doc of vscode.workspace.textDocuments) {
this._validate(doc);
}
}
private _validate(document: vscode.TextDocument): void {
// Check the language before reading the text, which copies the whole document.
if (!TRIPLATE_LANGUAGE_IDS.has(document.languageId)) {
this._collection.delete(document.uri);
return;
}
const text = document.getText();
if (!isTemplate(text)) {
this._collection.delete(document.uri);
} else {
try {
compile(text);
this._collection.delete(document.uri);
} catch (error) {
this._collection.set(document.uri, [this._toDiagnostic(document, error)]);
}
}
}
private _toDiagnostic(document: vscode.TextDocument, error: unknown): vscode.Diagnostic {
const message = error instanceof Error ? error.message : 'Failed to compile triplate template.';
const range = this._toRange(document, error);
const diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error);
diagnostic.source = 'triplate';
return diagnostic;
}
private _toRange(document: vscode.TextDocument, error: unknown): vscode.Range {
// TriplateError carries 1-based line/column for the offending location.
Eif (error instanceof TriplateError && error.line !== undefined) {
const line = error.line - 1;
const character = (error.column ?? 1) - 1;
Eif (line >= 0 && line < document.lineCount) {
const endCharacter = Math.max(character + 1, document.lineAt(line).range.end.character);
return new vscode.Range(line, character, line, endCharacter);
}
}
return new vscode.Range(0, 0, 0, 0);
}
dispose(): void {
for (const sub of this._subscriptions) {
sub.dispose();
}
}
}
|