@difizen/libro-codemirror 0.0.2-alpha.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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +0 -0
  3. package/es/auto-complete/closebrackets.d.ts +12 -0
  4. package/es/auto-complete/closebrackets.d.ts.map +1 -0
  5. package/es/auto-complete/closebrackets.js +408 -0
  6. package/es/auto-complete/completion.d.ts +57 -0
  7. package/es/auto-complete/completion.d.ts.map +1 -0
  8. package/es/auto-complete/completion.js +265 -0
  9. package/es/auto-complete/config.d.ts +22 -0
  10. package/es/auto-complete/config.d.ts.map +1 -0
  11. package/es/auto-complete/config.js +44 -0
  12. package/es/auto-complete/filter.d.ts +13 -0
  13. package/es/auto-complete/filter.d.ts.map +1 -0
  14. package/es/auto-complete/filter.js +191 -0
  15. package/es/auto-complete/index.d.ts +17 -0
  16. package/es/auto-complete/index.d.ts.map +1 -0
  17. package/es/auto-complete/index.js +107 -0
  18. package/es/auto-complete/snippet.d.ts +14 -0
  19. package/es/auto-complete/snippet.d.ts.map +1 -0
  20. package/es/auto-complete/snippet.js +447 -0
  21. package/es/auto-complete/state.d.ts +63 -0
  22. package/es/auto-complete/state.d.ts.map +1 -0
  23. package/es/auto-complete/state.js +452 -0
  24. package/es/auto-complete/theme.d.ts +6 -0
  25. package/es/auto-complete/theme.d.ts.map +1 -0
  26. package/es/auto-complete/theme.js +151 -0
  27. package/es/auto-complete/tooltip.d.ts +5 -0
  28. package/es/auto-complete/tooltip.d.ts.map +1 -0
  29. package/es/auto-complete/tooltip.js +365 -0
  30. package/es/auto-complete/view.d.ts +43 -0
  31. package/es/auto-complete/view.d.ts.map +1 -0
  32. package/es/auto-complete/view.js +372 -0
  33. package/es/auto-complete/word.d.ts +3 -0
  34. package/es/auto-complete/word.d.ts.map +1 -0
  35. package/es/auto-complete/word.js +119 -0
  36. package/es/completion.d.ts +6 -0
  37. package/es/completion.d.ts.map +1 -0
  38. package/es/completion.js +84 -0
  39. package/es/config.d.ts +184 -0
  40. package/es/config.d.ts.map +1 -0
  41. package/es/config.js +473 -0
  42. package/es/editor.d.ts +361 -0
  43. package/es/editor.d.ts.map +1 -0
  44. package/es/editor.js +1126 -0
  45. package/es/factory.d.ts +3 -0
  46. package/es/factory.d.ts.map +1 -0
  47. package/es/factory.js +12 -0
  48. package/es/hyperlink.d.ts +15 -0
  49. package/es/hyperlink.d.ts.map +1 -0
  50. package/es/hyperlink.js +120 -0
  51. package/es/indent.d.ts +8 -0
  52. package/es/indent.d.ts.map +1 -0
  53. package/es/indent.js +58 -0
  54. package/es/indentation-markers/config.d.ts +17 -0
  55. package/es/indentation-markers/config.d.ts.map +1 -0
  56. package/es/indentation-markers/config.js +10 -0
  57. package/es/indentation-markers/index.d.ts +3 -0
  58. package/es/indentation-markers/index.d.ts.map +1 -0
  59. package/es/indentation-markers/index.js +160 -0
  60. package/es/indentation-markers/map.d.ts +77 -0
  61. package/es/indentation-markers/map.d.ts.map +1 -0
  62. package/es/indentation-markers/map.js +265 -0
  63. package/es/indentation-markers/utils.d.ts +27 -0
  64. package/es/indentation-markers/utils.d.ts.map +1 -0
  65. package/es/indentation-markers/utils.js +91 -0
  66. package/es/index.d.ts +11 -0
  67. package/es/index.d.ts.map +1 -0
  68. package/es/index.js +10 -0
  69. package/es/libro-icon.d.ts +3 -0
  70. package/es/libro-icon.d.ts.map +1 -0
  71. package/es/libro-icon.js +2 -0
  72. package/es/mimetype.d.ts +22 -0
  73. package/es/mimetype.d.ts.map +1 -0
  74. package/es/mimetype.js +59 -0
  75. package/es/mode.d.ts +86 -0
  76. package/es/mode.d.ts.map +1 -0
  77. package/es/mode.js +284 -0
  78. package/es/monitor.d.ts +32 -0
  79. package/es/monitor.d.ts.map +1 -0
  80. package/es/monitor.js +129 -0
  81. package/es/python-lang.d.ts +3 -0
  82. package/es/python-lang.d.ts.map +1 -0
  83. package/es/python-lang.js +7 -0
  84. package/es/style/base.css +131 -0
  85. package/es/style/theme.css +12 -0
  86. package/es/style/variables.css +403 -0
  87. package/es/theme.d.ts +35 -0
  88. package/es/theme.d.ts.map +1 -0
  89. package/es/theme.js +225 -0
  90. package/es/tooltip.d.ts +10 -0
  91. package/es/tooltip.d.ts.map +1 -0
  92. package/es/tooltip.js +170 -0
  93. package/package.json +74 -0
  94. package/src/auto-complete/README.md +71 -0
  95. package/src/auto-complete/closebrackets.ts +423 -0
  96. package/src/auto-complete/completion.ts +345 -0
  97. package/src/auto-complete/config.ts +101 -0
  98. package/src/auto-complete/filter.ts +215 -0
  99. package/src/auto-complete/index.ts +112 -0
  100. package/src/auto-complete/snippet.ts +394 -0
  101. package/src/auto-complete/state.ts +472 -0
  102. package/src/auto-complete/theme.ts +126 -0
  103. package/src/auto-complete/tooltip.ts +386 -0
  104. package/src/auto-complete/view.ts +343 -0
  105. package/src/auto-complete/word.ts +118 -0
  106. package/src/completion.ts +61 -0
  107. package/src/config.ts +689 -0
  108. package/src/editor.ts +1078 -0
  109. package/src/factory.ts +10 -0
  110. package/src/hyperlink.ts +95 -0
  111. package/src/indent.ts +69 -0
  112. package/src/indentation-markers/config.ts +31 -0
  113. package/src/indentation-markers/index.ts +192 -0
  114. package/src/indentation-markers/map.ts +273 -0
  115. package/src/indentation-markers/utils.ts +84 -0
  116. package/src/index.ts +11 -0
  117. package/src/libro-icon.ts +4 -0
  118. package/src/mimetype.ts +49 -0
  119. package/src/mode.ts +269 -0
  120. package/src/monitor.ts +105 -0
  121. package/src/python-lang.ts +7 -0
  122. package/src/style/base.css +129 -0
  123. package/src/style/theme.css +12 -0
  124. package/src/style/variables.css +405 -0
  125. package/src/theme.ts +231 -0
  126. package/src/tooltip.ts +145 -0
