@progressive-development/pd-content 1.1.0 → 1.1.2
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/dist/node_modules/.pnpm/@codemirror_autocomplete@6.20.1/node_modules/@codemirror/autocomplete/dist/index.js +550 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-css@6.3.1/node_modules/@codemirror/lang-css/dist/index.js +264 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-html@6.4.11/node_modules/@codemirror/lang-html/dist/index.js +661 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-java@6.0.2/node_modules/@codemirror/lang-java/dist/index.js +44 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-javascript@6.2.5/node_modules/@codemirror/lang-javascript/dist/index.js +346 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-json@6.0.2/node_modules/@codemirror/lang-json/dist/index.js +32 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-markdown@6.5.0/node_modules/@codemirror/lang-markdown/dist/index.js +492 -0
- package/dist/node_modules/.pnpm/@codemirror_lang-python@6.2.1/node_modules/@codemirror/lang-python/dist/index.js +308 -0
- package/dist/node_modules/.pnpm/@codemirror_language@6.12.3/node_modules/@codemirror/language/dist/index.js +1572 -0
- package/dist/node_modules/.pnpm/@codemirror_state@6.6.0/node_modules/@codemirror/state/dist/index.js +3881 -0
- package/dist/node_modules/.pnpm/@codemirror_view@6.40.0/node_modules/@codemirror/view/dist/index.js +9657 -0
- package/dist/node_modules/.pnpm/@lezer_common@1.5.1/node_modules/@lezer/common/dist/index.js +2196 -0
- package/dist/node_modules/.pnpm/@lezer_css@1.3.3/node_modules/@lezer/css/dist/index.js +147 -0
- package/dist/node_modules/.pnpm/@lezer_highlight@1.2.3/node_modules/@lezer/highlight/dist/index.js +898 -0
- package/dist/node_modules/.pnpm/@lezer_html@1.3.13/node_modules/@lezer/html/dist/index.js +349 -0
- package/dist/node_modules/.pnpm/@lezer_java@1.1.3/node_modules/@lezer/java/dist/index.js +67 -0
- package/dist/node_modules/.pnpm/@lezer_javascript@1.5.4/node_modules/@lezer/javascript/dist/index.js +192 -0
- package/dist/node_modules/.pnpm/@lezer_json@1.0.3/node_modules/@lezer/json/dist/index.js +37 -0
- package/dist/node_modules/.pnpm/@lezer_lr@1.4.8/node_modules/@lezer/lr/dist/index.js +1884 -0
- package/dist/node_modules/.pnpm/@lezer_markdown@1.6.3/node_modules/@lezer/markdown/dist/index.js +2335 -0
- package/dist/node_modules/.pnpm/@lezer_python@1.1.18/node_modules/@lezer/python/dist/index.js +326 -0
- package/dist/node_modules/.pnpm/@marijn_find-cluster-break@1.0.2/node_modules/@marijn/find-cluster-break/src/index.js +82 -0
- package/dist/node_modules/.pnpm/style-mod@4.1.3/node_modules/style-mod/src/style-mod.js +174 -0
- package/dist/node_modules/.pnpm/w3c-keyname@2.2.8/node_modules/w3c-keyname/index.js +121 -0
- package/dist/pd-badge-order/PdBadgeItem.d.ts +11 -0
- package/dist/pd-badge-order/PdBadgeItem.d.ts.map +1 -1
- package/dist/pd-badge-order/PdBadgeItem.js +162 -10
- package/dist/pd-badge-order/PdBadgeOrder.d.ts +56 -17
- package/dist/pd-badge-order/PdBadgeOrder.d.ts.map +1 -1
- package/dist/pd-badge-order/PdBadgeOrder.js +308 -153
- package/dist/pd-badge-order/types.js +3 -1
- package/dist/pd-code-snippet/PdCodeSnippet.d.ts +29 -0
- package/dist/pd-code-snippet/PdCodeSnippet.d.ts.map +1 -1
- package/dist/pd-code-snippet/PdCodeSnippet.js +117 -67
- package/dist/pd-code-snippet/codemirror-setup.d.ts +10 -0
- package/dist/pd-code-snippet/codemirror-setup.d.ts.map +1 -0
- package/dist/pd-code-snippet/codemirror-setup.js +101 -0
- package/dist/pd-more-info/PdMoreInfo.d.ts +48 -4
- package/dist/pd-more-info/PdMoreInfo.d.ts.map +1 -1
- package/dist/pd-more-info/PdMoreInfo.js +132 -17
- package/dist/pd-notice-box/PdNoticeBox.js +1 -1
- package/dist/pd-panel-viewer/PdPanel.d.ts +3 -0
- package/dist/pd-panel-viewer/PdPanel.d.ts.map +1 -1
- package/dist/pd-panel-viewer/PdPanel.js +8 -1
- package/dist/pd-panel-viewer/PdPanelViewer.d.ts +1 -0
- package/dist/pd-panel-viewer/PdPanelViewer.d.ts.map +1 -1
- package/dist/pd-panel-viewer/PdPanelViewer.js +44 -28
- package/dist/pd-timeline/PdTimeline.d.ts +2 -0
- package/dist/pd-timeline/PdTimeline.d.ts.map +1 -1
- package/dist/pd-timeline/PdTimeline.js +50 -19
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +16 -6
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { Transaction, Text, StateEffect, Annotation, EditorSelection, StateField, Prec, MapMode, Facet, RangeValue } from '../../../../../@codemirror_state@6.6.0/node_modules/@codemirror/state/dist/index.js';
|
|
2
|
+
import { Decoration, EditorView, keymap, WidgetType } from '../../../../../@codemirror_view@6.40.0/node_modules/@codemirror/view/dist/index.js';
|
|
3
|
+
import { syntaxTree, indentUnit } from '../../../../../@codemirror_language@6.12.3/node_modules/@codemirror/language/dist/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
An instance of this is passed to completion source functions.
|
|
7
|
+
*/
|
|
8
|
+
class CompletionContext {
|
|
9
|
+
/**
|
|
10
|
+
Create a new completion context. (Mostly useful for testing
|
|
11
|
+
completion sources—in the editor, the extension will create
|
|
12
|
+
these for you.)
|
|
13
|
+
*/
|
|
14
|
+
constructor(
|
|
15
|
+
/**
|
|
16
|
+
The editor state that the completion happens in.
|
|
17
|
+
*/
|
|
18
|
+
state,
|
|
19
|
+
/**
|
|
20
|
+
The position at which the completion is happening.
|
|
21
|
+
*/
|
|
22
|
+
pos,
|
|
23
|
+
/**
|
|
24
|
+
Indicates whether completion was activated explicitly, or
|
|
25
|
+
implicitly by typing. The usual way to respond to this is to
|
|
26
|
+
only return completions when either there is part of a
|
|
27
|
+
completable entity before the cursor, or `explicit` is true.
|
|
28
|
+
*/
|
|
29
|
+
explicit,
|
|
30
|
+
/**
|
|
31
|
+
The editor view. May be undefined if the context was created
|
|
32
|
+
in a situation where there is no such view available, such as
|
|
33
|
+
in synchronous updates via
|
|
34
|
+
[`CompletionResult.update`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.update)
|
|
35
|
+
or when called by test code.
|
|
36
|
+
*/
|
|
37
|
+
view) {
|
|
38
|
+
this.state = state;
|
|
39
|
+
this.pos = pos;
|
|
40
|
+
this.explicit = explicit;
|
|
41
|
+
this.view = view;
|
|
42
|
+
/**
|
|
43
|
+
@internal
|
|
44
|
+
*/
|
|
45
|
+
this.abortListeners = [];
|
|
46
|
+
/**
|
|
47
|
+
@internal
|
|
48
|
+
*/
|
|
49
|
+
this.abortOnDocChange = false;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
Get the extent, content, and (if there is a token) type of the
|
|
53
|
+
token before `this.pos`.
|
|
54
|
+
*/
|
|
55
|
+
tokenBefore(types) {
|
|
56
|
+
let token = syntaxTree(this.state).resolveInner(this.pos, -1);
|
|
57
|
+
while (token && types.indexOf(token.name) < 0)
|
|
58
|
+
token = token.parent;
|
|
59
|
+
return token ? { from: token.from, to: this.pos,
|
|
60
|
+
text: this.state.sliceDoc(token.from, this.pos),
|
|
61
|
+
type: token.type } : null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
Get the match of the given expression directly before the
|
|
65
|
+
cursor.
|
|
66
|
+
*/
|
|
67
|
+
matchBefore(expr) {
|
|
68
|
+
let line = this.state.doc.lineAt(this.pos);
|
|
69
|
+
let start = Math.max(line.from, this.pos - 250);
|
|
70
|
+
let str = line.text.slice(start - line.from, this.pos - line.from);
|
|
71
|
+
let found = str.search(ensureAnchor(expr));
|
|
72
|
+
return found < 0 ? null : { from: start + found, to: this.pos, text: str.slice(found) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
Yields true when the query has been aborted. Can be useful in
|
|
76
|
+
asynchronous queries to avoid doing work that will be ignored.
|
|
77
|
+
*/
|
|
78
|
+
get aborted() { return this.abortListeners == null; }
|
|
79
|
+
/**
|
|
80
|
+
Allows you to register abort handlers, which will be called when
|
|
81
|
+
the query is
|
|
82
|
+
[aborted](https://codemirror.net/6/docs/ref/#autocomplete.CompletionContext.aborted).
|
|
83
|
+
|
|
84
|
+
By default, running queries will not be aborted for regular
|
|
85
|
+
typing or backspacing, on the assumption that they are likely to
|
|
86
|
+
return a result with a
|
|
87
|
+
[`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor) field that
|
|
88
|
+
allows the result to be used after all. Passing `onDocChange:
|
|
89
|
+
true` will cause this query to be aborted for any document
|
|
90
|
+
change.
|
|
91
|
+
*/
|
|
92
|
+
addEventListener(type, listener, options) {
|
|
93
|
+
if (type == "abort" && this.abortListeners) {
|
|
94
|
+
this.abortListeners.push(listener);
|
|
95
|
+
if (options && options.onDocChange)
|
|
96
|
+
this.abortOnDocChange = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function toSet(chars) {
|
|
101
|
+
let flat = Object.keys(chars).join("");
|
|
102
|
+
let words = /\w/.test(flat);
|
|
103
|
+
if (words)
|
|
104
|
+
flat = flat.replace(/\w/g, "");
|
|
105
|
+
return `[${words ? "\\w" : ""}${flat.replace(/[^\w\s]/g, "\\$&")}]`;
|
|
106
|
+
}
|
|
107
|
+
function prefixMatch(options) {
|
|
108
|
+
let first = Object.create(null), rest = Object.create(null);
|
|
109
|
+
for (let { label } of options) {
|
|
110
|
+
first[label[0]] = true;
|
|
111
|
+
for (let i = 1; i < label.length; i++)
|
|
112
|
+
rest[label[i]] = true;
|
|
113
|
+
}
|
|
114
|
+
let source = toSet(first) + toSet(rest) + "*$";
|
|
115
|
+
return [new RegExp("^" + source), new RegExp(source)];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
Given a a fixed array of options, return an autocompleter that
|
|
119
|
+
completes them.
|
|
120
|
+
*/
|
|
121
|
+
function completeFromList(list) {
|
|
122
|
+
let options = list.map(o => typeof o == "string" ? { label: o } : o);
|
|
123
|
+
let [validFor, match] = options.every(o => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
|
|
124
|
+
return (context) => {
|
|
125
|
+
let token = context.matchBefore(match);
|
|
126
|
+
return token || context.explicit ? { from: token ? token.from : context.pos, options, validFor } : null;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
Wrap the given completion source so that it will not fire when the
|
|
131
|
+
cursor is in a syntax node with one of the given names.
|
|
132
|
+
*/
|
|
133
|
+
function ifNotIn(nodes, source) {
|
|
134
|
+
return (context) => {
|
|
135
|
+
for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) {
|
|
136
|
+
if (nodes.indexOf(pos.name) > -1)
|
|
137
|
+
return null;
|
|
138
|
+
if (pos.type.isTop)
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
return source(context);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Make sure the given regexp has a $ at its end and, if `start` is
|
|
145
|
+
// true, a ^ at its start.
|
|
146
|
+
function ensureAnchor(expr, start) {
|
|
147
|
+
var _a;
|
|
148
|
+
let { source } = expr;
|
|
149
|
+
let addEnd = source[source.length - 1] != "$";
|
|
150
|
+
if (!addEnd)
|
|
151
|
+
return expr;
|
|
152
|
+
return new RegExp(`${""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
This annotation is added to transactions that are produced by
|
|
156
|
+
picking a completion.
|
|
157
|
+
*/
|
|
158
|
+
const pickedCompletion = /*@__PURE__*/Annotation.define();
|
|
159
|
+
|
|
160
|
+
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
161
|
+
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
162
|
+
"& > ul": {
|
|
163
|
+
fontFamily: "monospace",
|
|
164
|
+
whiteSpace: "nowrap",
|
|
165
|
+
overflow: "hidden auto",
|
|
166
|
+
maxWidth_fallback: "700px",
|
|
167
|
+
maxWidth: "min(700px, 95vw)",
|
|
168
|
+
minWidth: "250px",
|
|
169
|
+
maxHeight: "10em",
|
|
170
|
+
height: "100%",
|
|
171
|
+
listStyle: "none",
|
|
172
|
+
margin: 0,
|
|
173
|
+
padding: 0,
|
|
174
|
+
"& > li, & > completion-section": {
|
|
175
|
+
padding: "1px 3px",
|
|
176
|
+
lineHeight: 1.2
|
|
177
|
+
},
|
|
178
|
+
"& > li": {
|
|
179
|
+
overflowX: "hidden",
|
|
180
|
+
textOverflow: "ellipsis",
|
|
181
|
+
cursor: "pointer"
|
|
182
|
+
},
|
|
183
|
+
"& > completion-section": {
|
|
184
|
+
display: "list-item",
|
|
185
|
+
borderBottom: "1px solid silver",
|
|
186
|
+
paddingLeft: "0.5em",
|
|
187
|
+
opacity: 0.7
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
192
|
+
background: "#17c",
|
|
193
|
+
color: "white",
|
|
194
|
+
},
|
|
195
|
+
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
196
|
+
background: "#777",
|
|
197
|
+
},
|
|
198
|
+
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
199
|
+
background: "#347",
|
|
200
|
+
color: "white",
|
|
201
|
+
},
|
|
202
|
+
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
203
|
+
background: "#444",
|
|
204
|
+
},
|
|
205
|
+
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
206
|
+
content: '"···"',
|
|
207
|
+
opacity: 0.5,
|
|
208
|
+
display: "block",
|
|
209
|
+
textAlign: "center"
|
|
210
|
+
},
|
|
211
|
+
".cm-tooltip.cm-completionInfo": {
|
|
212
|
+
position: "absolute",
|
|
213
|
+
padding: "3px 9px",
|
|
214
|
+
width: "max-content",
|
|
215
|
+
maxWidth: `${400 /* Info.Width */}px`,
|
|
216
|
+
boxSizing: "border-box",
|
|
217
|
+
whiteSpace: "pre-line"
|
|
218
|
+
},
|
|
219
|
+
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
220
|
+
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
221
|
+
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
|
|
222
|
+
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
|
|
223
|
+
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
224
|
+
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
225
|
+
".cm-snippetFieldPosition": {
|
|
226
|
+
verticalAlign: "text-top",
|
|
227
|
+
width: 0,
|
|
228
|
+
height: "1.15em",
|
|
229
|
+
display: "inline-block",
|
|
230
|
+
margin: "0 -0.7px -.7em",
|
|
231
|
+
borderLeft: "1.4px dotted #888"
|
|
232
|
+
},
|
|
233
|
+
".cm-completionMatchedText": {
|
|
234
|
+
textDecoration: "underline"
|
|
235
|
+
},
|
|
236
|
+
".cm-completionDetail": {
|
|
237
|
+
marginLeft: "0.5em",
|
|
238
|
+
fontStyle: "italic"
|
|
239
|
+
},
|
|
240
|
+
".cm-completionIcon": {
|
|
241
|
+
fontSize: "90%",
|
|
242
|
+
width: ".8em",
|
|
243
|
+
display: "inline-block",
|
|
244
|
+
textAlign: "center",
|
|
245
|
+
paddingRight: ".6em",
|
|
246
|
+
opacity: "0.6",
|
|
247
|
+
boxSizing: "content-box"
|
|
248
|
+
},
|
|
249
|
+
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
250
|
+
"&:after": { content: "'ƒ'" }
|
|
251
|
+
},
|
|
252
|
+
".cm-completionIcon-class": {
|
|
253
|
+
"&:after": { content: "'○'" }
|
|
254
|
+
},
|
|
255
|
+
".cm-completionIcon-interface": {
|
|
256
|
+
"&:after": { content: "'◌'" }
|
|
257
|
+
},
|
|
258
|
+
".cm-completionIcon-variable": {
|
|
259
|
+
"&:after": { content: "'𝑥'" }
|
|
260
|
+
},
|
|
261
|
+
".cm-completionIcon-constant": {
|
|
262
|
+
"&:after": { content: "'𝐶'" }
|
|
263
|
+
},
|
|
264
|
+
".cm-completionIcon-type": {
|
|
265
|
+
"&:after": { content: "'𝑡'" }
|
|
266
|
+
},
|
|
267
|
+
".cm-completionIcon-enum": {
|
|
268
|
+
"&:after": { content: "'∪'" }
|
|
269
|
+
},
|
|
270
|
+
".cm-completionIcon-property": {
|
|
271
|
+
"&:after": { content: "'□'" }
|
|
272
|
+
},
|
|
273
|
+
".cm-completionIcon-keyword": {
|
|
274
|
+
"&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
|
|
275
|
+
},
|
|
276
|
+
".cm-completionIcon-namespace": {
|
|
277
|
+
"&:after": { content: "'▢'" }
|
|
278
|
+
},
|
|
279
|
+
".cm-completionIcon-text": {
|
|
280
|
+
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
class FieldPos {
|
|
285
|
+
constructor(field, line, from, to) {
|
|
286
|
+
this.field = field;
|
|
287
|
+
this.line = line;
|
|
288
|
+
this.from = from;
|
|
289
|
+
this.to = to;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
class FieldRange {
|
|
293
|
+
constructor(field, from, to) {
|
|
294
|
+
this.field = field;
|
|
295
|
+
this.from = from;
|
|
296
|
+
this.to = to;
|
|
297
|
+
}
|
|
298
|
+
map(changes) {
|
|
299
|
+
let from = changes.mapPos(this.from, -1, MapMode.TrackDel);
|
|
300
|
+
let to = changes.mapPos(this.to, 1, MapMode.TrackDel);
|
|
301
|
+
return from == null || to == null ? null : new FieldRange(this.field, from, to);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
class Snippet {
|
|
305
|
+
constructor(lines, fieldPositions) {
|
|
306
|
+
this.lines = lines;
|
|
307
|
+
this.fieldPositions = fieldPositions;
|
|
308
|
+
}
|
|
309
|
+
instantiate(state, pos) {
|
|
310
|
+
let text = [], lineStart = [pos];
|
|
311
|
+
let lineObj = state.doc.lineAt(pos), baseIndent = /^\s*/.exec(lineObj.text)[0];
|
|
312
|
+
for (let line of this.lines) {
|
|
313
|
+
if (text.length) {
|
|
314
|
+
let indent = baseIndent, tabs = /^\t*/.exec(line)[0].length;
|
|
315
|
+
for (let i = 0; i < tabs; i++)
|
|
316
|
+
indent += state.facet(indentUnit);
|
|
317
|
+
lineStart.push(pos + indent.length - tabs);
|
|
318
|
+
line = indent + line.slice(tabs);
|
|
319
|
+
}
|
|
320
|
+
text.push(line);
|
|
321
|
+
pos += line.length + 1;
|
|
322
|
+
}
|
|
323
|
+
let ranges = this.fieldPositions.map(pos => new FieldRange(pos.field, lineStart[pos.line] + pos.from, lineStart[pos.line] + pos.to));
|
|
324
|
+
return { text, ranges };
|
|
325
|
+
}
|
|
326
|
+
static parse(template) {
|
|
327
|
+
let fields = [];
|
|
328
|
+
let lines = [], positions = [], m;
|
|
329
|
+
for (let line of template.split(/\r\n?|\n/)) {
|
|
330
|
+
while (m = /[#$]\{(?:(\d+)(?::([^{}]*))?|((?:\\[{}]|[^{}])*))\}/.exec(line)) {
|
|
331
|
+
let seq = m[1] ? +m[1] : null, rawName = m[2] || m[3] || "", found = -1;
|
|
332
|
+
let name = rawName.replace(/\\[{}]/g, m => m[1]);
|
|
333
|
+
for (let i = 0; i < fields.length; i++) {
|
|
334
|
+
if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
|
|
335
|
+
found = i;
|
|
336
|
+
}
|
|
337
|
+
if (found < 0) {
|
|
338
|
+
let i = 0;
|
|
339
|
+
while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
|
|
340
|
+
i++;
|
|
341
|
+
fields.splice(i, 0, { seq, name });
|
|
342
|
+
found = i;
|
|
343
|
+
for (let pos of positions)
|
|
344
|
+
if (pos.field >= found)
|
|
345
|
+
pos.field++;
|
|
346
|
+
}
|
|
347
|
+
for (let pos of positions)
|
|
348
|
+
if (pos.line == lines.length && pos.from > m.index) {
|
|
349
|
+
let snip = m[2] ? 3 + (m[1] || "").length : 2;
|
|
350
|
+
pos.from -= snip;
|
|
351
|
+
pos.to -= snip;
|
|
352
|
+
}
|
|
353
|
+
positions.push(new FieldPos(found, lines.length, m.index, m.index + name.length));
|
|
354
|
+
line = line.slice(0, m.index) + rawName + line.slice(m.index + m[0].length);
|
|
355
|
+
}
|
|
356
|
+
line = line.replace(/\\([{}])/g, (_, brace, index) => {
|
|
357
|
+
for (let pos of positions)
|
|
358
|
+
if (pos.line == lines.length && pos.from > index) {
|
|
359
|
+
pos.from--;
|
|
360
|
+
pos.to--;
|
|
361
|
+
}
|
|
362
|
+
return brace;
|
|
363
|
+
});
|
|
364
|
+
lines.push(line);
|
|
365
|
+
}
|
|
366
|
+
return new Snippet(lines, positions);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
let fieldMarker = /*@__PURE__*/Decoration.widget({ widget: /*@__PURE__*/new class extends WidgetType {
|
|
370
|
+
toDOM() {
|
|
371
|
+
let span = document.createElement("span");
|
|
372
|
+
span.className = "cm-snippetFieldPosition";
|
|
373
|
+
return span;
|
|
374
|
+
}
|
|
375
|
+
ignoreEvent() { return false; }
|
|
376
|
+
} });
|
|
377
|
+
let fieldRange = /*@__PURE__*/Decoration.mark({ class: "cm-snippetField" });
|
|
378
|
+
class ActiveSnippet {
|
|
379
|
+
constructor(ranges, active) {
|
|
380
|
+
this.ranges = ranges;
|
|
381
|
+
this.active = active;
|
|
382
|
+
this.deco = Decoration.set(ranges.map(r => (r.from == r.to ? fieldMarker : fieldRange).range(r.from, r.to)), true);
|
|
383
|
+
}
|
|
384
|
+
map(changes) {
|
|
385
|
+
let ranges = [];
|
|
386
|
+
for (let r of this.ranges) {
|
|
387
|
+
let mapped = r.map(changes);
|
|
388
|
+
if (!mapped)
|
|
389
|
+
return null;
|
|
390
|
+
ranges.push(mapped);
|
|
391
|
+
}
|
|
392
|
+
return new ActiveSnippet(ranges, this.active);
|
|
393
|
+
}
|
|
394
|
+
selectionInsideField(sel) {
|
|
395
|
+
return sel.ranges.every(range => this.ranges.some(r => r.field == this.active && r.from <= range.from && r.to >= range.to));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const setActive = /*@__PURE__*/StateEffect.define({
|
|
399
|
+
map(value, changes) { return value && value.map(changes); }
|
|
400
|
+
});
|
|
401
|
+
const moveToField = /*@__PURE__*/StateEffect.define();
|
|
402
|
+
const snippetState = /*@__PURE__*/StateField.define({
|
|
403
|
+
create() { return null; },
|
|
404
|
+
update(value, tr) {
|
|
405
|
+
for (let effect of tr.effects) {
|
|
406
|
+
if (effect.is(setActive))
|
|
407
|
+
return effect.value;
|
|
408
|
+
if (effect.is(moveToField) && value)
|
|
409
|
+
return new ActiveSnippet(value.ranges, effect.value);
|
|
410
|
+
}
|
|
411
|
+
if (value && tr.docChanged)
|
|
412
|
+
value = value.map(tr.changes);
|
|
413
|
+
if (value && tr.selection && !value.selectionInsideField(tr.selection))
|
|
414
|
+
value = null;
|
|
415
|
+
return value;
|
|
416
|
+
},
|
|
417
|
+
provide: f => EditorView.decorations.from(f, val => val ? val.deco : Decoration.none)
|
|
418
|
+
});
|
|
419
|
+
function fieldSelection(ranges, field) {
|
|
420
|
+
return EditorSelection.create(ranges.filter(r => r.field == field).map(r => EditorSelection.range(r.from, r.to)));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
Convert a snippet template to a function that can
|
|
424
|
+
[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written
|
|
425
|
+
using syntax like this:
|
|
426
|
+
|
|
427
|
+
"for (let ${index} = 0; ${index} < ${end}; ${index}++) {\n\t${}\n}"
|
|
428
|
+
|
|
429
|
+
Each `${}` placeholder (you may also use `#{}`) indicates a field
|
|
430
|
+
that the user can fill in. Its name, if any, will be the default
|
|
431
|
+
content for the field.
|
|
432
|
+
|
|
433
|
+
When the snippet is activated by calling the returned function,
|
|
434
|
+
the code is inserted at the given position. Newlines in the
|
|
435
|
+
template are indented by the indentation of the start line, plus
|
|
436
|
+
one [indent unit](https://codemirror.net/6/docs/ref/#language.indentUnit) per tab character after
|
|
437
|
+
the newline.
|
|
438
|
+
|
|
439
|
+
On activation, (all instances of) the first field are selected.
|
|
440
|
+
The user can move between fields with Tab and Shift-Tab as long as
|
|
441
|
+
the fields are active. Moving to the last field or moving the
|
|
442
|
+
cursor out of the current field deactivates the fields.
|
|
443
|
+
|
|
444
|
+
The order of fields defaults to textual order, but you can add
|
|
445
|
+
numbers to placeholders (`${1}` or `${1:defaultText}`) to provide
|
|
446
|
+
a custom order.
|
|
447
|
+
|
|
448
|
+
To include a literal `{` or `}` in your template, put a backslash
|
|
449
|
+
in front of it. This will be removed and the brace will not be
|
|
450
|
+
interpreted as indicating a placeholder.
|
|
451
|
+
*/
|
|
452
|
+
function snippet(template) {
|
|
453
|
+
let snippet = Snippet.parse(template);
|
|
454
|
+
return (editor, completion, from, to) => {
|
|
455
|
+
let { text, ranges } = snippet.instantiate(editor.state, from);
|
|
456
|
+
let { main } = editor.state.selection;
|
|
457
|
+
let spec = {
|
|
458
|
+
changes: { from, to: to == main.from ? main.to : to, insert: Text.of(text) },
|
|
459
|
+
scrollIntoView: true,
|
|
460
|
+
annotations: completion ? [pickedCompletion.of(completion), Transaction.userEvent.of("input.complete")] : undefined
|
|
461
|
+
};
|
|
462
|
+
if (ranges.length)
|
|
463
|
+
spec.selection = fieldSelection(ranges, 0);
|
|
464
|
+
if (ranges.some(r => r.field > 0)) {
|
|
465
|
+
let active = new ActiveSnippet(ranges, 0);
|
|
466
|
+
let effects = spec.effects = [setActive.of(active)];
|
|
467
|
+
if (editor.state.field(snippetState, false) === undefined)
|
|
468
|
+
effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
|
|
469
|
+
}
|
|
470
|
+
editor.dispatch(editor.state.update(spec));
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function moveField(dir) {
|
|
474
|
+
return ({ state, dispatch }) => {
|
|
475
|
+
let active = state.field(snippetState, false);
|
|
476
|
+
if (!active || dir < 0 && active.active == 0)
|
|
477
|
+
return false;
|
|
478
|
+
let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
|
|
479
|
+
dispatch(state.update({
|
|
480
|
+
selection: fieldSelection(active.ranges, next),
|
|
481
|
+
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
|
|
482
|
+
scrollIntoView: true
|
|
483
|
+
}));
|
|
484
|
+
return true;
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
A command that clears the active snippet, if any.
|
|
489
|
+
*/
|
|
490
|
+
const clearSnippet = ({ state, dispatch }) => {
|
|
491
|
+
let active = state.field(snippetState, false);
|
|
492
|
+
if (!active)
|
|
493
|
+
return false;
|
|
494
|
+
dispatch(state.update({ effects: setActive.of(null) }));
|
|
495
|
+
return true;
|
|
496
|
+
};
|
|
497
|
+
/**
|
|
498
|
+
Move to the next snippet field, if available.
|
|
499
|
+
*/
|
|
500
|
+
const nextSnippetField = /*@__PURE__*/moveField(1);
|
|
501
|
+
/**
|
|
502
|
+
Move to the previous snippet field, if available.
|
|
503
|
+
*/
|
|
504
|
+
const prevSnippetField = /*@__PURE__*/moveField(-1);
|
|
505
|
+
const defaultSnippetKeymap = [
|
|
506
|
+
{ key: "Tab", run: nextSnippetField, shift: prevSnippetField },
|
|
507
|
+
{ key: "Escape", run: clearSnippet }
|
|
508
|
+
];
|
|
509
|
+
/**
|
|
510
|
+
A facet that can be used to configure the key bindings used by
|
|
511
|
+
snippets. The default binds Tab to
|
|
512
|
+
[`nextSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.nextSnippetField), Shift-Tab to
|
|
513
|
+
[`prevSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.prevSnippetField), and Escape
|
|
514
|
+
to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet).
|
|
515
|
+
*/
|
|
516
|
+
const snippetKeymap = /*@__PURE__*/Facet.define({
|
|
517
|
+
combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
|
|
518
|
+
});
|
|
519
|
+
const addSnippetKeymap = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
|
|
520
|
+
/**
|
|
521
|
+
Create a completion from a snippet. Returns an object with the
|
|
522
|
+
properties from `completion`, plus an `apply` function that
|
|
523
|
+
applies the snippet.
|
|
524
|
+
*/
|
|
525
|
+
function snippetCompletion(template, completion) {
|
|
526
|
+
return { ...completion, apply: snippet(template) };
|
|
527
|
+
}
|
|
528
|
+
const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({
|
|
529
|
+
mousedown(event, view) {
|
|
530
|
+
let active = view.state.field(snippetState, false), pos;
|
|
531
|
+
if (!active || (pos = view.posAtCoords({ x: event.clientX, y: event.clientY })) == null)
|
|
532
|
+
return false;
|
|
533
|
+
let match = active.ranges.find(r => r.from <= pos && r.to >= pos);
|
|
534
|
+
if (!match || match.field == active.active)
|
|
535
|
+
return false;
|
|
536
|
+
view.dispatch({
|
|
537
|
+
selection: fieldSelection(active.ranges, match.field),
|
|
538
|
+
effects: setActive.of(active.ranges.some(r => r.field > match.field)
|
|
539
|
+
? new ActiveSnippet(active.ranges, match.field) : null),
|
|
540
|
+
scrollIntoView: true
|
|
541
|
+
});
|
|
542
|
+
return true;
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
const closedBracket = /*@__PURE__*/new class extends RangeValue {
|
|
546
|
+
};
|
|
547
|
+
closedBracket.startSide = 1;
|
|
548
|
+
closedBracket.endSide = -1;
|
|
549
|
+
|
|
550
|
+
export { CompletionContext, clearSnippet, completeFromList, ifNotIn, nextSnippetField, pickedCompletion, prevSnippetField, snippet, snippetCompletion, snippetKeymap };
|