@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,386 @@
1
+ /* eslint-disable no-param-reassign */
2
+ /* eslint-disable @typescript-eslint/no-parameter-properties */
3
+ /* eslint-disable @typescript-eslint/parameter-properties */
4
+ /* eslint-disable prefer-const */
5
+ import type { StateField, EditorState } from '@codemirror/state';
6
+ import type { EditorView, ViewUpdate, TooltipView } from '@codemirror/view';
7
+ import { Direction, logException } from '@codemirror/view';
8
+
9
+ import type { Option, Completion } from './completion.js';
10
+ import { applyCompletion } from './completion.js';
11
+ import type { CompletionConfig } from './config.js';
12
+ import { completionConfig } from './config.js';
13
+ import type { CompletionState } from './state.js';
14
+ import { Info } from './theme.js';
15
+
16
+ type OptionContentSource = (
17
+ completion: Completion,
18
+ state: EditorState,
19
+ match: readonly number[],
20
+ ) => Node | null;
21
+
22
+ function optionContent(config: Required<CompletionConfig>): OptionContentSource[] {
23
+ const content = config.addToOptions.slice() as {
24
+ render: OptionContentSource;
25
+ position: number;
26
+ }[];
27
+ if (config.icons) {
28
+ content.push({
29
+ render(completion: Completion) {
30
+ const icon = document.createElement('div');
31
+ icon.classList.add('cm-completionIcon');
32
+ if (completion.type) {
33
+ icon.classList.add(
34
+ ...completion.type.split(/\s+/g).map((cls) => 'cm-completionIcon-' + cls),
35
+ );
36
+ }
37
+ icon.setAttribute('aria-hidden', 'true');
38
+ return icon;
39
+ },
40
+ position: 20,
41
+ });
42
+ }
43
+ content.push(
44
+ {
45
+ render(completion: Completion, _s: EditorState, match: readonly number[]) {
46
+ const labelElt = document.createElement('span');
47
+ labelElt.className = 'cm-completionLabel';
48
+ let { label } = completion,
49
+ off = 0;
50
+ for (let j = 1; j < match.length; ) {
51
+ const from = match[j++],
52
+ to = match[j++];
53
+ if (from > off) {
54
+ labelElt.appendChild(document.createTextNode(label.slice(off, from)));
55
+ }
56
+ const span = labelElt.appendChild(document.createElement('span'));
57
+ span.appendChild(document.createTextNode(label.slice(from, to)));
58
+ span.className = 'cm-completionMatchedText';
59
+ off = to;
60
+ }
61
+ if (off < label.length) {
62
+ labelElt.appendChild(document.createTextNode(label.slice(off)));
63
+ }
64
+ return labelElt;
65
+ },
66
+ position: 50,
67
+ },
68
+ {
69
+ render(completion: Completion) {
70
+ if (!completion.detail) {
71
+ return null;
72
+ }
73
+ const detailElt = document.createElement('span');
74
+ detailElt.className = 'cm-completionDetail';
75
+ detailElt.textContent = completion.detail;
76
+ return detailElt;
77
+ },
78
+ position: 80,
79
+ },
80
+ );
81
+ return content.sort((a, b) => a.position - b.position).map((a) => a.render);
82
+ }
83
+
84
+ function rangeAroundSelected(total: number, selected: number, max: number) {
85
+ if (total <= max) {
86
+ return { from: 0, to: total };
87
+ }
88
+ if (selected < 0) {
89
+ selected = 0;
90
+ }
91
+ if (selected <= total >> 1) {
92
+ const off = Math.floor(selected / max);
93
+ return { from: off * max, to: (off + 1) * max };
94
+ }
95
+ const off = Math.floor((total - selected) / max);
96
+ return { from: total - (off + 1) * max, to: total - off * max };
97
+ }
98
+
99
+ class CompletionTooltip {
100
+ dom: HTMLElement;
101
+ info: HTMLElement | null = null;
102
+ list: HTMLElement;
103
+ placeInfo = {
104
+ read: () => this.measureInfo(),
105
+ write: (
106
+ pos: { top: string; bottom: string; class: string; maxWidth: string } | null,
107
+ ) => this.positionInfo(pos),
108
+ key: this,
109
+ };
110
+ range: { from: number; to: number };
111
+ optionContent: OptionContentSource[];
112
+ optionClass: (option: Completion) => string;
113
+
114
+ constructor(
115
+ readonly view: EditorView,
116
+ readonly stateField: StateField<CompletionState>,
117
+ ) {
118
+ const cState = view.state.field(stateField);
119
+ const { options, selected } = cState.open!;
120
+ const config = view.state.facet(completionConfig);
121
+ this.optionContent = optionContent(config);
122
+ this.optionClass = config.optionClass;
123
+
124
+ this.range = rangeAroundSelected(
125
+ options.length,
126
+ selected,
127
+ config.maxRenderedOptions,
128
+ );
129
+
130
+ this.dom = document.createElement('div');
131
+ this.dom.className = 'cm-tooltip-autocomplete';
132
+ this.dom.addEventListener('mousedown', (e: MouseEvent) => {
133
+ for (
134
+ let dom = e.target as HTMLElement | null, match;
135
+ dom && dom !== this.dom;
136
+ dom = dom.parentNode as HTMLElement
137
+ ) {
138
+ if (
139
+ dom.nodeName === 'LI' &&
140
+ (match = /-(\d+)$/.exec(dom.id)) &&
141
+ +match[1] < options.length
142
+ ) {
143
+ applyCompletion(view, options[+match[1]]);
144
+ e.preventDefault();
145
+ return;
146
+ }
147
+ }
148
+ });
149
+ this.list = this.dom.appendChild(
150
+ this.createListBox(options, cState.id, this.range),
151
+ );
152
+ this.list.addEventListener('scroll', () => {
153
+ if (this.info) {
154
+ this.view.requestMeasure(this.placeInfo);
155
+ }
156
+ });
157
+ }
158
+
159
+ mount() {
160
+ this.updateSel();
161
+ }
162
+
163
+ update(update: ViewUpdate) {
164
+ if (
165
+ update.state.field(this.stateField) !== update.startState.field(this.stateField)
166
+ ) {
167
+ this.updateSel();
168
+ }
169
+ }
170
+
171
+ positioned() {
172
+ if (this.info) {
173
+ this.view.requestMeasure(this.placeInfo);
174
+ }
175
+ }
176
+
177
+ updateSel() {
178
+ const cState = this.view.state.field(this.stateField),
179
+ open = cState.open!;
180
+ if (
181
+ (open.selected > -1 && open.selected < this.range.from) ||
182
+ open.selected >= this.range.to
183
+ ) {
184
+ this.range = rangeAroundSelected(
185
+ open.options.length,
186
+ open.selected,
187
+ this.view.state.facet(completionConfig).maxRenderedOptions,
188
+ );
189
+ this.list.remove();
190
+ this.list = this.dom.appendChild(
191
+ this.createListBox(open.options, cState.id, this.range),
192
+ );
193
+ this.list.addEventListener('scroll', () => {
194
+ if (this.info) {
195
+ this.view.requestMeasure(this.placeInfo);
196
+ }
197
+ });
198
+ }
199
+ if (this.updateSelectedOption(open.selected)) {
200
+ if (this.info) {
201
+ this.info.remove();
202
+ this.info = null;
203
+ }
204
+ const { completion } = open.options[open.selected];
205
+ const { info } = completion;
206
+ if (!info) {
207
+ return;
208
+ }
209
+ const infoResult =
210
+ typeof info === 'string' ? document.createTextNode(info) : info(completion);
211
+ if (!infoResult) {
212
+ return;
213
+ }
214
+ if ('then' in infoResult) {
215
+ infoResult
216
+ .then((node) => {
217
+ if (node && this.view.state.field(this.stateField, false) === cState) {
218
+ return this.addInfoPane(node);
219
+ }
220
+ return undefined;
221
+ })
222
+ .catch((e) => logException(this.view.state, e, 'completion info'));
223
+ } else {
224
+ this.addInfoPane(infoResult);
225
+ }
226
+ }
227
+ }
228
+
229
+ addInfoPane(content: Node) {
230
+ const dom = (this.info = document.createElement('div'));
231
+ dom.className = 'cm-tooltip cm-completionInfo';
232
+ dom.appendChild(content);
233
+ this.dom.appendChild(dom);
234
+ this.view.requestMeasure(this.placeInfo);
235
+ }
236
+
237
+ updateSelectedOption(selected: number) {
238
+ let set: null | HTMLElement = null;
239
+ for (
240
+ let opt = this.list.firstChild as HTMLElement | null, i = this.range.from;
241
+ opt;
242
+ opt = opt.nextSibling as HTMLElement | null, i++
243
+ ) {
244
+ if (i === selected) {
245
+ if (!opt.hasAttribute('aria-selected')) {
246
+ opt.setAttribute('aria-selected', 'true');
247
+ set = opt;
248
+ }
249
+ } else {
250
+ if (opt.hasAttribute('aria-selected')) {
251
+ opt.removeAttribute('aria-selected');
252
+ }
253
+ }
254
+ }
255
+ if (set) {
256
+ scrollIntoView(this.list, set);
257
+ }
258
+ return set;
259
+ }
260
+
261
+ measureInfo() {
262
+ const sel = this.dom.querySelector('[aria-selected]');
263
+ if (!sel || !this.info) {
264
+ return null;
265
+ }
266
+ const win = this.dom.ownerDocument.defaultView || window;
267
+ const listRect = this.dom.getBoundingClientRect();
268
+ const infoRect = this.info.getBoundingClientRect();
269
+ const selRect = sel.getBoundingClientRect();
270
+ if (
271
+ selRect.top > Math.min(win.innerHeight, listRect.bottom) - 10 ||
272
+ selRect.bottom < Math.max(0, listRect.top) + 10
273
+ ) {
274
+ return null;
275
+ }
276
+ let rtl = this.view.textDirection === Direction.RTL,
277
+ left = rtl,
278
+ narrow = false,
279
+ maxWidth;
280
+ let top = '',
281
+ bottom = '';
282
+ const spaceLeft = listRect.left,
283
+ spaceRight = win.innerWidth - listRect.right;
284
+ if (left && spaceLeft < Math.min(infoRect.width, spaceRight)) {
285
+ left = false;
286
+ } else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft)) {
287
+ left = true;
288
+ }
289
+ if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
290
+ top =
291
+ Math.max(0, Math.min(selRect.top, win.innerHeight - infoRect.height)) -
292
+ listRect.top +
293
+ 'px';
294
+ maxWidth = Math.min(Info.Width, left ? spaceLeft : spaceRight) + 'px';
295
+ } else {
296
+ narrow = true;
297
+ maxWidth =
298
+ Math.min(
299
+ Info.Width,
300
+ (rtl ? listRect.right : win.innerWidth - listRect.left) - Info.Margin,
301
+ ) + 'px';
302
+ const spaceBelow = win.innerHeight - listRect.bottom;
303
+ if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) {
304
+ // Below the completion
305
+ top = selRect.bottom - listRect.top + 'px';
306
+ }
307
+ // Above it
308
+ else {
309
+ bottom = listRect.bottom - selRect.top + 'px';
310
+ }
311
+ }
312
+ return {
313
+ top,
314
+ bottom,
315
+ maxWidth,
316
+ class: narrow ? (rtl ? 'left-narrow' : 'right-narrow') : left ? 'left' : 'right',
317
+ };
318
+ }
319
+
320
+ positionInfo(
321
+ pos: { top: string; bottom: string; class: string; maxWidth: string } | null,
322
+ ) {
323
+ if (this.info) {
324
+ if (pos) {
325
+ this.info.style.top = pos.top;
326
+ this.info.style.bottom = pos.bottom;
327
+ this.info.style.maxWidth = pos.maxWidth;
328
+ this.info.className =
329
+ 'cm-tooltip cm-completionInfo cm-completionInfo-' + pos.class;
330
+ } else {
331
+ this.info.style.top = '-1e6px';
332
+ }
333
+ }
334
+ }
335
+
336
+ createListBox(
337
+ options: readonly Option[],
338
+ id: string,
339
+ range: { from: number; to: number },
340
+ ) {
341
+ const ul = document.createElement('ul');
342
+ ul.id = id;
343
+ ul.setAttribute('role', 'listbox');
344
+ ul.setAttribute('aria-expanded', 'true');
345
+ ul.setAttribute('aria-label', this.view.state.phrase('Completions'));
346
+ for (let i = range.from; i < range.to; i++) {
347
+ const { completion, match } = options[i];
348
+ const li = ul.appendChild(document.createElement('li'));
349
+ li.id = id + '-' + i;
350
+ li.setAttribute('role', 'option');
351
+ const cls = this.optionClass(completion);
352
+ if (cls) {
353
+ li.className = cls;
354
+ }
355
+ for (const source of this.optionContent) {
356
+ const node = source(completion, this.view.state, match);
357
+ if (node) {
358
+ li.appendChild(node);
359
+ }
360
+ }
361
+ }
362
+ if (range.from) {
363
+ ul.classList.add('cm-completionListIncompleteTop');
364
+ }
365
+ if (range.to < options.length) {
366
+ ul.classList.add('cm-completionListIncompleteBottom');
367
+ }
368
+ return ul;
369
+ }
370
+ }
371
+
372
+ // We allocate a new function instance every time the completion
373
+ // changes to force redrawing/repositioning of the tooltip
374
+ export function completionTooltip(stateField: StateField<CompletionState>) {
375
+ return (view: EditorView): TooltipView => new CompletionTooltip(view, stateField);
376
+ }
377
+
378
+ function scrollIntoView(container: HTMLElement, element: HTMLElement) {
379
+ const parent = container.getBoundingClientRect();
380
+ const self = element.getBoundingClientRect();
381
+ if (self.top < parent.top) {
382
+ container.scrollTop -= parent.top - self.top;
383
+ } else if (self.bottom > parent.bottom) {
384
+ container.scrollTop += self.bottom - parent.bottom;
385
+ }
386
+ }