@kurrawongai/codemirror-lang-turtle12 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -7
- package/dist/index.cjs +79 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +77 -16
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts +16 -0
- package/package.json +2 -1
- package/src/highlight.ts +12 -0
- package/src/index.ts +42 -4
- package/src/lint.ts +53 -0
- package/src/turtle.grammar +0 -0
package/src/index.ts
CHANGED
|
@@ -14,10 +14,13 @@ import { parser as rawParser } from './turtle.grammar';
|
|
|
14
14
|
import { turtleHighlighting } from './highlight';
|
|
15
15
|
import { turtleCompletionSource } from './complete';
|
|
16
16
|
import type { CompletionOptions } from './complete';
|
|
17
|
+
import { parseErrorLinter } from './lint';
|
|
17
18
|
|
|
18
19
|
export type { CompletionOptions, PrefixMap, PrefixSource } from './complete';
|
|
19
20
|
export { documentPrefixes, knownPrefixes, turtleCompletionSource } from './complete';
|
|
20
21
|
export { turtleHighlighting } from './highlight';
|
|
22
|
+
export { parseErrorLinter, parseErrors } from './lint';
|
|
23
|
+
export type { ParseError } from './lint';
|
|
21
24
|
|
|
22
25
|
/** The unconfigured grammar, for callers that want their own `top` or props. */
|
|
23
26
|
export const parser: LRParser = rawParser;
|
|
@@ -46,7 +49,20 @@ const statementFold = (node: SyntaxNode, state: EditorState) => {
|
|
|
46
49
|
};
|
|
47
50
|
|
|
48
51
|
/**
|
|
49
|
-
*
|
|
52
|
+
* Fold an RDF Patch transaction from the end of its `TX .` row to the start of
|
|
53
|
+
* its `TC .` or `TA .` row, so both rows stay visible.
|
|
54
|
+
*/
|
|
55
|
+
const transactionFold = (node: SyntaxNode, state: EditorState) => {
|
|
56
|
+
const begin = node.getChild('TxBegin');
|
|
57
|
+
const end = node.getChild('TxCommit') ?? node.getChild('TxAbort');
|
|
58
|
+
if (!begin || !end) return null;
|
|
59
|
+
const from = state.doc.lineAt(begin.to).to;
|
|
60
|
+
const to = state.doc.lineAt(end.from).from - 1;
|
|
61
|
+
return to > from ? { from, to } : null;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Highlighting, folding and indentation, shared by all five dialects.
|
|
50
66
|
*
|
|
51
67
|
* Exported so a consumer can re-configure the parser with props of their own —
|
|
52
68
|
* extra `styleTags`, say, to tag the three RDF 1.2 constructs' regions apart
|
|
@@ -64,6 +80,7 @@ export const turtleProps = [
|
|
|
64
80
|
ReifiedTriple: delimitedIndent({ closing: '>>' }),
|
|
65
81
|
}),
|
|
66
82
|
foldNodeProp.add({
|
|
83
|
+
Transaction: transactionFold,
|
|
67
84
|
BlankNodePropertyList: foldInside,
|
|
68
85
|
Collection: foldInside,
|
|
69
86
|
WrappedGraph: foldInside,
|
|
@@ -78,9 +95,17 @@ const languageData = {
|
|
|
78
95
|
indentOnInput: /^\s*[\]})]$/,
|
|
79
96
|
};
|
|
80
97
|
|
|
98
|
+
const LANGUAGE_NAMES: Record<string, string> = {
|
|
99
|
+
TurtleDoc: 'turtle',
|
|
100
|
+
TrigDoc: 'trig',
|
|
101
|
+
NTriplesDoc: 'ntriples',
|
|
102
|
+
NQuadsDoc: 'nquads',
|
|
103
|
+
RdfPatchDoc: 'rdfpatch',
|
|
104
|
+
};
|
|
105
|
+
|
|
81
106
|
function define(top: string) {
|
|
82
107
|
return LRLanguage.define({
|
|
83
|
-
name: top
|
|
108
|
+
name: LANGUAGE_NAMES[top],
|
|
84
109
|
parser: rawParser.configure({ top, props: turtleProps }),
|
|
85
110
|
languageData,
|
|
86
111
|
});
|
|
@@ -90,10 +115,12 @@ export const turtleLanguage = define('TurtleDoc');
|
|
|
90
115
|
export const trigLanguage = define('TrigDoc');
|
|
91
116
|
export const ntriplesLanguage = define('NTriplesDoc');
|
|
92
117
|
export const nquadsLanguage = define('NQuadsDoc');
|
|
118
|
+
export const rdfPatchLanguage = define('RdfPatchDoc');
|
|
93
119
|
|
|
94
120
|
function support(language: LRLanguage, options: CompletionOptions, dialect: 'turtle' | 'trig') {
|
|
95
121
|
return new LanguageSupport(language, [
|
|
96
122
|
language.data.of({ autocomplete: turtleCompletionSource(options, dialect) }),
|
|
123
|
+
parseErrorLinter(),
|
|
97
124
|
]);
|
|
98
125
|
}
|
|
99
126
|
|
|
@@ -115,10 +142,21 @@ export function trig(options: CompletionOptions = {}): LanguageSupport {
|
|
|
115
142
|
* an editor that colours it as valid is lying about the file it will write.
|
|
116
143
|
*/
|
|
117
144
|
export function ntriples(): LanguageSupport {
|
|
118
|
-
return new LanguageSupport(ntriplesLanguage);
|
|
145
|
+
return new LanguageSupport(ntriplesLanguage, parseErrorLinter());
|
|
119
146
|
}
|
|
120
147
|
|
|
121
148
|
/** RDF 1.2 N-Quads. */
|
|
122
149
|
export function nquads(): LanguageSupport {
|
|
123
|
-
return new LanguageSupport(nquadsLanguage);
|
|
150
|
+
return new LanguageSupport(nquadsLanguage, parseErrorLinter());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* RDF Patch (https://afs.github.io/rdf-delta/rdf-patch.html).
|
|
155
|
+
*
|
|
156
|
+
* Rows take N-Quads terms. `A` and `PA` rows are tagged `inserted`, and `D` and
|
|
157
|
+
* `PD` rows `deleted`, so a style can show a patch as a diff. A transaction
|
|
158
|
+
* folds between its `TX .` and `TC .` rows.
|
|
159
|
+
*/
|
|
160
|
+
export function rdfPatch(): LanguageSupport {
|
|
161
|
+
return new LanguageSupport(rdfPatchLanguage, parseErrorLinter());
|
|
124
162
|
}
|
package/src/lint.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ensureSyntaxTree } from '@codemirror/language';
|
|
2
|
+
import { linter } from '@codemirror/lint';
|
|
3
|
+
import type { EditorState, Extension } from '@codemirror/state';
|
|
4
|
+
|
|
5
|
+
export interface ParseError {
|
|
6
|
+
from: number;
|
|
7
|
+
to: number;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* How long to spend completing the parse before reporting errors. A partial
|
|
13
|
+
* tree ends in error nodes that are not in the document, so nothing is
|
|
14
|
+
* reported until the parse reaches the end.
|
|
15
|
+
*/
|
|
16
|
+
const PARSE_TIMEOUT_MS = 1000;
|
|
17
|
+
|
|
18
|
+
/** Whitespace and `#` comments: what may separate error nodes in one run. */
|
|
19
|
+
const GAP = /^(?:\s|#.*)*$/;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Every parse error in the document.
|
|
23
|
+
*
|
|
24
|
+
* The parser recovers by skipping one token at a time, so `this is a test`
|
|
25
|
+
* produces four error nodes. Nodes separated only by whitespace or comments are
|
|
26
|
+
* merged into one error.
|
|
27
|
+
*/
|
|
28
|
+
export function parseErrors(state: EditorState): ParseError[] {
|
|
29
|
+
const tree = ensureSyntaxTree(state, state.doc.length, PARSE_TIMEOUT_MS);
|
|
30
|
+
if (!tree) return [];
|
|
31
|
+
const runs: { from: number; to: number }[] = [];
|
|
32
|
+
tree.iterate({
|
|
33
|
+
enter: (node) => {
|
|
34
|
+
if (!node.type.isError) return;
|
|
35
|
+
const last = runs[runs.length - 1];
|
|
36
|
+
if (last && (node.from <= last.to || GAP.test(state.sliceDoc(last.to, node.from))))
|
|
37
|
+
last.to = Math.max(last.to, node.to);
|
|
38
|
+
else runs.push({ from: node.from, to: node.to });
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return runs.map(({ from, to }) => ({ from, to, message: errorMessage(state.sliceDoc(from, to)) }));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function errorMessage(text: string): string {
|
|
45
|
+
const shown = text.replace(/\s+/g, ' ').trim();
|
|
46
|
+
if (!shown) return 'Syntax error.';
|
|
47
|
+
return `Unexpected “${shown.length > 32 ? `${shown.slice(0, 32)}…` : shown}”.`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Underline every parse error. Included in `turtle()`, `trig()`, `ntriples()`, `nquads()` and `rdfPatch()`. */
|
|
51
|
+
export function parseErrorLinter(): Extension {
|
|
52
|
+
return linter((view) => parseErrors(view.state).map((error) => ({ ...error, severity: 'error' })));
|
|
53
|
+
}
|
package/src/turtle.grammar
CHANGED
|
Binary file
|