qword 1.0.2 → 1.1.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.
- package/ChangeLog +22 -0
- package/README.md +2 -3
- package/assets/style.css +10 -10
- package/client/_clipboard.js +30 -33
- package/client/_clipboard.spec.js +160 -0
- package/client/content.d.ts +8 -0
- package/client/content.js +14 -0
- package/client/content.spec.js +69 -0
- package/client/create.d.ts +32 -0
- package/client/create.js +95 -0
- package/client/create.spec.js +192 -0
- package/client/decorations.d.ts +40 -0
- package/client/decorations.js +110 -0
- package/client/decorations.spec.js +135 -0
- package/client/dom.d.ts +7 -0
- package/client/dom.js +17 -0
- package/client/dom.spec.js +78 -0
- package/client/editor.d.ts +72 -0
- package/client/editor.js +164 -0
- package/client/editor.spec.js +307 -0
- package/client/events.d.ts +7 -0
- package/client/events.js +8 -0
- package/client/events.spec.js +100 -0
- package/client/highlight.d.ts +3 -0
- package/client/highlight.js +82 -0
- package/client/highlight.spec.js +28 -0
- package/client/history.d.ts +4 -0
- package/client/history.js +16 -0
- package/client/history.spec.js +57 -0
- package/client/index.d.ts +60 -0
- package/client/index.js +37 -0
- package/client/loadremote.js +10 -4
- package/client/options.d.ts +30 -0
- package/client/options.js +59 -0
- package/client/options.spec.js +157 -0
- package/client/position.d.ts +21 -0
- package/client/position.js +33 -0
- package/client/position.spec.js +176 -0
- package/client/qword.js +141 -391
- package/client/save.js +2 -2
- package/client/scroll.d.ts +10 -0
- package/client/scroll.js +9 -0
- package/client/scroll.spec.js +58 -0
- package/client/types.d.ts +24 -0
- package/dist/qword.js +175 -1
- package/dist/qword.js.map +1 -1
- package/dist-dev/qword.js +544 -660
- package/package.json +30 -15
- package/server/edit.js +8 -3
- package/server/edit.spec.js +101 -0
- package/test/errors.ts +21 -0
- package/test/types.ts +134 -0
- package/client/codemirror/show-trailing.js +0 -54
- package/client/codemirror/use-soft-tabs.js +0 -38
- package/client/set-key-map.js +0 -3
- package/dist/2b22a33a0a80bb926bbc.png +0 -1
- package/dist/8e9019bd86255ac9a618.png +0 -1
- package/dist/codemirror.js +0 -2
- package/dist/codemirror.js.map +0 -1
- package/dist/dword.js +0 -2
- package/dist/dword.js.map +0 -1
- package/dist-dev/2b22a33a0a80bb926bbc.png +0 -1
- package/dist-dev/8e9019bd86255ac9a618.png +0 -1
- package/dist-dev/codemirror.js +0 -104
- package/dist-dev/dword.js +0 -960
- package/modules/codemirror-emmet/dist/emmet.js +0 -48661
- package/modules/codemirror-emmet/dist/emmet.min.js +0 -8
- package/modules/codemirror-emmet/dist/emmetPlugin.js +0 -840
- package/modules/codemirror-emmet/dist/emmetPlugin.min.js +0 -1
- package/modules/codemirror-emmet/dist/index.html +0 -46
- package/modules/jshint/README.md +0 -114
- package/modules/jshint/dist/jshint-rhino.js +0 -32143
- package/modules/jshint/dist/jshint.js +0 -32141
- package/modules/jshint/package.json +0 -77
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {createEditor} from './create.js';
|
|
3
|
+
|
|
4
|
+
function makeContainer() {
|
|
5
|
+
const element = document.createElement('div');
|
|
6
|
+
document.body.appendChild(element);
|
|
7
|
+
|
|
8
|
+
return element;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test('create: createEditor doc matches value option', (t) => {
|
|
12
|
+
const view = createEditor(makeContainer(), {
|
|
13
|
+
value: 'hello',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const result = view.state.doc.toString();
|
|
17
|
+
const expected = 'hello';
|
|
18
|
+
|
|
19
|
+
view.destroy();
|
|
20
|
+
|
|
21
|
+
t.equal(result, expected);
|
|
22
|
+
t.end();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('create: createEditor readOnly true sets state readOnly', (t) => {
|
|
26
|
+
const view = createEditor(makeContainer(), {
|
|
27
|
+
readOnly: true,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
view.destroy();
|
|
31
|
+
|
|
32
|
+
t.ok(view.state.readOnly);
|
|
33
|
+
t.end();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('create: createEditor lineNumbers false does not throw', (t) => {
|
|
37
|
+
const view = createEditor(makeContainer(), {
|
|
38
|
+
lineNumbers: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
view.destroy();
|
|
42
|
+
|
|
43
|
+
t.ok(view.state);
|
|
44
|
+
t.end();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('create: createEditor foldGutter true does not throw', (t) => {
|
|
48
|
+
const view = createEditor(makeContainer(), {
|
|
49
|
+
foldGutter: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
view.destroy();
|
|
53
|
+
|
|
54
|
+
t.ok(view.state);
|
|
55
|
+
t.end();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('create: createEditor vim keyMap does not throw', (t) => {
|
|
59
|
+
const view = createEditor(makeContainer(), {
|
|
60
|
+
keyMap: 'vim',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
view.destroy();
|
|
64
|
+
|
|
65
|
+
t.ok(view.state);
|
|
66
|
+
t.end();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('create: createEditor emacs keyMap does not throw', (t) => {
|
|
70
|
+
const view = createEditor(makeContainer(), {
|
|
71
|
+
keyMap: 'emacs',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
view.destroy();
|
|
75
|
+
|
|
76
|
+
t.ok(view.state);
|
|
77
|
+
t.end();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('create: createEditor nord theme does not throw', (t) => {
|
|
81
|
+
const view = createEditor(makeContainer(), {
|
|
82
|
+
theme: 'nord',
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
view.destroy();
|
|
86
|
+
|
|
87
|
+
t.ok(view.state);
|
|
88
|
+
t.end();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('create: createEditor json mode does not throw', (t) => {
|
|
92
|
+
const view = createEditor(makeContainer(), {
|
|
93
|
+
mode: 'json',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
view.destroy();
|
|
97
|
+
|
|
98
|
+
t.ok(view.state);
|
|
99
|
+
t.end();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('create: createEditor html mode does not throw', (t) => {
|
|
103
|
+
const view = createEditor(makeContainer(), {
|
|
104
|
+
mode: 'html',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
view.destroy();
|
|
108
|
+
|
|
109
|
+
t.ok(view.state);
|
|
110
|
+
t.end();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('create: createEditor mode object does not throw', (t) => {
|
|
114
|
+
const view = createEditor(makeContainer(), {
|
|
115
|
+
mode: {
|
|
116
|
+
name: 'javascript',
|
|
117
|
+
json: true,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
view.destroy();
|
|
122
|
+
|
|
123
|
+
t.ok(view.state);
|
|
124
|
+
t.end();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('create: createEditor updateListener is called on change', (t) => {
|
|
128
|
+
let called = false;
|
|
129
|
+
const view = createEditor(makeContainer(), {
|
|
130
|
+
updateListener: () => {
|
|
131
|
+
called = true;
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
view.dispatch({
|
|
136
|
+
changes: {
|
|
137
|
+
from: 0,
|
|
138
|
+
to: 0,
|
|
139
|
+
insert: 'x',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
view.destroy();
|
|
143
|
+
|
|
144
|
+
t.ok(called);
|
|
145
|
+
t.end();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('create: createEditor exposes themeCompartment on view', (t) => {
|
|
149
|
+
const view = createEditor(makeContainer());
|
|
150
|
+
view.destroy();
|
|
151
|
+
|
|
152
|
+
t.ok(view._themeCompartment);
|
|
153
|
+
t.end();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('create: createEditor exposes keymapCompartment on view', (t) => {
|
|
157
|
+
const view = createEditor(makeContainer());
|
|
158
|
+
view.destroy();
|
|
159
|
+
|
|
160
|
+
t.ok(view._keymapCompartment);
|
|
161
|
+
t.end();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('create: createEditor exposes langCompartment on view', (t) => {
|
|
165
|
+
const view = createEditor(makeContainer());
|
|
166
|
+
view.destroy();
|
|
167
|
+
|
|
168
|
+
t.ok(view._langCompartment);
|
|
169
|
+
t.end();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('create: createEditor exposes historyCompartment on view', (t) => {
|
|
173
|
+
const view = createEditor(makeContainer());
|
|
174
|
+
view.destroy();
|
|
175
|
+
|
|
176
|
+
t.ok(view._historyCompartment);
|
|
177
|
+
t.end();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('create: createEditor produces hl-keyword spans for javascript', (t) => {
|
|
181
|
+
const view = createEditor(makeContainer(), {
|
|
182
|
+
value: 'const x = 1;',
|
|
183
|
+
mode: 'javascript',
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const spans = view.dom.querySelectorAll('span.hl-keyword');
|
|
187
|
+
|
|
188
|
+
view.destroy();
|
|
189
|
+
|
|
190
|
+
t.ok(spans.length > 0);
|
|
191
|
+
t.end();
|
|
192
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Range,
|
|
3
|
+
StateEffect,
|
|
4
|
+
StateField,
|
|
5
|
+
type Transaction,
|
|
6
|
+
} from '@codemirror/state';
|
|
7
|
+
import {
|
|
8
|
+
Decoration,
|
|
9
|
+
EditorView,
|
|
10
|
+
type DecorationSet,
|
|
11
|
+
} from '@codemirror/view';
|
|
12
|
+
import type {SourcePosition} from './types.js';
|
|
13
|
+
|
|
14
|
+
export const setMarkEffect: StateEffect<Range<Decoration>>;
|
|
15
|
+
export const clearMarkEffect: StateEffect<null>;
|
|
16
|
+
export const setLineEffect: StateEffect<{
|
|
17
|
+
line: number;
|
|
18
|
+
cls: string;
|
|
19
|
+
}>;
|
|
20
|
+
export const clearLineEffect: StateEffect<{
|
|
21
|
+
line: number;
|
|
22
|
+
cls: string;
|
|
23
|
+
}>;
|
|
24
|
+
|
|
25
|
+
export const markField: StateField<DecorationSet>;
|
|
26
|
+
export const lineField: StateField<DecorationSet>;
|
|
27
|
+
|
|
28
|
+
export type MarkHandle = {
|
|
29
|
+
clear: () => void;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export function markText(view: EditorView, from: SourcePosition, to: SourcePosition, {
|
|
33
|
+
className,
|
|
34
|
+
}: {
|
|
35
|
+
className: string;
|
|
36
|
+
}): MarkHandle;
|
|
37
|
+
|
|
38
|
+
export function addLineClass(view: EditorView, line: number, _where: string, className: string): void;
|
|
39
|
+
|
|
40
|
+
export function removeLineClass(view: EditorView, line: number, _where: string, className: string): void;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {StateEffect, StateField} from '@codemirror/state';
|
|
2
|
+
import {EditorView, Decoration} from '@codemirror/view';
|
|
3
|
+
import {positionToOffset} from './position.js';
|
|
4
|
+
|
|
5
|
+
const noop = () => {};
|
|
6
|
+
|
|
7
|
+
export const setMarkEffect = StateEffect.define();
|
|
8
|
+
export const clearMarkEffect = StateEffect.define();
|
|
9
|
+
export const setLineEffect = StateEffect.define();
|
|
10
|
+
export const clearLineEffect = StateEffect.define();
|
|
11
|
+
|
|
12
|
+
export const markField = StateField.define({
|
|
13
|
+
create: () => Decoration.none,
|
|
14
|
+
update(decorations, transaction) {
|
|
15
|
+
decorations = decorations.map(transaction.changes);
|
|
16
|
+
|
|
17
|
+
for (const effect of transaction.effects) {
|
|
18
|
+
if (effect.is(setMarkEffect))
|
|
19
|
+
decorations = decorations.update({
|
|
20
|
+
add: [effect.value],
|
|
21
|
+
sort: true,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (effect.is(clearMarkEffect))
|
|
25
|
+
decorations = Decoration.none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return decorations;
|
|
29
|
+
},
|
|
30
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const lineField = StateField.define({
|
|
34
|
+
create: () => Decoration.none,
|
|
35
|
+
update(decorations, transaction) {
|
|
36
|
+
decorations = decorations.map(transaction.changes);
|
|
37
|
+
|
|
38
|
+
for (const effect of transaction.effects) {
|
|
39
|
+
if (effect.is(setLineEffect)) {
|
|
40
|
+
const {line, cls} = effect.value;
|
|
41
|
+
const {from} = transaction.state.doc.line(Math.min(line + 1, transaction.state.doc.lines));
|
|
42
|
+
|
|
43
|
+
decorations = decorations.update({
|
|
44
|
+
add: [
|
|
45
|
+
Decoration
|
|
46
|
+
.line({
|
|
47
|
+
class: cls,
|
|
48
|
+
})
|
|
49
|
+
.range(from),
|
|
50
|
+
],
|
|
51
|
+
sort: true,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (effect.is(clearLineEffect)) {
|
|
56
|
+
const {line, cls} = effect.value;
|
|
57
|
+
|
|
58
|
+
decorations = decorations.update({
|
|
59
|
+
filter: (from, _, decoration) => !(decoration.spec.class === cls && transaction.state.doc.lineAt(from).number === line + 1),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return decorations;
|
|
65
|
+
},
|
|
66
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function markText(view, from, to, {className}) {
|
|
70
|
+
const fromOffset = positionToOffset(view.state.doc, from);
|
|
71
|
+
const toOffset = positionToOffset(view.state.doc, to);
|
|
72
|
+
|
|
73
|
+
if (fromOffset === toOffset)
|
|
74
|
+
return {
|
|
75
|
+
clear: noop,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const decoration = Decoration
|
|
79
|
+
.mark({
|
|
80
|
+
class: className,
|
|
81
|
+
})
|
|
82
|
+
.range(fromOffset, toOffset);
|
|
83
|
+
|
|
84
|
+
view.dispatch({
|
|
85
|
+
effects: setMarkEffect.of(decoration),
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
clear: () => view.dispatch({
|
|
89
|
+
effects: clearMarkEffect.of(null),
|
|
90
|
+
}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function addLineClass(view, line, _where, className) {
|
|
95
|
+
view.dispatch({
|
|
96
|
+
effects: setLineEffect.of({
|
|
97
|
+
line,
|
|
98
|
+
cls: className,
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function removeLineClass(view, line, _where, className) {
|
|
104
|
+
view.dispatch({
|
|
105
|
+
effects: clearLineEffect.of({
|
|
106
|
+
line,
|
|
107
|
+
cls: className,
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {EditorState} from '@codemirror/state';
|
|
3
|
+
import {EditorView} from '@codemirror/view';
|
|
4
|
+
import {
|
|
5
|
+
markField,
|
|
6
|
+
lineField,
|
|
7
|
+
markText,
|
|
8
|
+
addLineClass,
|
|
9
|
+
removeLineClass,
|
|
10
|
+
} from './decorations.js';
|
|
11
|
+
|
|
12
|
+
function makeView(document_ = 'hello world') {
|
|
13
|
+
const element = document.createElement('div');
|
|
14
|
+
document.body.appendChild(element);
|
|
15
|
+
|
|
16
|
+
return new EditorView({
|
|
17
|
+
state: EditorState.create({
|
|
18
|
+
doc: document_,
|
|
19
|
+
extensions: [markField, lineField],
|
|
20
|
+
}),
|
|
21
|
+
parent: element,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('decorations: markText returns object with clear function', (t) => {
|
|
26
|
+
const view = makeView();
|
|
27
|
+
const mark = markText(view, {line: 0, ch: 0}, {line: 0, ch: 5}, {
|
|
28
|
+
className: 'marked',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const result = typeof mark.clear;
|
|
32
|
+
const expected = 'function';
|
|
33
|
+
|
|
34
|
+
view.destroy();
|
|
35
|
+
|
|
36
|
+
t.equal(result, expected);
|
|
37
|
+
t.end();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('decorations: markText adds one decoration to markField', (t) => {
|
|
41
|
+
const view = makeView();
|
|
42
|
+
|
|
43
|
+
markText(view, {line: 0, ch: 0}, {line: 0, ch: 5}, {
|
|
44
|
+
className: 'marked',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
view.destroy();
|
|
48
|
+
|
|
49
|
+
t.equal(view.state.field(markField).size, 1);
|
|
50
|
+
t.end();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('decorations: mark clear removes all decorations from markField', (t) => {
|
|
54
|
+
const view = makeView();
|
|
55
|
+
const mark = markText(view, {line: 0, ch: 0}, {line: 0, ch: 5}, {
|
|
56
|
+
className: 'marked',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
mark.clear();
|
|
60
|
+
|
|
61
|
+
view.destroy();
|
|
62
|
+
|
|
63
|
+
t.equal(view.state.field(markField).size, 0);
|
|
64
|
+
t.end();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('decorations: markText returns a clear no-op for empty range', (t) => {
|
|
68
|
+
const view = makeView();
|
|
69
|
+
|
|
70
|
+
markText(view, {line: 0, ch: 0}, {line: 0, ch: 0}, {
|
|
71
|
+
className: 'marked',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
view.destroy();
|
|
75
|
+
|
|
76
|
+
t.equal(view.state.field(markField).size, 0);
|
|
77
|
+
t.end();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('decorations: markText empty range clear does not throw', (t) => {
|
|
81
|
+
const view = makeView();
|
|
82
|
+
const mark = markText(view, {line: 0, ch: 0}, {line: 0, ch: 0}, {
|
|
83
|
+
className: 'marked',
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
mark.clear();
|
|
87
|
+
|
|
88
|
+
view.destroy();
|
|
89
|
+
|
|
90
|
+
t.ok(mark.clear);
|
|
91
|
+
t.end();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('decorations: addLineClass adds decoration to lineField', (t) => {
|
|
95
|
+
const view = makeView();
|
|
96
|
+
addLineClass(view, 0, 'text', 'errorMarker');
|
|
97
|
+
|
|
98
|
+
view.destroy();
|
|
99
|
+
|
|
100
|
+
t.equal(view.state.field(lineField).size, 1);
|
|
101
|
+
t.end();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('decorations: removeLineClass removes decoration from lineField', (t) => {
|
|
105
|
+
const view = makeView();
|
|
106
|
+
addLineClass(view, 0, 'text', 'errorMarker');
|
|
107
|
+
removeLineClass(view, 0, 'text', 'errorMarker');
|
|
108
|
+
|
|
109
|
+
view.destroy();
|
|
110
|
+
|
|
111
|
+
t.equal(view.state.field(lineField).size, 0);
|
|
112
|
+
t.end();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('decorations: removeLineClass for other line keeps decoration', (t) => {
|
|
116
|
+
const view = makeView('a\nb');
|
|
117
|
+
addLineClass(view, 0, 'text', 'errorMarker');
|
|
118
|
+
removeLineClass(view, 1, 'text', 'errorMarker');
|
|
119
|
+
|
|
120
|
+
view.destroy();
|
|
121
|
+
|
|
122
|
+
t.equal(view.state.field(lineField).size, 1);
|
|
123
|
+
t.end();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('decorations: removeLineClass for other class keeps decoration', (t) => {
|
|
127
|
+
const view = makeView();
|
|
128
|
+
addLineClass(view, 0, 'text', 'errorMarker');
|
|
129
|
+
removeLineClass(view, 0, 'text', 'otherClass');
|
|
130
|
+
|
|
131
|
+
view.destroy();
|
|
132
|
+
|
|
133
|
+
t.equal(view.state.field(lineField).size, 1);
|
|
134
|
+
t.end();
|
|
135
|
+
});
|
package/client/dom.d.ts
ADDED
package/client/dom.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {EditorView} from '@codemirror/view';
|
|
2
|
+
|
|
3
|
+
export function getView(container) {
|
|
4
|
+
const element = container.querySelector('.cm-editor');
|
|
5
|
+
return element ? EditorView.findFromDOM(element) : null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function refresh(view) {
|
|
9
|
+
view.requestMeasure();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function observeResize(view, container) {
|
|
13
|
+
const observer = new ResizeObserver(() => view.requestMeasure());
|
|
14
|
+
observer.observe(container);
|
|
15
|
+
|
|
16
|
+
return () => observer.disconnect();
|
|
17
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {EditorState} from '@codemirror/state';
|
|
3
|
+
import {EditorView} from '@codemirror/view';
|
|
4
|
+
import {
|
|
5
|
+
getView,
|
|
6
|
+
refresh,
|
|
7
|
+
observeResize,
|
|
8
|
+
} from './dom.js';
|
|
9
|
+
|
|
10
|
+
function makeViewInContainer() {
|
|
11
|
+
const container = document.createElement('div');
|
|
12
|
+
document.body.appendChild(container);
|
|
13
|
+
const view = new EditorView({
|
|
14
|
+
state: EditorState.create({
|
|
15
|
+
doc: 'hello',
|
|
16
|
+
}),
|
|
17
|
+
parent: container,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
view,
|
|
22
|
+
container,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
test('dom: getView returns EditorView from container', (t) => {
|
|
27
|
+
const {view, container} = makeViewInContainer();
|
|
28
|
+
const result = getView(container);
|
|
29
|
+
const expected = view;
|
|
30
|
+
|
|
31
|
+
view.destroy();
|
|
32
|
+
|
|
33
|
+
t.equal(result, expected);
|
|
34
|
+
t.end();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('dom: getView returns null for empty container', (t) => {
|
|
38
|
+
const container = document.createElement('div');
|
|
39
|
+
const result = getView(container);
|
|
40
|
+
|
|
41
|
+
t.notOk(result);
|
|
42
|
+
t.end();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('dom: refresh calls requestMeasure without throwing', (t) => {
|
|
46
|
+
const {view} = makeViewInContainer();
|
|
47
|
+
refresh(view);
|
|
48
|
+
|
|
49
|
+
view.destroy();
|
|
50
|
+
|
|
51
|
+
t.ok(true);
|
|
52
|
+
t.end();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('dom: observeResize returns a function', (t) => {
|
|
56
|
+
const {view, container} = makeViewInContainer();
|
|
57
|
+
const cleanup = observeResize(view, container);
|
|
58
|
+
const result = typeof cleanup;
|
|
59
|
+
const expected = 'function';
|
|
60
|
+
|
|
61
|
+
view.destroy();
|
|
62
|
+
cleanup();
|
|
63
|
+
|
|
64
|
+
t.equal(result, expected);
|
|
65
|
+
t.end();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('dom: observeResize cleanup disconnects without throwing', (t) => {
|
|
69
|
+
const {view, container} = makeViewInContainer();
|
|
70
|
+
const cleanup = observeResize(view, container);
|
|
71
|
+
|
|
72
|
+
cleanup();
|
|
73
|
+
|
|
74
|
+
view.destroy();
|
|
75
|
+
|
|
76
|
+
t.ok(true);
|
|
77
|
+
t.end();
|
|
78
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {QwordEditorView} from './create.js';
|
|
2
|
+
import type {CreateEditorOptions} from './create.js';
|
|
3
|
+
import type {
|
|
4
|
+
MarkHandle,
|
|
5
|
+
} from './decorations.js';
|
|
6
|
+
import type {
|
|
7
|
+
OptionKey,
|
|
8
|
+
OptionValue,
|
|
9
|
+
} from './options.js';
|
|
10
|
+
import type {
|
|
11
|
+
SourceCode,
|
|
12
|
+
SourcePosition,
|
|
13
|
+
CharOffset,
|
|
14
|
+
KeyMap,
|
|
15
|
+
EditorMode,
|
|
16
|
+
EditorTheme,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
export type EditorOptions = CreateEditorOptions;
|
|
20
|
+
|
|
21
|
+
export default class Editor {
|
|
22
|
+
constructor(element: Element, options?: EditorOptions);
|
|
23
|
+
|
|
24
|
+
getValue(): SourceCode;
|
|
25
|
+
|
|
26
|
+
setValue(value: SourceCode): this;
|
|
27
|
+
|
|
28
|
+
getCursorIndex(): CharOffset;
|
|
29
|
+
|
|
30
|
+
getCursor(): {
|
|
31
|
+
row: number;
|
|
32
|
+
column: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
posFromIndex(index: CharOffset): SourcePosition;
|
|
36
|
+
|
|
37
|
+
focus(): this;
|
|
38
|
+
|
|
39
|
+
moveCursorTo(row: number, column?: number): this;
|
|
40
|
+
|
|
41
|
+
markRange(from: SourcePosition, to: SourcePosition, className: string): MarkHandle;
|
|
42
|
+
|
|
43
|
+
addLineClass(line: number, className: string): this;
|
|
44
|
+
|
|
45
|
+
removeLineClass(line: number, className: string): this;
|
|
46
|
+
|
|
47
|
+
setOption(name: OptionKey, value: OptionValue): this;
|
|
48
|
+
|
|
49
|
+
setMode(mode: EditorMode): this;
|
|
50
|
+
|
|
51
|
+
setTheme(theme: EditorTheme): this;
|
|
52
|
+
|
|
53
|
+
setKeyMap(name: KeyMap): this;
|
|
54
|
+
|
|
55
|
+
setModeForPath(path: string): this;
|
|
56
|
+
|
|
57
|
+
isChanged(): boolean;
|
|
58
|
+
|
|
59
|
+
clearHistory(): this;
|
|
60
|
+
|
|
61
|
+
on(event: string, handler: (...args: unknown[]) => void): this;
|
|
62
|
+
|
|
63
|
+
addListener(event: string, handler: (...args: unknown[]) => void): this;
|
|
64
|
+
|
|
65
|
+
once(event: string, handler: (...args: unknown[]) => void): this;
|
|
66
|
+
|
|
67
|
+
removeListener(event: string, handler: (...args: unknown[]) => void): this;
|
|
68
|
+
|
|
69
|
+
emit(...args: unknown[]): this;
|
|
70
|
+
|
|
71
|
+
get _view(): QwordEditorView;
|
|
72
|
+
}
|