@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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import assert from 'assert';
|
|
3
|
+
|
|
4
|
+
// import { CodeMirrorEditor, codeMirrorEditorFactory } from './index.js';
|
|
5
|
+
|
|
6
|
+
describe('libro-codemirror', () => {
|
|
7
|
+
it('#import', () => {
|
|
8
|
+
assert(true);
|
|
9
|
+
// assert(CodeMirrorEditor);
|
|
10
|
+
// assert(codeMirrorEditorFactory);
|
|
11
|
+
});
|
|
12
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
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 './lsp/index.js';
|
|
8
|
+
export * from './mode.js';
|
|
9
|
+
export * from './module.js';
|
|
10
|
+
export * from './factory.js';
|
|
11
|
+
export * from './monitor.js';
|
|
12
|
+
export * from './theme.js';
|
|
13
|
+
|
|
14
|
+
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" fill-rule="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" fill-rule="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,175 @@
|
|
|
1
|
+
import { pythonLanguage } from '@codemirror/lang-python';
|
|
2
|
+
import { CompletionItemKind, CompletionTriggerKind } from '@difizen/libro-lsp';
|
|
3
|
+
|
|
4
|
+
import type { Completion, CompletionSource } from '../auto-complete/index.js';
|
|
5
|
+
|
|
6
|
+
import type { CMLSPExtension } from './protocol.js';
|
|
7
|
+
import { offsetToPos, renderMarkupContent } from './util.js';
|
|
8
|
+
|
|
9
|
+
export type CompletionItemDetailReolve = (
|
|
10
|
+
completion: Completion,
|
|
11
|
+
) => Node | null | Promise<Node | null>;
|
|
12
|
+
|
|
13
|
+
const CompletionItemKindMap = Object.fromEntries(
|
|
14
|
+
Object.entries(CompletionItemKind).map(([key, value]) => [value, key]),
|
|
15
|
+
) as Record<CompletionItemKind, string>;
|
|
16
|
+
|
|
17
|
+
function toSet(chars: Set<string>) {
|
|
18
|
+
let preamble = '';
|
|
19
|
+
let flat = Array.from(chars).join('');
|
|
20
|
+
const words = /\w/.test(flat);
|
|
21
|
+
if (words) {
|
|
22
|
+
preamble += '\\w';
|
|
23
|
+
flat = flat.replace(/\w/g, '');
|
|
24
|
+
}
|
|
25
|
+
return `[${preamble}${flat.replace(/[^\w\s]/g, '\\$&')}]`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function prefixMatch(options: Completion[]) {
|
|
29
|
+
const first = new Set<string>();
|
|
30
|
+
const rest = new Set<string>();
|
|
31
|
+
|
|
32
|
+
for (const { apply } of options) {
|
|
33
|
+
const [initial, ...restStr] = apply as string;
|
|
34
|
+
first.add(initial);
|
|
35
|
+
for (const char of restStr) {
|
|
36
|
+
rest.add(char);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const source = toSet(first) + toSet(rest) + '*$';
|
|
41
|
+
return [new RegExp('^' + source), new RegExp(source)];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const lspPythonCompletion: CMLSPExtension = ({ lspProvider }) => {
|
|
45
|
+
const completionSource: CompletionSource = async (context) => {
|
|
46
|
+
/**
|
|
47
|
+
* 只在显式的使用tab触发时调用kernel completion
|
|
48
|
+
* 只在只在隐式的输入时触发时调用lsp completion
|
|
49
|
+
*/
|
|
50
|
+
if (!lspProvider || context.explicit === true) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { virtualDocument: doc, lspConnection, editor } = await lspProvider();
|
|
55
|
+
|
|
56
|
+
const { state } = context;
|
|
57
|
+
let { pos } = context;
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
!lspConnection ||
|
|
61
|
+
!lspConnection.isReady ||
|
|
62
|
+
!lspConnection.provides('completionProvider')
|
|
63
|
+
) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const { line, character } = offsetToPos(state.doc, pos);
|
|
68
|
+
|
|
69
|
+
const rootPos = doc.transformFromEditorToRoot(editor, {
|
|
70
|
+
line,
|
|
71
|
+
ch: character,
|
|
72
|
+
isEditor: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!rootPos) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const virtualPos = doc.virtualPositionAtDocument(rootPos);
|
|
80
|
+
|
|
81
|
+
const result = await lspConnection.clientRequests[
|
|
82
|
+
'textDocument/completion'
|
|
83
|
+
].request({
|
|
84
|
+
position: { line: virtualPos.line, character: virtualPos.ch },
|
|
85
|
+
textDocument: {
|
|
86
|
+
uri: doc.documentInfo.uri,
|
|
87
|
+
},
|
|
88
|
+
context: {
|
|
89
|
+
triggerKind: CompletionTriggerKind.Invoked,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!result) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const items = 'items' in result ? result.items : result;
|
|
98
|
+
|
|
99
|
+
let options = items.map((item) => {
|
|
100
|
+
const { detail, label, kind, textEdit, documentation, sortText, filterText } =
|
|
101
|
+
item;
|
|
102
|
+
const completion: Completion & {
|
|
103
|
+
filterText: string;
|
|
104
|
+
sortText?: string;
|
|
105
|
+
apply: string;
|
|
106
|
+
} = {
|
|
107
|
+
label,
|
|
108
|
+
detail,
|
|
109
|
+
apply: textEdit?.newText ?? label,
|
|
110
|
+
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
|
111
|
+
sortText: sortText ?? label,
|
|
112
|
+
filterText: filterText ?? label,
|
|
113
|
+
};
|
|
114
|
+
if (documentation) {
|
|
115
|
+
const resolver: CompletionItemDetailReolve = async () => {
|
|
116
|
+
return renderMarkupContent(documentation);
|
|
117
|
+
};
|
|
118
|
+
completion.info = resolver;
|
|
119
|
+
} else {
|
|
120
|
+
const resolver: CompletionItemDetailReolve = async () => {
|
|
121
|
+
const itemResult =
|
|
122
|
+
await lspConnection.clientRequests['completionItem/resolve'].request(item);
|
|
123
|
+
return itemResult.documentation
|
|
124
|
+
? renderMarkupContent(itemResult.documentation)
|
|
125
|
+
: null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
completion.info = resolver;
|
|
129
|
+
}
|
|
130
|
+
return completion;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const [, match] = prefixMatch(options);
|
|
134
|
+
const token = context.matchBefore(match);
|
|
135
|
+
|
|
136
|
+
// TODO: sort 方法需要进一步改进
|
|
137
|
+
if (token) {
|
|
138
|
+
pos = token.from;
|
|
139
|
+
const word = token.text.toLowerCase();
|
|
140
|
+
if (/^\w+$/.test(word)) {
|
|
141
|
+
options = options
|
|
142
|
+
.filter(({ filterText }) => filterText.toLowerCase().startsWith(word))
|
|
143
|
+
.sort(
|
|
144
|
+
({ apply: a, sortText: sortTexta }, { apply: b, sortText: sortTextb }) => {
|
|
145
|
+
switch (true) {
|
|
146
|
+
case sortTexta !== undefined && sortTextb !== undefined:
|
|
147
|
+
return sortTexta!.localeCompare(sortTextb!);
|
|
148
|
+
case a.startsWith(token.text) && !b.startsWith(token.text):
|
|
149
|
+
return -1;
|
|
150
|
+
case !a.startsWith(token.text) && b.startsWith(token.text):
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
options = options.sort(({ sortText: sortTexta }, { sortText: sortTextb }) => {
|
|
159
|
+
switch (true) {
|
|
160
|
+
case sortTexta !== undefined && sortTextb !== undefined:
|
|
161
|
+
return sortTexta!.localeCompare(sortTextb!);
|
|
162
|
+
}
|
|
163
|
+
return 0;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
from: pos,
|
|
169
|
+
options,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
return pythonLanguage.data.of({
|
|
173
|
+
autocomplete: completionSource,
|
|
174
|
+
});
|
|
175
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-parameter-properties */
|
|
2
|
+
/* eslint-disable @typescript-eslint/parameter-properties */
|
|
3
|
+
import type { TransactionSpec } from '@codemirror/state';
|
|
4
|
+
import { StateEffect } from '@codemirror/state';
|
|
5
|
+
import type {
|
|
6
|
+
Command,
|
|
7
|
+
EditorView,
|
|
8
|
+
KeyBinding,
|
|
9
|
+
PluginValue,
|
|
10
|
+
ViewUpdate,
|
|
11
|
+
} from '@codemirror/view';
|
|
12
|
+
import { ViewPlugin } from '@codemirror/view';
|
|
13
|
+
|
|
14
|
+
import { insertCompletionText } from '../auto-complete/index.js';
|
|
15
|
+
|
|
16
|
+
import type { CMLSPExtension, LSPExtensionOptions } from './protocol.js';
|
|
17
|
+
import { offsetToPos, posToOffset } from './util.js';
|
|
18
|
+
|
|
19
|
+
export const startFormatEffect = StateEffect.define<boolean>();
|
|
20
|
+
|
|
21
|
+
export const formatCell: Command = (view: EditorView) => {
|
|
22
|
+
view.dispatch({ effects: startFormatEffect.of(true) });
|
|
23
|
+
return true;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const formatKeymap: readonly KeyBinding[] = [{ key: 'Alt-f', run: formatCell }];
|
|
27
|
+
|
|
28
|
+
class FormatPlugin implements PluginValue {
|
|
29
|
+
constructor(
|
|
30
|
+
readonly view: EditorView,
|
|
31
|
+
readonly options: LSPExtensionOptions,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
update(update: ViewUpdate) {
|
|
35
|
+
for (const tr of update.transactions) {
|
|
36
|
+
for (const effect of tr.effects) {
|
|
37
|
+
if (effect.is(startFormatEffect)) {
|
|
38
|
+
this.doFormat();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async doFormat() {
|
|
45
|
+
const lspProvider = await this.options.lspProvider?.();
|
|
46
|
+
if (!lspProvider) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// const { state } = this.view;
|
|
50
|
+
// const currentLine = state.doc.lineAt(state.selection.main.head).number;
|
|
51
|
+
|
|
52
|
+
const { editor, virtualDocument, lspConnection } = lspProvider;
|
|
53
|
+
const virtualStartPos = virtualDocument.transformEditorToVirtual(editor, {
|
|
54
|
+
line: 0,
|
|
55
|
+
ch: 0,
|
|
56
|
+
isEditor: true,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const end = offsetToPos(this.view.state.doc, this.view.state.doc.length);
|
|
60
|
+
|
|
61
|
+
const virtualEndPos = virtualDocument.transformEditorToVirtual(editor, {
|
|
62
|
+
line: end.line,
|
|
63
|
+
ch: end.character,
|
|
64
|
+
isEditor: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!virtualStartPos || !virtualEndPos) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
lspConnection.clientRequests['textDocument/rangeFormatting']
|
|
72
|
+
.request({
|
|
73
|
+
textDocument: { uri: virtualDocument.uri },
|
|
74
|
+
range: {
|
|
75
|
+
start: { line: virtualStartPos.line, character: virtualStartPos.ch },
|
|
76
|
+
end: { line: virtualEndPos.line, character: virtualEndPos.ch },
|
|
77
|
+
},
|
|
78
|
+
options: {
|
|
79
|
+
tabSize: this.view.state.tabSize,
|
|
80
|
+
insertSpaces: true,
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
.then((result) => {
|
|
84
|
+
if (result && result?.length) {
|
|
85
|
+
const items = result;
|
|
86
|
+
const transaction: TransactionSpec[] = [];
|
|
87
|
+
items.forEach((item) => {
|
|
88
|
+
const defaultNewLine = {
|
|
89
|
+
line: end.line + 1,
|
|
90
|
+
ch: 0,
|
|
91
|
+
};
|
|
92
|
+
const editorStart =
|
|
93
|
+
virtualDocument.transformVirtualToEditor({
|
|
94
|
+
line: item.range.start.line,
|
|
95
|
+
ch: item.range.start.character,
|
|
96
|
+
isVirtual: true,
|
|
97
|
+
}) ?? defaultNewLine;
|
|
98
|
+
const editorEnd =
|
|
99
|
+
virtualDocument.transformVirtualToEditor({
|
|
100
|
+
line: item.range.end.line,
|
|
101
|
+
ch: item.range.end.character,
|
|
102
|
+
isVirtual: true,
|
|
103
|
+
}) ?? defaultNewLine;
|
|
104
|
+
|
|
105
|
+
if (!editorStart || !editorEnd) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const from = posToOffset(this.view.state.doc, {
|
|
109
|
+
line: editorStart.line,
|
|
110
|
+
character: editorStart.ch,
|
|
111
|
+
});
|
|
112
|
+
const to = posToOffset(this.view.state.doc, {
|
|
113
|
+
line: editorEnd.line,
|
|
114
|
+
character: editorEnd.ch,
|
|
115
|
+
});
|
|
116
|
+
// FIXME: 需要处理新增行的情况,目前在virtualdocument无法处理
|
|
117
|
+
// console.log('format', item.range, editorStart, editorEnd, from, to);
|
|
118
|
+
if (from !== undefined && to !== undefined) {
|
|
119
|
+
const trans = insertCompletionText(
|
|
120
|
+
this.view.state,
|
|
121
|
+
item.newText,
|
|
122
|
+
from,
|
|
123
|
+
to,
|
|
124
|
+
);
|
|
125
|
+
transaction.push(trans);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// console.log(transaction, 'format trans');
|
|
129
|
+
|
|
130
|
+
this.view.dispatch(...transaction);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
})
|
|
134
|
+
.catch(console.error);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
destroy() {
|
|
138
|
+
//
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const lspFormat: CMLSPExtension = (options) => {
|
|
143
|
+
return [ViewPlugin.define((view) => new FormatPlugin(view, options))];
|
|
144
|
+
};
|
package/src/lsp/index.ts
ADDED
package/src/lsp/lint.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { Diagnostic } from '@codemirror/lint';
|
|
2
|
+
import { setDiagnostics } from '@codemirror/lint';
|
|
3
|
+
import type { PluginValue, EditorView } from '@codemirror/view';
|
|
4
|
+
import { ViewPlugin } from '@codemirror/view';
|
|
5
|
+
import { DiagnosticSeverity } from '@difizen/libro-lsp';
|
|
6
|
+
|
|
7
|
+
import type { CMLSPExtension, LSPExtensionOptions } from './protocol.js';
|
|
8
|
+
import { posToOffset } from './util.js';
|
|
9
|
+
|
|
10
|
+
class LintPlugin implements PluginValue {
|
|
11
|
+
constructor(
|
|
12
|
+
readonly view: EditorView,
|
|
13
|
+
readonly options: LSPExtensionOptions,
|
|
14
|
+
) {
|
|
15
|
+
this.processDiagnostic();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
processDiagnostic() {
|
|
19
|
+
if (!this.options.lspProvider) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
this.options
|
|
23
|
+
.lspProvider()
|
|
24
|
+
.then(({ lspConnection, virtualDocument, editor }) => {
|
|
25
|
+
if (!lspConnection) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
lspConnection.serverNotifications['textDocument/publishDiagnostics'].event(
|
|
29
|
+
(e) => {
|
|
30
|
+
const diagnostics = e.diagnostics
|
|
31
|
+
.map(({ range, message, severity = DiagnosticSeverity.Information }) => {
|
|
32
|
+
const currentEditor = virtualDocument.getEditorAtVirtualLine({
|
|
33
|
+
line: range.start.line,
|
|
34
|
+
ch: range.start.character,
|
|
35
|
+
isVirtual: true,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// the diagnostic range must be in current editor
|
|
39
|
+
if (editor !== currentEditor) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const editorStart = virtualDocument.transformVirtualToEditor({
|
|
44
|
+
line: range.start.line,
|
|
45
|
+
ch: range.start.character,
|
|
46
|
+
isVirtual: true,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
let offset: number | undefined;
|
|
50
|
+
if (editorStart) {
|
|
51
|
+
offset = posToOffset(this.view.state.doc, {
|
|
52
|
+
line: editorStart.line,
|
|
53
|
+
character: editorStart.ch,
|
|
54
|
+
})!;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const editorEnd = virtualDocument.transformVirtualToEditor({
|
|
58
|
+
line: range.end.line,
|
|
59
|
+
ch: range.end.character,
|
|
60
|
+
isVirtual: true,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
let end: number | undefined;
|
|
64
|
+
if (editorEnd) {
|
|
65
|
+
end = posToOffset(this.view.state.doc, {
|
|
66
|
+
line: editorEnd.line,
|
|
67
|
+
character: editorEnd.ch,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
from: offset,
|
|
72
|
+
to: end,
|
|
73
|
+
severity: (
|
|
74
|
+
{
|
|
75
|
+
[DiagnosticSeverity.Error]: 'error',
|
|
76
|
+
[DiagnosticSeverity.Warning]: 'warning',
|
|
77
|
+
[DiagnosticSeverity.Information]: 'info',
|
|
78
|
+
[DiagnosticSeverity.Hint]: 'info',
|
|
79
|
+
} as const
|
|
80
|
+
)[severity],
|
|
81
|
+
message,
|
|
82
|
+
} as Diagnostic;
|
|
83
|
+
})
|
|
84
|
+
.filter<Diagnostic>(isDiagnostic)
|
|
85
|
+
.sort((a, b) => {
|
|
86
|
+
switch (true) {
|
|
87
|
+
case a.from < b.from:
|
|
88
|
+
return -1;
|
|
89
|
+
case a.from > b.from:
|
|
90
|
+
return 1;
|
|
91
|
+
}
|
|
92
|
+
return 0;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this.view.dispatch(setDiagnostics(this.view.state, diagnostics));
|
|
96
|
+
},
|
|
97
|
+
);
|
|
98
|
+
return;
|
|
99
|
+
})
|
|
100
|
+
.catch(console.error);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
update() {
|
|
104
|
+
//
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
destroy() {
|
|
108
|
+
//
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export const lspLint: CMLSPExtension = (options) => {
|
|
113
|
+
return [ViewPlugin.define((view) => new LintPlugin(view, options))];
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function isDiagnostic(item: any): item is Diagnostic {
|
|
117
|
+
return (
|
|
118
|
+
item !== undefined &&
|
|
119
|
+
item !== null &&
|
|
120
|
+
item.from !== null &&
|
|
121
|
+
item.to !== null &&
|
|
122
|
+
item.from !== undefined &&
|
|
123
|
+
item.to !== undefined
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { hoverTooltip } from '@codemirror/view';
|
|
2
|
+
|
|
3
|
+
import type { CMLSPExtension } from './protocol.js';
|
|
4
|
+
import { offsetToPos, posToOffset, renderMarkupContent } from './util.js';
|
|
5
|
+
|
|
6
|
+
export const lspTooltip: CMLSPExtension = (options) => {
|
|
7
|
+
return hoverTooltip(async (view, pos) => {
|
|
8
|
+
if (!options.lspProvider) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
lspConnection: connection,
|
|
14
|
+
virtualDocument: doc,
|
|
15
|
+
editor,
|
|
16
|
+
} = await options.lspProvider();
|
|
17
|
+
|
|
18
|
+
if (!connection || !connection.isReady || !connection.provides('hoverProvider')) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { line, character } = offsetToPos(view.state.doc, pos);
|
|
23
|
+
|
|
24
|
+
const virtualPos = doc.transformEditorToVirtual(editor, {
|
|
25
|
+
line,
|
|
26
|
+
ch: character,
|
|
27
|
+
isEditor: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!virtualPos) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const result = await connection.clientRequests['textDocument/hover'].request({
|
|
35
|
+
position: { line: virtualPos.line, character: virtualPos.ch },
|
|
36
|
+
textDocument: {
|
|
37
|
+
uri: doc.documentInfo.uri,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
if (!result) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const { contents, range } = result;
|
|
44
|
+
let offset = posToOffset(view.state.doc, { line, character })!;
|
|
45
|
+
let end;
|
|
46
|
+
if (range) {
|
|
47
|
+
const editorStart = doc.transformVirtualToEditor({
|
|
48
|
+
line: range.start.line,
|
|
49
|
+
ch: range.start.character,
|
|
50
|
+
isVirtual: true,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (editorStart) {
|
|
54
|
+
offset = posToOffset(view.state.doc, {
|
|
55
|
+
line: editorStart.line,
|
|
56
|
+
character: editorStart.ch,
|
|
57
|
+
})!;
|
|
58
|
+
}
|
|
59
|
+
const editorEnd = doc.transformVirtualToEditor({
|
|
60
|
+
line: range.end.line,
|
|
61
|
+
ch: range.end.character,
|
|
62
|
+
isVirtual: true,
|
|
63
|
+
});
|
|
64
|
+
if (editorEnd) {
|
|
65
|
+
end = posToOffset(view.state.doc, {
|
|
66
|
+
line: editorEnd.line,
|
|
67
|
+
character: editorEnd.ch,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const dom = renderMarkupContent(contents);
|
|
73
|
+
|
|
74
|
+
return { pos: offset, end, create: () => ({ dom }), above: false };
|
|
75
|
+
});
|
|
76
|
+
};
|