@difizen/libro-codemirror 0.0.0-snapshot-20241017072317
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/LICENSE +21 -0
- package/README.md +0 -0
- package/es/auto-complete/closebrackets.d.ts +12 -0
- package/es/auto-complete/closebrackets.d.ts.map +1 -0
- package/es/auto-complete/closebrackets.js +409 -0
- package/es/auto-complete/completion.d.ts +57 -0
- package/es/auto-complete/completion.d.ts.map +1 -0
- package/es/auto-complete/completion.js +267 -0
- package/es/auto-complete/config.d.ts +22 -0
- package/es/auto-complete/config.d.ts.map +1 -0
- package/es/auto-complete/config.js +44 -0
- package/es/auto-complete/filter.d.ts +13 -0
- package/es/auto-complete/filter.d.ts.map +1 -0
- package/es/auto-complete/filter.js +190 -0
- package/es/auto-complete/index.d.ts +17 -0
- package/es/auto-complete/index.d.ts.map +1 -0
- package/es/auto-complete/index.js +107 -0
- package/es/auto-complete/snippet.d.ts +14 -0
- package/es/auto-complete/snippet.d.ts.map +1 -0
- package/es/auto-complete/snippet.js +445 -0
- package/es/auto-complete/state.d.ts +63 -0
- package/es/auto-complete/state.d.ts.map +1 -0
- package/es/auto-complete/state.js +448 -0
- package/es/auto-complete/theme.d.ts +6 -0
- package/es/auto-complete/theme.d.ts.map +1 -0
- package/es/auto-complete/theme.js +150 -0
- package/es/auto-complete/tooltip.d.ts +5 -0
- package/es/auto-complete/tooltip.d.ts.map +1 -0
- package/es/auto-complete/tooltip.js +365 -0
- package/es/auto-complete/view.d.ts +38 -0
- package/es/auto-complete/view.d.ts.map +1 -0
- package/es/auto-complete/view.js +368 -0
- package/es/auto-complete/word.d.ts +3 -0
- package/es/auto-complete/word.d.ts.map +1 -0
- package/es/auto-complete/word.js +119 -0
- package/es/completion.d.ts +6 -0
- package/es/completion.d.ts.map +1 -0
- package/es/completion.js +84 -0
- package/es/config.d.ts +189 -0
- package/es/config.d.ts.map +1 -0
- package/es/config.js +482 -0
- package/es/editor-contribution.d.ts +9 -0
- package/es/editor-contribution.d.ts.map +1 -0
- package/es/editor-contribution.js +35 -0
- package/es/editor.d.ts +397 -0
- package/es/editor.d.ts.map +1 -0
- package/es/editor.js +1338 -0
- package/es/factory.d.ts +4 -0
- package/es/factory.d.ts.map +1 -0
- package/es/factory.js +24 -0
- package/es/hyperlink.d.ts +15 -0
- package/es/hyperlink.d.ts.map +1 -0
- package/es/hyperlink.js +120 -0
- package/es/indent.d.ts +8 -0
- package/es/indent.d.ts.map +1 -0
- package/es/indent.js +58 -0
- package/es/indentation-markers/config.d.ts +17 -0
- package/es/indentation-markers/config.d.ts.map +1 -0
- package/es/indentation-markers/config.js +10 -0
- package/es/indentation-markers/index.d.ts +3 -0
- package/es/indentation-markers/index.d.ts.map +1 -0
- package/es/indentation-markers/index.js +160 -0
- package/es/indentation-markers/map.d.ts +77 -0
- package/es/indentation-markers/map.d.ts.map +1 -0
- package/es/indentation-markers/map.js +265 -0
- package/es/indentation-markers/utils.d.ts +27 -0
- package/es/indentation-markers/utils.d.ts.map +1 -0
- package/es/indentation-markers/utils.js +91 -0
- package/es/index.d.ts +13 -0
- package/es/index.d.ts.map +1 -0
- package/es/index.js +12 -0
- package/es/libro-icon.d.ts +3 -0
- package/es/libro-icon.d.ts.map +1 -0
- package/es/libro-icon.js +2 -0
- package/es/lsp/completion.d.ts +5 -0
- package/es/lsp/completion.d.ts.map +1 -0
- package/es/lsp/completion.js +245 -0
- package/es/lsp/format.d.ts +7 -0
- package/es/lsp/format.d.ts.map +1 -0
- package/es/lsp/format.js +193 -0
- package/es/lsp/index.d.ts +7 -0
- package/es/lsp/index.d.ts.map +1 -0
- package/es/lsp/index.js +6 -0
- package/es/lsp/lint.d.ts +3 -0
- package/es/lsp/lint.d.ts.map +1 -0
- package/es/lsp/lint.js +113 -0
- package/es/lsp/protocol.d.ts +7 -0
- package/es/lsp/protocol.d.ts.map +1 -0
- package/es/lsp/protocol.js +1 -0
- package/es/lsp/tooltip.d.ts +3 -0
- package/es/lsp/tooltip.d.ts.map +1 -0
- package/es/lsp/tooltip.js +113 -0
- package/es/lsp/util.d.ts +15 -0
- package/es/lsp/util.d.ts.map +1 -0
- package/es/lsp/util.js +57 -0
- package/es/mimetype.d.ts +22 -0
- package/es/mimetype.d.ts.map +1 -0
- package/es/mimetype.js +59 -0
- package/es/mode.d.ts +86 -0
- package/es/mode.d.ts.map +1 -0
- package/es/mode.js +280 -0
- package/es/module.d.ts +3 -0
- package/es/module.d.ts.map +1 -0
- package/es/module.js +4 -0
- package/es/monitor.d.ts +32 -0
- package/es/monitor.d.ts.map +1 -0
- package/es/monitor.js +129 -0
- package/es/python-lang.d.ts +3 -0
- package/es/python-lang.d.ts.map +1 -0
- package/es/python-lang.js +7 -0
- package/es/style/base.css +129 -0
- package/es/style/theme.css +12 -0
- package/es/style/variables.css +405 -0
- package/es/theme.d.ts +35 -0
- package/es/theme.d.ts.map +1 -0
- package/es/theme.js +223 -0
- package/es/tooltip.d.ts +10 -0
- package/es/tooltip.d.ts.map +1 -0
- package/es/tooltip.js +168 -0
- package/package.json +74 -0
- package/src/auto-complete/README.md +71 -0
- package/src/auto-complete/closebrackets.ts +423 -0
- package/src/auto-complete/completion.ts +345 -0
- package/src/auto-complete/config.ts +101 -0
- package/src/auto-complete/filter.ts +214 -0
- package/src/auto-complete/index.ts +112 -0
- package/src/auto-complete/snippet.ts +392 -0
- package/src/auto-complete/state.ts +465 -0
- package/src/auto-complete/theme.ts +127 -0
- package/src/auto-complete/tooltip.ts +386 -0
- package/src/auto-complete/view.ts +339 -0
- package/src/auto-complete/word.ts +118 -0
- package/src/completion.ts +61 -0
- package/src/config.ts +701 -0
- package/src/editor-contribution.ts +22 -0
- package/src/editor.ts +1287 -0
- package/src/factory.ts +31 -0
- package/src/hyperlink.ts +95 -0
- package/src/indent.ts +69 -0
- package/src/indentation-markers/config.ts +31 -0
- package/src/indentation-markers/index.ts +192 -0
- package/src/indentation-markers/map.ts +273 -0
- package/src/indentation-markers/utils.ts +84 -0
- package/src/index.spec.ts +12 -0
- package/src/index.ts +14 -0
- package/src/libro-icon.tsx +4 -0
- package/src/lsp/completion.ts +175 -0
- package/src/lsp/format.ts +144 -0
- package/src/lsp/index.ts +6 -0
- package/src/lsp/lint.ts +125 -0
- package/src/lsp/protocol.ts +8 -0
- package/src/lsp/tooltip.ts +76 -0
- package/src/lsp/util.ts +69 -0
- package/src/mimetype.ts +49 -0
- package/src/mode.ts +265 -0
- package/src/module.ts +8 -0
- package/src/monitor.ts +105 -0
- package/src/python-lang.ts +7 -0
- package/src/style/base.css +129 -0
- package/src/style/theme.css +12 -0
- package/src/style/variables.css +405 -0
- package/src/theme.ts +231 -0
- package/src/tooltip.ts +143 -0
package/src/lsp/util.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Text } from '@codemirror/state';
|
|
2
|
+
import hljs from 'highlight.js';
|
|
3
|
+
import MarkdownIt from 'markdown-it';
|
|
4
|
+
import type * as lsp from 'vscode-languageserver-protocol';
|
|
5
|
+
import 'highlight.js/styles/github.css';
|
|
6
|
+
|
|
7
|
+
export function posToOffset(doc: Text, pos: { line: number; character: number }) {
|
|
8
|
+
if (pos.line >= doc.lines) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const offset = doc.line(pos.line + 1).from + pos.character;
|
|
12
|
+
if (offset > doc.length) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
return offset;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function offsetToPos(doc: Text, offset: number) {
|
|
19
|
+
const line = doc.lineAt(offset);
|
|
20
|
+
return {
|
|
21
|
+
line: line.number - 1,
|
|
22
|
+
character: offset - line.from,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function formatContents(
|
|
27
|
+
contents: lsp.MarkupContent | lsp.MarkedString | lsp.MarkedString[],
|
|
28
|
+
): string {
|
|
29
|
+
if (Array.isArray(contents)) {
|
|
30
|
+
return contents.map((c) => formatContents(c) + '\n\n').join('');
|
|
31
|
+
} else if (typeof contents === 'string') {
|
|
32
|
+
return contents;
|
|
33
|
+
} else {
|
|
34
|
+
return contents.value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const renderMarkdownContent = (val: string) => {
|
|
39
|
+
const render = new MarkdownIt({
|
|
40
|
+
html: true,
|
|
41
|
+
linkify: true,
|
|
42
|
+
breaks: true,
|
|
43
|
+
highlight: function (str, lang) {
|
|
44
|
+
if (lang && hljs.getLanguage(lang)) {
|
|
45
|
+
try {
|
|
46
|
+
const hl = hljs.highlight(lang, str).value;
|
|
47
|
+
return hl;
|
|
48
|
+
} catch (__) {
|
|
49
|
+
//
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return ''; // use external default escaping
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return render.render(val);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const renderMarkupContent = (
|
|
61
|
+
contents: lsp.MarkupContent | lsp.MarkedString | lsp.MarkedString[],
|
|
62
|
+
) => {
|
|
63
|
+
const dom = document.createElement('div');
|
|
64
|
+
dom.classList.add('documentation');
|
|
65
|
+
|
|
66
|
+
const res = renderMarkdownContent(formatContents(contents));
|
|
67
|
+
dom.innerHTML = res;
|
|
68
|
+
return dom;
|
|
69
|
+
};
|
package/src/mimetype.ts
ADDED
|
@@ -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,265 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
import { markdown } from '@codemirror/lang-markdown';
|
|
3
|
+
import type { LanguageSupport } from '@codemirror/language';
|
|
4
|
+
import { LanguageDescription } from '@codemirror/language';
|
|
5
|
+
import { defaultMimeType } from '@difizen/libro-code-editor';
|
|
6
|
+
import { PathExt } from '@difizen/libro-common';
|
|
7
|
+
import { highlightTree } from '@lezer/highlight';
|
|
8
|
+
|
|
9
|
+
import { python } from './python-lang.js';
|
|
10
|
+
import { jupyterHighlightStyle } from './theme.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The interface of a codemirror language spec.
|
|
14
|
+
*/
|
|
15
|
+
export interface ISpec {
|
|
16
|
+
name: string;
|
|
17
|
+
alias?: readonly string[];
|
|
18
|
+
mime: string | readonly string[];
|
|
19
|
+
load?: () => Promise<LanguageSupport>;
|
|
20
|
+
extensions?: readonly string[];
|
|
21
|
+
filename?: RegExp;
|
|
22
|
+
support?: LanguageSupport;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Code mirror uses two similar structures, a plain object with optional fields,
|
|
26
|
+
// and a class with the same fields but all mandatory. Maybe adopting the same
|
|
27
|
+
// pattern would be less confusing (although far more verbose)
|
|
28
|
+
function makeSpec(spec: ISpec): ISpec {
|
|
29
|
+
const res = LanguageDescription.of(spec) as unknown as ISpec;
|
|
30
|
+
res.mime = spec.mime;
|
|
31
|
+
return res;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const modeList: ISpec[] = [
|
|
35
|
+
makeSpec({
|
|
36
|
+
name: 'Python',
|
|
37
|
+
mime: 'text/x-python',
|
|
38
|
+
extensions: ['BUILD', 'bzl', 'py', 'pyw'],
|
|
39
|
+
filename: /^(BUCK|BUILD)$/,
|
|
40
|
+
load() {
|
|
41
|
+
return Promise.resolve(python());
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
makeSpec({
|
|
45
|
+
name: 'Markdown',
|
|
46
|
+
mime: 'text/x-markdown',
|
|
47
|
+
extensions: ['md', 'markdown', 'mkd'],
|
|
48
|
+
async load() {
|
|
49
|
+
return Promise.resolve(markdown());
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the raw list of available modes specs.
|
|
56
|
+
*
|
|
57
|
+
* @alpha
|
|
58
|
+
* @returns The available modes
|
|
59
|
+
*/
|
|
60
|
+
export function getModeInfo(): ISpec[] {
|
|
61
|
+
return modeList;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Find a codemirror mode by MIME.
|
|
66
|
+
*
|
|
67
|
+
* @alpha
|
|
68
|
+
* @param mime Mime type to look for
|
|
69
|
+
* @returns The mode or null
|
|
70
|
+
*/
|
|
71
|
+
export function findByMIME(mime: string | readonly string[]): ISpec | null {
|
|
72
|
+
if (Array.isArray(mime)) {
|
|
73
|
+
for (let i = 0; i < mime.length; i++) {
|
|
74
|
+
const spec = findByMIME(mime[i]);
|
|
75
|
+
if (spec) {
|
|
76
|
+
return spec;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const _mime = (mime as string).toLowerCase();
|
|
82
|
+
for (let i = 0; i < modeList.length; i++) {
|
|
83
|
+
const info = modeList[i];
|
|
84
|
+
if (Array.isArray(info.mime)) {
|
|
85
|
+
for (let j = 0; j < info.mime.length; j++) {
|
|
86
|
+
if (info.mime[j] === _mime) {
|
|
87
|
+
return info;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} else if (info.mime === _mime) {
|
|
91
|
+
return info;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (/\+xml$/.test(_mime)) {
|
|
95
|
+
return findByMIME('application/xml');
|
|
96
|
+
}
|
|
97
|
+
if (/\+json$/.test(_mime)) {
|
|
98
|
+
return findByMIME('application/json');
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Find a codemirror mode by name.
|
|
105
|
+
*
|
|
106
|
+
* @alpha
|
|
107
|
+
* @param name The mode name
|
|
108
|
+
* @returns The mode or null
|
|
109
|
+
*/
|
|
110
|
+
export function findByName(name: string): ISpec | null {
|
|
111
|
+
const _name = name.toLowerCase();
|
|
112
|
+
for (let i = 0; i < modeList.length; i++) {
|
|
113
|
+
const info = modeList[i];
|
|
114
|
+
if (info.name.toLowerCase() === _name) {
|
|
115
|
+
return info;
|
|
116
|
+
}
|
|
117
|
+
if (info.alias) {
|
|
118
|
+
for (let j = 0; j < info.alias.length; j++) {
|
|
119
|
+
if (info.alias[j].toLowerCase() === _name) {
|
|
120
|
+
return info;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Find a codemirror mode by extension.
|
|
130
|
+
*
|
|
131
|
+
* @alpha
|
|
132
|
+
* @param ext The extension name
|
|
133
|
+
* @returns The mode or null
|
|
134
|
+
*/
|
|
135
|
+
export function findByExtension(ext: string | readonly string[]): ISpec | null {
|
|
136
|
+
if (Array.isArray(ext)) {
|
|
137
|
+
for (let i = 0; i < ext.length; i++) {
|
|
138
|
+
const spec = findByExtension(ext[i]);
|
|
139
|
+
if (spec) {
|
|
140
|
+
return spec;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const _ext = (ext as string).toLowerCase();
|
|
146
|
+
for (let i = 0; i < modeList.length; i++) {
|
|
147
|
+
const info = modeList[i];
|
|
148
|
+
for (let j = 0; j < info.extensions!.length; j++) {
|
|
149
|
+
if (info.extensions![j].toLowerCase() === _ext) {
|
|
150
|
+
return info;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Find a codemirror mode by filename.
|
|
159
|
+
*
|
|
160
|
+
* @param name File name
|
|
161
|
+
* @returns The mode or null
|
|
162
|
+
*/
|
|
163
|
+
export function findByFileName(name: string): ISpec | null {
|
|
164
|
+
const basename = PathExt.basename(name);
|
|
165
|
+
for (let i = 0; i < modeList.length; i++) {
|
|
166
|
+
const info = modeList[i];
|
|
167
|
+
if (info.filename && info.filename.test(basename)) {
|
|
168
|
+
return info;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const dot = basename.lastIndexOf('.');
|
|
172
|
+
const ext = dot > -1 && basename.substring(dot + 1, basename.length);
|
|
173
|
+
if (ext) {
|
|
174
|
+
return findByExtension(ext);
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Find a codemirror mode by name or CodeMirror spec.
|
|
181
|
+
*
|
|
182
|
+
* @alpha
|
|
183
|
+
* @param mode The CodeMirror mode
|
|
184
|
+
* @param fallback Whether to fallback to default mimetype spec or not
|
|
185
|
+
* @returns The mode or null
|
|
186
|
+
*/
|
|
187
|
+
export function findBest(mode: string | ISpec, fallback = true): ISpec | null {
|
|
188
|
+
const modename = typeof mode === 'string' ? mode : mode.name;
|
|
189
|
+
const mimetype = typeof mode !== 'string' ? mode.mime : modename;
|
|
190
|
+
const ext = typeof mode !== 'string' ? (mode.extensions ?? []) : [];
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
(modename ? findByName(modename) : null) ??
|
|
194
|
+
(mimetype ? findByMIME(mimetype) : null) ??
|
|
195
|
+
findByExtension(ext) ??
|
|
196
|
+
(fallback ? findByMIME(defaultMimeType) : null)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Ensure a codemirror mode is available by name or Codemirror spec.
|
|
202
|
+
*
|
|
203
|
+
* @param mode - The mode to ensure. If it is a string, uses [findBest]
|
|
204
|
+
* to get the appropriate spec.
|
|
205
|
+
*
|
|
206
|
+
* @returns A promise that resolves when the mode is available.
|
|
207
|
+
*/
|
|
208
|
+
export async function ensure(mode: string | ISpec): Promise<ISpec | null> {
|
|
209
|
+
const spec = findBest(mode);
|
|
210
|
+
if (spec) {
|
|
211
|
+
spec.support = await spec.load!();
|
|
212
|
+
return spec;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Register a new mode for CodeMirror
|
|
219
|
+
*
|
|
220
|
+
* @alpha
|
|
221
|
+
* @param mode Mode to register
|
|
222
|
+
*/
|
|
223
|
+
export function registerModeInfo(mode: ISpec): void {
|
|
224
|
+
const info = findBest(mode, false);
|
|
225
|
+
if (info) {
|
|
226
|
+
throw new Error(`${mode.mime} already registered`);
|
|
227
|
+
}
|
|
228
|
+
modeList.push(makeSpec(mode));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Parse and style a string.
|
|
233
|
+
*
|
|
234
|
+
* @alpha
|
|
235
|
+
* @param code Code to highlight
|
|
236
|
+
* @param mode Code mode
|
|
237
|
+
* @param el HTML element into which the highlighted code will be inserted
|
|
238
|
+
*/
|
|
239
|
+
export function run(code: string, mode: ISpec, el: HTMLElement): void {
|
|
240
|
+
const language = mode.support?.language;
|
|
241
|
+
if (!language) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const tree = language.parser.parse(code);
|
|
246
|
+
// position state required because unstyled tokens are not emitted
|
|
247
|
+
// in highlightTree
|
|
248
|
+
let pos = 0;
|
|
249
|
+
highlightTree(tree, jupyterHighlightStyle, (from, to, classes) => {
|
|
250
|
+
if (from > pos) {
|
|
251
|
+
// No style applied to the token between pos and from
|
|
252
|
+
el.appendChild(document.createTextNode(code.slice(pos, from)));
|
|
253
|
+
}
|
|
254
|
+
const sp = el.appendChild(document.createElement('span'));
|
|
255
|
+
sp.className = classes;
|
|
256
|
+
sp.appendChild(document.createTextNode(code.slice(from, to)));
|
|
257
|
+
pos = to;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (pos < tree.length - 1) {
|
|
261
|
+
// No style applied on the trailing text
|
|
262
|
+
el.appendChild(document.createTextNode(code.slice(pos, tree.length)));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// }
|
package/src/module.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CodeEditorModule } from '@difizen/libro-code-editor';
|
|
2
|
+
import { ManaModule } from '@difizen/mana-app';
|
|
3
|
+
|
|
4
|
+
import { CodeMirrorEditorContribution } from './editor-contribution.js';
|
|
5
|
+
|
|
6
|
+
export const CodeMirrorEditorModule = ManaModule.create()
|
|
7
|
+
.register(CodeMirrorEditorContribution)
|
|
8
|
+
.dependOn(CodeEditorModule);
|
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,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
|
+
}
|