@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,84 @@
1
+ import type { EditorState, Line } from '@codemirror/state';
2
+ import type { EditorView } from '@codemirror/view';
3
+
4
+ /**
5
+ * Gets the visible lines in the editor. Lines will not be repeated.
6
+ *
7
+ * @param view - The editor view to get the visible lines from.
8
+ * @param state - The editor state. Defaults to the view's current one.
9
+ */
10
+ export function getVisibleLines(view: EditorView, state = view.state) {
11
+ const lines = new Set<Line>();
12
+
13
+ for (const { from, to } of view.visibleRanges) {
14
+ let pos = from;
15
+
16
+ while (pos <= to) {
17
+ const line = state.doc.lineAt(pos);
18
+
19
+ if (!lines.has(line)) {
20
+ lines.add(line);
21
+ }
22
+
23
+ pos = line.to + 1;
24
+ }
25
+ }
26
+
27
+ return lines;
28
+ }
29
+
30
+ /**
31
+ * Gets the line at the position of the primary cursor.
32
+ *
33
+ * @param state - The editor state from which to extract the line.
34
+ */
35
+ export function getCurrentLine(state: EditorState) {
36
+ const currentPos = state.selection.main.head;
37
+ return state.doc.lineAt(currentPos);
38
+ }
39
+
40
+ /**
41
+ * Returns the number of columns that a string is indented, controlling for
42
+ * tabs. This is useful for determining the indentation level of a line.
43
+ *
44
+ * Note that this only returns the number of _visible_ columns, not the number
45
+ * of whitespace characters at the start of the string.
46
+ *
47
+ * @param str - The string to check.
48
+ * @param tabSize - The size of a tab character. Usually 2 or 4.
49
+ */
50
+ export function numColumns(str: string, tabSize: number) {
51
+ // as far as I can tell, this is pretty much the fastest way to do this,
52
+ // at least involving iteration. `str.length - str.trimStart().length` is
53
+ // much faster, but it has some edge cases that are hard to deal with.
54
+
55
+ let col = 0;
56
+
57
+ // eslint-disable-next-line no-restricted-syntax
58
+ loop: for (let i = 0; i < str.length; i++) {
59
+ switch (str[i]) {
60
+ case ' ': {
61
+ col += 1;
62
+ continue loop;
63
+ }
64
+
65
+ case '\t': {
66
+ // if the current column is a multiple of the tab size, we can just
67
+ // add the tab size to the column. otherwise, we need to add the
68
+ // difference between the tab size and the current column.
69
+ col += tabSize - (col % tabSize);
70
+ continue loop;
71
+ }
72
+
73
+ case '\r': {
74
+ continue loop;
75
+ }
76
+
77
+ default: {
78
+ break loop;
79
+ }
80
+ }
81
+ }
82
+
83
+ return col;
84
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ import './style/base.css';
2
+ import './style/theme.css';
3
+ import './style/variables.css';
4
+
5
+ export * from './config.js';
6
+ export * from './editor.js';
7
+ export * from './mode.js';
8
+ export * from './theme.js';
9
+ export * from './factory.js';
10
+ export * from './monitor.js';
11
+ export * from './auto-complete/index.js';
@@ -0,0 +1,4 @@
1
+ export const FoldIcon =
2
+ '<svg width="8px" height="6px" viewBox="0 0 8 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>1.通用/2.Icon图标/Line/Down</title><g id="0424" stroke="none" stroke-width="1" fill="none" fillRule="evenodd"><g id="Notebook-cell-色阶" transform="translate(-2015.000000, -434.000000)" fill="#A4AECB"><g id="编组-15" transform="translate(2004.000000, 407.000000)"><g id="1.通用/2.Icon图标/Line/Down" transform="translate(11.250000, 27.500000)"><path d="M7.34387369,0 L6.61145181,0 C6.56164712,0 6.51477212,0.0244140625 6.48547525,0.064453125 L3.71106119,3.88867188 L0.936647123,0.064453125 C0.907350248,0.0244140625 0.860475248,0 0.81067056,0 L0.0782486852,0 C0.0147721227,0 -0.0223372523,0.072265625 0.0147721227,0.124023438 L3.4581315,4.87109375 C3.5831315,5.04296875 3.83899087,5.04296875 3.96301431,4.87109375 L7.40637369,0.124023437 C7.44445962,0.072265625 7.40735025,0 7.34387369,0 Z" id="Down"></path></g></g></g></g></svg>';
3
+ export const UnFoldIcon =
4
+ '<svg width="6px" height="8px" viewBox="0 0 6 8" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>1.通用/2.Icon图标/Line/Down收起</title><g id="0424" stroke="none" stroke-width="1" fill="none" fillRule="evenodd"><g id="Notebook-cell-色阶" transform="translate(-2094.000000, -433.000000)" fill="#A4AECB"><g id="编组-15备份" transform="translate(2082.000000, 407.000000)"><g id="1.通用/2.Icon图标/Line/Down" transform="translate(15.000000, 30.039124) rotate(270.000000) translate(-15.000000, -30.039124) translate(11.289124, 27.539124)"><path d="M7.34387369,1.77635684e-15 L6.61145181,1.77635684e-15 C6.56164712,1.77635684e-15 6.51477212,0.0244140625 6.48547525,0.064453125 L3.71106119,3.88867188 L0.936647123,0.064453125 C0.907350248,0.0244140625 0.860475248,1.77635684e-15 0.81067056,1.77635684e-15 L0.0782486852,1.77635684e-15 C0.0147721227,1.77635684e-15 -0.0223372523,0.072265625 0.0147721227,0.124023438 L3.4581315,4.87109375 C3.5831315,5.04296875 3.83899087,5.04296875 3.96301431,4.87109375 L7.40637369,0.124023438 C7.44445962,0.072265625 7.40735025,1.77635684e-15 7.34387369,1.77635684e-15 Z" id="Down"></path></g></g></g></g></svg>';
@@ -0,0 +1,49 @@
1
+ import type { IEditorMimeTypeService } from '@difizen/libro-code-editor';
2
+ import { defaultMimeType } from '@difizen/libro-code-editor';
3
+ import type { ILanguageInfoMetadata } from '@difizen/libro-common';
4
+
5
+ import { findBest, findByFileName } from './mode.js';
6
+
7
+ const extname = (path: string) => {
8
+ return path.split('.').pop();
9
+ };
10
+
11
+ /**
12
+ * The mime type service for CodeMirror.
13
+ */
14
+ export class CodeMirrorMimeTypeService implements IEditorMimeTypeService {
15
+ /**
16
+ * Returns a mime type for the given language info.
17
+ *
18
+ * #### Notes
19
+ * If a mime type cannot be found returns the default mime type `text/plain`, never `null`.
20
+ */
21
+ getMimeTypeByLanguage(info: ILanguageInfoMetadata): string {
22
+ const ext = info.file_extension || '';
23
+ const mode = findBest(
24
+ (info.codemirror_mode as any) || {
25
+ mimetype: info.mimetype,
26
+ name: info.name,
27
+ ext: [ext.split('.').slice(-1)[0]],
28
+ },
29
+ );
30
+ return mode ? (mode.mime as string) : defaultMimeType;
31
+ }
32
+
33
+ /**
34
+ * Returns a mime type for the given file path.
35
+ *
36
+ * #### Notes
37
+ * If a mime type cannot be found returns the default mime type `text/plain`, never `null`.
38
+ */
39
+ getMimeTypeByFilePath(path: string): string {
40
+ const ext = extname(path);
41
+ if (ext === '.ipy') {
42
+ return 'text/x-python';
43
+ } else if (ext === '.md') {
44
+ return 'text/x-ipythongfm';
45
+ }
46
+ const mode = findByFileName(path) || findBest('');
47
+ return mode ? (mode.mime as string) : defaultMimeType;
48
+ }
49
+ }
package/src/mode.ts ADDED
@@ -0,0 +1,269 @@
1
+ import { markdown } from '@codemirror/lang-markdown';
2
+ import type { LanguageSupport } from '@codemirror/language';
3
+ import { LanguageDescription } from '@codemirror/language';
4
+ import { defaultMimeType } from '@difizen/libro-code-editor';
5
+
6
+ // This ensures the language spec for python will be loaded when
7
+ // we instantiate a new editor instance, which is required since
8
+ // python is the default language and we don't want to split
9
+ // the editor constructor because of asynchronous loading.
10
+ import { PathExt } from '@difizen/libro-common';
11
+ import { highlightTree } from '@lezer/highlight';
12
+
13
+ import { python } from './python-lang.js';
14
+ import { jupyterHighlightStyle } from './theme.js';
15
+
16
+ /**
17
+ * The interface of a codemirror language spec.
18
+ */
19
+ export interface ISpec {
20
+ name: string;
21
+ alias?: readonly string[];
22
+ mime: string | readonly string[];
23
+ load?: () => Promise<LanguageSupport>;
24
+ extensions?: readonly string[];
25
+ filename?: RegExp;
26
+ support?: LanguageSupport;
27
+ }
28
+
29
+ // Code mirror uses two similar structures, a plain object with optional fields,
30
+ // and a class with the same fields but all mandatory. Maybe adopting the same
31
+ // pattern would be less confusing (although far more verbose)
32
+ function makeSpec(spec: ISpec): ISpec {
33
+ const res = LanguageDescription.of(spec) as unknown as ISpec;
34
+ res.mime = spec.mime;
35
+ return res;
36
+ }
37
+
38
+ const modeList: ISpec[] = [
39
+ makeSpec({
40
+ name: 'Python',
41
+ mime: 'text/x-python',
42
+ extensions: ['BUILD', 'bzl', 'py', 'pyw'],
43
+ filename: /^(BUCK|BUILD)$/,
44
+ load() {
45
+ return Promise.resolve(python());
46
+ },
47
+ }),
48
+ makeSpec({
49
+ name: 'Markdown',
50
+ mime: 'text/x-markdown',
51
+ extensions: ['md', 'markdown', 'mkd'],
52
+ async load() {
53
+ return Promise.resolve(markdown());
54
+ },
55
+ }),
56
+ ];
57
+
58
+ /**
59
+ * Get the raw list of available modes specs.
60
+ *
61
+ * @alpha
62
+ * @returns The available modes
63
+ */
64
+ export function getModeInfo(): ISpec[] {
65
+ return modeList;
66
+ }
67
+
68
+ /**
69
+ * Find a codemirror mode by MIME.
70
+ *
71
+ * @alpha
72
+ * @param mime Mime type to look for
73
+ * @returns The mode or null
74
+ */
75
+ export function findByMIME(mime: string | readonly string[]): ISpec | null {
76
+ if (Array.isArray(mime)) {
77
+ for (let i = 0; i < mime.length; i++) {
78
+ const spec = findByMIME(mime[i]);
79
+ if (spec) {
80
+ return spec;
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ const _mime = (mime as string).toLowerCase();
86
+ for (let i = 0; i < modeList.length; i++) {
87
+ const info = modeList[i];
88
+ if (Array.isArray(info.mime)) {
89
+ for (let j = 0; j < info.mime.length; j++) {
90
+ if (info.mime[j] === _mime) {
91
+ return info;
92
+ }
93
+ }
94
+ } else if (info.mime === _mime) {
95
+ return info;
96
+ }
97
+ }
98
+ if (/\+xml$/.test(_mime)) {
99
+ return findByMIME('application/xml');
100
+ }
101
+ if (/\+json$/.test(_mime)) {
102
+ return findByMIME('application/json');
103
+ }
104
+ return null;
105
+ }
106
+
107
+ /**
108
+ * Find a codemirror mode by name.
109
+ *
110
+ * @alpha
111
+ * @param name The mode name
112
+ * @returns The mode or null
113
+ */
114
+ export function findByName(name: string): ISpec | null {
115
+ const _name = name.toLowerCase();
116
+ for (let i = 0; i < modeList.length; i++) {
117
+ const info = modeList[i];
118
+ if (info.name.toLowerCase() === _name) {
119
+ return info;
120
+ }
121
+ if (info.alias) {
122
+ for (let j = 0; j < info.alias.length; j++) {
123
+ if (info.alias[j].toLowerCase() === _name) {
124
+ return info;
125
+ }
126
+ }
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+
132
+ /**
133
+ * Find a codemirror mode by extension.
134
+ *
135
+ * @alpha
136
+ * @param ext The extension name
137
+ * @returns The mode or null
138
+ */
139
+ export function findByExtension(ext: string | readonly string[]): ISpec | null {
140
+ if (Array.isArray(ext)) {
141
+ for (let i = 0; i < ext.length; i++) {
142
+ const spec = findByExtension(ext[i]);
143
+ if (spec) {
144
+ return spec;
145
+ }
146
+ }
147
+ return null;
148
+ }
149
+ const _ext = (ext as string).toLowerCase();
150
+ for (let i = 0; i < modeList.length; i++) {
151
+ const info = modeList[i];
152
+ for (let j = 0; j < info.extensions!.length; j++) {
153
+ if (info.extensions![j].toLowerCase() === _ext) {
154
+ return info;
155
+ }
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+
161
+ /**
162
+ * Find a codemirror mode by filename.
163
+ *
164
+ * @param name File name
165
+ * @returns The mode or null
166
+ */
167
+ export function findByFileName(name: string): ISpec | null {
168
+ const basename = PathExt.basename(name);
169
+ for (let i = 0; i < modeList.length; i++) {
170
+ const info = modeList[i];
171
+ if (info.filename && info.filename.test(basename)) {
172
+ return info;
173
+ }
174
+ }
175
+ const dot = basename.lastIndexOf('.');
176
+ const ext = dot > -1 && basename.substring(dot + 1, basename.length);
177
+ if (ext) {
178
+ return findByExtension(ext);
179
+ }
180
+ return null;
181
+ }
182
+
183
+ /**
184
+ * Find a codemirror mode by name or CodeMirror spec.
185
+ *
186
+ * @alpha
187
+ * @param mode The CodeMirror mode
188
+ * @param fallback Whether to fallback to default mimetype spec or not
189
+ * @returns The mode or null
190
+ */
191
+ export function findBest(mode: string | ISpec, fallback = true): ISpec | null {
192
+ const modename = typeof mode === 'string' ? mode : mode.name;
193
+ const mimetype = typeof mode !== 'string' ? mode.mime : modename;
194
+ const ext = typeof mode !== 'string' ? mode.extensions ?? [] : [];
195
+
196
+ return (
197
+ (modename ? findByName(modename) : null) ??
198
+ (mimetype ? findByMIME(mimetype) : null) ??
199
+ findByExtension(ext) ??
200
+ (fallback ? findByMIME(defaultMimeType) : null)
201
+ );
202
+ }
203
+
204
+ /**
205
+ * Ensure a codemirror mode is available by name or Codemirror spec.
206
+ *
207
+ * @param mode - The mode to ensure. If it is a string, uses [findBest]
208
+ * to get the appropriate spec.
209
+ *
210
+ * @returns A promise that resolves when the mode is available.
211
+ */
212
+ export async function ensure(mode: string | ISpec): Promise<ISpec | null> {
213
+ const spec = findBest(mode);
214
+ if (spec) {
215
+ spec.support = await spec.load!();
216
+ return spec;
217
+ }
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * Register a new mode for CodeMirror
223
+ *
224
+ * @alpha
225
+ * @param mode Mode to register
226
+ */
227
+ export function registerModeInfo(mode: ISpec): void {
228
+ const info = findBest(mode, false);
229
+ if (info) {
230
+ throw new Error(`${mode.mime} already registered`);
231
+ }
232
+ modeList.push(makeSpec(mode));
233
+ }
234
+
235
+ /**
236
+ * Parse and style a string.
237
+ *
238
+ * @alpha
239
+ * @param code Code to highlight
240
+ * @param mode Code mode
241
+ * @param el HTML element into which the highlighted code will be inserted
242
+ */
243
+ export function run(code: string, mode: ISpec, el: HTMLElement): void {
244
+ const language = mode.support?.language;
245
+ if (!language) {
246
+ return;
247
+ }
248
+
249
+ const tree = language.parser.parse(code);
250
+ // position state required because unstyled tokens are not emitted
251
+ // in highlightTree
252
+ let pos = 0;
253
+ highlightTree(tree, jupyterHighlightStyle, (from, to, classes) => {
254
+ if (from > pos) {
255
+ // No style applied to the token between pos and from
256
+ el.appendChild(document.createTextNode(code.slice(pos, from)));
257
+ }
258
+ const sp = el.appendChild(document.createElement('span'));
259
+ sp.className = classes;
260
+ sp.appendChild(document.createTextNode(code.slice(from, to)));
261
+ pos = to;
262
+ });
263
+
264
+ if (pos < tree.length - 1) {
265
+ // No style applied on the trailing text
266
+ el.appendChild(document.createTextNode(code.slice(pos, tree.length)));
267
+ }
268
+ }
269
+ // }
package/src/monitor.ts ADDED
@@ -0,0 +1,105 @@
1
+ import { EditorView, hasHoverTooltips } from '@codemirror/view';
2
+ import { Emitter } from '@difizen/mana-app';
3
+
4
+ import {
5
+ closeCompletionEffect,
6
+ setSelectedEffect,
7
+ startCompletionEffect,
8
+ } from './auto-complete/state.js';
9
+
10
+ interface DocStatus {
11
+ source: string[];
12
+ cursor: number;
13
+ changes?: string;
14
+ }
15
+
16
+ interface CompletionChange {
17
+ start?: DocStatus;
18
+ accept?: DocStatus;
19
+ close?: DocStatus;
20
+ selectIndex?: number;
21
+ }
22
+
23
+ export class CompletionMonitor {
24
+ protected static instance: CompletionMonitor;
25
+
26
+ static getInstance() {
27
+ if (!CompletionMonitor.instance) {
28
+ CompletionMonitor.instance = new CompletionMonitor();
29
+ }
30
+ return CompletionMonitor.instance;
31
+ }
32
+
33
+ protected completionChangeEmitter: Emitter<CompletionChange> = new Emitter();
34
+
35
+ get compeltionChange() {
36
+ return this.completionChangeEmitter.event;
37
+ }
38
+
39
+ protected tooltipChangeEmitter: Emitter<boolean> = new Emitter();
40
+
41
+ get onTooltipChange() {
42
+ return this.tooltipChangeEmitter.event;
43
+ }
44
+
45
+ protected currentChange: CompletionChange | undefined;
46
+
47
+ start(doc: DocStatus) {
48
+ this.currentChange = { start: doc, selectIndex: 0 };
49
+ }
50
+
51
+ accept(doc: DocStatus) {
52
+ this.currentChange = { ...this.currentChange, ...{ accept: doc } };
53
+ this.emitChange(this.currentChange);
54
+ }
55
+
56
+ close(doc: DocStatus) {
57
+ this.currentChange = { ...this.currentChange, ...{ close: doc, selectIndex: -1 } };
58
+ this.emitChange(this.currentChange);
59
+ }
60
+
61
+ updateIndex(index: number) {
62
+ this.currentChange = { ...this.currentChange, ...{ selectIndex: index } };
63
+ }
64
+
65
+ emitChange(change: CompletionChange) {
66
+ this.completionChangeEmitter.fire(change);
67
+ }
68
+ }
69
+
70
+ export interface MonitorPluginOptions {
71
+ onTooltipChange?: (visible: boolean) => void;
72
+ }
73
+
74
+ export const monitorPlugin = (options: MonitorPluginOptions) =>
75
+ EditorView.updateListener.of((update) => {
76
+ for (const trans of update.transactions) {
77
+ const { effects } = trans;
78
+
79
+ options?.onTooltipChange?.(hasHoverTooltips(update.state));
80
+
81
+ if (trans.isUserEvent('input.complete')) {
82
+ CompletionMonitor.getInstance().accept({
83
+ changes: JSON.stringify(trans.changes.toJSON()),
84
+ cursor: update.state.selection.main.head,
85
+ source: update.state.doc.toJSON(),
86
+ });
87
+ }
88
+ for (const effect of effects) {
89
+ if (effect.is(startCompletionEffect)) {
90
+ CompletionMonitor.getInstance().start({
91
+ cursor: update.state.selection.main.head,
92
+ source: update.state.doc.toJSON(),
93
+ });
94
+ } else if (effect.is(closeCompletionEffect)) {
95
+ CompletionMonitor.getInstance().close({
96
+ cursor: update.state.selection.main.head,
97
+ source: update.state.doc.toJSON(),
98
+ });
99
+ } else if (effect.is(setSelectedEffect)) {
100
+ CompletionMonitor.getInstance().updateIndex(effect.value);
101
+ }
102
+ }
103
+ }
104
+ return null;
105
+ });
@@ -0,0 +1,7 @@
1
+ import { pythonLanguage } from '@codemirror/lang-python';
2
+ import { LanguageSupport } from '@codemirror/language';
3
+
4
+ /// Python language support. 去掉了默认内置的代码提示
5
+ export function python() {
6
+ return new LanguageSupport(pythonLanguage, []);
7
+ }
@@ -0,0 +1,129 @@
1
+ /* stylelint-disable function-url-quotes */
2
+ .cm-editor {
3
+ line-height: var(--jp-code-line-height);
4
+ font-size: var(--jp-code-font-size);
5
+ font-family: var(--jp-code-font-family);
6
+ border: 0;
7
+ border-radius: 4px;
8
+ height: auto;
9
+ background: var(--mana-libro-input-background);
10
+ padding: 12px 0 18px;
11
+
12
+ /* Changed to auto to autogrow */
13
+ }
14
+
15
+ .cm-editor pre {
16
+ padding: 0 var(--jp-code-padding);
17
+ }
18
+
19
+ .cm-selectionMatch {
20
+ background-color: #e3ebff !important;
21
+ }
22
+
23
+ .jp-CodeMirrorEditor[data-type='inline'] .cm-dialog {
24
+ background-color: var(--jp-layout-color0);
25
+ color: var(--jp-content-font-color1);
26
+ }
27
+
28
+ .jp-CodeMirrorEditor {
29
+ cursor: text;
30
+ }
31
+
32
+ /* When zoomed out 67% and 33% on a screen of 1440 width x 900 height */
33
+ @media screen and (min-width: 2138px) and (max-width: 4319px) {
34
+ .jp-CodeMirrorEditor[data-type='inline'] .cm-cursor {
35
+ border-left: var(--jp-code-cursor-width1) solid var(--jp-editor-cursor-color);
36
+ }
37
+ }
38
+
39
+ /* When zoomed out less than 33% */
40
+ @media screen and (min-width: 4320px) {
41
+ .jp-CodeMirrorEditor[data-type='inline'] .cm-cursor {
42
+ border-left: var(--jp-code-cursor-width2) solid var(--jp-editor-cursor-color);
43
+ }
44
+ }
45
+
46
+ .cm-editor.jp-mod-readOnly .cm-cursor {
47
+ display: none;
48
+ }
49
+
50
+ .jp-CollaboratorCursor {
51
+ border-left: 5px solid transparent;
52
+ border-right: 5px solid transparent;
53
+ border-top: none;
54
+ border-bottom: 3px solid;
55
+ background-clip: content-box;
56
+ margin-left: -5px;
57
+ margin-right: -5px;
58
+ }
59
+
60
+ .cm-searching,
61
+ .cm-searching span {
62
+ background-color: var(--jp-search-unselected-match-background-color);
63
+ color: var(--jp-search-unselected-match-color);
64
+ }
65
+
66
+ .cm-searching span::selection {
67
+ background-color: var(--jp-search-selected-match-background-color) !important;
68
+ color: var(--jp-search-selected-match-color) !important;
69
+ }
70
+
71
+ .cm-trailingspace {
72
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAsElEQVQIHQGlAFr/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7+r3zKmT0/+pk9P/7+r3zAAAAAAAAAAABAAAAAAAAAAA6OPzM+/q9wAAAAAA6OPzMwAAAAAAAAAAAgAAAAAAAAAAGR8NiRQaCgAZIA0AGR8NiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQyoYJ/SY80UAAAAASUVORK5CYII=);
73
+ background-position: center left;
74
+ background-repeat: repeat-x;
75
+ }
76
+
77
+ .jp-CollaboratorCursor-hover {
78
+ position: absolute;
79
+ z-index: 1;
80
+ transform: translateX(-50%);
81
+ color: white;
82
+ border-radius: 3px;
83
+ padding: 1px 4px;
84
+ text-align: center;
85
+ font-size: var(--jp-ui-font-size1);
86
+ white-space: nowrap;
87
+ }
88
+
89
+ .jp-CodeMirror-ruler {
90
+ border-left: 1px dashed var(--jp-border-color2);
91
+ }
92
+
93
+ /* Styles for shared cursors (remote cursor locations and selected ranges) */
94
+ .jp-CodeMirrorEditor .remote-caret {
95
+ position: relative;
96
+ border-left: 2px solid black;
97
+ margin-left: -1px;
98
+ margin-right: -1px;
99
+ box-sizing: border-box;
100
+ }
101
+
102
+ .jp-CodeMirrorEditor .remote-caret > div {
103
+ white-space: nowrap;
104
+ position: absolute;
105
+ top: -1.15em;
106
+ padding-bottom: 0.05em;
107
+ left: -2px;
108
+ font-size: 0.95em;
109
+ background-color: rgb(250, 129, 0);
110
+ font-family: var(--jp-ui-font-family);
111
+ font-weight: bold;
112
+ line-height: normal;
113
+ user-select: none;
114
+ color: white;
115
+ padding-left: 2px;
116
+ padding-right: 2px;
117
+ z-index: 3;
118
+ transition: opacity 0.3s ease-in-out;
119
+ }
120
+
121
+ .jp-CodeMirrorEditor .remote-caret.hide-name > div {
122
+ transition-delay: 0.7s;
123
+ opacity: 0;
124
+ }
125
+
126
+ .jp-CodeMirrorEditor .remote-caret:hover > div {
127
+ opacity: 1;
128
+ transition-delay: 0s;
129
+ }
@@ -0,0 +1,12 @@
1
+ @import './variables.css';
2
+
3
+ /* Set the default typography for monospace elements */
4
+ tt,
5
+ code,
6
+ kbd,
7
+ samp,
8
+ pre {
9
+ font-family: var(--jp-code-font-family);
10
+ font-size: var(--jp-code-font-size);
11
+ line-height: var(--jp-code-line-height);
12
+ }