@matdata/yasqe 4.6.1

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/build/ts/grammar/tokenizer.d.ts +37 -0
  3. package/build/ts/src/CodeMirror.d.ts +21 -0
  4. package/build/ts/src/autocompleters/classes.d.ts +3 -0
  5. package/build/ts/src/autocompleters/index.d.ts +39 -0
  6. package/build/ts/src/autocompleters/prefixes.d.ts +3 -0
  7. package/build/ts/src/autocompleters/properties.d.ts +3 -0
  8. package/build/ts/src/autocompleters/variables.d.ts +3 -0
  9. package/build/ts/src/defaults.d.ts +75 -0
  10. package/build/ts/src/imgs.d.ts +7 -0
  11. package/build/ts/src/index.d.ts +303 -0
  12. package/build/ts/src/prefixFold.d.ts +8 -0
  13. package/build/ts/src/prefixUtils.d.ts +9 -0
  14. package/build/ts/src/sparql.d.ts +20 -0
  15. package/build/ts/src/tokenUtils.d.ts +4 -0
  16. package/build/ts/src/tooltip.d.ts +2 -0
  17. package/build/ts/src/trie.d.ts +13 -0
  18. package/build/yasqe.html +108 -0
  19. package/build/yasqe.min.css +2 -0
  20. package/build/yasqe.min.css.map +1 -0
  21. package/build/yasqe.min.js +3 -0
  22. package/build/yasqe.min.js.LICENSE.txt +3 -0
  23. package/build/yasqe.min.js.map +1 -0
  24. package/grammar/README.md +12 -0
  25. package/grammar/_tokenizer-table.js +4776 -0
  26. package/grammar/build.sh +2 -0
  27. package/grammar/sparql11-grammar.pl +834 -0
  28. package/grammar/sparqljs-browser-min.js +4535 -0
  29. package/grammar/tokenizer.ts +729 -0
  30. package/grammar/util/gen_ll1.pl +37 -0
  31. package/grammar/util/gen_sparql11.pl +11 -0
  32. package/grammar/util/ll1.pl +175 -0
  33. package/grammar/util/output_to_javascript.pl +75 -0
  34. package/grammar/util/prune.pl +49 -0
  35. package/grammar/util/rewrite.pl +104 -0
  36. package/package.json +40 -0
  37. package/src/CodeMirror.ts +54 -0
  38. package/src/autocompleters/classes.ts +32 -0
  39. package/src/autocompleters/index.ts +346 -0
  40. package/src/autocompleters/prefixes.ts +130 -0
  41. package/src/autocompleters/properties.ts +28 -0
  42. package/src/autocompleters/show-hint.scss +38 -0
  43. package/src/autocompleters/variables.ts +52 -0
  44. package/src/defaults.ts +149 -0
  45. package/src/imgs.ts +14 -0
  46. package/src/index.ts +1089 -0
  47. package/src/prefixFold.ts +93 -0
  48. package/src/prefixUtils.ts +65 -0
  49. package/src/scss/buttons.scss +275 -0
  50. package/src/scss/codemirrorMods.scss +36 -0
  51. package/src/scss/yasqe.scss +89 -0
  52. package/src/sparql.ts +215 -0
  53. package/src/tokenUtils.ts +121 -0
  54. package/src/tooltip.ts +31 -0
  55. package/src/trie.ts +238 -0
