@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,472 @@
1
+ /* eslint-disable @typescript-eslint/no-this-alias */
2
+ /* eslint-disable @typescript-eslint/no-use-before-define */
3
+ /* eslint-disable @typescript-eslint/no-parameter-properties */
4
+ /* eslint-disable @typescript-eslint/parameter-properties */
5
+ /* eslint-disable prefer-const */
6
+ import type { Transaction, EditorState, ChangeDesc } from '@codemirror/state';
7
+ import { StateField, StateEffect } from '@codemirror/state';
8
+ import type { Tooltip } from '@codemirror/view';
9
+ import { EditorView, showTooltip } from '@codemirror/view';
10
+
11
+ import type { CompletionSource, CompletionResult, Completion } from './completion.js';
12
+ import {
13
+ Option,
14
+ cur,
15
+ asSource,
16
+ ensureAnchor,
17
+ CompletionContext,
18
+ } from './completion.js';
19
+ import type { CompletionConfig } from './config.js';
20
+ import { completionConfig } from './config.js';
21
+ import { FuzzyMatcher } from './filter.js';
22
+ import { completionTooltip } from './tooltip.js';
23
+
24
+ // Used to pick a preferred option when two options with the same
25
+ // label occur in the result.
26
+ function score(option: Completion) {
27
+ return (
28
+ (option.boost || 0) * 100 +
29
+ (option.apply ? 10 : 0) +
30
+ (option.info ? 5 : 0) +
31
+ (option.type ? 1 : 0)
32
+ );
33
+ }
34
+
35
+ function sortOptions(active: readonly ActiveSource[], state: EditorState) {
36
+ let options = [],
37
+ i = 0;
38
+ for (const a of active) {
39
+ if (a.hasResult()) {
40
+ if (a.result.filter === false) {
41
+ const getMatch = a.result.getMatch;
42
+ for (const option of a.result.options) {
43
+ const match = [1e9 - i++];
44
+ if (getMatch) {
45
+ for (const n of getMatch(option)) {
46
+ match.push(n);
47
+ }
48
+ }
49
+ options.push(new Option(option, a, match));
50
+ }
51
+ } else {
52
+ let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)),
53
+ match;
54
+ for (const option of a.result.options) {
55
+ if ((match = matcher.match(option.label))) {
56
+ if (option.boost !== undefined) {
57
+ match[0] += option.boost;
58
+ }
59
+ options.push(new Option(option, a, match));
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ let result = [],
66
+ prev = null;
67
+ const compare = state.facet(completionConfig).compareCompletions;
68
+ for (const opt of options.sort(
69
+ (a, b) => b.match[0] - a.match[0] || compare(a.completion, b.completion),
70
+ )) {
71
+ if (
72
+ !prev ||
73
+ prev.label !== opt.completion.label ||
74
+ prev.detail !== opt.completion.detail ||
75
+ (prev.type !== null &&
76
+ opt.completion.type !== null &&
77
+ prev.type !== opt.completion.type) ||
78
+ prev.apply !== opt.completion.apply
79
+ ) {
80
+ result.push(opt);
81
+ } else if (score(opt.completion) > score(prev)) {
82
+ result[result.length - 1] = opt;
83
+ }
84
+ prev = opt.completion;
85
+ }
86
+ return result;
87
+ }
88
+
89
+ class CompletionDialog {
90
+ constructor(
91
+ readonly options: readonly Option[],
92
+ readonly attrs: Record<string, string>,
93
+ readonly tooltip: Tooltip,
94
+ readonly timestamp: number,
95
+ readonly selected: number,
96
+ ) {}
97
+
98
+ setSelected(selected: number, id: string) {
99
+ return selected === this.selected || selected >= this.options.length
100
+ ? this
101
+ : new CompletionDialog(
102
+ this.options,
103
+ makeAttrs(id, selected),
104
+ this.tooltip,
105
+ this.timestamp,
106
+ selected,
107
+ );
108
+ }
109
+
110
+ static build(
111
+ active: readonly ActiveSource[],
112
+ state: EditorState,
113
+ id: string,
114
+ prev: CompletionDialog | null,
115
+ conf: Required<CompletionConfig>,
116
+ ): CompletionDialog | null {
117
+ const options = sortOptions(active, state);
118
+ if (!options.length) {
119
+ return null;
120
+ }
121
+ let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
122
+ if (prev && prev.selected !== selected && prev.selected !== -1) {
123
+ const selectedValue = prev.options[prev.selected].completion;
124
+ for (let i = 0; i < options.length; i++) {
125
+ if (options[i].completion === selectedValue) {
126
+ selected = i;
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ return new CompletionDialog(
132
+ options,
133
+ makeAttrs(id, selected),
134
+ {
135
+ pos: active.reduce((a, b) => (b.hasResult() ? Math.min(a, b.from) : a), 1e8),
136
+ create: completionTooltip(completionState),
137
+ above: conf.aboveCursor,
138
+ },
139
+ prev ? prev.timestamp : Date.now(),
140
+ selected,
141
+ );
142
+ }
143
+
144
+ map(changes: ChangeDesc) {
145
+ return new CompletionDialog(
146
+ this.options,
147
+ this.attrs,
148
+ { ...this.tooltip, pos: changes.mapPos(this.tooltip.pos) },
149
+ this.timestamp,
150
+ this.selected,
151
+ );
152
+ }
153
+ }
154
+
155
+ export class CompletionState {
156
+ constructor(
157
+ readonly active: readonly ActiveSource[],
158
+ readonly id: string,
159
+ readonly open: CompletionDialog | null,
160
+ ) {}
161
+
162
+ static start() {
163
+ return new CompletionState(
164
+ none,
165
+ 'cm-ac-' + Math.floor(Math.random() * 2e6).toString(36),
166
+ null,
167
+ );
168
+ }
169
+
170
+ update(tr: Transaction) {
171
+ let { state } = tr,
172
+ conf = state.facet(completionConfig);
173
+ const sources =
174
+ conf.override ||
175
+ state
176
+ .languageDataAt<CompletionSource | readonly (string | Completion)[]>(
177
+ 'autocomplete',
178
+ cur(state),
179
+ )
180
+ .map(asSource);
181
+ let active: readonly ActiveSource[] = sources.map((source) => {
182
+ const value =
183
+ this.active.find((s) => s.source === source) ||
184
+ new ActiveSource(
185
+ source,
186
+ this.active.some((a) => a.state !== State.Inactive)
187
+ ? State.Pending
188
+ : State.Inactive,
189
+ );
190
+ return value.update(tr, conf);
191
+ });
192
+ if (
193
+ active.length === this.active.length &&
194
+ active.every((a, i) => a === this.active[i])
195
+ ) {
196
+ active = this.active;
197
+ }
198
+
199
+ let open =
200
+ tr.selection ||
201
+ active.some((a) => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
202
+ !sameResults(active, this.active)
203
+ ? CompletionDialog.build(active, state, this.id, this.open, conf)
204
+ : this.open && tr.docChanged
205
+ ? this.open.map(tr.changes)
206
+ : this.open;
207
+ if (
208
+ !open &&
209
+ active.every((a) => a.state !== State.Pending) &&
210
+ active.some((a) => a.hasResult())
211
+ ) {
212
+ active = active.map((a) =>
213
+ a.hasResult() ? new ActiveSource(a.source, State.Inactive) : a,
214
+ );
215
+ }
216
+ for (const effect of tr.effects) {
217
+ if (effect.is(setSelectedEffect)) {
218
+ open = open && open.setSelected(effect.value, this.id);
219
+ }
220
+ }
221
+
222
+ return active === this.active && open === this.open
223
+ ? this
224
+ : new CompletionState(active, this.id, open);
225
+ }
226
+
227
+ get tooltip(): Tooltip | null {
228
+ return this.open ? this.open.tooltip : null;
229
+ }
230
+
231
+ get attrs() {
232
+ return this.open ? this.open.attrs : baseAttrs;
233
+ }
234
+ }
235
+
236
+ function sameResults(a: readonly ActiveSource[], b: readonly ActiveSource[]) {
237
+ if (a === b) {
238
+ return true;
239
+ }
240
+ for (let iA = 0, iB = 0; ; ) {
241
+ while (iA < a.length && !a[iA].hasResult) {
242
+ iA++;
243
+ }
244
+ while (iB < b.length && !b[iB].hasResult) {
245
+ iB++;
246
+ }
247
+ const endA = iA === a.length,
248
+ endB = iB === b.length;
249
+ if (endA || endB) {
250
+ return endA === endB;
251
+ }
252
+ if ((a[iA++] as ActiveResult).result !== (b[iB++] as ActiveResult).result) {
253
+ return false;
254
+ }
255
+ }
256
+ }
257
+
258
+ const baseAttrs = {
259
+ 'aria-autocomplete': 'list',
260
+ };
261
+
262
+ function makeAttrs(id: string, selected: number) {
263
+ const result: Record<string, string> = {
264
+ 'aria-autocomplete': 'list',
265
+ 'aria-haspopup': 'listbox',
266
+ 'aria-controls': id,
267
+ };
268
+ if (selected > -1) {
269
+ result['aria-activedescendant'] = id + '-' + selected;
270
+ }
271
+ return result;
272
+ }
273
+
274
+ const none: readonly any[] = [];
275
+
276
+ export const enum State {
277
+ Inactive = 0,
278
+ Pending = 1,
279
+ Result = 2,
280
+ }
281
+
282
+ export function getUserEvent(tr: Transaction): 'input' | 'delete' | null {
283
+ return tr.isUserEvent('input.type')
284
+ ? 'input'
285
+ : tr.isUserEvent('delete.backward')
286
+ ? 'delete'
287
+ : null;
288
+ }
289
+
290
+ export class ActiveSource {
291
+ constructor(
292
+ readonly source: CompletionSource,
293
+ readonly state: State,
294
+ readonly explicitPos: number = -1,
295
+ ) {}
296
+
297
+ hasResult(): this is ActiveResult {
298
+ return false;
299
+ }
300
+
301
+ update(tr: Transaction, conf: Required<CompletionConfig>): ActiveSource {
302
+ let event = getUserEvent(tr),
303
+ value: ActiveSource = this;
304
+ if (event) {
305
+ value = value.handleUserEvent(tr, event, conf);
306
+ } else if (tr.docChanged) {
307
+ value = value.handleChange(tr);
308
+ } else if (tr.selection && value.state !== State.Inactive) {
309
+ value = new ActiveSource(value.source, State.Inactive);
310
+ }
311
+
312
+ for (const effect of tr.effects) {
313
+ if (effect.is(startCompletionEffect)) {
314
+ value = new ActiveSource(
315
+ value.source,
316
+ State.Pending,
317
+ effect.value ? cur(tr.state) : -1,
318
+ );
319
+ } else if (effect.is(closeCompletionEffect)) {
320
+ value = new ActiveSource(value.source, State.Inactive);
321
+ } else if (effect.is(setActiveEffect)) {
322
+ for (const active of effect.value) {
323
+ if (active.source === value.source) {
324
+ value = active;
325
+ }
326
+ }
327
+ }
328
+ }
329
+ return value;
330
+ }
331
+
332
+ handleUserEvent(
333
+ tr: Transaction,
334
+ type: 'input' | 'delete',
335
+ conf: Required<CompletionConfig>,
336
+ ): ActiveSource {
337
+ return type === 'delete' || !conf.activateOnTyping
338
+ ? this.map(tr.changes)
339
+ : new ActiveSource(this.source, State.Pending);
340
+ }
341
+
342
+ handleChange(tr: Transaction): ActiveSource {
343
+ return tr.changes.touchesRange(cur(tr.startState))
344
+ ? new ActiveSource(this.source, State.Inactive)
345
+ : this.map(tr.changes);
346
+ }
347
+
348
+ map(changes: ChangeDesc) {
349
+ return changes.empty || this.explicitPos < 0
350
+ ? this
351
+ : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
352
+ }
353
+ }
354
+
355
+ export class ActiveResult extends ActiveSource {
356
+ constructor(
357
+ source: CompletionSource,
358
+ explicitPos: number,
359
+ readonly result: CompletionResult,
360
+ readonly from: number,
361
+ readonly to: number,
362
+ ) {
363
+ super(source, State.Result, explicitPos);
364
+ }
365
+
366
+ override hasResult(): this is ActiveResult {
367
+ return true;
368
+ }
369
+
370
+ override handleUserEvent(
371
+ tr: Transaction,
372
+ type: 'input' | 'delete',
373
+ conf: Required<CompletionConfig>,
374
+ ): ActiveSource {
375
+ const from = tr.changes.mapPos(this.from),
376
+ to = tr.changes.mapPos(this.to, 1);
377
+ const pos = cur(tr.state);
378
+ if (
379
+ (this.explicitPos < 0 ? pos <= from : pos < this.from) ||
380
+ pos > to ||
381
+ (type === 'delete' && cur(tr.startState) === this.from)
382
+ ) {
383
+ return new ActiveSource(
384
+ this.source,
385
+ type === 'input' && conf.activateOnTyping ? State.Pending : State.Inactive,
386
+ );
387
+ }
388
+ let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos),
389
+ updated;
390
+ if (checkValid(this.result.validFor, tr.state, from, to)) {
391
+ return new ActiveResult(this.source, explicitPos, this.result, from, to);
392
+ }
393
+ if (
394
+ this.result.update &&
395
+ (updated = this.result.update(
396
+ this.result,
397
+ from,
398
+ to,
399
+ new CompletionContext(tr.state, pos, explicitPos >= 0),
400
+ ))
401
+ ) {
402
+ return new ActiveResult(
403
+ this.source,
404
+ explicitPos,
405
+ updated,
406
+ updated.from,
407
+ updated.to ?? cur(tr.state),
408
+ );
409
+ }
410
+ return new ActiveSource(this.source, State.Pending, explicitPos);
411
+ }
412
+
413
+ override handleChange(tr: Transaction): ActiveSource {
414
+ return tr.changes.touchesRange(this.from, this.to)
415
+ ? new ActiveSource(this.source, State.Inactive)
416
+ : this.map(tr.changes);
417
+ }
418
+
419
+ override map(mapping: ChangeDesc) {
420
+ return mapping.empty
421
+ ? this
422
+ : new ActiveResult(
423
+ this.source,
424
+ this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos),
425
+ this.result,
426
+ mapping.mapPos(this.from),
427
+ mapping.mapPos(this.to, 1),
428
+ );
429
+ }
430
+ }
431
+
432
+ function checkValid(
433
+ validFor:
434
+ | undefined
435
+ | RegExp
436
+ | ((text: string, from: number, to: number, state: EditorState) => boolean),
437
+ state: EditorState,
438
+ from: number,
439
+ to: number,
440
+ ) {
441
+ if (!validFor) {
442
+ return false;
443
+ }
444
+ const text = state.sliceDoc(from, to);
445
+ return typeof validFor === 'function'
446
+ ? validFor(text, from, to, state)
447
+ : ensureAnchor(validFor, true).test(text);
448
+ }
449
+
450
+ export const startCompletionEffect = StateEffect.define<boolean>();
451
+ export const closeCompletionEffect = StateEffect.define<null>();
452
+ export const setActiveEffect = StateEffect.define<readonly ActiveSource[]>({
453
+ map(sources, mapping) {
454
+ return sources.map((s) => s.map(mapping));
455
+ },
456
+ });
457
+ export const setSelectedEffect = StateEffect.define<number>();
458
+
459
+ export const completionState = StateField.define<CompletionState>({
460
+ create() {
461
+ return CompletionState.start();
462
+ },
463
+
464
+ update(value, tr) {
465
+ return value.update(tr);
466
+ },
467
+
468
+ provide: (f) => [
469
+ showTooltip.from(f, (val) => val.tooltip),
470
+ EditorView.contentAttributes.from(f, (state) => state.attrs),
471
+ ],
472
+ });
@@ -0,0 +1,126 @@
1
+ import { EditorView } from '@codemirror/view';
2
+
3
+ export const enum Info {
4
+ Margin = 30,
5
+ Width = 400,
6
+ }
7
+
8
+ export const baseTheme = EditorView.baseTheme({
9
+ '.cm-tooltip.cm-tooltip-autocomplete': {
10
+ '& > ul': {
11
+ fontFamily: 'monospace',
12
+ whiteSpace: 'nowrap',
13
+ overflow: 'hidden auto',
14
+ maxWidth_fallback: '700px',
15
+ maxWidth: 'min(700px, 95vw)',
16
+ minWidth: '250px',
17
+ maxHeight: '10em',
18
+ listStyle: 'none',
19
+ margin: 0,
20
+ padding: 0,
21
+
22
+ '& > li': {
23
+ overflowX: 'hidden',
24
+ textOverflow: 'ellipsis',
25
+ cursor: 'pointer',
26
+ padding: '1px 3px',
27
+ lineHeight: 1.2,
28
+ },
29
+ },
30
+ },
31
+
32
+ '&light .cm-tooltip-autocomplete ul li[aria-selected]': {
33
+ background: '#17c',
34
+ color: 'white',
35
+ },
36
+
37
+ '&dark .cm-tooltip-autocomplete ul li[aria-selected]': {
38
+ background: '#347',
39
+ color: 'white',
40
+ },
41
+
42
+ '.cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after': {
43
+ content: '"···"',
44
+ opacity: 0.5,
45
+ display: 'block',
46
+ textAlign: 'center',
47
+ },
48
+
49
+ '.cm-tooltip.cm-completionInfo': {
50
+ position: 'absolute',
51
+ padding: '3px 9px',
52
+ width: 'max-content',
53
+ maxWidth: `${Info.Width}px`,
54
+ boxSizing: 'border-box',
55
+ maxHeight: '500px',
56
+ overflow: 'auto',
57
+ },
58
+
59
+ '.cm-completionInfo.cm-completionInfo-left': { right: '100%' },
60
+ '.cm-completionInfo.cm-completionInfo-right': { left: '100%' },
61
+ '.cm-completionInfo.cm-completionInfo-left-narrow': { right: `${Info.Margin}px` },
62
+ '.cm-completionInfo.cm-completionInfo-right-narrow': { left: `${Info.Margin}px` },
63
+
64
+ '&light .cm-snippetField': { backgroundColor: '#00000022' },
65
+ '&dark .cm-snippetField': { backgroundColor: '#ffffff22' },
66
+ '.cm-snippetFieldPosition': {
67
+ verticalAlign: 'text-top',
68
+ width: 0,
69
+ height: '1.15em',
70
+ display: 'inline-block',
71
+ margin: '0 -0.7px -.7em',
72
+ borderLeft: '1.4px dotted #888',
73
+ },
74
+
75
+ '.cm-completionMatchedText': {
76
+ textDecoration: 'underline',
77
+ },
78
+
79
+ '.cm-completionDetail': {
80
+ marginLeft: '0.5em',
81
+ fontStyle: 'italic',
82
+ },
83
+
84
+ '.cm-completionIcon': {
85
+ fontSize: '90%',
86
+ width: '.8em',
87
+ display: 'inline-block',
88
+ textAlign: 'center',
89
+ paddingRight: '.6em',
90
+ opacity: '0.6',
91
+ },
92
+
93
+ '.cm-completionIcon-function, .cm-completionIcon-method': {
94
+ '&:after': { content: "'ƒ'" },
95
+ },
96
+ '.cm-completionIcon-class': {
97
+ '&:after': { content: "'○'" },
98
+ },
99
+ '.cm-completionIcon-interface': {
100
+ '&:after': { content: "'◌'" },
101
+ },
102
+ '.cm-completionIcon-variable': {
103
+ '&:after': { content: "'𝑥'" },
104
+ },
105
+ '.cm-completionIcon-constant': {
106
+ '&:after': { content: "'𝐶'" },
107
+ },
108
+ '.cm-completionIcon-type': {
109
+ '&:after': { content: "'𝑡'" },
110
+ },
111
+ '.cm-completionIcon-enum': {
112
+ '&:after': { content: "'∪'" },
113
+ },
114
+ '.cm-completionIcon-property': {
115
+ '&:after': { content: "'□'" },
116
+ },
117
+ '.cm-completionIcon-keyword': {
118
+ '&:after': { content: "'🔑\uFE0E'" }, // Disable emoji rendering
119
+ },
120
+ '.cm-completionIcon-namespace': {
121
+ '&:after': { content: "'▢'" },
122
+ },
123
+ '.cm-completionIcon-text': {
124
+ '&:after': { content: "'abc'", fontSize: '50%', verticalAlign: 'middle' },
125
+ },
126
+ });