@jupyterlab/codemirror 4.0.0-alpha.9 → 4.0.0-beta.1
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/lib/codemirror-ipythongfm.d.ts +9 -3
- package/lib/codemirror-ipythongfm.js +52 -30
- package/lib/codemirror-ipythongfm.js.map +1 -1
- package/lib/commands.d.ts +13 -0
- package/lib/commands.js +32 -0
- package/lib/commands.js.map +1 -0
- package/lib/editor.d.ts +48 -273
- package/lib/editor.js +220 -819
- package/lib/editor.js.map +1 -1
- package/lib/extension.d.ts +236 -0
- package/lib/extension.js +696 -0
- package/lib/extension.js.map +1 -0
- package/lib/extensions/customStyle.d.ts +25 -0
- package/lib/extensions/customStyle.js +51 -0
- package/lib/extensions/customStyle.js.map +1 -0
- package/lib/extensions/index.d.ts +3 -0
- package/lib/extensions/index.js +8 -0
- package/lib/extensions/index.js.map +1 -0
- package/lib/extensions/rulers.d.ts +8 -0
- package/lib/extensions/rulers.js +85 -0
- package/lib/extensions/rulers.js.map +1 -0
- package/lib/extensions/ybinding.d.ts +129 -0
- package/lib/extensions/ybinding.js +229 -0
- package/lib/extensions/ybinding.js.map +1 -0
- package/lib/factory.d.ts +15 -5
- package/lib/factory.js +53 -24
- package/lib/factory.js.map +1 -1
- package/lib/index.d.ts +7 -19
- package/lib/index.js +7 -23
- package/lib/index.js.map +1 -1
- package/lib/language.d.ts +96 -0
- package/lib/language.js +1687 -0
- package/lib/language.js.map +1 -0
- package/lib/mimetype.d.ts +3 -0
- package/lib/mimetype.js +16 -5
- package/lib/mimetype.js.map +1 -1
- package/lib/searchprovider.d.ts +219 -0
- package/lib/searchprovider.js +640 -0
- package/lib/searchprovider.js.map +1 -0
- package/lib/theme.d.ts +57 -0
- package/lib/theme.js +194 -0
- package/lib/theme.js.map +1 -0
- package/lib/token.d.ts +375 -0
- package/lib/token.js +18 -0
- package/lib/token.js.map +1 -0
- package/package.json +45 -41
- package/src/codemirror-ipythongfm.ts +109 -0
- package/src/commands.ts +34 -0
- package/src/editor.ts +783 -0
- package/src/extension.ts +890 -0
- package/src/extensions/customStyle.ts +79 -0
- package/src/extensions/index.ts +8 -0
- package/src/extensions/rulers.ts +112 -0
- package/src/extensions/ybinding.ts +295 -0
- package/src/factory.ts +100 -0
- package/src/index.ts +17 -0
- package/src/language.ts +1726 -0
- package/src/mimetype.ts +54 -0
- package/src/searchprovider.ts +813 -0
- package/src/theme.ts +219 -0
- package/src/token.ts +436 -0
- package/src/typings.d.ts +13 -0
- package/style/base.css +33 -187
- package/style/index.css +1 -7
- package/style/index.js +1 -7
- package/lib/codemirror-ipython.d.ts +0 -2
- package/lib/codemirror-ipython.js +0 -30
- package/lib/codemirror-ipython.js.map +0 -1
- package/lib/mode.d.ts +0 -82
- package/lib/mode.js +0 -150
- package/lib/mode.js.map +0 -1
- package/lib/syntaxstatus.d.ts +0 -67
- package/lib/syntaxstatus.js +0 -140
- package/lib/syntaxstatus.js.map +0 -1
- package/lib/tokens.d.ts +0 -18
- package/lib/tokens.js +0 -8
- package/lib/tokens.js.map +0 -1
- package/typings/codemirror/codemirror.d.ts +0 -227
package/lib/editor.js
CHANGED
|
@@ -1,69 +1,19 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/ban-types */
|
|
2
2
|
// Copyright (c) Jupyter Development Team.
|
|
3
3
|
// Distributed under the terms of the Modified BSD License.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
t[p] = s[p];
|
|
10
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
11
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
12
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
13
|
-
t[p[i]] = s[p[i]];
|
|
14
|
-
}
|
|
15
|
-
return t;
|
|
16
|
-
};
|
|
17
|
-
import { showDialog } from '@jupyterlab/apputils';
|
|
18
|
-
import { CodeEditor } from '@jupyterlab/codeeditor';
|
|
19
|
-
import { nullTranslator } from '@jupyterlab/translation';
|
|
20
|
-
import { ArrayExt } from '@lumino/algorithm';
|
|
21
|
-
import { JSONExt, UUID } from '@lumino/coreutils';
|
|
22
|
-
import { DisposableDelegate } from '@lumino/disposable';
|
|
23
|
-
import { Poll } from '@lumino/polling';
|
|
4
|
+
import { insertNewlineAndIndent } from '@codemirror/commands';
|
|
5
|
+
import { ensureSyntaxTree } from '@codemirror/language';
|
|
6
|
+
import { Compartment, EditorSelection, EditorState, Prec } from '@codemirror/state';
|
|
7
|
+
import { EditorView } from '@codemirror/view';
|
|
8
|
+
import { UUID } from '@lumino/coreutils';
|
|
24
9
|
import { Signal } from '@lumino/signaling';
|
|
25
|
-
import
|
|
26
|
-
import '
|
|
27
|
-
import '
|
|
28
|
-
import 'codemirror/addon/edit/closebrackets.js';
|
|
29
|
-
import 'codemirror/addon/edit/matchbrackets.js';
|
|
30
|
-
import 'codemirror/addon/fold/brace-fold.js';
|
|
31
|
-
import 'codemirror/addon/fold/comment-fold.js';
|
|
32
|
-
import 'codemirror/addon/fold/foldcode.js';
|
|
33
|
-
import 'codemirror/addon/fold/foldgutter.js';
|
|
34
|
-
import 'codemirror/addon/fold/indent-fold.js';
|
|
35
|
-
import 'codemirror/addon/fold/markdown-fold.js';
|
|
36
|
-
import 'codemirror/addon/fold/xml-fold.js';
|
|
37
|
-
import 'codemirror/addon/mode/simple';
|
|
38
|
-
import 'codemirror/addon/scroll/scrollpastend.js';
|
|
39
|
-
import 'codemirror/addon/search/jump-to-line';
|
|
40
|
-
import 'codemirror/addon/search/search';
|
|
41
|
-
import 'codemirror/addon/search/searchcursor';
|
|
42
|
-
import 'codemirror/addon/selection/active-line';
|
|
43
|
-
import 'codemirror/addon/selection/mark-selection';
|
|
44
|
-
import 'codemirror/addon/selection/selection-pointer';
|
|
45
|
-
import 'codemirror/addon/edit/trailingspace.js';
|
|
46
|
-
import 'codemirror/keymap/emacs.js';
|
|
47
|
-
import 'codemirror/keymap/sublime.js';
|
|
48
|
-
import { CodemirrorBinding } from 'y-codemirror';
|
|
49
|
-
import { Mode } from './mode';
|
|
50
|
-
// import 'codemirror/keymap/vim.js'; lazy loading of vim mode is available in ../codemirror-extension/index.ts
|
|
10
|
+
import './codemirror-ipythongfm';
|
|
11
|
+
import { ExtensionsHandler } from './extension';
|
|
12
|
+
import { EditorLanguageRegistry } from './language';
|
|
51
13
|
/**
|
|
52
14
|
* The class name added to CodeMirrorWidget instances.
|
|
53
15
|
*/
|
|
54
16
|
const EDITOR_CLASS = 'jp-CodeMirrorEditor';
|
|
55
|
-
/**
|
|
56
|
-
* The class name added to read only cell editor widgets.
|
|
57
|
-
*/
|
|
58
|
-
const READ_ONLY_CLASS = 'jp-mod-readOnly';
|
|
59
|
-
/**
|
|
60
|
-
* The class name for the hover box for collaborator cursors.
|
|
61
|
-
*/
|
|
62
|
-
const COLLABORATOR_CURSOR_CLASS = 'jp-CollaboratorCursor';
|
|
63
|
-
/**
|
|
64
|
-
* The class name for the hover box for collaborator cursors.
|
|
65
|
-
*/
|
|
66
|
-
const COLLABORATOR_HOVER_CLASS = 'jp-CollaboratorCursor-hover';
|
|
67
17
|
/**
|
|
68
18
|
* The key code for the up arrow key.
|
|
69
19
|
*/
|
|
@@ -72,13 +22,6 @@ const UP_ARROW = 38;
|
|
|
72
22
|
* The key code for the down arrow key.
|
|
73
23
|
*/
|
|
74
24
|
const DOWN_ARROW = 40;
|
|
75
|
-
/**
|
|
76
|
-
* The time that a collaborator name hover persists.
|
|
77
|
-
*/
|
|
78
|
-
const HOVER_TIMEOUT = 1000;
|
|
79
|
-
// @todo Remove the duality of having a modeldb and a y-codemirror
|
|
80
|
-
// binding as it just introduces a lot of additional complexity without gaining anything.
|
|
81
|
-
const USE_YCODEMIRROR_BINDING = true;
|
|
82
25
|
/**
|
|
83
26
|
* CodeMirror editor.
|
|
84
27
|
*/
|
|
@@ -87,119 +30,50 @@ export class CodeMirrorEditor {
|
|
|
87
30
|
* Construct a CodeMirror editor.
|
|
88
31
|
*/
|
|
89
32
|
constructor(options) {
|
|
90
|
-
var _a;
|
|
33
|
+
var _a, _b, _c, _d, _e, _f;
|
|
91
34
|
/**
|
|
92
35
|
* A signal emitted when either the top or bottom edge is requested.
|
|
93
36
|
*/
|
|
94
37
|
this.edgeRequested = new Signal(this);
|
|
95
|
-
this.selectionMarkers = {};
|
|
96
|
-
this._keydownHandlers = new Array();
|
|
97
|
-
this._changeGuard = false;
|
|
98
|
-
this._uuid = '';
|
|
99
|
-
this._needsRefresh = false;
|
|
100
38
|
this._isDisposed = false;
|
|
101
|
-
this.
|
|
39
|
+
this._language = new Compartment();
|
|
40
|
+
this._uuid = '';
|
|
41
|
+
this._languages = (_a = options.languages) !== null && _a !== void 0 ? _a : new EditorLanguageRegistry();
|
|
42
|
+
this._configurator =
|
|
43
|
+
(_d = (_b = options.extensionsRegistry) === null || _b === void 0 ? void 0 : _b.createNew({
|
|
44
|
+
...options,
|
|
45
|
+
inline: (_c = options.inline) !== null && _c !== void 0 ? _c : false
|
|
46
|
+
})) !== null && _d !== void 0 ? _d : new ExtensionsHandler();
|
|
102
47
|
const host = (this.host = options.host);
|
|
103
|
-
this.translator = options.translator || nullTranslator;
|
|
104
|
-
this._trans = this.translator.load('jupyterlab');
|
|
105
48
|
host.classList.add(EDITOR_CLASS);
|
|
106
49
|
host.classList.add('jp-Editor');
|
|
107
50
|
host.addEventListener('focus', this, true);
|
|
108
51
|
host.addEventListener('blur', this, true);
|
|
109
52
|
host.addEventListener('scroll', this, true);
|
|
110
|
-
this._uuid = options.uuid
|
|
111
|
-
// Handle selection style.
|
|
112
|
-
const style = options.selectionStyle || {};
|
|
113
|
-
this._selectionStyle = Object.assign(Object.assign({}, CodeEditor.defaultSelectionStyle), style);
|
|
53
|
+
this._uuid = (_e = options.uuid) !== null && _e !== void 0 ? _e : UUID.uuid4();
|
|
114
54
|
const model = (this._model = options.model);
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// every time the model is switched, we need to re-initialize the editor binding
|
|
120
|
-
this.model.sharedModelSwitched.connect(this._initializeEditorBinding, this);
|
|
121
|
-
const doc = editor.getDoc();
|
|
122
|
-
// Handle initial values for text, mimetype, and selections.
|
|
123
|
-
if (!USE_YCODEMIRROR_BINDING) {
|
|
124
|
-
doc.setValue(model.value.text);
|
|
125
|
-
}
|
|
126
|
-
this._onMimeTypeChanged();
|
|
127
|
-
this._onCursorActivity();
|
|
128
|
-
this._poll = new Poll({
|
|
129
|
-
factory: async () => {
|
|
130
|
-
this._checkSync();
|
|
131
|
-
},
|
|
132
|
-
frequency: { interval: 3000, backoff: false },
|
|
133
|
-
standby: () => {
|
|
134
|
-
// If changed, only stand by when hidden, otherwise always stand by.
|
|
135
|
-
return this._lastChange ? 'when-hidden' : true;
|
|
55
|
+
// Default keydown handler - it will have low priority
|
|
56
|
+
const onKeyDown = EditorView.domEventHandlers({
|
|
57
|
+
keydown: (event, view) => {
|
|
58
|
+
return this.onKeydown(event);
|
|
136
59
|
}
|
|
137
60
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
model.value.changed.connect(this._onValueChanged, this);
|
|
141
|
-
}
|
|
142
|
-
model.mimeTypeChanged.connect(this._onMimeTypeChanged, this);
|
|
143
|
-
model.selections.changed.connect(this._onSelectionsChanged, this);
|
|
144
|
-
CodeMirror.on(editor, 'keydown', (editor, event) => {
|
|
145
|
-
const index = ArrayExt.findFirstIndex(this._keydownHandlers, handler => {
|
|
146
|
-
if (handler(this, event) === true) {
|
|
147
|
-
event.preventDefault();
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
150
|
-
return false;
|
|
151
|
-
});
|
|
152
|
-
if (index === -1) {
|
|
153
|
-
this.onKeydown(event);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
if (USE_YCODEMIRROR_BINDING) {
|
|
157
|
-
(_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.on('cursorActivity', () => this._onCursorActivity());
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
CodeMirror.on(editor, 'cursorActivity', () => this._onCursorActivity());
|
|
161
|
-
CodeMirror.on(editor.getDoc(), 'beforeChange', (instance, change) => {
|
|
162
|
-
this._beforeDocChanged(instance, change);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
CodeMirror.on(editor.getDoc(), 'change', (instance, change) => {
|
|
166
|
-
// Manually refresh after setValue to make sure editor is properly sized.
|
|
167
|
-
if (change.origin === 'setValue' && this.hasFocus()) {
|
|
168
|
-
this.refresh();
|
|
169
|
-
}
|
|
170
|
-
this._lastChange = change;
|
|
171
|
-
});
|
|
172
|
-
// Turn off paste handling in codemirror since sometimes we want to
|
|
173
|
-
// replace it with our own.
|
|
174
|
-
editor.on('paste', (instance, event) => {
|
|
175
|
-
var _a;
|
|
176
|
-
const handlePaste = (_a = this._config['handlePaste']) !== null && _a !== void 0 ? _a : true;
|
|
177
|
-
if (!handlePaste) {
|
|
178
|
-
event.codemirrorIgnore = true;
|
|
179
|
-
}
|
|
61
|
+
const updateListener = EditorView.updateListener.of((update) => {
|
|
62
|
+
this._onDocChanged(update);
|
|
180
63
|
});
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
(_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
197
|
-
const sharedModel = this.model.sharedModel;
|
|
198
|
-
const opts = sharedModel.undoManager
|
|
199
|
-
? { yUndoManager: sharedModel.undoManager }
|
|
200
|
-
: {};
|
|
201
|
-
const awareness = sharedModel.awareness;
|
|
202
|
-
this._yeditorBinding = new CodemirrorBinding(sharedModel.ysource, this.editor, awareness, opts);
|
|
64
|
+
this._editor = Private.createEditor(host, this._configurator, [
|
|
65
|
+
// We need to set the order to high, otherwise the keybinding for ArrowUp/ArrowDown
|
|
66
|
+
// will process the event shunting our edge detection code.
|
|
67
|
+
Prec.high(onKeyDown),
|
|
68
|
+
updateListener,
|
|
69
|
+
// Initialize with empty extension
|
|
70
|
+
this._language.of([]),
|
|
71
|
+
...((_f = options.extensions) !== null && _f !== void 0 ? _f : [])
|
|
72
|
+
], model.sharedModel.source);
|
|
73
|
+
this._onMimeTypeChanged();
|
|
74
|
+
this._onCursorActivity();
|
|
75
|
+
this._configurator.configChanged.connect(this.onConfigChanged, this);
|
|
76
|
+
model.mimeTypeChanged.connect(this._onMimeTypeChanged, this);
|
|
203
77
|
}
|
|
204
78
|
/**
|
|
205
79
|
* The uuid of this editor;
|
|
@@ -210,15 +84,6 @@ export class CodeMirrorEditor {
|
|
|
210
84
|
set uuid(value) {
|
|
211
85
|
this._uuid = value;
|
|
212
86
|
}
|
|
213
|
-
/**
|
|
214
|
-
* The selection style of this editor.
|
|
215
|
-
*/
|
|
216
|
-
get selectionStyle() {
|
|
217
|
-
return this._selectionStyle;
|
|
218
|
-
}
|
|
219
|
-
set selectionStyle(value) {
|
|
220
|
-
this._selectionStyle = value;
|
|
221
|
-
}
|
|
222
87
|
/**
|
|
223
88
|
* Get the codemirror editor wrapped by the editor.
|
|
224
89
|
*/
|
|
@@ -229,13 +94,13 @@ export class CodeMirrorEditor {
|
|
|
229
94
|
* Get the codemirror doc wrapped by the widget.
|
|
230
95
|
*/
|
|
231
96
|
get doc() {
|
|
232
|
-
return this._editor.
|
|
97
|
+
return this._editor.state.doc;
|
|
233
98
|
}
|
|
234
99
|
/**
|
|
235
100
|
* Get the number of lines in the editor.
|
|
236
101
|
*/
|
|
237
102
|
get lineCount() {
|
|
238
|
-
return this.doc.
|
|
103
|
+
return this.doc.lines;
|
|
239
104
|
}
|
|
240
105
|
/**
|
|
241
106
|
* Returns a model for this editor.
|
|
@@ -247,13 +112,13 @@ export class CodeMirrorEditor {
|
|
|
247
112
|
* The height of a line in the editor in pixels.
|
|
248
113
|
*/
|
|
249
114
|
get lineHeight() {
|
|
250
|
-
return this._editor.
|
|
115
|
+
return this._editor.defaultLineHeight;
|
|
251
116
|
}
|
|
252
117
|
/**
|
|
253
118
|
* The widget of a character in the editor in pixels.
|
|
254
119
|
*/
|
|
255
120
|
get charWidth() {
|
|
256
|
-
return this._editor.
|
|
121
|
+
return this._editor.defaultCharacterWidth;
|
|
257
122
|
}
|
|
258
123
|
/**
|
|
259
124
|
* Tests whether the editor is disposed.
|
|
@@ -272,28 +137,27 @@ export class CodeMirrorEditor {
|
|
|
272
137
|
this.host.removeEventListener('focus', this, true);
|
|
273
138
|
this.host.removeEventListener('blur', this, true);
|
|
274
139
|
this.host.removeEventListener('scroll', this, true);
|
|
275
|
-
|
|
276
|
-
this._yeditorBinding.destroy();
|
|
277
|
-
}
|
|
278
|
-
this._keydownHandlers.length = 0;
|
|
279
|
-
this._poll.dispose();
|
|
140
|
+
this._configurator.dispose();
|
|
280
141
|
Signal.clearData(this);
|
|
142
|
+
this.editor.destroy();
|
|
281
143
|
}
|
|
282
144
|
/**
|
|
283
145
|
* Get a config option for the editor.
|
|
284
146
|
*/
|
|
285
147
|
getOption(option) {
|
|
286
|
-
return this.
|
|
148
|
+
return this._configurator.getOption(option);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Whether the option exists or not.
|
|
152
|
+
*/
|
|
153
|
+
hasOption(option) {
|
|
154
|
+
return this._configurator.hasOption(option);
|
|
287
155
|
}
|
|
288
156
|
/**
|
|
289
157
|
* Set a config option for the editor.
|
|
290
158
|
*/
|
|
291
159
|
setOption(option, value) {
|
|
292
|
-
|
|
293
|
-
if (this._config[option] !== value) {
|
|
294
|
-
this._config[option] = value;
|
|
295
|
-
Private.setOption(this.editor, option, value, this._config);
|
|
296
|
-
}
|
|
160
|
+
this._configurator.setOption(option, value);
|
|
297
161
|
}
|
|
298
162
|
/**
|
|
299
163
|
* Set config options for the editor.
|
|
@@ -304,37 +168,40 @@ export class CodeMirrorEditor {
|
|
|
304
168
|
* is set.
|
|
305
169
|
*/
|
|
306
170
|
setOptions(options) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
171
|
+
this._configurator.setOptions(options);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Inject an extension into the editor
|
|
175
|
+
*
|
|
176
|
+
* @alpha
|
|
177
|
+
* @experimental
|
|
178
|
+
* @param ext CodeMirror 6 extension
|
|
179
|
+
*/
|
|
180
|
+
injectExtension(ext) {
|
|
181
|
+
this._configurator.injectExtension(this._editor, ext);
|
|
316
182
|
}
|
|
317
183
|
/**
|
|
318
184
|
* Returns the content for the given line number.
|
|
319
185
|
*/
|
|
320
186
|
getLine(line) {
|
|
321
|
-
|
|
187
|
+
// TODO: CM6 remove +1 when CM6 first line number has propagated
|
|
188
|
+
line = line + 1;
|
|
189
|
+
return line <= this.doc.lines ? this.doc.line(line).text : undefined;
|
|
322
190
|
}
|
|
323
191
|
/**
|
|
324
192
|
* Find an offset for the given position.
|
|
325
193
|
*/
|
|
326
194
|
getOffsetAt(position) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
line: position.line
|
|
330
|
-
});
|
|
195
|
+
// TODO: CM6 remove +1 when CM6 first line number has propagated
|
|
196
|
+
return this.doc.line(position.line + 1).from + position.column;
|
|
331
197
|
}
|
|
332
198
|
/**
|
|
333
199
|
* Find a position for the given offset.
|
|
334
200
|
*/
|
|
335
201
|
getPositionAt(offset) {
|
|
336
|
-
|
|
337
|
-
|
|
202
|
+
// TODO: CM6 remove -1 when CM6 first line number has propagated
|
|
203
|
+
const line = this.doc.lineAt(offset);
|
|
204
|
+
return { line: line.number - 1, column: offset - line.from };
|
|
338
205
|
}
|
|
339
206
|
/**
|
|
340
207
|
* Undo one edit (if any undo events are stored).
|
|
@@ -352,8 +219,7 @@ export class CodeMirrorEditor {
|
|
|
352
219
|
* Clear the undo history.
|
|
353
220
|
*/
|
|
354
221
|
clearHistory() {
|
|
355
|
-
|
|
356
|
-
(_b = (_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.yUndoManager) === null || _b === void 0 ? void 0 : _b.clear();
|
|
222
|
+
this.model.sharedModel.clearUndoHistory();
|
|
357
223
|
}
|
|
358
224
|
/**
|
|
359
225
|
* Brings browser focus to this editor text.
|
|
@@ -365,127 +231,60 @@ export class CodeMirrorEditor {
|
|
|
365
231
|
* Test whether the editor has keyboard focus.
|
|
366
232
|
*/
|
|
367
233
|
hasFocus() {
|
|
368
|
-
return this._editor.
|
|
234
|
+
return this._editor.hasFocus;
|
|
369
235
|
}
|
|
370
236
|
/**
|
|
371
237
|
* Explicitly blur the editor.
|
|
372
238
|
*/
|
|
373
239
|
blur() {
|
|
374
|
-
this._editor.
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Repaint editor.
|
|
378
|
-
*/
|
|
379
|
-
refresh() {
|
|
380
|
-
this._editor.refresh();
|
|
381
|
-
this._needsRefresh = false;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Refresh the editor if it is focused;
|
|
385
|
-
* otherwise postpone refreshing till focusing.
|
|
386
|
-
*/
|
|
387
|
-
resizeToFit() {
|
|
388
|
-
if (this.hasFocus()) {
|
|
389
|
-
this.refresh();
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
this._needsRefresh = true;
|
|
393
|
-
}
|
|
394
|
-
this._clearHover();
|
|
395
|
-
}
|
|
396
|
-
// todo: docs, maybe define overlay options as a type?
|
|
397
|
-
addOverlay(mode, options) {
|
|
398
|
-
this._editor.addOverlay(mode, options);
|
|
399
|
-
}
|
|
400
|
-
removeOverlay(mode) {
|
|
401
|
-
this._editor.removeOverlay(mode);
|
|
402
|
-
}
|
|
403
|
-
getSearchCursor(query, start, caseFold) {
|
|
404
|
-
return this._editor.getDoc().getSearchCursor(query, start, caseFold);
|
|
405
|
-
}
|
|
406
|
-
getCursor(start) {
|
|
407
|
-
return this._editor.getDoc().getCursor(start);
|
|
240
|
+
this._editor.contentDOM.blur();
|
|
408
241
|
}
|
|
409
242
|
get state() {
|
|
410
243
|
return this._editor.state;
|
|
411
244
|
}
|
|
412
|
-
operation(fn) {
|
|
413
|
-
return this._editor.operation(fn);
|
|
414
|
-
}
|
|
415
245
|
firstLine() {
|
|
416
|
-
return
|
|
246
|
+
// TODO: return 1 when CM6 first line number has propagated
|
|
247
|
+
return 0;
|
|
417
248
|
}
|
|
418
249
|
lastLine() {
|
|
419
|
-
return this.
|
|
420
|
-
}
|
|
421
|
-
scrollIntoView(pos, margin) {
|
|
422
|
-
this._editor.scrollIntoView(pos, margin);
|
|
423
|
-
}
|
|
424
|
-
scrollIntoViewCentered(pos) {
|
|
425
|
-
var _a, _b;
|
|
426
|
-
const top = this._editor.charCoords(pos, 'local').top;
|
|
427
|
-
const height = this._editor.getWrapperElement().offsetHeight;
|
|
428
|
-
(_b = (_a = this.host).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
429
|
-
behavior: 'auto',
|
|
430
|
-
block: 'center',
|
|
431
|
-
inline: 'center'
|
|
432
|
-
});
|
|
433
|
-
this._editor.scrollTo(null, top - height / 2);
|
|
250
|
+
return this.doc.lines - 1;
|
|
434
251
|
}
|
|
435
252
|
cursorCoords(where, mode) {
|
|
436
|
-
|
|
253
|
+
const selection = this.state.selection.main;
|
|
254
|
+
const pos = where ? selection.from : selection.to;
|
|
255
|
+
const rect = this.editor.coordsAtPos(pos);
|
|
256
|
+
return rect;
|
|
437
257
|
}
|
|
438
258
|
getRange(from, to, separator) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
* Add a keydown handler to the editor.
|
|
443
|
-
*
|
|
444
|
-
* @param handler - A keydown handler.
|
|
445
|
-
*
|
|
446
|
-
* @returns A disposable that can be used to remove the handler.
|
|
447
|
-
*/
|
|
448
|
-
addKeydownHandler(handler) {
|
|
449
|
-
this._keydownHandlers.push(handler);
|
|
450
|
-
return new DisposableDelegate(() => {
|
|
451
|
-
ArrayExt.removeAllWhere(this._keydownHandlers, val => val === handler);
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Set the size of the editor in pixels.
|
|
456
|
-
*/
|
|
457
|
-
setSize(dimension) {
|
|
458
|
-
if (dimension) {
|
|
459
|
-
this._editor.setSize(dimension.width, dimension.height);
|
|
460
|
-
}
|
|
461
|
-
else {
|
|
462
|
-
this._editor.setSize(null, null);
|
|
463
|
-
}
|
|
464
|
-
this._needsRefresh = false;
|
|
259
|
+
const fromOffset = this.getOffsetAt(this._toPosition(from));
|
|
260
|
+
const toOffset = this.getOffsetAt(this._toPosition(to));
|
|
261
|
+
return this.state.sliceDoc(fromOffset, toOffset);
|
|
465
262
|
}
|
|
466
263
|
/**
|
|
467
264
|
* Reveal the given position in the editor.
|
|
468
265
|
*/
|
|
469
266
|
revealPosition(position) {
|
|
470
|
-
const
|
|
471
|
-
this._editor.
|
|
267
|
+
const offset = this.getOffsetAt(position);
|
|
268
|
+
this._editor.dispatch({
|
|
269
|
+
effects: EditorView.scrollIntoView(offset)
|
|
270
|
+
});
|
|
472
271
|
}
|
|
473
272
|
/**
|
|
474
273
|
* Reveal the given selection in the editor.
|
|
475
274
|
*/
|
|
476
275
|
revealSelection(selection) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
276
|
+
const start = this.getOffsetAt(selection.start);
|
|
277
|
+
const end = this.getOffsetAt(selection.end);
|
|
278
|
+
this._editor.dispatch({
|
|
279
|
+
effects: EditorView.scrollIntoView(EditorSelection.range(start, end))
|
|
280
|
+
});
|
|
482
281
|
}
|
|
483
282
|
/**
|
|
484
283
|
* Get the window coordinates given a cursor position.
|
|
485
284
|
*/
|
|
486
285
|
getCoordinateForPosition(position) {
|
|
487
|
-
const
|
|
488
|
-
const rect = this.editor.
|
|
286
|
+
const offset = this.getOffsetAt(position);
|
|
287
|
+
const rect = this.editor.coordsAtPos(offset);
|
|
489
288
|
return rect;
|
|
490
289
|
}
|
|
491
290
|
/**
|
|
@@ -497,14 +296,18 @@ export class CodeMirrorEditor {
|
|
|
497
296
|
* contained in the editor.
|
|
498
297
|
*/
|
|
499
298
|
getPositionForCoordinate(coordinate) {
|
|
500
|
-
|
|
299
|
+
const offset = this.editor.posAtCoords({
|
|
300
|
+
x: coordinate.left,
|
|
301
|
+
y: coordinate.top
|
|
302
|
+
});
|
|
303
|
+
return this.getPositionAt(offset) || null;
|
|
501
304
|
}
|
|
502
305
|
/**
|
|
503
306
|
* Returns the primary position of the cursor, never `null`.
|
|
504
307
|
*/
|
|
505
308
|
getCursorPosition() {
|
|
506
|
-
const
|
|
507
|
-
return this.
|
|
309
|
+
const offset = this.state.selection.main.head;
|
|
310
|
+
return this.getPositionAt(offset);
|
|
508
311
|
}
|
|
509
312
|
/**
|
|
510
313
|
* Set the primary position of the cursor.
|
|
@@ -513,12 +316,15 @@ export class CodeMirrorEditor {
|
|
|
513
316
|
* This will remove any secondary cursors.
|
|
514
317
|
*/
|
|
515
318
|
setCursorPosition(position, options) {
|
|
516
|
-
const
|
|
517
|
-
this.
|
|
319
|
+
const offset = this.getOffsetAt(position);
|
|
320
|
+
this.editor.dispatch({
|
|
321
|
+
selection: { anchor: offset },
|
|
322
|
+
scrollIntoView: true
|
|
323
|
+
});
|
|
518
324
|
// If the editor does not have focus, this cursor change
|
|
519
325
|
// will get screened out in _onCursorsChanged(). Make an
|
|
520
326
|
// exception for this method.
|
|
521
|
-
if (!this.editor.hasFocus
|
|
327
|
+
if (!this.editor.hasFocus) {
|
|
522
328
|
this.model.selections.set(this.uuid, this.getSelections());
|
|
523
329
|
}
|
|
524
330
|
}
|
|
@@ -538,11 +344,15 @@ export class CodeMirrorEditor {
|
|
|
538
344
|
* Gets the selections for all the cursors, never `null` or empty.
|
|
539
345
|
*/
|
|
540
346
|
getSelections() {
|
|
541
|
-
const selections = this.
|
|
347
|
+
const selections = this.state.selection.ranges; //= [{anchor: number, head: number}]
|
|
542
348
|
if (selections.length > 0) {
|
|
543
|
-
|
|
349
|
+
const sel = selections.map(r => ({
|
|
350
|
+
anchor: this._toCodeMirrorPosition(this.getPositionAt(r.from)),
|
|
351
|
+
head: this._toCodeMirrorPosition(this.getPositionAt(r.to))
|
|
352
|
+
}));
|
|
353
|
+
return sel.map(selection => this._toSelection(selection));
|
|
544
354
|
}
|
|
545
|
-
const cursor = this.
|
|
355
|
+
const cursor = this._toCodeMirrorPosition(this.getPositionAt(this.state.selection.main.head));
|
|
546
356
|
const selection = this._toSelection({ anchor: cursor, head: cursor });
|
|
547
357
|
return [selection];
|
|
548
358
|
}
|
|
@@ -552,50 +362,98 @@ export class CodeMirrorEditor {
|
|
|
552
362
|
* Passing an empty array resets a cursor position to the start of a document.
|
|
553
363
|
*/
|
|
554
364
|
setSelections(selections) {
|
|
555
|
-
const
|
|
556
|
-
|
|
365
|
+
const sel = selections.length
|
|
366
|
+
? selections.map(r => EditorSelection.range(this.getOffsetAt(r.start), this.getOffsetAt(r.end)))
|
|
367
|
+
: [EditorSelection.range(0, 0)];
|
|
368
|
+
this.editor.dispatch({ selection: EditorSelection.create(sel) });
|
|
557
369
|
}
|
|
558
370
|
/**
|
|
559
371
|
* Replaces the current selection with the given text.
|
|
560
372
|
*
|
|
373
|
+
* Behaviour for multiple selections is undefined.
|
|
374
|
+
*
|
|
561
375
|
* @param text The text to be inserted.
|
|
562
376
|
*/
|
|
563
377
|
replaceSelection(text) {
|
|
564
|
-
this.
|
|
378
|
+
const firstSelection = this.getSelections()[0];
|
|
379
|
+
this.model.sharedModel.updateSource(this.getOffsetAt(firstSelection.start), this.getOffsetAt(firstSelection.end), text);
|
|
380
|
+
const newPosition = this.getPositionAt(this.getOffsetAt(firstSelection.start) + text.length);
|
|
381
|
+
this.setSelection({ start: newPosition, end: newPosition });
|
|
565
382
|
}
|
|
566
383
|
/**
|
|
567
384
|
* Get a list of tokens for the current editor text content.
|
|
568
385
|
*/
|
|
569
386
|
getTokens() {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
387
|
+
const tokens = [];
|
|
388
|
+
const tree = ensureSyntaxTree(this.state, this.doc.length);
|
|
389
|
+
if (tree) {
|
|
390
|
+
tree.iterate({
|
|
391
|
+
enter: (node) => {
|
|
392
|
+
// If it has a child, it is not a leaf, but we still want to enter
|
|
393
|
+
if (node.node.firstChild !== null) {
|
|
394
|
+
return true;
|
|
395
|
+
}
|
|
396
|
+
tokens.push({
|
|
397
|
+
value: this.state.sliceDoc(node.from, node.to),
|
|
398
|
+
offset: node.from,
|
|
399
|
+
type: node.name
|
|
400
|
+
});
|
|
401
|
+
return true;
|
|
402
|
+
}
|
|
403
|
+
});
|
|
578
404
|
}
|
|
579
405
|
return tokens;
|
|
580
406
|
}
|
|
581
407
|
/**
|
|
582
408
|
* Get the token at a given editor position.
|
|
583
409
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
410
|
+
getTokenAt(offset) {
|
|
411
|
+
const tree = ensureSyntaxTree(this.state, offset);
|
|
412
|
+
let token = null;
|
|
413
|
+
if (tree) {
|
|
414
|
+
tree.iterate({
|
|
415
|
+
enter: (node) => {
|
|
416
|
+
// If it has a child, it is not a leaf, but we still want to enter
|
|
417
|
+
if (node.node.firstChild !== null) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
if (offset >= node.from && offset <= node.to) {
|
|
421
|
+
token = {
|
|
422
|
+
value: this.state.sliceDoc(node.from, node.to),
|
|
423
|
+
offset: node.from,
|
|
424
|
+
type: node.name
|
|
425
|
+
};
|
|
426
|
+
// We have just found the relevant leaf token, no need to iterate further
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (token !== null) {
|
|
434
|
+
return token;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
return {
|
|
438
|
+
value: '',
|
|
439
|
+
offset: offset
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Get the token a the cursor position.
|
|
445
|
+
*/
|
|
446
|
+
getTokenAtCursor() {
|
|
447
|
+
return this.getTokenAt(this.state.selection.main.head);
|
|
593
448
|
}
|
|
594
449
|
/**
|
|
595
450
|
* Insert a new indented line at the current cursor position.
|
|
596
451
|
*/
|
|
597
452
|
newIndentedLine() {
|
|
598
|
-
|
|
453
|
+
insertNewlineAndIndent({
|
|
454
|
+
state: this.state,
|
|
455
|
+
dispatch: this.editor.dispatch
|
|
456
|
+
});
|
|
599
457
|
}
|
|
600
458
|
/**
|
|
601
459
|
* Execute a codemirror command on the editor.
|
|
@@ -603,31 +461,31 @@ export class CodeMirrorEditor {
|
|
|
603
461
|
* @param command - The name of the command to execute.
|
|
604
462
|
*/
|
|
605
463
|
execCommand(command) {
|
|
606
|
-
this.
|
|
464
|
+
command(this.editor);
|
|
465
|
+
}
|
|
466
|
+
onConfigChanged(configurator, changes) {
|
|
467
|
+
configurator.reconfigureExtensions(this._editor, changes);
|
|
607
468
|
}
|
|
608
469
|
/**
|
|
609
470
|
* Handle keydown events from the editor.
|
|
610
471
|
*/
|
|
611
472
|
onKeydown(event) {
|
|
612
|
-
const position = this.
|
|
613
|
-
|
|
614
|
-
if (line === 0 && column === 0 && event.keyCode === UP_ARROW) {
|
|
473
|
+
const position = this.state.selection.main.head;
|
|
474
|
+
if (position === 0 && event.keyCode === UP_ARROW) {
|
|
615
475
|
if (!event.shiftKey) {
|
|
616
476
|
this.edgeRequested.emit('top');
|
|
617
477
|
}
|
|
618
478
|
return false;
|
|
619
479
|
}
|
|
620
|
-
|
|
480
|
+
const line = this.doc.lineAt(position).number;
|
|
481
|
+
if (line === 1 && event.keyCode === UP_ARROW) {
|
|
621
482
|
if (!event.shiftKey) {
|
|
622
483
|
this.edgeRequested.emit('topLine');
|
|
623
484
|
}
|
|
624
485
|
return false;
|
|
625
486
|
}
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
if (line === lastLine &&
|
|
629
|
-
column === lastCh &&
|
|
630
|
-
event.keyCode === DOWN_ARROW) {
|
|
487
|
+
const length = this.doc.length;
|
|
488
|
+
if (position === length && event.keyCode === DOWN_ARROW) {
|
|
631
489
|
if (!event.shiftKey) {
|
|
632
490
|
this.edgeRequested.emit('bottom');
|
|
633
491
|
}
|
|
@@ -635,108 +493,25 @@ export class CodeMirrorEditor {
|
|
|
635
493
|
}
|
|
636
494
|
return false;
|
|
637
495
|
}
|
|
638
|
-
/**
|
|
639
|
-
* Converts selections to code mirror selections.
|
|
640
|
-
*/
|
|
641
|
-
_toCodeMirrorSelections(selections) {
|
|
642
|
-
if (selections.length > 0) {
|
|
643
|
-
return selections.map(selection => this._toCodeMirrorSelection(selection));
|
|
644
|
-
}
|
|
645
|
-
const position = { line: 0, ch: 0 };
|
|
646
|
-
return [{ anchor: position, head: position }];
|
|
647
|
-
}
|
|
648
496
|
/**
|
|
649
497
|
* Handles a mime type change.
|
|
650
498
|
*/
|
|
651
499
|
_onMimeTypeChanged() {
|
|
652
|
-
const mime = this._model.mimeType;
|
|
653
|
-
const editor = this._editor;
|
|
654
|
-
const extraKeys = (editor.getOption('extraKeys') ||
|
|
655
|
-
{});
|
|
656
|
-
const isCode = mime !== 'text/plain' && mime !== 'text/x-ipythongfm';
|
|
657
|
-
if (isCode) {
|
|
658
|
-
extraKeys['Backspace'] = 'delSpaceToPrevTabStop';
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
delete extraKeys['Backspace'];
|
|
662
|
-
}
|
|
663
|
-
this.setOption('extraKeys', extraKeys);
|
|
664
500
|
// TODO: should we provide a hook for when the mode is done being set?
|
|
665
|
-
|
|
501
|
+
this._languages
|
|
502
|
+
.getLanguage(this._model.mimeType)
|
|
503
|
+
.then(language => {
|
|
666
504
|
var _a;
|
|
667
|
-
this.
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (uuid !== this.uuid) {
|
|
676
|
-
this._cleanSelections(uuid);
|
|
677
|
-
if (args.type !== 'remove' && args.newValue) {
|
|
678
|
-
this._markSelections(uuid, args.newValue);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Clean selections for the given uuid.
|
|
684
|
-
*/
|
|
685
|
-
_cleanSelections(uuid) {
|
|
686
|
-
const markers = this.selectionMarkers[uuid];
|
|
687
|
-
if (markers) {
|
|
688
|
-
markers.forEach(marker => {
|
|
689
|
-
marker.clear();
|
|
505
|
+
this._editor.dispatch({
|
|
506
|
+
effects: this._language.reconfigure((_a = language === null || language === void 0 ? void 0 : language.support) !== null && _a !== void 0 ? _a : [])
|
|
507
|
+
});
|
|
508
|
+
})
|
|
509
|
+
.catch(reason => {
|
|
510
|
+
console.log(`Failed to load language for '${this._model.mimeType}'.`, reason);
|
|
511
|
+
this._editor.dispatch({
|
|
512
|
+
effects: this._language.reconfigure([])
|
|
690
513
|
});
|
|
691
|
-
}
|
|
692
|
-
delete this.selectionMarkers[uuid];
|
|
693
|
-
}
|
|
694
|
-
/**
|
|
695
|
-
* Marks selections.
|
|
696
|
-
*/
|
|
697
|
-
_markSelections(uuid, selections) {
|
|
698
|
-
const markers = [];
|
|
699
|
-
// If we are marking selections corresponding to an active hover,
|
|
700
|
-
// remove it.
|
|
701
|
-
if (uuid === this._hoverId) {
|
|
702
|
-
this._clearHover();
|
|
703
|
-
}
|
|
704
|
-
// If we can id the selection to a specific collaborator,
|
|
705
|
-
// use that information.
|
|
706
|
-
let collaborator;
|
|
707
|
-
if (this._model.modelDB.collaborators) {
|
|
708
|
-
collaborator = this._model.modelDB.collaborators.get(uuid);
|
|
709
|
-
}
|
|
710
|
-
// Style each selection for the uuid.
|
|
711
|
-
selections.forEach(selection => {
|
|
712
|
-
// Only render selections if the start is not equal to the end.
|
|
713
|
-
// In that case, we don't need to render the cursor.
|
|
714
|
-
if (!JSONExt.deepEqual(selection.start, selection.end)) {
|
|
715
|
-
// Selections only appear to render correctly if the anchor
|
|
716
|
-
// is before the head in the document. That is, reverse selections
|
|
717
|
-
// do not appear as intended.
|
|
718
|
-
const forward = selection.start.line < selection.end.line ||
|
|
719
|
-
(selection.start.line === selection.end.line &&
|
|
720
|
-
selection.start.column <= selection.end.column);
|
|
721
|
-
const anchor = this._toCodeMirrorPosition(forward ? selection.start : selection.end);
|
|
722
|
-
const head = this._toCodeMirrorPosition(forward ? selection.end : selection.start);
|
|
723
|
-
let markerOptions;
|
|
724
|
-
if (collaborator) {
|
|
725
|
-
markerOptions = this._toTextMarkerOptions(Object.assign(Object.assign({}, selection.style), { color: collaborator.color }));
|
|
726
|
-
}
|
|
727
|
-
else {
|
|
728
|
-
markerOptions = this._toTextMarkerOptions(selection.style);
|
|
729
|
-
}
|
|
730
|
-
markers.push(this.doc.markText(anchor, head, markerOptions));
|
|
731
|
-
}
|
|
732
|
-
else if (collaborator) {
|
|
733
|
-
const caret = this._getCaret(collaborator);
|
|
734
|
-
markers.push(this.doc.setBookmark(this._toCodeMirrorPosition(selection.end), {
|
|
735
|
-
widget: caret
|
|
736
|
-
}));
|
|
737
|
-
}
|
|
738
514
|
});
|
|
739
|
-
this.selectionMarkers[uuid] = markers;
|
|
740
515
|
}
|
|
741
516
|
/**
|
|
742
517
|
* Handles a cursor activity event.
|
|
@@ -744,7 +519,7 @@ export class CodeMirrorEditor {
|
|
|
744
519
|
_onCursorActivity() {
|
|
745
520
|
// Only add selections if the editor has focus. This avoids unwanted
|
|
746
521
|
// triggering of cursor activity due to collaborator actions.
|
|
747
|
-
if (this._editor.hasFocus
|
|
522
|
+
if (this._editor.hasFocus) {
|
|
748
523
|
const selections = this.getSelections();
|
|
749
524
|
this.model.selections.set(this.uuid, selections);
|
|
750
525
|
}
|
|
@@ -756,31 +531,7 @@ export class CodeMirrorEditor {
|
|
|
756
531
|
return {
|
|
757
532
|
uuid: this.uuid,
|
|
758
533
|
start: this._toPosition(selection.anchor),
|
|
759
|
-
end: this._toPosition(selection.head)
|
|
760
|
-
style: this.selectionStyle
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Converts the selection style to a text marker options.
|
|
765
|
-
*/
|
|
766
|
-
_toTextMarkerOptions(style) {
|
|
767
|
-
const r = parseInt(style.color.slice(1, 3), 16);
|
|
768
|
-
const g = parseInt(style.color.slice(3, 5), 16);
|
|
769
|
-
const b = parseInt(style.color.slice(5, 7), 16);
|
|
770
|
-
const css = `background-color: rgba( ${r}, ${g}, ${b}, 0.15)`;
|
|
771
|
-
return {
|
|
772
|
-
className: style.className,
|
|
773
|
-
title: style.displayName,
|
|
774
|
-
css
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Converts an editor selection to a code mirror selection.
|
|
779
|
-
*/
|
|
780
|
-
_toCodeMirrorSelection(selection) {
|
|
781
|
-
return {
|
|
782
|
-
anchor: this._toCodeMirrorPosition(selection.start),
|
|
783
|
-
head: this._toCodeMirrorPosition(selection.end)
|
|
534
|
+
end: this._toPosition(selection.head)
|
|
784
535
|
};
|
|
785
536
|
}
|
|
786
537
|
/**
|
|
@@ -801,60 +552,13 @@ export class CodeMirrorEditor {
|
|
|
801
552
|
ch: position.column
|
|
802
553
|
};
|
|
803
554
|
}
|
|
804
|
-
/**
|
|
805
|
-
* Handle model value changes.
|
|
806
|
-
*/
|
|
807
|
-
_onValueChanged(value, args) {
|
|
808
|
-
if (this._changeGuard) {
|
|
809
|
-
return;
|
|
810
|
-
}
|
|
811
|
-
this._changeGuard = true;
|
|
812
|
-
const doc = this.doc;
|
|
813
|
-
switch (args.type) {
|
|
814
|
-
case 'insert': {
|
|
815
|
-
const pos = doc.posFromIndex(args.start);
|
|
816
|
-
// Replace the range, including a '+input' origin,
|
|
817
|
-
// which indicates that CodeMirror may merge changes
|
|
818
|
-
// for undo/redo purposes.
|
|
819
|
-
doc.replaceRange(args.value, pos, pos, '+input');
|
|
820
|
-
break;
|
|
821
|
-
}
|
|
822
|
-
case 'remove': {
|
|
823
|
-
const from = doc.posFromIndex(args.start);
|
|
824
|
-
const to = doc.posFromIndex(args.end);
|
|
825
|
-
// Replace the range, including a '+input' origin,
|
|
826
|
-
// which indicates that CodeMirror may merge changes
|
|
827
|
-
// for undo/redo purposes.
|
|
828
|
-
doc.replaceRange('', from, to, '+input');
|
|
829
|
-
break;
|
|
830
|
-
}
|
|
831
|
-
case 'set':
|
|
832
|
-
doc.setValue(args.value);
|
|
833
|
-
break;
|
|
834
|
-
default:
|
|
835
|
-
break;
|
|
836
|
-
}
|
|
837
|
-
this._changeGuard = false;
|
|
838
|
-
}
|
|
839
555
|
/**
|
|
840
556
|
* Handles document changes.
|
|
841
557
|
*/
|
|
842
|
-
|
|
843
|
-
if (
|
|
844
|
-
|
|
845
|
-
}
|
|
846
|
-
this._changeGuard = true;
|
|
847
|
-
const value = this._model.value;
|
|
848
|
-
const start = doc.indexFromPos(change.from);
|
|
849
|
-
const end = doc.indexFromPos(change.to);
|
|
850
|
-
const inserted = change.text.join('\n');
|
|
851
|
-
if (end !== start) {
|
|
852
|
-
value.remove(start, end);
|
|
558
|
+
_onDocChanged(update) {
|
|
559
|
+
if (update.transactions.length && update.transactions[0].selection) {
|
|
560
|
+
this._onCursorActivity();
|
|
853
561
|
}
|
|
854
|
-
if (inserted) {
|
|
855
|
-
value.insert(start, inserted);
|
|
856
|
-
}
|
|
857
|
-
this._changeGuard = false;
|
|
858
562
|
}
|
|
859
563
|
/**
|
|
860
564
|
* Handle the DOM events for the editor.
|
|
@@ -874,9 +578,6 @@ export class CodeMirrorEditor {
|
|
|
874
578
|
case 'blur':
|
|
875
579
|
this._evtBlur(event);
|
|
876
580
|
break;
|
|
877
|
-
case 'scroll':
|
|
878
|
-
this._evtScroll();
|
|
879
|
-
break;
|
|
880
581
|
default:
|
|
881
582
|
break;
|
|
882
583
|
}
|
|
@@ -885,9 +586,6 @@ export class CodeMirrorEditor {
|
|
|
885
586
|
* Handle `focus` events for the editor.
|
|
886
587
|
*/
|
|
887
588
|
_evtFocus(event) {
|
|
888
|
-
if (this._needsRefresh) {
|
|
889
|
-
this.refresh();
|
|
890
|
-
}
|
|
891
589
|
this.host.classList.add('jp-mod-focused');
|
|
892
590
|
// Update the selections on editor gaining focus because
|
|
893
591
|
// the onCursorActivity function filters usual cursor events
|
|
@@ -900,321 +598,24 @@ export class CodeMirrorEditor {
|
|
|
900
598
|
_evtBlur(event) {
|
|
901
599
|
this.host.classList.remove('jp-mod-focused');
|
|
902
600
|
}
|
|
903
|
-
/**
|
|
904
|
-
* Handle `scroll` events for the editor.
|
|
905
|
-
*/
|
|
906
|
-
_evtScroll() {
|
|
907
|
-
// Remove any active hover.
|
|
908
|
-
this._clearHover();
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Clear the hover for a caret, due to things like
|
|
912
|
-
* scrolling, resizing, deactivation, etc, where
|
|
913
|
-
* the position is no longer valid.
|
|
914
|
-
*/
|
|
915
|
-
_clearHover() {
|
|
916
|
-
if (this._caretHover) {
|
|
917
|
-
window.clearTimeout(this._hoverTimeout);
|
|
918
|
-
document.body.removeChild(this._caretHover);
|
|
919
|
-
this._caretHover = null;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
/**
|
|
923
|
-
* Construct a caret element representing the position
|
|
924
|
-
* of a collaborator's cursor.
|
|
925
|
-
*/
|
|
926
|
-
_getCaret(collaborator) {
|
|
927
|
-
// FIXME-TRANS: Is this localizable?
|
|
928
|
-
const name = collaborator ? collaborator.displayName : 'Anonymous';
|
|
929
|
-
const color = collaborator
|
|
930
|
-
? collaborator.color
|
|
931
|
-
: this._selectionStyle.color;
|
|
932
|
-
const caret = document.createElement('span');
|
|
933
|
-
caret.className = COLLABORATOR_CURSOR_CLASS;
|
|
934
|
-
caret.style.borderBottomColor = color;
|
|
935
|
-
caret.onmouseenter = () => {
|
|
936
|
-
this._clearHover();
|
|
937
|
-
this._hoverId = collaborator.sessionId;
|
|
938
|
-
const rect = caret.getBoundingClientRect();
|
|
939
|
-
// Construct and place the hover box.
|
|
940
|
-
const hover = document.createElement('div');
|
|
941
|
-
hover.className = COLLABORATOR_HOVER_CLASS;
|
|
942
|
-
hover.style.left = String(rect.left) + 'px';
|
|
943
|
-
hover.style.top = String(rect.bottom) + 'px';
|
|
944
|
-
hover.textContent = name;
|
|
945
|
-
hover.style.backgroundColor = color;
|
|
946
|
-
// If the user mouses over the hover, take over the timer.
|
|
947
|
-
hover.onmouseenter = () => {
|
|
948
|
-
window.clearTimeout(this._hoverTimeout);
|
|
949
|
-
};
|
|
950
|
-
hover.onmouseleave = () => {
|
|
951
|
-
this._hoverTimeout = window.setTimeout(() => {
|
|
952
|
-
this._clearHover();
|
|
953
|
-
}, HOVER_TIMEOUT);
|
|
954
|
-
};
|
|
955
|
-
this._caretHover = hover;
|
|
956
|
-
document.body.appendChild(hover);
|
|
957
|
-
};
|
|
958
|
-
caret.onmouseleave = () => {
|
|
959
|
-
this._hoverTimeout = window.setTimeout(() => {
|
|
960
|
-
this._clearHover();
|
|
961
|
-
}, HOVER_TIMEOUT);
|
|
962
|
-
};
|
|
963
|
-
return caret;
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Check for an out of sync editor.
|
|
967
|
-
*/
|
|
968
|
-
_checkSync() {
|
|
969
|
-
const change = this._lastChange;
|
|
970
|
-
if (!change) {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
this._lastChange = null;
|
|
974
|
-
const editor = this._editor;
|
|
975
|
-
const doc = editor.getDoc();
|
|
976
|
-
if (doc.getValue() === this._model.value.text) {
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
void showDialog({
|
|
980
|
-
title: this._trans.__('Code Editor out of Sync'),
|
|
981
|
-
body: this._trans.__('Please open your browser JavaScript console for bug report instructions')
|
|
982
|
-
});
|
|
983
|
-
console.warn('If you are able and willing to publicly share the text or code in your editor, you can help us debug the "Code Editor out of Sync" message by pasting the following to the public issue at https://github.com/jupyterlab/jupyterlab/issues/2951. Please note that the data below includes the text/code in your editor.');
|
|
984
|
-
console.warn(JSON.stringify({
|
|
985
|
-
model: this._model.value.text,
|
|
986
|
-
view: doc.getValue(),
|
|
987
|
-
selections: this.getSelections(),
|
|
988
|
-
cursor: this.getCursorPosition(),
|
|
989
|
-
lineSep: editor.getOption('lineSeparator'),
|
|
990
|
-
mode: editor.getOption('mode'),
|
|
991
|
-
change
|
|
992
|
-
}));
|
|
993
|
-
}
|
|
994
601
|
}
|
|
995
|
-
/**
|
|
996
|
-
* The namespace for `CodeMirrorEditor` statics.
|
|
997
|
-
*/
|
|
998
|
-
(function (CodeMirrorEditor) {
|
|
999
|
-
/**
|
|
1000
|
-
* The default configuration options for an editor.
|
|
1001
|
-
*/
|
|
1002
|
-
CodeMirrorEditor.defaultConfig = Object.assign(Object.assign({}, CodeEditor.defaultConfig), { mode: 'null', theme: 'jupyter', smartIndent: true, electricChars: true, keyMap: 'default', extraKeys: null, gutters: [], fixedGutter: true, showCursorWhenSelecting: false, coverGutterNextToScrollbar: false, dragDrop: true, lineSeparator: null, scrollbarStyle: 'native', lineWiseCopyCut: true, scrollPastEnd: false, styleActiveLine: false, styleSelectedText: true, selectionPointer: false, rulers: [], foldGutter: false, handlePaste: true });
|
|
1003
|
-
/**
|
|
1004
|
-
* Add a command to CodeMirror.
|
|
1005
|
-
*
|
|
1006
|
-
* @param name - The name of the command to add.
|
|
1007
|
-
*
|
|
1008
|
-
* @param command - The command function.
|
|
1009
|
-
*/
|
|
1010
|
-
function addCommand(name, command) {
|
|
1011
|
-
CodeMirror.commands[name] = command;
|
|
1012
|
-
}
|
|
1013
|
-
CodeMirrorEditor.addCommand = addCommand;
|
|
1014
|
-
})(CodeMirrorEditor || (CodeMirrorEditor = {}));
|
|
1015
602
|
/**
|
|
1016
603
|
* The namespace for module private data.
|
|
1017
604
|
*/
|
|
1018
605
|
var Private;
|
|
1019
606
|
(function (Private) {
|
|
1020
|
-
function createEditor(host,
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
el.style.lineHeight = lineHeight.toString();
|
|
1032
|
-
}
|
|
1033
|
-
if (readOnly) {
|
|
1034
|
-
el.classList.add(READ_ONLY_CLASS);
|
|
1035
|
-
}
|
|
1036
|
-
if (lineWrap === 'wordWrapColumn') {
|
|
1037
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1038
|
-
lines.style.width = `${wordWrapColumn}ch`;
|
|
1039
|
-
}
|
|
1040
|
-
if (lineWrap === 'bounded') {
|
|
1041
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1042
|
-
lines.style.maxWidth = `${wordWrapColumn}ch`;
|
|
1043
|
-
}
|
|
1044
|
-
host.appendChild(el);
|
|
1045
|
-
}, bareConfig);
|
|
607
|
+
function createEditor(host, editorConfig, additionalExtensions, doc) {
|
|
608
|
+
const extensions = editorConfig.getInitialExtensions();
|
|
609
|
+
extensions.push(...additionalExtensions);
|
|
610
|
+
const view = new EditorView({
|
|
611
|
+
state: EditorState.create({
|
|
612
|
+
doc,
|
|
613
|
+
extensions
|
|
614
|
+
}),
|
|
615
|
+
parent: host
|
|
616
|
+
});
|
|
617
|
+
return view;
|
|
1046
618
|
}
|
|
1047
619
|
Private.createEditor = createEditor;
|
|
1048
|
-
/**
|
|
1049
|
-
* Indent or insert a tab as appropriate.
|
|
1050
|
-
*/
|
|
1051
|
-
function indentMoreOrinsertTab(cm) {
|
|
1052
|
-
const doc = cm.getDoc();
|
|
1053
|
-
const from = doc.getCursor('from');
|
|
1054
|
-
const to = doc.getCursor('to');
|
|
1055
|
-
const sel = !posEq(from, to);
|
|
1056
|
-
if (sel) {
|
|
1057
|
-
CodeMirror.commands['indentMore'](cm);
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
// Check for start of line.
|
|
1061
|
-
const line = doc.getLine(from.line);
|
|
1062
|
-
const before = line.slice(0, from.ch);
|
|
1063
|
-
if (/^\s*$/.test(before)) {
|
|
1064
|
-
CodeMirror.commands['indentMore'](cm);
|
|
1065
|
-
}
|
|
1066
|
-
else {
|
|
1067
|
-
if (cm.getOption('indentWithTabs')) {
|
|
1068
|
-
CodeMirror.commands['insertTab'](cm);
|
|
1069
|
-
}
|
|
1070
|
-
else {
|
|
1071
|
-
CodeMirror.commands['insertSoftTab'](cm);
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
Private.indentMoreOrinsertTab = indentMoreOrinsertTab;
|
|
1076
|
-
/**
|
|
1077
|
-
* Delete spaces to the previous tab stop in a codemirror editor.
|
|
1078
|
-
*/
|
|
1079
|
-
function delSpaceToPrevTabStop(cm) {
|
|
1080
|
-
var _a;
|
|
1081
|
-
const doc = cm.getDoc();
|
|
1082
|
-
// default tabsize is 2, according to codemirror docs: https://codemirror.net/doc/manual.html#config
|
|
1083
|
-
const tabSize = (_a = cm.getOption('indentUnit')) !== null && _a !== void 0 ? _a : 2;
|
|
1084
|
-
const ranges = doc.listSelections(); // handle multicursor
|
|
1085
|
-
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
1086
|
-
// iterate reverse so any deletions don't overlap
|
|
1087
|
-
const head = ranges[i].head;
|
|
1088
|
-
const anchor = ranges[i].anchor;
|
|
1089
|
-
const isSelection = !posEq(head, anchor);
|
|
1090
|
-
if (isSelection) {
|
|
1091
|
-
doc.replaceRange('', anchor, head);
|
|
1092
|
-
}
|
|
1093
|
-
else {
|
|
1094
|
-
const line = doc.getLine(head.line).substring(0, head.ch);
|
|
1095
|
-
if (line.match(/^\ +$/) !== null) {
|
|
1096
|
-
// delete tabs
|
|
1097
|
-
const prevTabStop = (Math.ceil(head.ch / tabSize) - 1) * tabSize;
|
|
1098
|
-
const from = CodeMirror.Pos(head.line, prevTabStop);
|
|
1099
|
-
doc.replaceRange('', from, head);
|
|
1100
|
-
}
|
|
1101
|
-
else {
|
|
1102
|
-
// delete non-tabs
|
|
1103
|
-
const from = cm.findPosH(head, -1, 'char', false);
|
|
1104
|
-
doc.replaceRange('', from, head);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
Private.delSpaceToPrevTabStop = delSpaceToPrevTabStop;
|
|
1110
|
-
/**
|
|
1111
|
-
* Test whether two CodeMirror positions are equal.
|
|
1112
|
-
*/
|
|
1113
|
-
function posEq(a, b) {
|
|
1114
|
-
return a.line === b.line && a.ch === b.ch;
|
|
1115
|
-
}
|
|
1116
|
-
Private.posEq = posEq;
|
|
1117
|
-
/**
|
|
1118
|
-
* Get the list of active gutters
|
|
1119
|
-
*
|
|
1120
|
-
* @param config Editor configuration
|
|
1121
|
-
*/
|
|
1122
|
-
function getActiveGutters(config) {
|
|
1123
|
-
// The order of the classes will be the gutters order
|
|
1124
|
-
const classToSwitch = {
|
|
1125
|
-
'CodeMirror-linenumbers': 'lineNumbers',
|
|
1126
|
-
'CodeMirror-foldgutter': 'codeFolding'
|
|
1127
|
-
};
|
|
1128
|
-
return Object.keys(classToSwitch).filter(gutter => config[classToSwitch[gutter]]);
|
|
1129
|
-
}
|
|
1130
|
-
/**
|
|
1131
|
-
* Set a config option for the editor.
|
|
1132
|
-
*/
|
|
1133
|
-
function setOption(editor, option, value, config) {
|
|
1134
|
-
const el = editor.getWrapperElement();
|
|
1135
|
-
switch (option) {
|
|
1136
|
-
case 'cursorBlinkRate':
|
|
1137
|
-
editor.setOption(option, value);
|
|
1138
|
-
break;
|
|
1139
|
-
case 'lineWrap': {
|
|
1140
|
-
const lineWrapping = value === 'off' ? false : true;
|
|
1141
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1142
|
-
const maxWidth = value === 'bounded' ? `${config.wordWrapColumn}ch` : null;
|
|
1143
|
-
const width = value === 'wordWrapColumn' ? `${config.wordWrapColumn}ch` : null;
|
|
1144
|
-
lines.style.setProperty('max-width', maxWidth);
|
|
1145
|
-
lines.style.setProperty('width', width);
|
|
1146
|
-
editor.setOption('lineWrapping', lineWrapping);
|
|
1147
|
-
break;
|
|
1148
|
-
}
|
|
1149
|
-
case 'wordWrapColumn': {
|
|
1150
|
-
const { lineWrap } = config;
|
|
1151
|
-
if (lineWrap === 'wordWrapColumn' || lineWrap === 'bounded') {
|
|
1152
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1153
|
-
const prop = lineWrap === 'wordWrapColumn' ? 'width' : 'maxWidth';
|
|
1154
|
-
lines.style[prop] = `${value}ch`;
|
|
1155
|
-
}
|
|
1156
|
-
break;
|
|
1157
|
-
}
|
|
1158
|
-
case 'tabSize':
|
|
1159
|
-
editor.setOption('indentUnit', value);
|
|
1160
|
-
break;
|
|
1161
|
-
case 'insertSpaces':
|
|
1162
|
-
editor.setOption('indentWithTabs', !value);
|
|
1163
|
-
break;
|
|
1164
|
-
case 'autoClosingBrackets':
|
|
1165
|
-
editor.setOption('autoCloseBrackets', value);
|
|
1166
|
-
break;
|
|
1167
|
-
case 'rulers': {
|
|
1168
|
-
const rulers = value;
|
|
1169
|
-
editor.setOption('rulers', rulers.map(column => {
|
|
1170
|
-
return {
|
|
1171
|
-
column,
|
|
1172
|
-
className: 'jp-CodeMirror-ruler'
|
|
1173
|
-
};
|
|
1174
|
-
}));
|
|
1175
|
-
break;
|
|
1176
|
-
}
|
|
1177
|
-
case 'readOnly':
|
|
1178
|
-
el.classList.toggle(READ_ONLY_CLASS, value);
|
|
1179
|
-
editor.setOption(option, value);
|
|
1180
|
-
break;
|
|
1181
|
-
case 'fontFamily':
|
|
1182
|
-
el.style.fontFamily = value;
|
|
1183
|
-
break;
|
|
1184
|
-
case 'fontSize':
|
|
1185
|
-
el.style.setProperty('font-size', value ? value + 'px' : null);
|
|
1186
|
-
break;
|
|
1187
|
-
case 'lineHeight':
|
|
1188
|
-
el.style.lineHeight = (value ? value.toString() : null);
|
|
1189
|
-
break;
|
|
1190
|
-
case 'gutters':
|
|
1191
|
-
editor.setOption(option, getActiveGutters(config));
|
|
1192
|
-
break;
|
|
1193
|
-
case 'lineNumbers':
|
|
1194
|
-
editor.setOption(option, value);
|
|
1195
|
-
editor.setOption('gutters', getActiveGutters(config));
|
|
1196
|
-
break;
|
|
1197
|
-
case 'codeFolding':
|
|
1198
|
-
editor.setOption('foldGutter', value);
|
|
1199
|
-
editor.setOption('gutters', getActiveGutters(config));
|
|
1200
|
-
break;
|
|
1201
|
-
case 'showTrailingSpace':
|
|
1202
|
-
editor.setOption(option, value);
|
|
1203
|
-
break;
|
|
1204
|
-
default:
|
|
1205
|
-
editor.setOption(option, value);
|
|
1206
|
-
break;
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
Private.setOption = setOption;
|
|
1210
620
|
})(Private || (Private = {}));
|
|
1211
|
-
/**
|
|
1212
|
-
* Add a CodeMirror command to delete until previous non blanking space
|
|
1213
|
-
* character or first multiple of tabsize tabstop.
|
|
1214
|
-
*/
|
|
1215
|
-
CodeMirrorEditor.addCommand('delSpaceToPrevTabStop', Private.delSpaceToPrevTabStop);
|
|
1216
|
-
/**
|
|
1217
|
-
* Add a CodeMirror command to indent or insert a tab as appropriate.
|
|
1218
|
-
*/
|
|
1219
|
-
CodeMirrorEditor.addCommand('indentMoreOrinsertTab', Private.indentMoreOrinsertTab);
|
|
1220
621
|
//# sourceMappingURL=editor.js.map
|