@@ -0,0 +1,346 @@
1
+ import { default as Yasqe, Token, Hint, Position, Config, HintFn, HintConfig } from "../";
2
+ import Trie from "../trie";
3
+ import { EventEmitter } from "events";
4
+ import { take } from "lodash-es";
5
+ const CodeMirror = require("codemirror");
6
+ require("./show-hint.scss");
7
+ export interface CompleterConfig {
8
+ onInitialize?: (this: CompleterConfig, yasqe: Yasqe) => void; //allows for e.g. registering event listeners in yasqe, like the prefix autocompleter does
9
+ isValidCompletionPosition: (yasqe: Yasqe) => boolean;
10
+ get: (yasqe: Yasqe, token?: AutocompletionToken) => Promise<string[]> | string[];
11
+ preProcessToken?: (yasqe: Yasqe, token: Token) => AutocompletionToken;
12
+ postProcessSuggestion?: (yasqe: Yasqe, token: AutocompletionToken, suggestedString: string) => string;
13
+ postprocessHints?: (yasqe: Yasqe, hints: Hint[]) => Hint[];
14
+ bulk: boolean;
15
+ autoShow?: boolean;
16
+ persistenceId?: Config["persistenceId"];
17
+ name: string;
18
+ }
19
+ const SUGGESTIONS_LIMIT = 100;
20
+ export interface AutocompletionToken extends Token {
21
+ autocompletionString?: string;
22
+ tokenPrefix?: string;
23
+ tokenPrefixUri?: string;
24
+ from?: Partial<Position>;
25
+ to?: Partial<Position>;
26
+ }
27
+ export class Completer extends EventEmitter {
28
+ protected yasqe: Yasqe;
29
+ private trie?: Trie;
30
+ private config: CompleterConfig;
31
+ constructor(yasqe: Yasqe, config: CompleterConfig) {
32
+ super();
33
+ this.yasqe = yasqe;
34
+ this.config = config;
35
+ }
36
+
37
+ // private selectHint(data:EditorChange, completion:any) {
38
+ // if (completion.text != this.yasqe.getTokenAt(this.yasqe.getDoc().getCursor()).string) {
39
+ // this.yasqe.getDoc().replaceRange(completion.text, data.from, data.to);
40
+ // }
41
+ // };
42
+ private getStorageId() {
43
+ return this.yasqe.getStorageId(this.config.persistenceId);
44
+ }
45
+
46
+ /**
47
+ * Store bulk completion in local storage, and populates the trie
48
+ */
49
+ private storeBulkCompletions(completions: string[]) {
50
+ if (!completions || !(completions instanceof Array)) return;
51
+ // store array as trie
52
+ this.trie = new Trie();
53
+ for (const c of completions) {
54
+ this.trie.insert(c);
55
+ }
56
+
57
+ // store in localstorage as well
58
+ var storageId = this.getStorageId();
59
+ if (storageId)
60
+ this.yasqe.storage.set(storageId, completions, 60 * 60 * 24 * 30, this.yasqe.handleLocalStorageQuotaFull);
61
+ }
62
+
63
+ /**
64
+ * Get completion list from `get` function
65
+ */
66
+ public getCompletions(token?: AutocompletionToken): Promise<string[]> {
67
+ if (!this.config.get) return Promise.resolve([]);
68
+
69
+ //No token, so probably getting as bulk
70
+ if (!token) {
71
+ if (this.config.get instanceof Array) return Promise.resolve(this.config.get);
72
+ //wrapping call in a promise.resolve, so this when a `get` is both async or sync
73
+ return Promise.resolve(this.config.get(this.yasqe)).then((suggestions) => {
74
+ if (suggestions instanceof Array) return suggestions;
75
+ return [];
76
+ });
77
+ }
78
+
79
+ //ok, there is a token
80
+ const stringToAutocomplete = token.autocompletionString || token.string;
81
+ if (this.trie) return Promise.resolve(take(this.trie.autoComplete(stringToAutocomplete), SUGGESTIONS_LIMIT));
82
+ if (this.config.get instanceof Array)
83
+ return Promise.resolve(
84
+ this.config.get.filter((possibleMatch) => possibleMatch.indexOf(stringToAutocomplete) === 0)
85
+ );
86
+ //assuming it's a function
87
+ return Promise.resolve(this.config.get(this.yasqe, token)).then((r) => {
88
+ if (r instanceof Array) return r;
89
+ return [];
90
+ });
91
+ }
92
+
93
+ /**
94
+ * Populates completions. Pre-fetches those if bulk is set to true
95
+ */
96
+ public initialize(): Promise<void> {
97
+ if (this.config.onInitialize) this.config.onInitialize(this.yasqe);
98
+ if (this.config.bulk) {
99
+ if (this.config.get instanceof Array) {
100
+ // we don't care whether the completions are already stored in
101
+ // localstorage. just use this one
102
+ this.storeBulkCompletions(this.config.get);
103
+ return Promise.resolve();
104
+ } else {
105
+ // if completions are defined in localstorage, use those! (calling the
106
+ // function may come with overhead (e.g. async calls))
107
+ var completionsFromStorage: string[] | undefined;
108
+ var storageId = this.getStorageId();
109
+ if (storageId) completionsFromStorage = this.yasqe.storage.get<string[]>(storageId);
110
+ if (completionsFromStorage && completionsFromStorage.length > 0) {
111
+ this.storeBulkCompletions(completionsFromStorage);
112
+ return Promise.resolve();
113
+ } else {
114
+ return this.getCompletions().then((c) => this.storeBulkCompletions(c));
115
+ }
116
+ }
117
+ }
118
+ return Promise.resolve();
119
+ }
120
+
121
+ private isValidPosition(): boolean {
122
+ if (!this.config.isValidCompletionPosition) return false; //no way to check whether we are in a valid position
123
+ if (!this.config.isValidCompletionPosition(this.yasqe)) {
124
+ this.emit("invalidPosition", this);
125
+ this.yasqe.hideNotification(this.config.name);
126
+ return false;
127
+ }
128
+ if (!this.config.autoShow) {
129
+ this.yasqe.showNotification(this.config.name, "Press CTRL - <spacebar> to autocomplete");
130
+ }
131
+ this.emit("validPosition", this);
132
+ return true;
133
+ }
134
+
135
+ private getHint(autocompletionToken: AutocompletionToken, suggestedString: string): Hint {
136
+ if (this.config.postProcessSuggestion) {
137
+ suggestedString = this.config.postProcessSuggestion(this.yasqe, autocompletionToken, suggestedString);
138
+ }
139
+ let from: Position | undefined;
140
+ let to: Position;
141
+ const cursor = this.yasqe.getDoc().getCursor();
142
+ if (autocompletionToken.from) {
143
+ from = { ...cursor, ...autocompletionToken.from };
144
+ }
145
+ // Need to set a 'to' part as well, as otherwise we'd be appending the result to the already typed filter
146
+ const line = this.yasqe.getDoc().getCursor().line;
147
+ if (autocompletionToken.to) {
148
+ to = { ch: autocompletionToken?.to?.ch || this.yasqe.getCompleteToken().end, line: line };
149
+ } else if (autocompletionToken.string.length > 0) {
150
+ to = { ch: this.yasqe.getCompleteToken().end, line: line };
151
+ } else {
152
+ to = <any>autocompletionToken.from;
153
+ }
154
+ return {
155
+ text: suggestedString,
156
+ displayText: suggestedString,
157
+ from: from,
158
+ to: to,
159
+ };
160
+ }
161
+
162
+ private getHints(token: AutocompletionToken): Promise<Hint[]> {
163
+ if (this.config.preProcessToken) {
164
+ token = this.config.preProcessToken(this.yasqe, token);
165
+ }
166
+
167
+ if (token)
168
+ return this.getCompletions(token)
169
+ .then((suggestions) => suggestions.map((s) => this.getHint(token, s)))
170
+ .then((hints) => {
171
+ if (this.config.postprocessHints) return this.config.postprocessHints(this.yasqe, hints);
172
+ return hints;
173
+ });
174
+ return Promise.resolve([]);
175
+ }
176
+ public autocomplete(fromAutoShow: boolean) {
177
+ //this part goes before the autoshow check, as we _would_ like notification showing to indicate a user can press ctrl-space
178
+ 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
+ if (
198
+ fromAutoShow && // from autoShow, i.e. this gets called each time the editor content changes
199
+ (!this.config.autoShow || this.yasqe.state.completionActive) // Don't show and don't create a new instance when its already active
200
+ ) {
201
+ return false;
202
+ }
203
+
204
+ const getHints: HintFn = () => {
205
+ return this.getHints(this.yasqe.getCompleteToken()).then((list) => {
206
+ const cur = this.yasqe.getDoc().getCursor();
207
+ const token: AutocompletionToken = this.yasqe.getCompleteToken();
208
+ const hintResult = {
209
+ list: list,
210
+ from: <Position>{
211
+ line: cur.line,
212
+ ch: token.start,
213
+ },
214
+ to: <Position>{
215
+ line: cur.line,
216
+ ch: token.end,
217
+ },
218
+ };
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
+ });
227
+ };
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;
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Converts rdf:type to http://.../type and converts <http://...> to http://...
255
+ * Stores additional info such as the used namespace and prefix in the token object
256
+ */
257
+ export function preprocessIriForCompletion(yasqe: Yasqe, token: AutocompletionToken) {
258
+ const queryPrefixes = yasqe.getPrefixesFromQuery();
259
+ const stringToPreprocess = token.string;
260
+
261
+ if (stringToPreprocess.indexOf("<") < 0) {
262
+ token.tokenPrefix = stringToPreprocess.substring(0, stringToPreprocess.indexOf(":") + 1);
263
+
264
+ if (queryPrefixes[token.tokenPrefix.slice(0, -1)] != null) {
265
+ token.tokenPrefixUri = queryPrefixes[token.tokenPrefix.slice(0, -1)];
266
+ }
267
+ }
268
+
269
+ token.autocompletionString = stringToPreprocess.trim();
270
+ if (stringToPreprocess.indexOf("<") < 0 && stringToPreprocess.indexOf(":") > -1) {
271
+ // hmm, the token is prefixed. We still need the complete uri for autocompletions. generate this!
272
+ for (var prefix in queryPrefixes) {
273
+ if (token.tokenPrefix === prefix + ":") {
274
+ token.autocompletionString = queryPrefixes[prefix];
275
+ token.autocompletionString += stringToPreprocess.substring(prefix.length + 1);
276
+ break;
277
+ }
278
+ }
279
+ }
280
+
281
+ if (token.autocompletionString.indexOf("<") == 0)
282
+ token.autocompletionString = token.autocompletionString.substring(1);
283
+ if (token.autocompletionString.indexOf(">", token.autocompletionString.length - 1) > 0)
284
+ token.autocompletionString = token.autocompletionString.substring(0, token.autocompletionString.length - 1);
285
+ return token;
286
+ }
287
+
288
+ export function postprocessIriCompletion(_yasqe: Yasqe, token: AutocompletionToken, suggestedString: string) {
289
+ if (token.tokenPrefix && token.autocompletionString && token.tokenPrefixUri) {
290
+ // we need to get the suggested string back to prefixed form
291
+ suggestedString = token.tokenPrefix + suggestedString.substring(token.tokenPrefixUri.length);
292
+ } else {
293
+ // it is a regular uri. add '<' and '>' to string
294
+ suggestedString = "<" + suggestedString + ">";
295
+ }
296
+ return suggestedString;
297
+ }
298
+
299
+ //Use protocol relative request when served via http[s]*. Otherwise (e.g. file://, fetch via http)
300
+ export const fetchFromLov = (
301
+ yasqe: Yasqe,
302
+ type: "class" | "property",
303
+ token?: AutocompletionToken
304
+ ): Promise<string[]> => {
305
+ var reqProtocol = window.location.protocol.indexOf("http") === 0 ? "https://" : "http://";
306
+ const notificationKey = "autocomplete_" + type;
307
+ if (!token || !token.string || token.string.trim().length == 0) {
308
+ yasqe.showNotification(notificationKey, "Nothing to autocomplete yet!");
309
+ return Promise.resolve([]);
310
+ }
311
+ // //if notification bar is there, show a loader
312
+ // yasqe.autocompleters.notifications
313
+ // .getEl(completer)
314
+ // .empty()
315
+ // .append($("<span>Fetchting autocompletions &nbsp;</span>"))
316
+ // .append($(yutils.svg.getElement(require("../imgs.js").loader)).addClass("notificationLoader"));
317
+ // doRequests();
318
+
319
+ const params = new URLSearchParams();
320
+ if (token.autocompletionString) params.append("q", token.autocompletionString);
321
+ params.append("page_size", "50");
322
+ params.append("type", type);
323
+ const url = `${reqProtocol}lov.linkeddata.es/dataset/lov/api/v2/autocomplete/terms?${params.toString()}`;
324
+ return fetch(url)
325
+ .then((response) => {
326
+ if (!response.ok) {
327
+ throw new Error("Failed fetching suggestions from Linked Open Vocabularies");
328
+ }
329
+ return response.json();
330
+ })
331
+ .then((result) => {
332
+ if (result.results) {
333
+ return result.results.map((r: any) => r.uri[0]);
334
+ }
335
+ return [];
336
+ })
337
+ .catch((_e) => {
338
+ yasqe.showNotification(notificationKey, "Failed fetching suggestions");
339
+ });
340
+ };
341
+
342
+ import variableCompleter from "./variables";
343
+ import prefixCompleter from "./prefixes";
344
+ import propertyCompleter from "./properties";
345
+ import classCompleter from "./classes";
346
+ export var completers: CompleterConfig[] = [variableCompleter, prefixCompleter, propertyCompleter, classCompleter];
@@ -0,0 +1,130 @@
1
+ import * as Autocompleter from "./";
2
+ import { sortBy } from "lodash-es";
3
+ var tokenTypes: { [id: string]: "prefixed" | "var" } = {
4
+ "string-2": "prefixed",
5
+ atom: "var",
6
+ };
7
+
8
+ var conf: Autocompleter.CompleterConfig = {
9
+ postprocessHints: function (_yasqe, hints) {
10
+ return sortBy(hints, (hint) => hint.text.split(":")[0]);
11
+ },
12
+ onInitialize: function (yasqe) {
13
+ /**
14
+ * This event listener makes sure we auto-add prefixes whenever we use them
15
+ */
16
+ yasqe.on("change", () => {
17
+ if (!yasqe.config.autocompleters || yasqe.config.autocompleters.indexOf(this.name) == -1) return; //this autocompleter is disabled
18
+ var cur = yasqe.getDoc().getCursor();
19
+
20
+ var token: Autocompleter.AutocompletionToken = yasqe.getTokenAt(cur);
21
+ if (token.type && tokenTypes[token.type] == "prefixed") {
22
+ var colonIndex = token.string.indexOf(":");
23
+ if (colonIndex !== -1) {
24
+ // check previous token isnt PREFIX, or a '<'(which would mean we are in a uri)
25
+ // var firstTokenString = yasqe.getNextNonWsToken(cur.line).string.toUpperCase();
26
+ var lastNonWsTokenString = yasqe.getPreviousNonWsToken(cur.line, token).string.toUpperCase();
27
+ var previousToken = yasqe.getTokenAt({
28
+ line: cur.line,
29
+ ch: token.start,
30
+ }); // needs to be null (beginning of line), or whitespace
31
+
32
+ if (
33
+ lastNonWsTokenString !== "PREFIX" &&
34
+ (previousToken.type == "ws" ||
35
+ previousToken.type == null ||
36
+ (previousToken.type === "punc" &&
37
+ (previousToken.string === "|" ||
38
+ previousToken.string === "/" ||
39
+ previousToken.string == "^^" ||
40
+ previousToken.string == "{" ||
41
+ previousToken.string === "(")))
42
+ ) {
43
+ // check whether it isn't defined already (saves us from looping
44
+ // through the array)
45
+ var currentPrefix = token.string.substring(0, colonIndex + 1);
46
+
47
+ var queryPrefixes = yasqe.getPrefixesFromQuery();
48
+ if (queryPrefixes[currentPrefix.slice(0, -1)] == null) {
49
+ // ok, so it isn't added yet!
50
+ // var completions = yasqe.autocompleters.getTrie(completerName).autoComplete(currentPrefix);
51
+ token.autocompletionString = currentPrefix;
52
+ yasqe.autocompleters[this.name]?.getCompletions(token).then((suggestions) => {
53
+ if (suggestions.length) {
54
+ yasqe.addPrefixes(suggestions[0]);
55
+ // Re-activate auto-completer after adding prefixes, so another auto-completer can kick in
56
+ yasqe.autocomplete();
57
+ }
58
+ }, console.warn);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ });
64
+ },
65
+ isValidCompletionPosition: function (yasqe) {
66
+ var cur = yasqe.getDoc().getCursor(),
67
+ token = yasqe.getTokenAt(cur);
68
+
69
+ // not at end of line
70
+ if (yasqe.getDoc().getLine(cur.line).length > cur.ch) return false;
71
+
72
+ if (token.type != "ws") {
73
+ // we want to complete token, e.g. when the prefix starts with an a
74
+ // (treated as a token in itself..)
75
+ // but we to avoid including the PREFIX tag. So when we have just
76
+ // typed a space after the prefix tag, don't get the complete token
77
+ token = yasqe.getCompleteToken();
78
+ }
79
+
80
+ // we shouldnt be at the uri part the prefix declaration
81
+ // also check whether current token isnt 'a' (that makes codemirror
82
+ // thing a namespace is a possiblecurrent
83
+ if (token.string.indexOf("a") !== 0 && token.state.possibleCurrent.indexOf("PNAME_NS") < 0) return false;
84
+
85
+ // First token of line needs to be PREFIX,
86
+ // there should be no trailing text (otherwise, text is wrongly inserted
87
+ // in between)
88
+ var previousToken = yasqe.getPreviousNonWsToken(cur.line, token);
89
+ if (!previousToken || previousToken.string.toUpperCase() != "PREFIX") return false;
90
+ return true;
91
+ },
92
+ get: function (yasqe) {
93
+ return fetch(yasqe.config.prefixCcApi)
94
+ .then((response) => {
95
+ if (!response.ok) {
96
+ throw new Error("Failed to fetch prefixes");
97
+ }
98
+ return response.json();
99
+ })
100
+ .then((resp) => {
101
+ const prefixArray: string[] = [];
102
+ for (const prefix in resp) {
103
+ const completeString = `${prefix}: <${resp[prefix]}>`;
104
+ prefixArray.push(completeString); // the array we want to store in local storage
105
+ }
106
+ return prefixArray.sort();
107
+ });
108
+ },
109
+ preProcessToken: function (yasqe, token) {
110
+ var previousToken = yasqe.getPreviousNonWsToken(yasqe.getDoc().getCursor().line, token);
111
+ if (previousToken && previousToken.string && previousToken.string.slice(-1) == ":") {
112
+ //combine both tokens! In this case we have the cursor at the end of line "PREFIX bla: <".
113
+ //we want the token to be "bla: <", en not "<"
114
+ token = {
115
+ start: previousToken.start,
116
+ end: token.end,
117
+ string: previousToken.string + " " + token.string,
118
+ state: token.state,
119
+ type: token.type,
120
+ };
121
+ }
122
+ return token;
123
+ },
124
+ bulk: true,
125
+ autoShow: true,
126
+ persistenceId: "prefixes",
127
+ name: "prefixes",
128
+ };
129
+
130
+ export default conf;
@@ -0,0 +1,28 @@
1
+ import * as Autocompleter from "./";
2
+
3
+ var conf: Autocompleter.CompleterConfig = {
4
+ onInitialize: function (_yasqe) {
5
+ // validPosition: yasqe.autocompleters.notifications.show,
6
+ // invalidPosition: yasqe.autocompleters.notifications.hide
7
+ },
8
+ get: function (yasqe, token) {
9
+ return Autocompleter.fetchFromLov(yasqe, "property", token);
10
+ },
11
+ isValidCompletionPosition: function (yasqe) {
12
+ const token = yasqe.getCompleteToken();
13
+ if (token.string.length == 0) return false; //we want -something- to autocomplete
14
+ if (token.string[0] === "?" || token.string[0] === "$") return false; // we are typing a var
15
+ if (token.state.possibleCurrent.indexOf("a") >= 0) return true; // predicate pos
16
+ return false;
17
+ },
18
+ preProcessToken: function (yasqe, token) {
19
+ return Autocompleter.preprocessIriForCompletion(yasqe, token);
20
+ },
21
+ postProcessSuggestion: function (yasqe, token, suggestedString) {
22
+ return Autocompleter.postprocessIriCompletion(yasqe, token, suggestedString);
23
+ },
24
+ bulk: false,
25
+ name: "property",
26
+ };
27
+
28
+ export default conf;
@@ -0,0 +1,38 @@
1
+ //This is a copy of the codemirror show-hint css
2
+ //Including this ourselves, as we don't want this css to be auto-scoped to yasqe by webpack
3
+ .CodeMirror-hints {
4
+ position: absolute;
5
+ z-index: 10;
6
+ overflow: hidden;
7
+ list-style: none;
8
+
9
+ margin: 0;
10
+ padding: 2px;
11
+
12
+ -webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
13
+ -moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
14
+ box-shadow: 2px 3px 5px rgba(0, 0, 0, 0.2);
15
+ border-radius: 3px;
16
+ border: 1px solid silver;
17
+
18
+ background: white;
19
+ font-size: 90%;
20
+ font-family: monospace;
21
+
22
+ max-height: 20em;
23
+ overflow-y: auto;
24
+ }
25
+
26
+ .CodeMirror-hint {
27
+ margin: 0;
28
+ padding: 0 4px;
29
+ border-radius: 2px;
30
+ white-space: pre;
31
+ color: black;
32
+ cursor: pointer;
33
+ }
34
+
35
+ li.CodeMirror-hint-active {
36
+ background: #08f;
37
+ color: white;
38
+ }
@@ -0,0 +1,52 @@
1
+ import * as autocompleter from "./";
2
+
3
+ var conf: autocompleter.CompleterConfig = {
4
+ name: "variables",
5
+ isValidCompletionPosition: function (yasqe) {
6
+ var token = yasqe.getTokenAt(yasqe.getDoc().getCursor());
7
+ if (token.type != "ws") {
8
+ token = yasqe.getCompleteToken(token);
9
+ if (token && (token.string[0] === "?" || token.string[0] === "$")) {
10
+ return true;
11
+ }
12
+ }
13
+ return false;
14
+ },
15
+ get: function (yasqe, token) {
16
+ if (!token || token.string.length == 0) return []; //nothing to autocomplete
17
+ const distinctVars: { [varname: string]: any } = {};
18
+ const vars: string[] = [];
19
+ //do this outside of codemirror. I expect jquery to be faster here (just finding dom elements with classnames)
20
+ //and: this'll still work when the query is incorrect (i.e., when simply typing '?')
21
+ const atoms = yasqe.getWrapperElement().querySelectorAll(".cm-atom");
22
+ for (let i = 0; i < atoms.length; i++) {
23
+ const atom = atoms[i];
24
+ var variable = atom.innerHTML;
25
+ if (variable[0] === "?" || variable[0] === "$") {
26
+ //ok, lets check if the next element in the div is an atom as well. In that case, they belong together (may happen sometimes when query is not syntactically valid)
27
+ // var nextElClass = nextEl.attr("class");
28
+ const nextEl: HTMLElement = <any>atom.nextSibling;
29
+ if (nextEl && nextEl.className && nextEl.className.indexOf("cm-atom") >= 0) {
30
+ variable += nextEl.innerText;
31
+ }
32
+ if (distinctVars[variable]) continue; //already in list
33
+ //skip single questionmarks
34
+ if (variable.length <= 1) continue;
35
+
36
+ //it should match our token ofcourse
37
+ if (variable.indexOf(token.string) !== 0) continue;
38
+
39
+ //skip exact matches
40
+ if (variable === token.string) continue;
41
+
42
+ //store in map so we have a unique list
43
+ distinctVars[variable] = true;
44
+ vars.push(variable);
45
+ }
46
+ }
47
+ return vars.sort();
48
+ },
49
+ bulk: false,
50
+ autoShow: true,
51
+ };
52
+ export default conf;