@matdata/yasqe 5.20.3 → 6.1.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/CHANGELOG.md +1 -1
- package/README.md +76 -59
- package/build/prefixes.json +2169 -2115
- package/build/ts/grammar/tokenizer.d.ts +9 -3
- package/build/ts/grammar/tokenizer.js +34 -6
- package/build/ts/grammar/tokenizer.js.map +1 -1
- package/build/ts/src/autocompleters/index.d.ts +4 -2
- package/build/ts/src/autocompleters/index.js +9 -33
- package/build/ts/src/autocompleters/index.js.map +1 -1
- package/build/ts/src/autocompleters/variables.js +17 -19
- package/build/ts/src/autocompleters/variables.js.map +1 -1
- package/build/ts/src/defaults.d.ts +17 -53
- package/build/ts/src/defaults.js +2 -5
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/editor/autocompletion.d.ts +32 -0
- package/build/ts/src/editor/autocompletion.js +94 -0
- package/build/ts/src/editor/autocompletion.js.map +1 -0
- package/build/ts/src/editor/doc.d.ts +32 -0
- package/build/ts/src/editor/doc.js +121 -0
- package/build/ts/src/editor/doc.js.map +1 -0
- package/build/ts/src/editor/facade.d.ts +94 -0
- package/build/ts/src/editor/facade.js +392 -0
- package/build/ts/src/editor/facade.js.map +1 -0
- package/build/ts/src/editor/folding.d.ts +16 -0
- package/build/ts/src/editor/folding.js +190 -0
- package/build/ts/src/editor/folding.js.map +1 -0
- package/build/ts/src/editor/gutters.d.ts +8 -0
- package/build/ts/src/editor/gutters.js +113 -0
- package/build/ts/src/editor/gutters.js.map +1 -0
- package/build/ts/src/editor/keymap.d.ts +11 -0
- package/build/ts/src/editor/keymap.js +88 -0
- package/build/ts/src/editor/keymap.js.map +1 -0
- package/build/ts/src/editor/language.d.ts +11 -0
- package/build/ts/src/editor/language.js +19 -0
- package/build/ts/src/editor/language.js.map +1 -0
- package/build/ts/src/editor/tokenizerRunner.d.ts +37 -0
- package/build/ts/src/editor/tokenizerRunner.js +115 -0
- package/build/ts/src/editor/tokenizerRunner.js.map +1 -0
- package/build/ts/src/index.d.ts +49 -100
- package/build/ts/src/index.js +179 -100
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/prefixes.json +2191 -2137
- package/build/ts/src/tooltip.js +9 -6
- package/build/ts/src/tooltip.js.map +1 -1
- package/build/yasqe.min.css +1 -1
- package/build/yasqe.min.css.map +3 -3
- package/build/yasqe.min.js +76 -91
- package/build/yasqe.min.js.map +4 -4
- package/grammar/tokenizer.ts +62 -8
- package/package.json +11 -6
- package/src/autocompleters/index.ts +24 -53
- package/src/autocompleters/show-hint.scss +17 -9
- package/src/autocompleters/variables.ts +20 -24
- package/src/defaults.ts +2 -6
- package/src/editor/autocompletion.ts +147 -0
- package/src/editor/doc.ts +151 -0
- package/src/editor/facade.ts +558 -0
- package/src/editor/folding.ts +225 -0
- package/src/editor/gutters.ts +152 -0
- package/src/editor/keymap.ts +133 -0
- package/src/editor/language.ts +47 -0
- package/src/editor/tokenizerRunner.ts +198 -0
- package/src/index.ts +193 -127
- package/src/prefixes.json +2169 -2115
- package/src/scss/buttons.scss +4 -2
- package/src/scss/codemirrorMods.scss +107 -11
- package/src/scss/yasqe.scss +4 -2
- package/src/tooltip.ts +16 -13
- package/build/ts/src/CodeMirror.d.ts +0 -30
- package/build/ts/src/CodeMirror.js +0 -20
- package/build/ts/src/CodeMirror.js.map +0 -1
- package/build/ts/src/prefixFold.d.ts +0 -8
- package/build/ts/src/prefixFold.js +0 -87
- package/build/ts/src/prefixFold.js.map +0 -1
- package/src/CodeMirror.ts +0 -54
- package/src/prefixFold.ts +0 -94
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs the SPARQL stream tokenizer over a CodeMirror 6 document and exposes
|
|
3
|
+
* CodeMirror 5 style token information (`getTokenAt`, `getTokenTypeAt`, `runMode`).
|
|
4
|
+
*
|
|
5
|
+
* CodeMirror 6 does not expose the stream-parser state of a `StreamLanguage`, but Yasqe relies
|
|
6
|
+
* heavily on that state (possible next tokens for autocompletion, declared prefixes/variables,
|
|
7
|
+
* syntax error information, query type detection, ...). This runner keeps a per-line cache of
|
|
8
|
+
* tokenizer states so that repeated lookups stay cheap.
|
|
9
|
+
*/
|
|
10
|
+
import { StringStream, StreamParser } from "@codemirror/language";
|
|
11
|
+
import type { Text } from "@codemirror/state";
|
|
12
|
+
import { State as TokenizerState, copyState } from "../../grammar/tokenizer";
|
|
13
|
+
export type { TokenizerState };
|
|
14
|
+
import { createSparqlParser } from "./language";
|
|
15
|
+
|
|
16
|
+
/** CodeMirror 5 compatible token shape. `line` and `ch` are 0-based. */
|
|
17
|
+
export interface Token {
|
|
18
|
+
start: number;
|
|
19
|
+
end: number;
|
|
20
|
+
string: string;
|
|
21
|
+
type: string | null;
|
|
22
|
+
state: TokenizerState;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface Position {
|
|
26
|
+
line: number;
|
|
27
|
+
ch: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TokenizerRunnerOptions {
|
|
31
|
+
tabSize?: number;
|
|
32
|
+
indentUnit?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type RunModeCallback = (
|
|
36
|
+
text: string,
|
|
37
|
+
style: string | null,
|
|
38
|
+
line: number,
|
|
39
|
+
col: number,
|
|
40
|
+
state: TokenizerState,
|
|
41
|
+
) => void;
|
|
42
|
+
|
|
43
|
+
export class TokenizerRunner {
|
|
44
|
+
private parser: StreamParser<TokenizerState>;
|
|
45
|
+
private tabSize: number;
|
|
46
|
+
private indentUnit: number;
|
|
47
|
+
/** Cached document the line states belong to */
|
|
48
|
+
private cachedDoc: Text | undefined;
|
|
49
|
+
/** State at the *start* of line `i` (0-based). Index 0 is always the start state. */
|
|
50
|
+
private lineStartStates: (TokenizerState | undefined)[] = [];
|
|
51
|
+
|
|
52
|
+
constructor(options: TokenizerRunnerOptions = {}) {
|
|
53
|
+
this.tabSize = options.tabSize ?? 4;
|
|
54
|
+
this.indentUnit = options.indentUnit ?? 2;
|
|
55
|
+
this.parser = createSparqlParser({ indentUnit: this.indentUnit });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private startState(): TokenizerState {
|
|
59
|
+
return this.parser.startState!(this.indentUnit);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Drop all cached line states from `fromLine` (0-based) onwards. Call this when the document
|
|
64
|
+
* changed. Passing no argument clears the whole cache.
|
|
65
|
+
*/
|
|
66
|
+
public invalidate(fromLine = 0) {
|
|
67
|
+
if (fromLine <= 0) {
|
|
68
|
+
this.lineStartStates = [];
|
|
69
|
+
this.cachedDoc = undefined;
|
|
70
|
+
} else {
|
|
71
|
+
this.lineStartStates.length = Math.min(this.lineStartStates.length, fromLine);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Make sure the cache is bound to `doc`. Because `Text` is immutable, a different document
|
|
77
|
+
* reference means the cache must be (partially) rebuilt. The caller is responsible for
|
|
78
|
+
* calling `invalidate(fromLine)` on edits; if that did not happen we play safe and reset.
|
|
79
|
+
*/
|
|
80
|
+
private bindDoc(doc: Text) {
|
|
81
|
+
if (this.cachedDoc !== doc) {
|
|
82
|
+
if (this.lineStartStates.length > doc.lines) this.lineStartStates.length = doc.lines;
|
|
83
|
+
this.cachedDoc = doc;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Run the tokenizer over a single line, mutating `state`. Optionally stop before `stopAt` (ch). */
|
|
88
|
+
private tokenizeLine(
|
|
89
|
+
lineText: string,
|
|
90
|
+
state: TokenizerState,
|
|
91
|
+
onToken?: (start: number, end: number, style: string | null) => void,
|
|
92
|
+
stopAt?: number,
|
|
93
|
+
) {
|
|
94
|
+
const stream = new StringStream(lineText, this.tabSize, this.indentUnit);
|
|
95
|
+
if (lineText.length === 0) {
|
|
96
|
+
if (this.parser.blankLine) this.parser.blankLine(state, this.indentUnit);
|
|
97
|
+
return stream;
|
|
98
|
+
}
|
|
99
|
+
while (!stream.eol()) {
|
|
100
|
+
if (stopAt !== undefined && stream.pos >= stopAt) break;
|
|
101
|
+
stream.start = stream.pos;
|
|
102
|
+
const style = this.parser.token(stream, state);
|
|
103
|
+
if (stream.pos <= stream.start) {
|
|
104
|
+
// Guard against a tokenizer that did not advance (would loop forever)
|
|
105
|
+
stream.pos = stream.start + 1;
|
|
106
|
+
}
|
|
107
|
+
if (onToken) onToken(stream.start, stream.pos, style);
|
|
108
|
+
}
|
|
109
|
+
return stream;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Returns a *copy* of the tokenizer state at the start of `line` (0-based). */
|
|
113
|
+
public getStateBefore(doc: Text, line: number, precise = false): TokenizerState {
|
|
114
|
+
if (precise) this.invalidate();
|
|
115
|
+
this.bindDoc(doc);
|
|
116
|
+
if (line < 0) line = 0;
|
|
117
|
+
if (line > doc.lines) line = doc.lines;
|
|
118
|
+
if (!this.lineStartStates[0]) this.lineStartStates[0] = this.startState();
|
|
119
|
+
|
|
120
|
+
// Find the closest cached state before `line`
|
|
121
|
+
let from = Math.min(line, this.lineStartStates.length - 1);
|
|
122
|
+
while (from > 0 && !this.lineStartStates[from]) from--;
|
|
123
|
+
let state = copyState(this.lineStartStates[from]!);
|
|
124
|
+
for (let l = from; l < line; l++) {
|
|
125
|
+
this.tokenizeLine(doc.line(l + 1).text, state);
|
|
126
|
+
this.lineStartStates[l + 1] = copyState(state);
|
|
127
|
+
}
|
|
128
|
+
return state;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Returns the tokenizer state after the last line of the document. */
|
|
132
|
+
public getFinalState(doc: Text, precise = false): TokenizerState {
|
|
133
|
+
const lastLine = doc.lines - 1;
|
|
134
|
+
const state = this.getStateBefore(doc, lastLine, precise);
|
|
135
|
+
this.tokenizeLine(doc.line(lastLine + 1).text, state);
|
|
136
|
+
return state;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* CodeMirror 5 compatible `getTokenAt`. Returns the token that ends at or after `pos.ch`
|
|
141
|
+
* together with the tokenizer state *after* that token.
|
|
142
|
+
*/
|
|
143
|
+
public getTokenAt(doc: Text, pos: Position, precise = false): Token {
|
|
144
|
+
const line = Math.max(0, Math.min(pos.line, doc.lines - 1));
|
|
145
|
+
const lineText = doc.line(line + 1).text;
|
|
146
|
+
const ch = Math.max(0, Math.min(pos.ch, lineText.length));
|
|
147
|
+
const state = this.getStateBefore(doc, line, precise);
|
|
148
|
+
|
|
149
|
+
let start = 0;
|
|
150
|
+
let end = 0;
|
|
151
|
+
let type: string | null = null;
|
|
152
|
+
if (lineText.length > 0 && ch > 0) {
|
|
153
|
+
this.tokenizeLine(
|
|
154
|
+
lineText,
|
|
155
|
+
state,
|
|
156
|
+
(s, e, style) => {
|
|
157
|
+
start = s;
|
|
158
|
+
end = e;
|
|
159
|
+
type = style;
|
|
160
|
+
},
|
|
161
|
+
ch,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return { start, end, string: lineText.slice(start, end), type, state };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** CodeMirror 5 compatible `getTokenTypeAt` */
|
|
168
|
+
public getTokenTypeAt(doc: Text, pos: Position): string | null {
|
|
169
|
+
return this.getTokenAt(doc, pos).type;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* CodeMirror 5 `runMode` equivalent: tokenizes `text` from scratch and invokes `callback`
|
|
174
|
+
* for every token (including newlines, reported with style `null`).
|
|
175
|
+
*/
|
|
176
|
+
public runMode(text: string, callback: RunModeCallback): TokenizerState {
|
|
177
|
+
const lines = text.split(/\r?\n/);
|
|
178
|
+
const state = this.startState();
|
|
179
|
+
for (let i = 0; i < lines.length; i++) {
|
|
180
|
+
const lineText = lines[i];
|
|
181
|
+
this.tokenizeLine(lineText, state, (start, end, style) => {
|
|
182
|
+
callback(lineText.slice(start, end), style, i, start, state);
|
|
183
|
+
});
|
|
184
|
+
if (i < lines.length - 1) callback("\n", null, i, lineText.length, state);
|
|
185
|
+
}
|
|
186
|
+
return state;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Shared runner used for stateless operations on arbitrary text. */
|
|
191
|
+
const defaultRunner = new TokenizerRunner();
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Tokenize arbitrary SPARQL text (independent of any editor instance).
|
|
195
|
+
*/
|
|
196
|
+
export function runMode(text: string, callback: RunModeCallback): TokenizerState {
|
|
197
|
+
return defaultRunner.runMode(text, callback);
|
|
198
|
+
}
|