@@ -0,0 +1,423 @@
1
+ import { syntaxTree } from '@codemirror/language';
2
+ import type {
3
+ EditorState,
4
+ Transaction,
5
+ Extension,
6
+ StateCommand,
7
+ Text,
8
+ } from '@codemirror/state';
9
+ import {
10
+ EditorSelection,
11
+ StateField,
12
+ StateEffect,
13
+ MapMode,
14
+ CharCategory,
15
+ codePointAt,
16
+ fromCodePoint,
17
+ codePointSize,
18
+ RangeSet,
19
+ RangeValue,
20
+ } from '@codemirror/state';
21
+ import type { KeyBinding } from '@codemirror/view';
22
+ import { EditorView } from '@codemirror/view';
23
+
24
+ /// Configures bracket closing behavior for a syntax (via
25
+ /// [language data](#state.EditorState.languageDataAt)) using the `"closeBrackets"`
26
+ /// identifier.
27
+ export interface CloseBracketConfig {
28
+ /// The opening brackets to close. Defaults to `["(", "[", "{", "'",
29
+ /// '"']`. Brackets may be single characters or a triple of quotes
30
+ /// (as in `"''''"`).
31
+ brackets?: string[];
32
+ /// Characters in front of which newly opened brackets are
33
+ /// automatically closed. Closing always happens in front of
34
+ /// whitespace. Defaults to `")]}:;>"`.
35
+ before?: string;
36
+ /// When determining whether a given node may be a string, recognize
37
+ /// these prefixes before the opening quote.
38
+ stringPrefixes?: string[];
39
+ }
40
+
41
+ const defaults: Required<CloseBracketConfig> = {
42
+ brackets: ['(', '[', '{', "'", '"'],
43
+ before: ')]}:;>',
44
+ stringPrefixes: [],
45
+ };
46
+
47
+ const closeBracketEffect = StateEffect.define<number>({
48
+ map(value, mapping) {
49
+ const mapped = mapping.mapPos(value, -1, MapMode.TrackAfter);
50
+ return mapped === null ? undefined : mapped;
51
+ },
52
+ });
53
+ const skipBracketEffect = StateEffect.define<number>({
54
+ map(value, mapping) {
55
+ return mapping.mapPos(value);
56
+ },
57
+ });
58
+
59
+ const closedBracket = new (class extends RangeValue {})();
60
+ closedBracket.startSide = 1;
61
+ closedBracket.endSide = -1;
62
+
63
+ const bracketState = StateField.define<RangeSet<typeof closedBracket>>({
64
+ create() {
65
+ return RangeSet.empty;
66
+ },
67
+ update(value, tr) {
68
+ if (tr.selection) {
69
+ const lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
70
+ const prevLineStart = tr.startState.doc.lineAt(
71
+ tr.startState.selection.main.head,
72
+ ).from;
73
+ if (lineStart !== tr.changes.mapPos(prevLineStart, -1)) {
74
+ value = RangeSet.empty;
75
+ }
76
+ }
77
+ value = value.map(tr.changes);
78
+ for (const effect of tr.effects) {
79
+ if (effect.is(closeBracketEffect)) {
80
+ value = value.update({
81
+ add: [closedBracket.range(effect.value, effect.value + 1)],
82
+ });
83
+ } else if (effect.is(skipBracketEffect)) {
84
+ value = value.update({ filter: (from) => from !== effect.value });
85
+ }
86
+ }
87
+ return value;
88
+ },
89
+ });
90
+
91
+ /// Extension to enable bracket-closing behavior. When a closeable
92
+ /// bracket is typed, its closing bracket is immediately inserted
93
+ /// after the cursor. When closing a bracket directly in front of a
94
+ /// closing bracket inserted by the extension, the cursor moves over
95
+ /// that bracket.
96
+ export function closeBrackets(): Extension {
97
+ return [inputHandler, bracketState];
98
+ }
99
+
100
+ const definedClosing = '()[]{}<>';
101
+
102
+ function closing(ch: number) {
103
+ for (let i = 0; i < definedClosing.length; i += 2) {
104
+ if (definedClosing.charCodeAt(i) === ch) {
105
+ return definedClosing.charAt(i + 1);
106
+ }
107
+ }
108
+ return fromCodePoint(ch < 128 ? ch : ch + 1);
109
+ }
110
+
111
+ function config(state: EditorState, pos: number) {
112
+ return state.languageDataAt<CloseBracketConfig>('closeBrackets', pos)[0] || defaults;
113
+ }
114
+
115
+ const android = typeof navigator === 'object' && /Android\b/.test(navigator.userAgent);
116
+
117
+ const inputHandler = EditorView.inputHandler.of((view, from, to, insert) => {
118
+ if ((android ? view.composing : view.compositionStarted) || view.state.readOnly) {
119
+ return false;
120
+ }
121
+ const sel = view.state.selection.main;
122
+ if (
123
+ insert.length > 2 ||
124
+ (insert.length === 2 && codePointSize(codePointAt(insert, 0)) === 1) ||
125
+ from !== sel.from ||
126
+ to !== sel.to
127
+ ) {
128
+ return false;
129
+ }
130
+ const tr = insertBracket(view.state, insert);
131
+ if (!tr) {
132
+ return false;
133
+ }
134
+ view.dispatch(tr);
135
+ return true;
136
+ });
137
+
138
+ /// Command that implements deleting a pair of matching brackets when
139
+ /// the cursor is between them.
140
+ export const deleteBracketPair: StateCommand = ({ state, dispatch }) => {
141
+ if (state.readOnly) {
142
+ return false;
143
+ }
144
+ const conf = config(state, state.selection.main.head);
145
+ const tokens = conf.brackets || defaults.brackets;
146
+ let dont = null;
147
+ const changes = state.changeByRange((range) => {
148
+ if (range.empty) {
149
+ const before = prevChar(state.doc, range.head);
150
+ for (const token of tokens) {
151
+ if (
152
+ token === before &&
153
+ nextChar(state.doc, range.head) === closing(codePointAt(token, 0))
154
+ ) {
155
+ return {
156
+ changes: {
157
+ from: range.head - token.length,
158
+ to: range.head + token.length,
159
+ },
160
+ range: EditorSelection.cursor(range.head - token.length),
161
+ };
162
+ }
163
+ }
164
+ }
165
+ return { range: (dont = range) };
166
+ });
167
+ if (!dont) {
168
+ dispatch(
169
+ state.update(changes, { scrollIntoView: true, userEvent: 'delete.backward' }),
170
+ );
171
+ }
172
+ return !dont;
173
+ };
174
+
175
+ /// Close-brackets related key bindings. Binds Backspace to
176
+ /// [`deleteBracketPair`](#autocomplete.deleteBracketPair).
177
+ export const closeBracketsKeymap: readonly KeyBinding[] = [
178
+ { key: 'Backspace', run: deleteBracketPair },
179
+ ];
180
+
181
+ /// Implements the extension's behavior on text insertion. If the
182
+ /// given string counts as a bracket in the language around the
183
+ /// selection, and replacing the selection with it requires custom
184
+ /// behavior (inserting a closing version or skipping past a
185
+ /// previously-closed bracket), this function returns a transaction
186
+ /// representing that custom behavior. (You only need this if you want
187
+ /// to programmatically insert brackets—the
188
+ /// [`closeBrackets`](#autocomplete.closeBrackets) extension will
189
+ /// take care of running this for user input.)
190
+ export function insertBracket(state: EditorState, bracket: string): Transaction | null {
191
+ const conf = config(state, state.selection.main.head);
192
+ const tokens = conf.brackets || defaults.brackets;
193
+ for (const tok of tokens) {
194
+ const closed = closing(codePointAt(tok, 0));
195
+ if (bracket === tok) {
196
+ return closed === tok
197
+ ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
198
+ : handleOpen(state, tok, closed, conf.before || defaults.before);
199
+ }
200
+ if (bracket === closed && closedBracketAt(state, state.selection.main.from)) {
201
+ return handleClose(state, tok, closed);
202
+ }
203
+ }
204
+ return null;
205
+ }
206
+
207
+ function closedBracketAt(state: EditorState, pos: number) {
208
+ let found = false;
209
+ state.field(bracketState).between(0, state.doc.length, (from) => {
210
+ if (from === pos) {
211
+ found = true;
212
+ }
213
+ });
214
+ return found;
215
+ }
216
+
217
+ function nextChar(doc: Text, pos: number) {
218
+ const next = doc.sliceString(pos, pos + 2);
219
+ return next.slice(0, codePointSize(codePointAt(next, 0)));
220
+ }
221
+
222
+ function prevChar(doc: Text, pos: number) {
223
+ const prev = doc.sliceString(pos - 2, pos);
224
+ return codePointSize(codePointAt(prev, 0)) === prev.length ? prev : prev.slice(1);
225
+ }
226
+
227
+ function handleOpen(
228
+ state: EditorState,
229
+ open: string,
230
+ close: string,
231
+ closeBefore: string,
232
+ ) {
233
+ let dont = null;
234
+ const changes = state.changeByRange((range) => {
235
+ if (!range.empty) {
236
+ return {
237
+ changes: [
238
+ { insert: open, from: range.from },
239
+ { insert: close, from: range.to },
240
+ ],
241
+ effects: closeBracketEffect.of(range.to + open.length),
242
+ range: EditorSelection.range(
243
+ range.anchor + open.length,
244
+ range.head + open.length,
245
+ ),
246
+ };
247
+ }
248
+ const next = nextChar(state.doc, range.head);
249
+ if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1) {
250
+ return {
251
+ changes: { insert: open + close, from: range.head },
252
+ effects: closeBracketEffect.of(range.head + open.length),
253
+ range: EditorSelection.cursor(range.head + open.length),
254
+ };
255
+ }
256
+ return { range: (dont = range) };
257
+ });
258
+ return dont
259
+ ? null
260
+ : state.update(changes, {
261
+ scrollIntoView: true,
262
+ userEvent: 'input.type',
263
+ });
264
+ }
265
+
266
+ function handleClose(state: EditorState, _open: string, close: string) {
267
+ let dont = null;
268
+ const moved = state.selection.ranges.map((range) => {
269
+ if (range.empty && nextChar(state.doc, range.head) === close) {
270
+ return EditorSelection.cursor(range.head + close.length);
271
+ }
272
+ return (dont = range);
273
+ });
274
+ return dont
275
+ ? null
276
+ : state.update({
277
+ selection: EditorSelection.create(moved, state.selection.mainIndex),
278
+ scrollIntoView: true,
279
+ effects: state.selection.ranges.map(({ from }) => skipBracketEffect.of(from)),
280
+ });
281
+ }
282
+
283
+ // Handles cases where the open and close token are the same, and
284
+ // possibly triple quotes (as in `"""abc"""`-style quoting).
285
+ function handleSame(
286
+ state: EditorState,
287
+ token: string,
288
+ allowTriple: boolean,
289
+ config: CloseBracketConfig,
290
+ ) {
291
+ const stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
292
+ let dont = null;
293
+ const changes = state.changeByRange((range) => {
294
+ if (!range.empty) {
295
+ return {
296
+ changes: [
297
+ { insert: token, from: range.from },
298
+ { insert: token, from: range.to },
299
+ ],
300
+ effects: closeBracketEffect.of(range.to + token.length),
301
+ range: EditorSelection.range(
302
+ range.anchor + token.length,
303
+ range.head + token.length,
304
+ ),
305
+ };
306
+ }
307
+ const pos = range.head;
308
+ let start;
309
+ const next = nextChar(state.doc, pos);
310
+ if (next === token) {
311
+ if (nodeStart(state, pos)) {
312
+ return {
313
+ changes: { insert: token + token, from: pos },
314
+ effects: closeBracketEffect.of(pos + token.length),
315
+ range: EditorSelection.cursor(pos + token.length),
316
+ };
317
+ } else if (closedBracketAt(state, pos)) {
318
+ const isTriple =
319
+ allowTriple &&
320
+ state.sliceDoc(pos, pos + token.length * 3) === token + token + token;
321
+ return {
322
+ range: EditorSelection.cursor(pos + token.length * (isTriple ? 3 : 1)),
323
+ effects: skipBracketEffect.of(pos),
324
+ };
325
+ }
326
+ } else if (
327
+ allowTriple &&
328
+ state.sliceDoc(pos - 2 * token.length, pos) === token + token &&
329
+ (start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 &&
330
+ nodeStart(state, start)
331
+ ) {
332
+ return {
333
+ changes: { insert: token + token + token + token, from: pos },
334
+ effects: closeBracketEffect.of(pos + token.length),
335
+ range: EditorSelection.cursor(pos + token.length),
336
+ };
337
+ } else if (state.charCategorizer(pos)(next) !== CharCategory.Word) {
338
+ if (
339
+ canStartStringAt(state, pos, stringPrefixes) > -1 &&
340
+ !probablyInString(state, pos, token, stringPrefixes)
341
+ ) {
342
+ return {
343
+ changes: { insert: token + token, from: pos },
344
+ effects: closeBracketEffect.of(pos + token.length),
345
+ range: EditorSelection.cursor(pos + token.length),
346
+ };
347
+ }
348
+ }
349
+ return { range: (dont = range) };
350
+ });
351
+ return dont
352
+ ? null
353
+ : state.update(changes, {
354
+ scrollIntoView: true,
355
+ userEvent: 'input.type',
356
+ });
357
+ }
358
+
359
+ function nodeStart(state: EditorState, pos: number) {
360
+ const tree = syntaxTree(state).resolveInner(pos + 1);
361
+ return tree.parent && tree.from === pos;
362
+ }
363
+
364
+ function probablyInString(
365
+ state: EditorState,
366
+ pos: number,
367
+ quoteToken: string,
368
+ prefixes: readonly string[],
369
+ ) {
370
+ let node = syntaxTree(state).resolveInner(pos, -1);
371
+ const maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
372
+ for (let i = 0; i < 5; i++) {
373
+ const start = state.sliceDoc(
374
+ node.from,
375
+ Math.min(node.to, node.from + quoteToken.length + maxPrefix),
376
+ );
377
+ const quotePos = start.indexOf(quoteToken);
378
+ if (
379
+ !quotePos ||
380
+ (quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1)
381
+ ) {
382
+ let first = node.firstChild;
383
+ while (
384
+ first &&
385
+ first.from === node.from &&
386
+ first.to - first.from > quoteToken.length + quotePos
387
+ ) {
388
+ if (state.sliceDoc(first.to - quoteToken.length, first.to) === quoteToken) {
389
+ return false;
390
+ }
391
+ first = first.firstChild;
392
+ }
393
+ return true;
394
+ }
395
+ const parent = node.to === pos && node.parent;
396
+ if (!parent) {
397
+ break;
398
+ }
399
+ node = parent;
400
+ }
401
+ return false;
402
+ }
403
+
404
+ function canStartStringAt(
405
+ state: EditorState,
406
+ pos: number,
407
+ prefixes: readonly string[],
408
+ ) {
409
+ const charCat = state.charCategorizer(pos);
410
+ if (charCat(state.sliceDoc(pos - 1, pos)) !== CharCategory.Word) {
411
+ return pos;
412
+ }
413
+ for (const prefix of prefixes) {
414
+ const start = pos - prefix.length;
415
+ if (
416
+ state.sliceDoc(start, pos) === prefix &&
417
+ charCat(state.sliceDoc(start - 1, start)) !== CharCategory.Word
418
+ ) {
419
+ return start;
420
+ }
421
+ }
422
+ return -1;
423
+ }