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
package/client/editor.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import Emitify from 'emitify';
|
|
2
|
+
import {createEditor} from './create.js';
|
|
3
|
+
import {getValue, setValue} from './content.js';
|
|
4
|
+
import {
|
|
5
|
+
posFromIndex,
|
|
6
|
+
getCursorIndex,
|
|
7
|
+
getCursor,
|
|
8
|
+
} from './position.js';
|
|
9
|
+
import {
|
|
10
|
+
markText,
|
|
11
|
+
addLineClass,
|
|
12
|
+
removeLineClass,
|
|
13
|
+
} from './decorations.js';
|
|
14
|
+
import {setOption} from './options.js';
|
|
15
|
+
import {clearHistory} from './history.js';
|
|
16
|
+
import {observeResize} from './dom.js';
|
|
17
|
+
|
|
18
|
+
export default class Editor {
|
|
19
|
+
#view;
|
|
20
|
+
#vimEnabled = false;
|
|
21
|
+
#savedValue = '';
|
|
22
|
+
#emitter;
|
|
23
|
+
constructor(element, options = {}) {
|
|
24
|
+
this.#emitter = Emitify();
|
|
25
|
+
|
|
26
|
+
this.#view = createEditor(element, {
|
|
27
|
+
...options,
|
|
28
|
+
updateListener: (update) => {
|
|
29
|
+
if (update.docChanged)
|
|
30
|
+
this.#emitter.emit('change', {
|
|
31
|
+
value: this.getValue(),
|
|
32
|
+
cursor: this.getCursorIndex(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (update.selectionSet)
|
|
36
|
+
this.#emitter.emit('cursorMove', this.getCursorIndex());
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
observeResize(this.#view, element);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getValue() {
|
|
44
|
+
return getValue(this.#view);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setValue(value) {
|
|
48
|
+
setValue(this.#view, value);
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getCursorIndex() {
|
|
53
|
+
return getCursorIndex(this.#view);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getCursor() {
|
|
57
|
+
return getCursor(this.#view);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
posFromIndex(index) {
|
|
61
|
+
return posFromIndex(this.#view, index);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
focus() {
|
|
65
|
+
this.#view.focus();
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
moveCursorTo(row, column = 0) {
|
|
70
|
+
const line = this.#view.state.doc.line(row + 1);
|
|
71
|
+
|
|
72
|
+
this.#view.dispatch({
|
|
73
|
+
selection: {
|
|
74
|
+
anchor: line.from + column,
|
|
75
|
+
},
|
|
76
|
+
scrollIntoView: true,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
markRange(from, to, className) {
|
|
83
|
+
return markText(this.#view, from, to, {
|
|
84
|
+
className,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addLineClass(line, className) {
|
|
89
|
+
addLineClass(this.#view, line, 'text', className);
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
removeLineClass(line, className) {
|
|
94
|
+
removeLineClass(this.#view, line, 'text', className);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
setOption(name, value) {
|
|
99
|
+
if (name === 'keyMap')
|
|
100
|
+
this.#vimEnabled = value === 'vim';
|
|
101
|
+
|
|
102
|
+
setOption(this.#view, name, value);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setMode(mode) {
|
|
107
|
+
return this.setOption('mode', mode);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setTheme(theme) {
|
|
111
|
+
return this.setOption('theme', theme);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
setKeyMap(name) {
|
|
115
|
+
return this.setOption('keyMap', name);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setModeForPath(path) {
|
|
119
|
+
return this.setMode(path
|
|
120
|
+
.split('.')
|
|
121
|
+
.pop());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
isChanged() {
|
|
125
|
+
return this.getValue() !== this.#savedValue;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
clearHistory() {
|
|
129
|
+
clearHistory(
|
|
130
|
+
this.#view,
|
|
131
|
+
this.#view._historyCompartment,
|
|
132
|
+
this.#vimEnabled,
|
|
133
|
+
);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
on(event, handler) {
|
|
138
|
+
this.#emitter.on(event, handler);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
addListener(event, handler) {
|
|
143
|
+
return this.on(event, handler);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
once(event, handler) {
|
|
147
|
+
this.#emitter.once(event, handler);
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
removeListener(event, handler) {
|
|
152
|
+
this.#emitter.removeListener(event, handler);
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
emit(...args) {
|
|
157
|
+
this.#emitter.emit(...args);
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get _view() {
|
|
162
|
+
return this.#view;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {once} from 'events';
|
|
3
|
+
import Editor from './editor.js';
|
|
4
|
+
|
|
5
|
+
function makeEditor(options = {}) {
|
|
6
|
+
const element = document.createElement('div');
|
|
7
|
+
document.body.appendChild(element);
|
|
8
|
+
|
|
9
|
+
return new Editor(element, options);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test('Editor: getValue returns document string', (t) => {
|
|
13
|
+
const editor = makeEditor({
|
|
14
|
+
value: 'hello',
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const result = editor.getValue();
|
|
18
|
+
const expected = 'hello';
|
|
19
|
+
|
|
20
|
+
t.equal(result, expected);
|
|
21
|
+
t.end();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('Editor: setValue returns this', (t) => {
|
|
25
|
+
const editor = makeEditor();
|
|
26
|
+
|
|
27
|
+
const result = editor.setValue('xyz');
|
|
28
|
+
|
|
29
|
+
t.equal(result, editor);
|
|
30
|
+
t.end();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Editor: setValue updates content', (t) => {
|
|
34
|
+
const editor = makeEditor();
|
|
35
|
+
editor.setValue('xyz');
|
|
36
|
+
|
|
37
|
+
const result = editor.getValue();
|
|
38
|
+
const expected = 'xyz';
|
|
39
|
+
|
|
40
|
+
t.equal(result, expected);
|
|
41
|
+
t.end();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('Editor: getCursorIndex returns a number', (t) => {
|
|
45
|
+
const editor = makeEditor({
|
|
46
|
+
value: 'hello',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const result = typeof editor.getCursorIndex();
|
|
50
|
+
const expected = 'number';
|
|
51
|
+
|
|
52
|
+
t.equal(result, expected);
|
|
53
|
+
t.end();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('Editor: getCursor returns row number', (t) => {
|
|
57
|
+
const editor = makeEditor({
|
|
58
|
+
value: 'hello',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const cursor = editor.getCursor();
|
|
62
|
+
const result = typeof cursor.row;
|
|
63
|
+
const expected = 'number';
|
|
64
|
+
|
|
65
|
+
t.equal(result, expected);
|
|
66
|
+
t.end();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Editor: getCursor returns column number', (t) => {
|
|
70
|
+
const editor = makeEditor({
|
|
71
|
+
value: 'hello',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const cursor = editor.getCursor();
|
|
75
|
+
const result = typeof cursor.column;
|
|
76
|
+
const expected = 'number';
|
|
77
|
+
|
|
78
|
+
t.equal(result, expected);
|
|
79
|
+
t.end();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('Editor: posFromIndex returns position', (t) => {
|
|
83
|
+
const editor = makeEditor({
|
|
84
|
+
value: 'hello world',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const result = editor.posFromIndex(6);
|
|
88
|
+
|
|
89
|
+
const expected = {
|
|
90
|
+
line: 0,
|
|
91
|
+
ch: 6,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
t.deepEqual(result, expected);
|
|
95
|
+
t.end();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('Editor: focus returns this', (t) => {
|
|
99
|
+
const editor = makeEditor();
|
|
100
|
+
|
|
101
|
+
const result = editor.focus();
|
|
102
|
+
|
|
103
|
+
t.equal(result, editor);
|
|
104
|
+
t.end();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('Editor: moveCursorTo returns this', (t) => {
|
|
108
|
+
const editor = makeEditor({
|
|
109
|
+
value: 'hello',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const result = editor.moveCursorTo(0, 0);
|
|
113
|
+
|
|
114
|
+
t.equal(result, editor);
|
|
115
|
+
t.end();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('Editor: markRange returns object with clear function', (t) => {
|
|
119
|
+
const editor = makeEditor({
|
|
120
|
+
value: 'hello world',
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const mark = editor.markRange({
|
|
124
|
+
line: 0,
|
|
125
|
+
ch: 0,
|
|
126
|
+
}, {
|
|
127
|
+
line: 0,
|
|
128
|
+
ch: 5,
|
|
129
|
+
}, 'marked');
|
|
130
|
+
|
|
131
|
+
const result = typeof mark.clear;
|
|
132
|
+
const expected = 'function';
|
|
133
|
+
|
|
134
|
+
t.equal(result, expected);
|
|
135
|
+
t.end();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('Editor: markRange takes index positions', (t) => {
|
|
139
|
+
const editor = makeEditor({
|
|
140
|
+
value: 'hello world',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const mark = editor.markRange({
|
|
144
|
+
line: 0,
|
|
145
|
+
ch: 0,
|
|
146
|
+
}, {
|
|
147
|
+
line: 0,
|
|
148
|
+
ch: 5,
|
|
149
|
+
}, 'marked');
|
|
150
|
+
|
|
151
|
+
const result = typeof mark.clear;
|
|
152
|
+
const expected = 'function';
|
|
153
|
+
|
|
154
|
+
t.equal(result, expected);
|
|
155
|
+
t.end();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('Editor: addLineClass returns this', (t) => {
|
|
159
|
+
const editor = makeEditor({
|
|
160
|
+
value: 'hello',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const result = editor.addLineClass(0, 'errorMarker');
|
|
164
|
+
const expected = editor;
|
|
165
|
+
|
|
166
|
+
t.equal(result, expected);
|
|
167
|
+
t.end();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('Editor: removeLineClass returns this', (t) => {
|
|
171
|
+
const editor = makeEditor({
|
|
172
|
+
value: 'hello',
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
editor.addLineClass(0, 'errorMarker');
|
|
176
|
+
const result = editor.removeLineClass(0, 'errorMarker');
|
|
177
|
+
const expected = editor;
|
|
178
|
+
|
|
179
|
+
t.equal(result, expected);
|
|
180
|
+
t.end();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('Editor: setOption emacs does not throw', (t) => {
|
|
184
|
+
const editor = makeEditor();
|
|
185
|
+
editor.setOption('keyMap', 'emacs');
|
|
186
|
+
|
|
187
|
+
t.ok(true);
|
|
188
|
+
t.end();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('Editor: setOption keyMap vim sets vim mode', (t) => {
|
|
192
|
+
const editor = makeEditor();
|
|
193
|
+
editor.setOption('keyMap', 'vim');
|
|
194
|
+
|
|
195
|
+
t.ok(true);
|
|
196
|
+
t.end();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('Editor: setMode returns this', (t) => {
|
|
200
|
+
const editor = makeEditor();
|
|
201
|
+
|
|
202
|
+
const result = editor.setMode('json');
|
|
203
|
+
|
|
204
|
+
t.equal(result, editor);
|
|
205
|
+
t.end();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('Editor: setTheme returns this', (t) => {
|
|
209
|
+
const editor = makeEditor();
|
|
210
|
+
|
|
211
|
+
const result = editor.setTheme('nord');
|
|
212
|
+
|
|
213
|
+
t.equal(result, editor);
|
|
214
|
+
t.end();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('Editor: setKeyMap returns this', (t) => {
|
|
218
|
+
const editor = makeEditor();
|
|
219
|
+
|
|
220
|
+
const result = editor.setKeyMap('emacs');
|
|
221
|
+
|
|
222
|
+
t.equal(result, editor);
|
|
223
|
+
t.end();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test('Editor: setModeForPath sets mode', (t) => {
|
|
227
|
+
const editor = makeEditor();
|
|
228
|
+
|
|
229
|
+
const result = editor.setModeForPath('/path/to/file.json');
|
|
230
|
+
|
|
231
|
+
t.equal(result, editor);
|
|
232
|
+
t.end();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('Editor: isChanged false for clean editor', (t) => {
|
|
236
|
+
const editor = makeEditor();
|
|
237
|
+
const result = editor.isChanged();
|
|
238
|
+
|
|
239
|
+
t.notOk(result);
|
|
240
|
+
t.end();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('Editor: isChanged true after setValue', (t) => {
|
|
244
|
+
const editor = makeEditor();
|
|
245
|
+
editor.setValue('changed');
|
|
246
|
+
const result = editor.isChanged();
|
|
247
|
+
|
|
248
|
+
t.ok(result);
|
|
249
|
+
t.end();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test('Editor: clearHistory returns this', (t) => {
|
|
253
|
+
const editor = makeEditor();
|
|
254
|
+
|
|
255
|
+
const result = editor.clearHistory();
|
|
256
|
+
|
|
257
|
+
t.equal(result, editor);
|
|
258
|
+
t.end();
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('Editor: on change event emits value and cursor', async (t) => {
|
|
262
|
+
const editor = makeEditor({
|
|
263
|
+
value: 'hello',
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const promise = once(editor, 'change');
|
|
267
|
+
|
|
268
|
+
editor.setValue('changed');
|
|
269
|
+
const [data] = await promise;
|
|
270
|
+
const result = typeof data.value;
|
|
271
|
+
const expected = 'string';
|
|
272
|
+
|
|
273
|
+
t.equal(result, expected);
|
|
274
|
+
t.end();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test('Editor: addListener is alias for on', (t) => {
|
|
278
|
+
const editor = makeEditor();
|
|
279
|
+
let called = false;
|
|
280
|
+
|
|
281
|
+
editor.addListener('change', () => {
|
|
282
|
+
called = true;
|
|
283
|
+
});
|
|
284
|
+
editor.setValue('x');
|
|
285
|
+
|
|
286
|
+
t.ok(called);
|
|
287
|
+
t.end();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test('Editor: emit calls registered handler', async (t) => {
|
|
291
|
+
const editor = makeEditor();
|
|
292
|
+
let received = null;
|
|
293
|
+
const [value] = await once(editor, 'custom');
|
|
294
|
+
|
|
295
|
+
received = value;
|
|
296
|
+
editor.emit('custom', 'hello');
|
|
297
|
+
|
|
298
|
+
t.equal(received, 'hello');
|
|
299
|
+
t.end();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('Editor: _view getter returns EditorView', (t) => {
|
|
303
|
+
const editor = makeEditor();
|
|
304
|
+
|
|
305
|
+
t.ok(editor._view);
|
|
306
|
+
t.end();
|
|
307
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type {EditorView} from '@codemirror/view';
|
|
2
|
+
|
|
3
|
+
export type EventBinding = [string, EventListener];
|
|
4
|
+
|
|
5
|
+
export function on(view: EditorView, event: string, handler: EventListener): EventBinding;
|
|
6
|
+
|
|
7
|
+
export function off(view: EditorView, event: string, handler: EventListener): void;
|
package/client/events.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {EditorState} from '@codemirror/state';
|
|
3
|
+
import {EditorView} from '@codemirror/view';
|
|
4
|
+
import {on, off} from './events.js';
|
|
5
|
+
|
|
6
|
+
function makeView() {
|
|
7
|
+
const element = document.createElement('div');
|
|
8
|
+
document.body.appendChild(element);
|
|
9
|
+
|
|
10
|
+
return new EditorView({
|
|
11
|
+
state: EditorState.create({
|
|
12
|
+
doc: 'hello',
|
|
13
|
+
}),
|
|
14
|
+
parent: element,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('events: on returns tuple of event name and handler', (t) => {
|
|
19
|
+
const view = makeView();
|
|
20
|
+
const handler = () => {};
|
|
21
|
+
const result = on(view, 'blur', handler);
|
|
22
|
+
const expected = ['blur', handler];
|
|
23
|
+
|
|
24
|
+
view.destroy();
|
|
25
|
+
|
|
26
|
+
t.deepEqual(result, expected);
|
|
27
|
+
t.end();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('events: on fires handler when event dispatched on contentDOM', (t) => {
|
|
31
|
+
const view = makeView();
|
|
32
|
+
let fired = false;
|
|
33
|
+
|
|
34
|
+
const [eventName, handler] = on(view, 'blur', () => {
|
|
35
|
+
fired = true;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
view.contentDOM.dispatchEvent(new FocusEvent('blur'));
|
|
39
|
+
off(view, eventName, handler);
|
|
40
|
+
view.destroy();
|
|
41
|
+
|
|
42
|
+
t.ok(fired);
|
|
43
|
+
t.end();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('events: off removes handler from contentDOM', (t) => {
|
|
47
|
+
const view = makeView();
|
|
48
|
+
let count = 0;
|
|
49
|
+
|
|
50
|
+
const [eventName, handler] = on(view, 'blur', () => {
|
|
51
|
+
count++;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
off(view, eventName, handler);
|
|
55
|
+
view.contentDOM.dispatchEvent(new FocusEvent('blur'));
|
|
56
|
+
view.destroy();
|
|
57
|
+
|
|
58
|
+
t.equal(count, 0);
|
|
59
|
+
t.end();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('events: handler fires on contentDOM blur (not view.dom)', (t) => {
|
|
63
|
+
const view = makeView();
|
|
64
|
+
let fired = false;
|
|
65
|
+
|
|
66
|
+
const [eventName, handler] = on(view, 'blur', () => {
|
|
67
|
+
fired = true;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Dispatch on contentDOM (the actual focusable element)
|
|
71
|
+
view.contentDOM.dispatchEvent(new FocusEvent('blur'));
|
|
72
|
+
|
|
73
|
+
view.destroy();
|
|
74
|
+
off(view, eventName, handler);
|
|
75
|
+
|
|
76
|
+
t.ok(fired, 'handler should fire when blur dispatched on contentDOM');
|
|
77
|
+
t.end();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('events: handler does NOT fire when event dispatched only on view.dom wrapper', (t) => {
|
|
81
|
+
const view = makeView();
|
|
82
|
+
let fired = false;
|
|
83
|
+
|
|
84
|
+
const [eventName, handler] = on(view, 'blur', () => {
|
|
85
|
+
fired = true;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Dispatch on view.dom (outer wrapper) - should NOT trigger handler
|
|
89
|
+
// since listener is on contentDOM, and events dispatched on view.dom
|
|
90
|
+
// won't bubble down to contentDOM
|
|
91
|
+
view.dom.dispatchEvent(new FocusEvent('blur', {
|
|
92
|
+
bubbles: false,
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
view.destroy();
|
|
96
|
+
off(view, eventName, handler);
|
|
97
|
+
|
|
98
|
+
t.notOk(fired, 'handler should NOT fire when event only on view.dom wrapper');
|
|
99
|
+
t.end();
|
|
100
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {HighlightStyle} from '@codemirror/language';
|
|
2
|
+
import {tags} from '@lezer/highlight';
|
|
3
|
+
|
|
4
|
+
export const highlightStyle = HighlightStyle.define([{
|
|
5
|
+
tag: tags.keyword,
|
|
6
|
+
class: 'hl-keyword',
|
|
7
|
+
}, {
|
|
8
|
+
tag: tags.definitionKeyword,
|
|
9
|
+
class: 'hl-keyword',
|
|
10
|
+
}, {
|
|
11
|
+
tag: tags.controlKeyword,
|
|
12
|
+
class: 'hl-keyword',
|
|
13
|
+
}, {
|
|
14
|
+
tag: tags.moduleKeyword,
|
|
15
|
+
class: 'hl-module-keyword',
|
|
16
|
+
}, {
|
|
17
|
+
tag: tags.operatorKeyword,
|
|
18
|
+
class: 'hl-operator',
|
|
19
|
+
}, {
|
|
20
|
+
tag: tags.string,
|
|
21
|
+
class: 'hl-string',
|
|
22
|
+
}, {
|
|
23
|
+
tag: tags.number,
|
|
24
|
+
class: 'hl-number',
|
|
25
|
+
}, {
|
|
26
|
+
tag: tags.integer,
|
|
27
|
+
class: 'hl-number',
|
|
28
|
+
}, {
|
|
29
|
+
tag: tags.float,
|
|
30
|
+
class: 'hl-number',
|
|
31
|
+
}, {
|
|
32
|
+
tag: tags.bool,
|
|
33
|
+
class: 'hl-constant',
|
|
34
|
+
}, {
|
|
35
|
+
tag: tags.null,
|
|
36
|
+
class: 'hl-constant',
|
|
37
|
+
}, {
|
|
38
|
+
tag: tags.comment,
|
|
39
|
+
class: 'hl-comment',
|
|
40
|
+
}, {
|
|
41
|
+
tag: tags.lineComment,
|
|
42
|
+
class: 'hl-comment',
|
|
43
|
+
}, {
|
|
44
|
+
tag: tags.blockComment,
|
|
45
|
+
class: 'hl-comment',
|
|
46
|
+
}, {
|
|
47
|
+
tag: tags.variableName,
|
|
48
|
+
class: 'hl-name',
|
|
49
|
+
}, {
|
|
50
|
+
tag: tags.function(tags.variableName),
|
|
51
|
+
class: 'hl-function',
|
|
52
|
+
}, {
|
|
53
|
+
tag: tags.definition(tags.variableName),
|
|
54
|
+
class: 'hl-name',
|
|
55
|
+
}, {
|
|
56
|
+
tag: tags.propertyName,
|
|
57
|
+
class: 'hl-name',
|
|
58
|
+
}, {
|
|
59
|
+
tag: tags.className,
|
|
60
|
+
class: 'hl-class',
|
|
61
|
+
}, {
|
|
62
|
+
tag: tags.typeName,
|
|
63
|
+
class: 'hl-type',
|
|
64
|
+
}, {
|
|
65
|
+
tag: tags.operator,
|
|
66
|
+
class: 'hl-operator',
|
|
67
|
+
}, {
|
|
68
|
+
tag: tags.punctuation,
|
|
69
|
+
class: 'hl-punctuation',
|
|
70
|
+
}, {
|
|
71
|
+
tag: tags.regexp,
|
|
72
|
+
class: 'hl-regexp',
|
|
73
|
+
}, {
|
|
74
|
+
tag: tags.escape,
|
|
75
|
+
class: 'hl-escape',
|
|
76
|
+
}, {
|
|
77
|
+
tag: tags.tagName,
|
|
78
|
+
class: 'hl-tag',
|
|
79
|
+
}, {
|
|
80
|
+
tag: tags.attributeName,
|
|
81
|
+
class: 'hl-attr',
|
|
82
|
+
}]);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {test} from 'supertape';
|
|
2
|
+
import {highlightStyle} from './highlight.js';
|
|
3
|
+
|
|
4
|
+
test('highlight: highlightStyle is defined', (t) => {
|
|
5
|
+
t.ok(highlightStyle);
|
|
6
|
+
t.end();
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('highlight: highlightStyle has specs array', (t) => {
|
|
10
|
+
const result = Array.isArray(highlightStyle.specs);
|
|
11
|
+
|
|
12
|
+
t.ok(result);
|
|
13
|
+
t.end();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('highlight: highlightStyle has keyword class', (t) => {
|
|
17
|
+
const hasKeyword = highlightStyle.specs.some((s) => s.class === 'hl-keyword');
|
|
18
|
+
|
|
19
|
+
t.ok(hasKeyword);
|
|
20
|
+
t.end();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('highlight: highlightStyle has function class', (t) => {
|
|
24
|
+
const hasFunction = highlightStyle.specs.some((s) => s.class === 'hl-function');
|
|
25
|
+
|
|
26
|
+
t.ok(hasFunction);
|
|
27
|
+
t.end();
|
|
28
|
+
});
|