@matdata/yasqe 5.20.2 → 6.0.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 +2210 -2150
- 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 +2306 -2237
- 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 +2210 -2150
- 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
package/grammar/tokenizer.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { StringStream, StreamParser, IndentContext } from "@codemirror/language";
|
|
2
2
|
export interface State {
|
|
3
|
-
tokenize: (stream:
|
|
3
|
+
tokenize: (stream: StringStream, state: State) => string;
|
|
4
4
|
inLiteral: "SINGLE" | "DOUBLE" | undefined;
|
|
5
5
|
errorStartPos: number | undefined;
|
|
6
6
|
errorEndPos: number | undefined;
|
|
@@ -52,7 +52,54 @@ export interface Token {
|
|
|
52
52
|
start: number;
|
|
53
53
|
}
|
|
54
54
|
import * as grammar from "./_tokenizer-table.js";
|
|
55
|
-
|
|
55
|
+
|
|
56
|
+
export interface TokenizerConfig {
|
|
57
|
+
indentUnit?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* All token style names that the tokenizer can emit (the space-separated
|
|
62
|
+
* parts of the `style` strings). Exported so that the CodeMirror 6 language
|
|
63
|
+
* definition can build a token table and a highlight style for them.
|
|
64
|
+
*/
|
|
65
|
+
export const TOKEN_STYLES = [
|
|
66
|
+
"ws",
|
|
67
|
+
"comment",
|
|
68
|
+
"variable-3",
|
|
69
|
+
"atom",
|
|
70
|
+
"meta",
|
|
71
|
+
"number",
|
|
72
|
+
"string",
|
|
73
|
+
"string-2",
|
|
74
|
+
"punc",
|
|
75
|
+
"keyword",
|
|
76
|
+
"error",
|
|
77
|
+
"bracket",
|
|
78
|
+
"bracket-level-0",
|
|
79
|
+
"bracket-level-1",
|
|
80
|
+
"bracket-level-2",
|
|
81
|
+
"bracket-level-3",
|
|
82
|
+
"bracket-paren",
|
|
83
|
+
"bracket-mismatch",
|
|
84
|
+
] as const;
|
|
85
|
+
export type TokenStyle = (typeof TOKEN_STYLES)[number];
|
|
86
|
+
|
|
87
|
+
export function copyState(s: State): State {
|
|
88
|
+
return {
|
|
89
|
+
...s,
|
|
90
|
+
possibleCurrent: s.possibleCurrent.slice(),
|
|
91
|
+
possibleNext: s.possibleNext.slice(),
|
|
92
|
+
stack: s.stack.slice(),
|
|
93
|
+
variables: { ...s.variables },
|
|
94
|
+
prefixes: { ...s.prefixes },
|
|
95
|
+
constructVariables: { ...s.constructVariables },
|
|
96
|
+
whereVariables: { ...s.whereVariables },
|
|
97
|
+
pendingToken: s.pendingToken ? { ...s.pendingToken } : undefined,
|
|
98
|
+
bracketStack: s.bracketStack.map((b) => ({ ...b })),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default function (config: TokenizerConfig = {}): StreamParser<State> {
|
|
56
103
|
const ll1_table = grammar.table;
|
|
57
104
|
|
|
58
105
|
const IRI_REF = '<[^<>"`|{}^\\\x00-\x20]*>';
|
|
@@ -324,7 +371,7 @@ export default function (config: CodeMirror.EditorConfiguration): CodeMirror.Mod
|
|
|
324
371
|
return possibles;
|
|
325
372
|
}
|
|
326
373
|
|
|
327
|
-
function tokenBase(stream:
|
|
374
|
+
function tokenBase(stream: StringStream, state: State): string {
|
|
328
375
|
// If we have a pending token from a previous split, consume it from stream and return style
|
|
329
376
|
if (state.pendingToken) {
|
|
330
377
|
const token = state.pendingToken;
|
|
@@ -764,7 +811,7 @@ export default function (config: CodeMirror.EditorConfiguration): CodeMirror.Mod
|
|
|
764
811
|
// "*[;,?[or([verbPath,verbSimple]),objectList]]": 1,
|
|
765
812
|
};
|
|
766
813
|
|
|
767
|
-
function indent(state: State, textAfter: string) {
|
|
814
|
+
function indent(state: State, textAfter: string, indentUnit: number) {
|
|
768
815
|
//just avoid we don't indent multi-line literals
|
|
769
816
|
if (state.inLiteral) return 0;
|
|
770
817
|
if (
|
|
@@ -800,12 +847,21 @@ export default function (config: CodeMirror.EditorConfiguration): CodeMirror.Mod
|
|
|
800
847
|
n += dn;
|
|
801
848
|
}
|
|
802
849
|
}
|
|
803
|
-
return n *
|
|
850
|
+
return n * indentUnit;
|
|
804
851
|
}
|
|
805
852
|
}
|
|
806
853
|
|
|
807
854
|
return {
|
|
855
|
+
name: "sparql11",
|
|
808
856
|
token: tokenBase,
|
|
857
|
+
copyState,
|
|
858
|
+
indent: (state: State, textAfter: string, context: IndentContext) =>
|
|
859
|
+
indent(state, textAfter, context.unit ?? config.indentUnit ?? 2),
|
|
860
|
+
languageData: {
|
|
861
|
+
commentTokens: { line: "#" },
|
|
862
|
+
indentOnInput: /^\s*[\}\]\)]$/,
|
|
863
|
+
closeBrackets: { brackets: ["(", "[", "{", "'", '"'] },
|
|
864
|
+
},
|
|
809
865
|
startState: function (): State {
|
|
810
866
|
return {
|
|
811
867
|
tokenize: tokenBase,
|
|
@@ -839,7 +895,5 @@ export default function (config: CodeMirror.EditorConfiguration): CodeMirror.Mod
|
|
|
839
895
|
bracketLevel: 0,
|
|
840
896
|
};
|
|
841
897
|
},
|
|
842
|
-
indent: indent,
|
|
843
|
-
electricChars: "}])",
|
|
844
898
|
};
|
|
845
899
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matdata/yasqe",
|
|
3
|
-
"description": "
|
|
4
|
-
"version": "
|
|
3
|
+
"description": "MatGUI query editor: a modern SPARQL query editor (formerly Yasgui/YASQE).",
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"main": "build/yasqe.min.js",
|
|
6
6
|
"types": "build/ts/src/index.d.ts",
|
|
7
7
|
"license": "MIT",
|
|
@@ -17,22 +17,27 @@
|
|
|
17
17
|
"Semantic Web",
|
|
18
18
|
"Linked Data"
|
|
19
19
|
],
|
|
20
|
-
"bugs": "https://github.com/Matdata-eu/
|
|
20
|
+
"bugs": "https://github.com/Matdata-eu/Matgui/issues/",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "https://github.com/Matdata-eu/
|
|
23
|
+
"url": "https://github.com/Matdata-eu/Matgui.git",
|
|
24
24
|
"directory": "packages/yasqe"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@codemirror/autocomplete": "^6.20.3",
|
|
28
|
+
"@codemirror/commands": "^6.11.0",
|
|
29
|
+
"@codemirror/language": "^6.12.4",
|
|
30
|
+
"@codemirror/search": "^6.7.2",
|
|
31
|
+
"@codemirror/state": "^6.7.2",
|
|
32
|
+
"@codemirror/view": "^6.43.10",
|
|
33
|
+
"@lezer/highlight": "^1.2.3",
|
|
27
34
|
"@matdata/yasgui-utils": "^4.6.1",
|
|
28
|
-
"codemirror": "^5.51.0",
|
|
29
35
|
"leaflet": "^1.9.4",
|
|
30
36
|
"lodash-es": "^4.17.15",
|
|
31
37
|
"query-string": "^6.10.1",
|
|
32
38
|
"sparql-formatter": "^1.0.2"
|
|
33
39
|
},
|
|
34
40
|
"devDependencies": {
|
|
35
|
-
"@types/codemirror": "0.0.100",
|
|
36
41
|
"@types/leaflet": "^1.9.21",
|
|
37
42
|
"@types/lodash-es": "^4.17.3",
|
|
38
43
|
"@types/node": "^22.5.4"
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { default as Yasqe, Token, Hint, Position, Config, HintFn,
|
|
1
|
+
import { default as Yasqe, Token, Hint, Position, Config, HintFn, HintList } from "../";
|
|
2
2
|
import Trie from "../trie";
|
|
3
3
|
import { EventEmitter } from "events";
|
|
4
4
|
import { take } from "lodash-es";
|
|
5
|
-
import CodeMirror from "codemirror";
|
|
6
5
|
import "./show-hint.scss";
|
|
7
6
|
export interface CompleterConfig {
|
|
8
7
|
onInitialize?: (this: CompleterConfig, yasqe: Yasqe) => void; //allows for e.g. registering event listeners in yasqe, like the prefix autocompleter does
|
|
@@ -173,39 +172,39 @@ export class Completer extends EventEmitter {
|
|
|
173
172
|
});
|
|
174
173
|
return Promise.resolve([]);
|
|
175
174
|
}
|
|
176
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Returns whether the cursor is currently at a position this completer can complete.
|
|
177
|
+
* Also takes care of showing/hiding the "press ctrl-space" notification.
|
|
178
|
+
*/
|
|
179
|
+
public isValidCompletionPosition(): boolean {
|
|
180
|
+
return this.isValidPosition();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Returns a hint function for the current cursor position, or `false` when this completer should not
|
|
185
|
+
* (or cannot) show completions right now. The caller (Yasqe) is responsible for opening the completion popup.
|
|
186
|
+
*/
|
|
187
|
+
public autocomplete(fromAutoShow: boolean): HintFn | false {
|
|
177
188
|
//this part goes before the autoshow check, as we _would_ like notification showing to indicate a user can press ctrl-space
|
|
178
189
|
if (!this.isValidPosition()) return false;
|
|
179
|
-
const previousCompletionItem = this.yasqe.state.completionActive;
|
|
180
|
-
|
|
181
|
-
// Showhint by defaults takes the autocomplete start position (the location of the cursor at the time of starting the autocompletion).
|
|
182
|
-
const cursor = this.yasqe.getDoc().getCursor();
|
|
183
|
-
if (
|
|
184
|
-
// When the cursor goes before current completionItem (e.g. using arrow keys), it would close the autocompletions.
|
|
185
|
-
// We want the autocompletion to be active at whatever point we are in the token, so let's modify this start pos with the start pos of the token
|
|
186
|
-
previousCompletionItem &&
|
|
187
|
-
cursor.sticky && // Is undefined at the end of the token, otherwise it is set as either "before" or "after" (The movement of the cursor)
|
|
188
|
-
cursor.ch !== previousCompletionItem.startPos.ch
|
|
189
|
-
) {
|
|
190
|
-
this.yasqe.state.completionActive.startPos = cursor;
|
|
191
|
-
} else if (previousCompletionItem && !cursor.sticky && cursor.ch < previousCompletionItem.startPos.ch) {
|
|
192
|
-
// A similar thing happens when pressing backspace, CodeMirror will close this autocomplete when 'startLen' changes downward
|
|
193
|
-
cursor.sticky = previousCompletionItem.startPos.sticky;
|
|
194
|
-
this.yasqe.state.completionActive.startPos.ch = cursor.ch;
|
|
195
|
-
this.yasqe.state.completionActive.startLen--;
|
|
196
|
-
}
|
|
197
190
|
if (
|
|
198
191
|
fromAutoShow && // from autoShow, i.e. this gets called each time the editor content changes
|
|
199
|
-
(!this.config.autoShow || this.yasqe.
|
|
192
|
+
(!this.config.autoShow || this.yasqe.isAutocompletionActive()) // Don't show and don't create a new instance when its already active
|
|
200
193
|
) {
|
|
201
194
|
return false;
|
|
202
195
|
}
|
|
196
|
+
return this.getHintFn();
|
|
197
|
+
}
|
|
203
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Hint function that computes the hints for the token at the current cursor position
|
|
201
|
+
*/
|
|
202
|
+
public getHintFn(): HintFn {
|
|
204
203
|
const getHints: HintFn = () => {
|
|
205
|
-
return this.getHints(this.yasqe.getCompleteToken()).then((list) => {
|
|
204
|
+
return this.getHints(this.yasqe.getCompleteToken()).then((list): HintList => {
|
|
206
205
|
const cur = this.yasqe.getDoc().getCursor();
|
|
207
206
|
const token: AutocompletionToken = this.yasqe.getCompleteToken();
|
|
208
|
-
|
|
207
|
+
return {
|
|
209
208
|
list: list,
|
|
210
209
|
from: <Position>{
|
|
211
210
|
line: cur.line,
|
|
@@ -216,37 +215,9 @@ export class Completer extends EventEmitter {
|
|
|
216
215
|
ch: token.end,
|
|
217
216
|
},
|
|
218
217
|
};
|
|
219
|
-
CodeMirror.on(hintResult, "shown", () => {
|
|
220
|
-
this.yasqe.emit("autocompletionShown", (this.yasqe as any).state.completionActive.widget);
|
|
221
|
-
});
|
|
222
|
-
CodeMirror.on(hintResult, "close", () => {
|
|
223
|
-
this.yasqe.emit("autocompletionClose");
|
|
224
|
-
});
|
|
225
|
-
return hintResult;
|
|
226
218
|
});
|
|
227
219
|
};
|
|
228
|
-
|
|
229
|
-
getHints.async = false; //in their code, async means using a callback
|
|
230
|
-
//we always return a promise, which should be properly handled regardless of this val
|
|
231
|
-
var hintConfig: HintConfig = {
|
|
232
|
-
closeCharacters: /[\s>"]/,
|
|
233
|
-
completeSingle: false,
|
|
234
|
-
hint: getHints,
|
|
235
|
-
container: this.yasqe.rootEl,
|
|
236
|
-
// Override these actions back to use their default function
|
|
237
|
-
// Otherwise these would navigate to the start/end of the suggestion list, while this can also be accomplished with PgUp and PgDn
|
|
238
|
-
extraKeys: {
|
|
239
|
-
Home: (yasqe, event) => {
|
|
240
|
-
yasqe.getDoc().setCursor({ ch: 0, line: event.data.from.line });
|
|
241
|
-
},
|
|
242
|
-
End: (yasqe, event) => {
|
|
243
|
-
yasqe.getDoc().setCursor({ ch: yasqe.getLine(event.data.to.line).length, line: event.data.to.line });
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
...this.yasqe.config.hintConfig,
|
|
247
|
-
};
|
|
248
|
-
this.yasqe.showHint(hintConfig);
|
|
249
|
-
return true;
|
|
220
|
+
return getHints;
|
|
250
221
|
}
|
|
251
222
|
}
|
|
252
223
|
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// Styling for the autocompletion tooltip (CodeMirror 6 `@codemirror/autocomplete`).
|
|
2
|
+
// The tooltip gets the legacy `CodeMirror-hints` class (see editor/autocompletion.ts) so that
|
|
3
|
+
// existing consumers / tests keep working.
|
|
4
|
+
// Including this ourselves, as we don't want this css to be auto-scoped to yasqe
|
|
5
|
+
.cm-tooltip.CodeMirror-hints,
|
|
3
6
|
.CodeMirror-hints {
|
|
4
|
-
position: absolute;
|
|
5
7
|
z-index: 10;
|
|
6
8
|
overflow: hidden;
|
|
7
|
-
list-style: none;
|
|
8
|
-
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 2px;
|
|
11
9
|
|
|
12
10
|
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
|
13
11
|
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
|
|
@@ -18,10 +16,18 @@
|
|
|
18
16
|
background: white;
|
|
19
17
|
font-size: 90%;
|
|
20
18
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
|
|
20
|
+
& > ul {
|
|
21
|
+
list-style: none;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 2px;
|
|
24
|
+
max-height: 20em;
|
|
25
|
+
overflow-y: auto;
|
|
26
|
+
font-family: inherit;
|
|
27
|
+
}
|
|
23
28
|
}
|
|
24
29
|
|
|
30
|
+
.CodeMirror-hints > ul > li,
|
|
25
31
|
.CodeMirror-hint {
|
|
26
32
|
margin: 0;
|
|
27
33
|
padding: 0 4px;
|
|
@@ -29,8 +35,10 @@
|
|
|
29
35
|
white-space: pre;
|
|
30
36
|
color: black;
|
|
31
37
|
cursor: pointer;
|
|
38
|
+
line-height: 1.4;
|
|
32
39
|
}
|
|
33
40
|
|
|
41
|
+
.CodeMirror-hints > ul > li[aria-selected],
|
|
34
42
|
li.CodeMirror-hint-active {
|
|
35
43
|
background: #08f;
|
|
36
44
|
color: white;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as autocompleter from "./";
|
|
2
|
+
import { runMode } from "../editor/tokenizerRunner";
|
|
2
3
|
|
|
3
4
|
var conf: autocompleter.CompleterConfig = {
|
|
4
5
|
name: "variables",
|
|
@@ -16,33 +17,28 @@ var conf: autocompleter.CompleterConfig = {
|
|
|
16
17
|
if (!token || token.string.length == 0) return []; //nothing to autocomplete
|
|
17
18
|
const distinctVars: { [varname: string]: any } = {};
|
|
18
19
|
const vars: string[] = [];
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
const atoms =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
if (distinctVars[variable]) continue; //already in list
|
|
33
|
-
//skip single questionmarks
|
|
34
|
-
if (variable.length <= 1) continue;
|
|
20
|
+
// Tokenize the whole query (independent of what is rendered in the viewport), collecting all
|
|
21
|
+
// variable tokens. This still works when the query is syntactically incorrect (e.g. when simply typing '?').
|
|
22
|
+
const atoms: string[] = [];
|
|
23
|
+
runMode(yasqe.getValue(), (stringVal: string, style: string | null) => {
|
|
24
|
+
if (style === "atom" && (stringVal[0] === "?" || stringVal[0] === "$")) {
|
|
25
|
+
atoms.push(stringVal);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
for (const variable of atoms) {
|
|
29
|
+
if (distinctVars[variable]) continue; //already in list
|
|
30
|
+
//skip single questionmarks
|
|
31
|
+
if (variable.length <= 1) continue;
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
//it should match our token ofcourse
|
|
34
|
+
if (variable.indexOf(token.string) !== 0) continue;
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
//skip exact matches
|
|
37
|
+
if (variable === token.string) continue;
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
39
|
+
//store in map so we have a unique list
|
|
40
|
+
distinctVars[variable] = true;
|
|
41
|
+
vars.push(variable);
|
|
46
42
|
}
|
|
47
43
|
return vars.sort();
|
|
48
44
|
},
|
package/src/defaults.ts
CHANGED
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { default as Yasqe, Config, PlainRequestConfig } from "./";
|
|
8
8
|
import * as queryString from "query-string";
|
|
9
|
-
import CodeMirror from "codemirror";
|
|
10
|
-
//need to pass Yasqe object as argument, as the imported version might not have inherited all (e.g. `fold`) props of Codemirror yet
|
|
11
9
|
export default function get() {
|
|
12
10
|
const prefixCcApi = null; // null = use bundled prefix data (packages/yasqe/src/prefixes.json, refreshed each build)
|
|
13
11
|
const config: Omit<Config, "requestConfig"> = {
|
|
@@ -23,11 +21,9 @@ SELECT * WHERE {
|
|
|
23
21
|
tabMode: "indent",
|
|
24
22
|
lineNumbers: true,
|
|
25
23
|
lineWrapping: true,
|
|
26
|
-
foldGutter:
|
|
27
|
-
rangeFinder: new (<any>CodeMirror).fold.combine((<any>CodeMirror).fold.brace, (<any>CodeMirror).fold.prefix),
|
|
28
|
-
},
|
|
24
|
+
foldGutter: true, // Folding of braces and prefix blocks is handled by the built-in SPARQL fold service
|
|
29
25
|
collapsePrefixesOnLoad: false,
|
|
30
|
-
gutters: ["gutterErrorBar", "gutterConstructWarning"
|
|
26
|
+
gutters: ["gutterErrorBar", "gutterConstructWarning"],
|
|
31
27
|
matchBrackets: true,
|
|
32
28
|
fixedGutter: true,
|
|
33
29
|
syntaxErrorCheck: true,
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeMirror 6 autocompletion bridge.
|
|
3
|
+
*
|
|
4
|
+
* YASQE's autocompleters (see ../autocompleters) were written against the CodeMirror 5
|
|
5
|
+
* `show-hint` addon: a "hint function" returns a list of `Hint`s together with the range
|
|
6
|
+
* that should be replaced. This module adapts that model onto `@codemirror/autocomplete`:
|
|
7
|
+
*
|
|
8
|
+
* - A single `CompletionSource` is registered. Whenever it is queried it asks the host
|
|
9
|
+
* (the `Yasqe` facade) for the currently active hint function and converts its result
|
|
10
|
+
* into a `CompletionResult`.
|
|
11
|
+
* - `Hint.from` / `Hint.to` are honoured per completion by using a custom `apply`.
|
|
12
|
+
* - Open/close transitions are observed through `completionStatus` and reported to the host
|
|
13
|
+
* so that the legacy `autocompletionShown` / `autocompletionClose` events keep working.
|
|
14
|
+
*/
|
|
15
|
+
import { EditorState, Extension, Text } from "@codemirror/state";
|
|
16
|
+
import { EditorView, ViewUpdate } from "@codemirror/view";
|
|
17
|
+
import {
|
|
18
|
+
autocompletion,
|
|
19
|
+
Completion,
|
|
20
|
+
CompletionContext,
|
|
21
|
+
CompletionResult,
|
|
22
|
+
completionStatus,
|
|
23
|
+
completionKeymap,
|
|
24
|
+
startCompletion,
|
|
25
|
+
closeCompletion,
|
|
26
|
+
} from "@codemirror/autocomplete";
|
|
27
|
+
import { keymap } from "@codemirror/view";
|
|
28
|
+
import { Prec } from "@codemirror/state";
|
|
29
|
+
import type { Position } from "./tokenizerRunner";
|
|
30
|
+
import { posToOffset, offsetToPos } from "./doc";
|
|
31
|
+
|
|
32
|
+
export interface Hint {
|
|
33
|
+
text: string;
|
|
34
|
+
displayText?: string;
|
|
35
|
+
className?: string;
|
|
36
|
+
/** Kept for API compatibility; custom rendering is not supported by the CM6 bridge. */
|
|
37
|
+
render?: (el: HTMLElement, self: Hint, data: any) => void;
|
|
38
|
+
from?: Position;
|
|
39
|
+
to?: Position;
|
|
40
|
+
}
|
|
41
|
+
export interface HintList {
|
|
42
|
+
list: Hint[];
|
|
43
|
+
from: Position;
|
|
44
|
+
to: Position;
|
|
45
|
+
}
|
|
46
|
+
export type HintFn = { async?: boolean } & (() => Promise<HintList | undefined> | HintList | undefined);
|
|
47
|
+
|
|
48
|
+
export interface AutocompletionHost {
|
|
49
|
+
/** Returns the hint function that should be used for the current completion session (if any) */
|
|
50
|
+
getHintFn: () => HintFn | undefined;
|
|
51
|
+
onShown?: (tooltipDom: HTMLElement | null) => void;
|
|
52
|
+
onClose?: () => void;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { posToOffset, offsetToPos };
|
|
56
|
+
|
|
57
|
+
function hintToCompletion(hint: Hint): Completion {
|
|
58
|
+
return {
|
|
59
|
+
label: hint.displayText || hint.text,
|
|
60
|
+
type: hint.className,
|
|
61
|
+
apply: (view: EditorView, _completion: Completion, from: number, to: number) => {
|
|
62
|
+
const doc = view.state.doc;
|
|
63
|
+
const replaceFrom = hint.from ? posToOffset(doc, hint.from) : from;
|
|
64
|
+
const replaceTo = hint.to ? posToOffset(doc, hint.to) : to;
|
|
65
|
+
const start = Math.min(replaceFrom, replaceTo);
|
|
66
|
+
const end = Math.max(replaceFrom, replaceTo);
|
|
67
|
+
view.dispatch({
|
|
68
|
+
changes: { from: start, to: end, insert: hint.text },
|
|
69
|
+
selection: { anchor: start + hint.text.length },
|
|
70
|
+
userEvent: "input.complete",
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function hintListToCompletionResult(hintList: HintList, doc: Text): CompletionResult | null {
|
|
77
|
+
if (!hintList || !hintList.list || hintList.list.length === 0) return null;
|
|
78
|
+
const from = posToOffset(doc, hintList.from);
|
|
79
|
+
const to = posToOffset(doc, hintList.to);
|
|
80
|
+
return {
|
|
81
|
+
from: Math.min(from, to),
|
|
82
|
+
to: Math.max(from, to),
|
|
83
|
+
options: hintList.list.map(hintToCompletion),
|
|
84
|
+
// Completers already filtered and ordered their suggestions
|
|
85
|
+
filter: false,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function createCompletionSource(host: AutocompletionHost) {
|
|
90
|
+
return async (context: CompletionContext): Promise<CompletionResult | null> => {
|
|
91
|
+
const hintFn = host.getHintFn();
|
|
92
|
+
if (!hintFn) return null;
|
|
93
|
+
const result = await hintFn();
|
|
94
|
+
if (!result || context.aborted) return null;
|
|
95
|
+
return hintListToCompletionResult(result, context.state.doc);
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Autocompletion extension. `host.getHintFn()` is consulted every time CodeMirror queries for completions.
|
|
101
|
+
*/
|
|
102
|
+
export function sparqlAutocompletion(host: AutocompletionHost): Extension {
|
|
103
|
+
const statusListener = EditorView.updateListener.of((update: ViewUpdate) => {
|
|
104
|
+
const before = completionStatus(update.startState);
|
|
105
|
+
const after = completionStatus(update.state);
|
|
106
|
+
if (before === null && after !== null) {
|
|
107
|
+
host.onShown?.(update.view.dom.querySelector<HTMLElement>(".cm-tooltip-autocomplete"));
|
|
108
|
+
} else if (before !== null && after === null) {
|
|
109
|
+
host.onClose?.();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return [
|
|
113
|
+
autocompletion({
|
|
114
|
+
override: [createCompletionSource(host)],
|
|
115
|
+
// YASQE decides itself when to open the popup (see Yasqe.autocomplete)
|
|
116
|
+
activateOnTyping: false,
|
|
117
|
+
closeOnBlur: true,
|
|
118
|
+
// We register the navigation keys ourselves (below) so that `Ctrl-Space` is exclusively handled by
|
|
119
|
+
// YASQE's `extraKeys` (which builds the hint function before opening the popup).
|
|
120
|
+
defaultKeymap: false,
|
|
121
|
+
icons: false,
|
|
122
|
+
// CM5 rendered the complete hint list; CM6 pages by 100 items by default. Render everything so that
|
|
123
|
+
// consumers that inspect the popup (and the existing tests) see the full list.
|
|
124
|
+
maxRenderedOptions: 100000,
|
|
125
|
+
// CM5 accepted a completion immediately after the popup opened; CM6 ignores Enter/arrows during the first
|
|
126
|
+
// 75ms (and lets the key fall through, inserting a newline). Keep the CM5 behaviour.
|
|
127
|
+
interactionDelay: 0,
|
|
128
|
+
// Keep the CM5 class names around so existing styling / tests keep working
|
|
129
|
+
tooltipClass: () => "CodeMirror-hints",
|
|
130
|
+
optionClass: (completion) => (completion.type ? completion.type : ""),
|
|
131
|
+
}),
|
|
132
|
+
Prec.highest(keymap.of(completionKeymap.filter((binding) => binding.key !== "Ctrl-Space"))),
|
|
133
|
+
statusListener,
|
|
134
|
+
];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function isCompletionActive(state: EditorState): boolean {
|
|
138
|
+
return completionStatus(state) !== null;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function openCompletion(view: EditorView): boolean {
|
|
142
|
+
return startCompletion(view);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function hideCompletion(view: EditorView): boolean {
|
|
146
|
+
return closeCompletion(view);
|
|
147
|
+
}
|