@jupyterlab/codemirror 4.0.0-alpha.1 → 4.0.0-alpha.12
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-ipython.d.ts +1 -2
- package/lib/codemirror-ipython.js +7 -25
- package/lib/codemirror-ipython.js.map +1 -1
- package/lib/codemirror-ipythongfm.d.ts +10 -3
- package/lib/codemirror-ipythongfm.js +60 -30
- package/lib/codemirror-ipythongfm.js.map +1 -1
- package/lib/editor.d.ts +51 -206
- package/lib/editor.js +385 -625
- package/lib/editor.js.map +1 -1
- package/lib/editorconfiguration.d.ts +149 -0
- package/lib/editorconfiguration.js +286 -0
- package/lib/editorconfiguration.js.map +1 -0
- package/lib/editortheme.d.ts +41 -0
- package/lib/editortheme.js +167 -0
- package/lib/editortheme.js.map +1 -0
- package/lib/factory.js +22 -19
- package/lib/factory.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/mimetype.js +9 -3
- package/lib/mimetype.js.map +1 -1
- package/lib/mode.d.ts +61 -54
- package/lib/mode.js +1472 -105
- package/lib/mode.js.map +1 -1
- package/lib/searchprovider.d.ts +181 -0
- package/lib/searchprovider.js +648 -0
- package/lib/searchprovider.js.map +1 -0
- package/lib/syntaxstatus.js +9 -3
- package/lib/syntaxstatus.js.map +1 -1
- package/package.json +47 -30
- package/style/base.css +14 -154
- package/style/index.css +2 -4
- package/style/index.js +2 -4
- package/lib/tokens.d.ts +0 -18
- package/lib/tokens.js +0 -9
- package/lib/tokens.js.map +0 -1
- package/typings/codemirror/codemirror.d.ts +0 -227
package/lib/editor.js
CHANGED
|
@@ -3,51 +3,23 @@
|
|
|
3
3
|
// Distributed under the terms of the Modified BSD License.
|
|
4
4
|
// / <reference types="codemirror"/>
|
|
5
5
|
// / <reference types="codemirror/searchcursor"/>
|
|
6
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
7
|
-
var t = {};
|
|
8
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
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
6
|
import { showDialog } from '@jupyterlab/apputils';
|
|
18
7
|
import { CodeEditor } from '@jupyterlab/codeeditor';
|
|
19
8
|
import { nullTranslator } from '@jupyterlab/translation';
|
|
20
9
|
import { ArrayExt } from '@lumino/algorithm';
|
|
21
|
-
import {
|
|
10
|
+
import { UUID } from '@lumino/coreutils';
|
|
22
11
|
import { DisposableDelegate } from '@lumino/disposable';
|
|
23
12
|
import { Poll } from '@lumino/polling';
|
|
24
13
|
import { Signal } from '@lumino/signaling';
|
|
25
|
-
import
|
|
26
|
-
import 'codemirror/
|
|
27
|
-
import 'codemirror/
|
|
28
|
-
import 'codemirror/
|
|
29
|
-
import 'codemirror
|
|
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';
|
|
14
|
+
import { indentMore, insertNewlineAndIndent, insertTab } from '@codemirror/commands';
|
|
15
|
+
import { ensureSyntaxTree } from '@codemirror/language';
|
|
16
|
+
import { EditorSelection, EditorState, Prec, StateEffect, StateField } from '@codemirror/state';
|
|
17
|
+
import { Decoration, EditorView, WidgetType } from '@codemirror/view';
|
|
18
|
+
import { yCollab } from 'y-codemirror.next';
|
|
49
19
|
import { Mode } from './mode';
|
|
50
|
-
|
|
20
|
+
import { Configuration } from './editorconfiguration';
|
|
21
|
+
import './codemirror-ipython';
|
|
22
|
+
import './codemirror-ipythongfm';
|
|
51
23
|
/**
|
|
52
24
|
* The class name added to CodeMirrorWidget instances.
|
|
53
25
|
*/
|
|
@@ -76,9 +48,6 @@ const DOWN_ARROW = 40;
|
|
|
76
48
|
* The time that a collaborator name hover persists.
|
|
77
49
|
*/
|
|
78
50
|
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
51
|
/**
|
|
83
52
|
* CodeMirror editor.
|
|
84
53
|
*/
|
|
@@ -87,18 +56,16 @@ export class CodeMirrorEditor {
|
|
|
87
56
|
* Construct a CodeMirror editor.
|
|
88
57
|
*/
|
|
89
58
|
constructor(options) {
|
|
90
|
-
var _a;
|
|
91
59
|
/**
|
|
92
60
|
* A signal emitted when either the top or bottom edge is requested.
|
|
93
61
|
*/
|
|
94
62
|
this.edgeRequested = new Signal(this);
|
|
95
|
-
this.
|
|
63
|
+
this._selectionMarkers = {};
|
|
96
64
|
this._keydownHandlers = new Array();
|
|
97
|
-
this._changeGuard = false;
|
|
98
65
|
this._uuid = '';
|
|
99
|
-
this._needsRefresh = false;
|
|
100
66
|
this._isDisposed = false;
|
|
101
67
|
this._lastChange = null;
|
|
68
|
+
this._editorConfig = new Configuration.EditorConfiguration();
|
|
102
69
|
const host = (this.host = options.host);
|
|
103
70
|
this.translator = options.translator || nullTranslator;
|
|
104
71
|
this._trans = this.translator.load('jupyterlab');
|
|
@@ -108,21 +75,110 @@ export class CodeMirrorEditor {
|
|
|
108
75
|
host.addEventListener('blur', this, true);
|
|
109
76
|
host.addEventListener('scroll', this, true);
|
|
110
77
|
this._uuid = options.uuid || UUID.uuid4();
|
|
78
|
+
// State and effects for handling the selection marks
|
|
79
|
+
this._addMark = StateEffect.define();
|
|
80
|
+
this._removeMark = StateEffect.define();
|
|
81
|
+
this._markField = StateField.define({
|
|
82
|
+
create: () => {
|
|
83
|
+
return Decoration.none;
|
|
84
|
+
},
|
|
85
|
+
update: (marks, transaction) => {
|
|
86
|
+
marks = marks.map(transaction.changes);
|
|
87
|
+
for (let ef of transaction.effects) {
|
|
88
|
+
if (ef.is(this._addMark)) {
|
|
89
|
+
let e = ef;
|
|
90
|
+
const decorations = this._buildMarkDecoration(e.value.uuid, e.value.selections);
|
|
91
|
+
marks = marks.update({ add: decorations });
|
|
92
|
+
this._selectionMarkers[e.value.uuid] = decorations;
|
|
93
|
+
}
|
|
94
|
+
else if (ef.is(this._removeMark)) {
|
|
95
|
+
let e = ef;
|
|
96
|
+
for (let rd of ef.value.decorations) {
|
|
97
|
+
marks = marks.update({
|
|
98
|
+
filter: (from, to, value) => {
|
|
99
|
+
return !(from === rd.from &&
|
|
100
|
+
to === rd.to &&
|
|
101
|
+
value === rd.value);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
delete this._selectionMarkers[e.value.uuid];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return marks;
|
|
109
|
+
},
|
|
110
|
+
provide: f => EditorView.decorations.from(f)
|
|
111
|
+
});
|
|
111
112
|
// Handle selection style.
|
|
112
113
|
const style = options.selectionStyle || {};
|
|
113
114
|
this._selectionStyle = Object.assign(Object.assign({}, CodeEditor.defaultSelectionStyle), style);
|
|
114
115
|
const model = (this._model = options.model);
|
|
115
116
|
const config = options.config || {};
|
|
116
117
|
const fullConfig = (this._config = Object.assign(Object.assign({}, CodeMirrorEditor.defaultConfig), config));
|
|
117
|
-
const editor = (this._editor = Private.createEditor(host, fullConfig));
|
|
118
118
|
this._initializeEditorBinding();
|
|
119
|
+
// Extension for handling DOM events
|
|
120
|
+
const domEventHandlers = EditorView.domEventHandlers({
|
|
121
|
+
keydown: (event, view) => {
|
|
122
|
+
const index = ArrayExt.findFirstIndex(this._keydownHandlers, handler => {
|
|
123
|
+
if (handler(this, event) === true) {
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
});
|
|
129
|
+
if (index === -1) {
|
|
130
|
+
return this.onKeydown(event);
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
const updateListener = EditorView.updateListener.of((update) => {
|
|
136
|
+
this._onDocChanged(update);
|
|
137
|
+
});
|
|
138
|
+
// The list of internal strings is available at https://codemirror.net/examples/translate/
|
|
139
|
+
const translation = EditorState.phrases.of({
|
|
140
|
+
// @codemirror/view
|
|
141
|
+
'Control character': this._trans.__('Control character'),
|
|
142
|
+
// @codemirror/commands
|
|
143
|
+
'Selection deleted': this._trans.__('Selection deleted'),
|
|
144
|
+
// @codemirror/language
|
|
145
|
+
'Folded lines': this._trans.__('Folded lines'),
|
|
146
|
+
'Unfolded lines': this._trans.__('Unfolded lines'),
|
|
147
|
+
to: this._trans.__('to'),
|
|
148
|
+
'folded code': this._trans.__('folded code'),
|
|
149
|
+
unfold: this._trans.__('unfold'),
|
|
150
|
+
'Fold line': this._trans.__('Fold line'),
|
|
151
|
+
'Unfold line': this._trans.__('Unfold line'),
|
|
152
|
+
// @codemirror/search
|
|
153
|
+
'Go to line': this._trans.__('Go to line'),
|
|
154
|
+
go: this._trans.__('go'),
|
|
155
|
+
Find: this._trans.__('Find'),
|
|
156
|
+
Replace: this._trans.__('Replace'),
|
|
157
|
+
next: this._trans.__('next'),
|
|
158
|
+
previous: this._trans.__('previous'),
|
|
159
|
+
all: this._trans.__('all'),
|
|
160
|
+
'match case': this._trans.__('match case'),
|
|
161
|
+
replace: this._trans.__('replace'),
|
|
162
|
+
'replace all': this._trans.__('replace all'),
|
|
163
|
+
close: this._trans.__('close'),
|
|
164
|
+
'current match': this._trans.__('current match'),
|
|
165
|
+
'replaced $ matches': this._trans.__('replaced $ matches'),
|
|
166
|
+
'replaced match on line $': this._trans.__('replaced match on line $'),
|
|
167
|
+
'on line': this._trans.__('on line'),
|
|
168
|
+
// @codemirror/autocomplete
|
|
169
|
+
Completions: this._trans.__('Completions'),
|
|
170
|
+
// @codemirror/lint
|
|
171
|
+
Diagnostics: this._trans.__('Diagnostics'),
|
|
172
|
+
'No diagnostics': this._trans.__('No diagnostics')
|
|
173
|
+
});
|
|
174
|
+
this._editor = Private.createEditor(host, fullConfig, this._yeditorBinding, this._editorConfig, [
|
|
175
|
+
this._markField,
|
|
176
|
+
Prec.high(domEventHandlers),
|
|
177
|
+
updateListener,
|
|
178
|
+
translation
|
|
179
|
+
]);
|
|
119
180
|
// every time the model is switched, we need to re-initialize the editor binding
|
|
120
181
|
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
182
|
this._onMimeTypeChanged();
|
|
127
183
|
this._onCursorActivity();
|
|
128
184
|
this._poll = new Poll({
|
|
@@ -135,71 +191,19 @@ export class CodeMirrorEditor {
|
|
|
135
191
|
return this._lastChange ? 'when-hidden' : true;
|
|
136
192
|
}
|
|
137
193
|
});
|
|
138
|
-
// Connect to changes.
|
|
139
|
-
if (!USE_YCODEMIRROR_BINDING) {
|
|
140
|
-
model.value.changed.connect(this._onValueChanged, this);
|
|
141
|
-
}
|
|
142
194
|
model.mimeTypeChanged.connect(this._onMimeTypeChanged, this);
|
|
143
195
|
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
|
-
}
|
|
180
|
-
});
|
|
181
|
-
// Manually refresh on paste to make sure editor is properly sized.
|
|
182
|
-
editor.getWrapperElement().addEventListener('paste', () => {
|
|
183
|
-
if (this.hasFocus()) {
|
|
184
|
-
this.refresh();
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
196
|
}
|
|
188
197
|
/**
|
|
189
198
|
* Initialize the editor binding.
|
|
190
199
|
*/
|
|
191
200
|
_initializeEditorBinding() {
|
|
192
|
-
var _a;
|
|
193
|
-
if (!USE_YCODEMIRROR_BINDING) {
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
(_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
197
201
|
const sharedModel = this.model.sharedModel;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
:
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
this._yeditorBinding = {
|
|
203
|
+
text: sharedModel.ysource,
|
|
204
|
+
awareness: sharedModel.awareness,
|
|
205
|
+
undoManager: sharedModel.undoManager
|
|
206
|
+
};
|
|
203
207
|
}
|
|
204
208
|
/**
|
|
205
209
|
* The uuid of this editor;
|
|
@@ -229,13 +233,13 @@ export class CodeMirrorEditor {
|
|
|
229
233
|
* Get the codemirror doc wrapped by the widget.
|
|
230
234
|
*/
|
|
231
235
|
get doc() {
|
|
232
|
-
return this._editor.
|
|
236
|
+
return this._editor.state.doc;
|
|
233
237
|
}
|
|
234
238
|
/**
|
|
235
239
|
* Get the number of lines in the editor.
|
|
236
240
|
*/
|
|
237
241
|
get lineCount() {
|
|
238
|
-
return this.doc.
|
|
242
|
+
return this.doc.lines;
|
|
239
243
|
}
|
|
240
244
|
/**
|
|
241
245
|
* Returns a model for this editor.
|
|
@@ -247,13 +251,13 @@ export class CodeMirrorEditor {
|
|
|
247
251
|
* The height of a line in the editor in pixels.
|
|
248
252
|
*/
|
|
249
253
|
get lineHeight() {
|
|
250
|
-
return this._editor.
|
|
254
|
+
return this._editor.defaultLineHeight;
|
|
251
255
|
}
|
|
252
256
|
/**
|
|
253
257
|
* The widget of a character in the editor in pixels.
|
|
254
258
|
*/
|
|
255
259
|
get charWidth() {
|
|
256
|
-
return this._editor.
|
|
260
|
+
return this._editor.defaultCharacterWidth;
|
|
257
261
|
}
|
|
258
262
|
/**
|
|
259
263
|
* Tests whether the editor is disposed.
|
|
@@ -272,12 +276,10 @@ export class CodeMirrorEditor {
|
|
|
272
276
|
this.host.removeEventListener('focus', this, true);
|
|
273
277
|
this.host.removeEventListener('blur', this, true);
|
|
274
278
|
this.host.removeEventListener('scroll', this, true);
|
|
275
|
-
if (this._yeditorBinding) {
|
|
276
|
-
this._yeditorBinding.destroy();
|
|
277
|
-
}
|
|
278
279
|
this._keydownHandlers.length = 0;
|
|
279
280
|
this._poll.dispose();
|
|
280
281
|
Signal.clearData(this);
|
|
282
|
+
this.editor.destroy();
|
|
281
283
|
}
|
|
282
284
|
/**
|
|
283
285
|
* Get a config option for the editor.
|
|
@@ -292,7 +294,7 @@ export class CodeMirrorEditor {
|
|
|
292
294
|
// Don't bother setting the option if it is already the same.
|
|
293
295
|
if (this._config[option] !== value) {
|
|
294
296
|
this._config[option] = value;
|
|
295
|
-
|
|
297
|
+
this._editorConfig.reconfigureExtension(this._editor, option, value);
|
|
296
298
|
}
|
|
297
299
|
}
|
|
298
300
|
/**
|
|
@@ -304,36 +306,34 @@ export class CodeMirrorEditor {
|
|
|
304
306
|
* is set.
|
|
305
307
|
*/
|
|
306
308
|
setOptions(options) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
editor.endOperation();
|
|
309
|
+
this._config = Object.assign(Object.assign({}, this._config), options);
|
|
310
|
+
this._editorConfig.reconfigureExtensions(this._editor, options);
|
|
311
|
+
}
|
|
312
|
+
injectExtension(ext) {
|
|
313
|
+
this._editorConfig.injectExtension(this._editor, ext);
|
|
315
314
|
}
|
|
316
315
|
/**
|
|
317
316
|
* Returns the content for the given line number.
|
|
318
317
|
*/
|
|
319
318
|
getLine(line) {
|
|
320
|
-
|
|
319
|
+
// TODO: CM6 remove +1 when CM6 first line number has propagated
|
|
320
|
+
line = line + 1;
|
|
321
|
+
return line <= this.doc.lines ? this.doc.line(line).text : undefined;
|
|
321
322
|
}
|
|
322
323
|
/**
|
|
323
324
|
* Find an offset for the given position.
|
|
324
325
|
*/
|
|
325
326
|
getOffsetAt(position) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
line: position.line
|
|
329
|
-
});
|
|
327
|
+
// TODO: CM6 remove +1 when CM6 first line number has propagated
|
|
328
|
+
return this.doc.line(position.line + 1).from + position.column;
|
|
330
329
|
}
|
|
331
330
|
/**
|
|
332
331
|
* Find a position for the given offset.
|
|
333
332
|
*/
|
|
334
333
|
getPositionAt(offset) {
|
|
335
|
-
|
|
336
|
-
|
|
334
|
+
// TODO: CM6 remove -1 when CM6 first line number has propagated
|
|
335
|
+
const line = this.doc.lineAt(offset);
|
|
336
|
+
return { line: line.number - 1, column: offset - line.from };
|
|
337
337
|
}
|
|
338
338
|
/**
|
|
339
339
|
* Undo one edit (if any undo events are stored).
|
|
@@ -352,7 +352,7 @@ export class CodeMirrorEditor {
|
|
|
352
352
|
*/
|
|
353
353
|
clearHistory() {
|
|
354
354
|
var _a, _b;
|
|
355
|
-
(_b = (_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.
|
|
355
|
+
(_b = (_a = this._yeditorBinding) === null || _a === void 0 ? void 0 : _a.undoManager) === null || _b === void 0 ? void 0 : _b.clear();
|
|
356
356
|
}
|
|
357
357
|
/**
|
|
358
358
|
* Brings browser focus to this editor text.
|
|
@@ -364,78 +364,41 @@ export class CodeMirrorEditor {
|
|
|
364
364
|
* Test whether the editor has keyboard focus.
|
|
365
365
|
*/
|
|
366
366
|
hasFocus() {
|
|
367
|
-
return this._editor.
|
|
367
|
+
return this._editor.hasFocus;
|
|
368
368
|
}
|
|
369
369
|
/**
|
|
370
370
|
* Explicitly blur the editor.
|
|
371
371
|
*/
|
|
372
372
|
blur() {
|
|
373
|
-
this._editor.
|
|
374
|
-
}
|
|
375
|
-
/**
|
|
376
|
-
* Repaint editor.
|
|
377
|
-
*/
|
|
378
|
-
refresh() {
|
|
379
|
-
this._editor.refresh();
|
|
380
|
-
this._needsRefresh = false;
|
|
373
|
+
this._editor.contentDOM.blur();
|
|
381
374
|
}
|
|
382
375
|
/**
|
|
383
376
|
* Refresh the editor if it is focused;
|
|
384
377
|
* otherwise postpone refreshing till focusing.
|
|
385
378
|
*/
|
|
386
379
|
resizeToFit() {
|
|
387
|
-
if (this.hasFocus()) {
|
|
388
|
-
this.refresh();
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
this._needsRefresh = true;
|
|
392
|
-
}
|
|
393
380
|
this._clearHover();
|
|
394
381
|
}
|
|
395
|
-
// todo: docs, maybe define overlay options as a type?
|
|
396
|
-
addOverlay(mode, options) {
|
|
397
|
-
this._editor.addOverlay(mode, options);
|
|
398
|
-
}
|
|
399
|
-
removeOverlay(mode) {
|
|
400
|
-
this._editor.removeOverlay(mode);
|
|
401
|
-
}
|
|
402
|
-
getSearchCursor(query, start, caseFold) {
|
|
403
|
-
return this._editor.getDoc().getSearchCursor(query, start, caseFold);
|
|
404
|
-
}
|
|
405
|
-
getCursor(start) {
|
|
406
|
-
return this._editor.getDoc().getCursor(start);
|
|
407
|
-
}
|
|
408
382
|
get state() {
|
|
409
383
|
return this._editor.state;
|
|
410
384
|
}
|
|
411
|
-
operation(fn) {
|
|
412
|
-
return this._editor.operation(fn);
|
|
413
|
-
}
|
|
414
385
|
firstLine() {
|
|
415
|
-
return
|
|
386
|
+
// TODO: return 1 when CM6 first line number has propagated
|
|
387
|
+
return 0;
|
|
416
388
|
}
|
|
417
389
|
lastLine() {
|
|
418
|
-
return this.
|
|
419
|
-
}
|
|
420
|
-
scrollIntoView(pos, margin) {
|
|
421
|
-
this._editor.scrollIntoView(pos, margin);
|
|
422
|
-
}
|
|
423
|
-
scrollIntoViewCentered(pos) {
|
|
424
|
-
var _a, _b;
|
|
425
|
-
const top = this._editor.charCoords(pos, 'local').top;
|
|
426
|
-
const height = this._editor.getWrapperElement().offsetHeight;
|
|
427
|
-
(_b = (_a = this.host).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
428
|
-
behavior: 'auto',
|
|
429
|
-
block: 'center',
|
|
430
|
-
inline: 'center'
|
|
431
|
-
});
|
|
432
|
-
this._editor.scrollTo(null, top - height / 2);
|
|
390
|
+
return this.doc.lines - 1;
|
|
433
391
|
}
|
|
434
392
|
cursorCoords(where, mode) {
|
|
435
|
-
|
|
393
|
+
const selection = this.state.selection.main;
|
|
394
|
+
const pos = where ? selection.from : selection.to;
|
|
395
|
+
const rect = this.editor.coordsAtPos(pos);
|
|
396
|
+
return rect;
|
|
436
397
|
}
|
|
437
398
|
getRange(from, to, separator) {
|
|
438
|
-
|
|
399
|
+
const fromOffset = this.getOffsetAt(this._toPosition(from));
|
|
400
|
+
const toOffset = this.getOffsetAt(this._toPosition(to));
|
|
401
|
+
return this.state.sliceDoc(fromOffset, toOffset);
|
|
439
402
|
}
|
|
440
403
|
/**
|
|
441
404
|
* Add a keydown handler to the editor.
|
|
@@ -450,41 +413,31 @@ export class CodeMirrorEditor {
|
|
|
450
413
|
ArrayExt.removeAllWhere(this._keydownHandlers, val => val === handler);
|
|
451
414
|
});
|
|
452
415
|
}
|
|
453
|
-
/**
|
|
454
|
-
* Set the size of the editor in pixels.
|
|
455
|
-
*/
|
|
456
|
-
setSize(dimension) {
|
|
457
|
-
if (dimension) {
|
|
458
|
-
this._editor.setSize(dimension.width, dimension.height);
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
this._editor.setSize(null, null);
|
|
462
|
-
}
|
|
463
|
-
this._needsRefresh = false;
|
|
464
|
-
}
|
|
465
416
|
/**
|
|
466
417
|
* Reveal the given position in the editor.
|
|
467
418
|
*/
|
|
468
419
|
revealPosition(position) {
|
|
469
|
-
const
|
|
470
|
-
this._editor.
|
|
420
|
+
const offset = this.getOffsetAt(position);
|
|
421
|
+
this._editor.dispatch({
|
|
422
|
+
effects: EditorView.scrollIntoView(offset)
|
|
423
|
+
});
|
|
471
424
|
}
|
|
472
425
|
/**
|
|
473
426
|
* Reveal the given selection in the editor.
|
|
474
427
|
*/
|
|
475
428
|
revealSelection(selection) {
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
429
|
+
const start = this.getOffsetAt(selection.start);
|
|
430
|
+
const end = this.getOffsetAt(selection.end);
|
|
431
|
+
this._editor.dispatch({
|
|
432
|
+
effects: EditorView.scrollIntoView(EditorSelection.range(start, end))
|
|
433
|
+
});
|
|
481
434
|
}
|
|
482
435
|
/**
|
|
483
436
|
* Get the window coordinates given a cursor position.
|
|
484
437
|
*/
|
|
485
438
|
getCoordinateForPosition(position) {
|
|
486
|
-
const
|
|
487
|
-
const rect = this.editor.
|
|
439
|
+
const offset = this.getOffsetAt(position);
|
|
440
|
+
const rect = this.editor.coordsAtPos(offset);
|
|
488
441
|
return rect;
|
|
489
442
|
}
|
|
490
443
|
/**
|
|
@@ -496,14 +449,18 @@ export class CodeMirrorEditor {
|
|
|
496
449
|
* contained in the editor.
|
|
497
450
|
*/
|
|
498
451
|
getPositionForCoordinate(coordinate) {
|
|
499
|
-
|
|
452
|
+
const offset = this.editor.posAtCoords({
|
|
453
|
+
x: coordinate.left,
|
|
454
|
+
y: coordinate.top
|
|
455
|
+
});
|
|
456
|
+
return this.getPositionAt(offset) || null;
|
|
500
457
|
}
|
|
501
458
|
/**
|
|
502
459
|
* Returns the primary position of the cursor, never `null`.
|
|
503
460
|
*/
|
|
504
461
|
getCursorPosition() {
|
|
505
|
-
const
|
|
506
|
-
return this.
|
|
462
|
+
const offset = this.state.selection.main.head;
|
|
463
|
+
return this.getPositionAt(offset);
|
|
507
464
|
}
|
|
508
465
|
/**
|
|
509
466
|
* Set the primary position of the cursor.
|
|
@@ -512,12 +469,15 @@ export class CodeMirrorEditor {
|
|
|
512
469
|
* This will remove any secondary cursors.
|
|
513
470
|
*/
|
|
514
471
|
setCursorPosition(position, options) {
|
|
515
|
-
const
|
|
516
|
-
this.
|
|
472
|
+
const offset = this.getOffsetAt(position);
|
|
473
|
+
this.editor.dispatch({
|
|
474
|
+
selection: { anchor: offset },
|
|
475
|
+
scrollIntoView: true
|
|
476
|
+
});
|
|
517
477
|
// If the editor does not have focus, this cursor change
|
|
518
478
|
// will get screened out in _onCursorsChanged(). Make an
|
|
519
479
|
// exception for this method.
|
|
520
|
-
if (!this.editor.hasFocus
|
|
480
|
+
if (!this.editor.hasFocus) {
|
|
521
481
|
this.model.selections.set(this.uuid, this.getSelections());
|
|
522
482
|
}
|
|
523
483
|
}
|
|
@@ -537,11 +497,15 @@ export class CodeMirrorEditor {
|
|
|
537
497
|
* Gets the selections for all the cursors, never `null` or empty.
|
|
538
498
|
*/
|
|
539
499
|
getSelections() {
|
|
540
|
-
const selections = this.
|
|
500
|
+
const selections = this.state.selection.ranges; //= [{anchor: number, head: number}]
|
|
541
501
|
if (selections.length > 0) {
|
|
542
|
-
|
|
502
|
+
const sel = selections.map(r => ({
|
|
503
|
+
anchor: this._toCodeMirrorPosition(this.getPositionAt(r.from)),
|
|
504
|
+
head: this._toCodeMirrorPosition(this.getPositionAt(r.to))
|
|
505
|
+
}));
|
|
506
|
+
return sel.map(selection => this._toSelection(selection));
|
|
543
507
|
}
|
|
544
|
-
const cursor = this.
|
|
508
|
+
const cursor = this._toCodeMirrorPosition(this.getPositionAt(this.state.selection.main.head));
|
|
545
509
|
const selection = this._toSelection({ anchor: cursor, head: cursor });
|
|
546
510
|
return [selection];
|
|
547
511
|
}
|
|
@@ -551,8 +515,10 @@ export class CodeMirrorEditor {
|
|
|
551
515
|
* Passing an empty array resets a cursor position to the start of a document.
|
|
552
516
|
*/
|
|
553
517
|
setSelections(selections) {
|
|
554
|
-
const
|
|
555
|
-
|
|
518
|
+
const sel = selections.length
|
|
519
|
+
? selections.map(r => EditorSelection.range(this.getOffsetAt(r.start), this.getOffsetAt(r.end)))
|
|
520
|
+
: [EditorSelection.range(0, 0)];
|
|
521
|
+
this.editor.dispatch({ selection: EditorSelection.create(sel) });
|
|
556
522
|
}
|
|
557
523
|
/**
|
|
558
524
|
* Replaces the current selection with the given text.
|
|
@@ -560,41 +526,62 @@ export class CodeMirrorEditor {
|
|
|
560
526
|
* @param text The text to be inserted.
|
|
561
527
|
*/
|
|
562
528
|
replaceSelection(text) {
|
|
563
|
-
this.
|
|
529
|
+
this.editor.dispatch(this.state.replaceSelection(text));
|
|
564
530
|
}
|
|
565
531
|
/**
|
|
566
532
|
* Get a list of tokens for the current editor text content.
|
|
567
533
|
*/
|
|
568
534
|
getTokens() {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
535
|
+
const tokens = [];
|
|
536
|
+
const tree = ensureSyntaxTree(this.state, this.doc.length);
|
|
537
|
+
if (tree) {
|
|
538
|
+
tree.iterate({
|
|
539
|
+
enter: (node) => {
|
|
540
|
+
tokens.push({
|
|
541
|
+
value: this.state.sliceDoc(node.from, node.to),
|
|
542
|
+
offset: node.from,
|
|
543
|
+
type: node.name
|
|
544
|
+
});
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
});
|
|
577
548
|
}
|
|
578
549
|
return tokens;
|
|
579
550
|
}
|
|
580
551
|
/**
|
|
581
552
|
* Get the token at a given editor position.
|
|
582
553
|
*/
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
554
|
+
getTokenAt(offset) {
|
|
555
|
+
const tree = ensureSyntaxTree(this.state, offset);
|
|
556
|
+
if (tree) {
|
|
557
|
+
const node = tree.resolveInner(offset);
|
|
558
|
+
return {
|
|
559
|
+
value: this.state.sliceDoc(node.from, node.to),
|
|
560
|
+
offset: node.from,
|
|
561
|
+
type: node.name
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
return {
|
|
566
|
+
value: '',
|
|
567
|
+
offset: offset
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Get the token a the cursor position.
|
|
573
|
+
*/
|
|
574
|
+
getTokenAtCursor() {
|
|
575
|
+
return this.getTokenAt(this.state.selection.main.head);
|
|
592
576
|
}
|
|
593
577
|
/**
|
|
594
578
|
* Insert a new indented line at the current cursor position.
|
|
595
579
|
*/
|
|
596
580
|
newIndentedLine() {
|
|
597
|
-
|
|
581
|
+
insertNewlineAndIndent({
|
|
582
|
+
state: this.state,
|
|
583
|
+
dispatch: this.editor.dispatch
|
|
584
|
+
});
|
|
598
585
|
}
|
|
599
586
|
/**
|
|
600
587
|
* Execute a codemirror command on the editor.
|
|
@@ -602,31 +589,28 @@ export class CodeMirrorEditor {
|
|
|
602
589
|
* @param command - The name of the command to execute.
|
|
603
590
|
*/
|
|
604
591
|
execCommand(command) {
|
|
605
|
-
this.
|
|
592
|
+
command(this.editor);
|
|
606
593
|
}
|
|
607
594
|
/**
|
|
608
595
|
* Handle keydown events from the editor.
|
|
609
596
|
*/
|
|
610
597
|
onKeydown(event) {
|
|
611
|
-
const position = this.
|
|
612
|
-
|
|
613
|
-
if (line === 0 && column === 0 && event.keyCode === UP_ARROW) {
|
|
598
|
+
const position = this.state.selection.main.head;
|
|
599
|
+
if (position === 0 && event.keyCode === UP_ARROW) {
|
|
614
600
|
if (!event.shiftKey) {
|
|
615
601
|
this.edgeRequested.emit('top');
|
|
616
602
|
}
|
|
617
603
|
return false;
|
|
618
604
|
}
|
|
619
|
-
|
|
605
|
+
const line = this.doc.lineAt(position).number;
|
|
606
|
+
if (line === 1 && event.keyCode === UP_ARROW) {
|
|
620
607
|
if (!event.shiftKey) {
|
|
621
608
|
this.edgeRequested.emit('topLine');
|
|
622
609
|
}
|
|
623
610
|
return false;
|
|
624
611
|
}
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
if (line === lastLine &&
|
|
628
|
-
column === lastCh &&
|
|
629
|
-
event.keyCode === DOWN_ARROW) {
|
|
612
|
+
const length = this.doc.length;
|
|
613
|
+
if (position === length && event.keyCode === DOWN_ARROW) {
|
|
630
614
|
if (!event.shiftKey) {
|
|
631
615
|
this.edgeRequested.emit('bottom');
|
|
632
616
|
}
|
|
@@ -634,36 +618,16 @@ export class CodeMirrorEditor {
|
|
|
634
618
|
}
|
|
635
619
|
return false;
|
|
636
620
|
}
|
|
637
|
-
/**
|
|
638
|
-
* Converts selections to code mirror selections.
|
|
639
|
-
*/
|
|
640
|
-
_toCodeMirrorSelections(selections) {
|
|
641
|
-
if (selections.length > 0) {
|
|
642
|
-
return selections.map(selection => this._toCodeMirrorSelection(selection));
|
|
643
|
-
}
|
|
644
|
-
const position = { line: 0, ch: 0 };
|
|
645
|
-
return [{ anchor: position, head: position }];
|
|
646
|
-
}
|
|
647
621
|
/**
|
|
648
622
|
* Handles a mime type change.
|
|
649
623
|
*/
|
|
650
624
|
_onMimeTypeChanged() {
|
|
651
625
|
const mime = this._model.mimeType;
|
|
652
|
-
const editor = this._editor;
|
|
653
|
-
const extraKeys = (editor.getOption('extraKeys') ||
|
|
654
|
-
{});
|
|
655
|
-
const isCode = mime !== 'text/plain' && mime !== 'text/x-ipythongfm';
|
|
656
|
-
if (isCode) {
|
|
657
|
-
extraKeys['Backspace'] = 'delSpaceToPrevTabStop';
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
delete extraKeys['Backspace'];
|
|
661
|
-
}
|
|
662
|
-
this.setOption('extraKeys', extraKeys);
|
|
663
626
|
// TODO: should we provide a hook for when the mode is done being set?
|
|
664
627
|
void Mode.ensure(mime).then(spec => {
|
|
665
|
-
|
|
666
|
-
|
|
628
|
+
if (spec) {
|
|
629
|
+
this._editorConfig.reconfigureExtension(this._editor, 'language', spec.support);
|
|
630
|
+
}
|
|
667
631
|
});
|
|
668
632
|
}
|
|
669
633
|
/**
|
|
@@ -682,19 +646,15 @@ export class CodeMirrorEditor {
|
|
|
682
646
|
* Clean selections for the given uuid.
|
|
683
647
|
*/
|
|
684
648
|
_cleanSelections(uuid) {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
})
|
|
690
|
-
}
|
|
691
|
-
delete this.selectionMarkers[uuid];
|
|
649
|
+
this.editor.dispatch({
|
|
650
|
+
effects: this._removeMark.of({
|
|
651
|
+
uuid: uuid,
|
|
652
|
+
decorations: this._selectionMarkers[uuid]
|
|
653
|
+
})
|
|
654
|
+
});
|
|
692
655
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
*/
|
|
696
|
-
_markSelections(uuid, selections) {
|
|
697
|
-
const markers = [];
|
|
656
|
+
_buildMarkDecoration(uuid, selections) {
|
|
657
|
+
const decorations = [];
|
|
698
658
|
// If we are marking selections corresponding to an active hover,
|
|
699
659
|
// remove it.
|
|
700
660
|
if (uuid === this._hoverId) {
|
|
@@ -708,34 +668,73 @@ export class CodeMirrorEditor {
|
|
|
708
668
|
}
|
|
709
669
|
// Style each selection for the uuid.
|
|
710
670
|
selections.forEach(selection => {
|
|
671
|
+
const from = selection.from;
|
|
672
|
+
const to = selection.to;
|
|
711
673
|
// Only render selections if the start is not equal to the end.
|
|
712
674
|
// In that case, we don't need to render the cursor.
|
|
713
|
-
if (
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const anchor = this._toCodeMirrorPosition(forward ? selection.start : selection.end);
|
|
721
|
-
const head = this._toCodeMirrorPosition(forward ? selection.end : selection.start);
|
|
722
|
-
let markerOptions;
|
|
723
|
-
if (collaborator) {
|
|
724
|
-
markerOptions = this._toTextMarkerOptions(Object.assign(Object.assign({}, selection.style), { color: collaborator.color }));
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
markerOptions = this._toTextMarkerOptions(selection.style);
|
|
728
|
-
}
|
|
729
|
-
markers.push(this.doc.markText(anchor, head, markerOptions));
|
|
675
|
+
if (from !== to) {
|
|
676
|
+
const style = collaborator
|
|
677
|
+
? Object.assign(Object.assign({}, selection.style), { color: collaborator.color }) : selection.style;
|
|
678
|
+
const decoration = Decoration.mark({
|
|
679
|
+
attributes: this._toMarkSpec(style)
|
|
680
|
+
});
|
|
681
|
+
decorations.push(from > to ? decoration.range(to, from) : decoration.range(to, from));
|
|
730
682
|
}
|
|
731
683
|
else if (collaborator) {
|
|
732
|
-
const caret =
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
684
|
+
const caret = Decoration.widget({
|
|
685
|
+
widget: this._getCaret(collaborator)
|
|
686
|
+
});
|
|
687
|
+
decorations.push(caret.range(from));
|
|
736
688
|
}
|
|
737
689
|
});
|
|
738
|
-
|
|
690
|
+
return decorations;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Converts the selection style to a text marker options.
|
|
694
|
+
*/
|
|
695
|
+
_toMarkSpec(style) {
|
|
696
|
+
const r = parseInt(style.color.slice(1, 3), 16);
|
|
697
|
+
const g = parseInt(style.color.slice(3, 5), 16);
|
|
698
|
+
const b = parseInt(style.color.slice(5, 7), 16);
|
|
699
|
+
const css = `background-color: rgba( ${r}, ${g}, ${b}, 0.15)`;
|
|
700
|
+
return {
|
|
701
|
+
className: style.className,
|
|
702
|
+
title: style.displayName,
|
|
703
|
+
css
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Construct a caret element representing the position
|
|
708
|
+
* of a collaborator's cursor.
|
|
709
|
+
*/
|
|
710
|
+
_getCaret(collaborator) {
|
|
711
|
+
return new Private.CaretWidget(collaborator, {
|
|
712
|
+
setHoverId: (sessionId) => {
|
|
713
|
+
this._clearHover();
|
|
714
|
+
this._hoverId = sessionId;
|
|
715
|
+
},
|
|
716
|
+
setHoverTimeout: () => {
|
|
717
|
+
this._hoverTimeout = window.setTimeout(() => {
|
|
718
|
+
this._clearHover();
|
|
719
|
+
}, HOVER_TIMEOUT);
|
|
720
|
+
},
|
|
721
|
+
clearHoverTimeout: () => {
|
|
722
|
+
window.clearTimeout(this._hoverTimeout);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Marks selections.
|
|
728
|
+
*/
|
|
729
|
+
_markSelections(uuid, selections) {
|
|
730
|
+
const sel = selections.map(selection => ({
|
|
731
|
+
from: this.getOffsetAt(selection.start),
|
|
732
|
+
to: this.getOffsetAt(selection.end),
|
|
733
|
+
style: selection.style
|
|
734
|
+
}));
|
|
735
|
+
this.editor.dispatch({
|
|
736
|
+
effects: this._addMark.of({ uuid: uuid, selections: sel })
|
|
737
|
+
});
|
|
739
738
|
}
|
|
740
739
|
/**
|
|
741
740
|
* Handles a cursor activity event.
|
|
@@ -743,7 +742,7 @@ export class CodeMirrorEditor {
|
|
|
743
742
|
_onCursorActivity() {
|
|
744
743
|
// Only add selections if the editor has focus. This avoids unwanted
|
|
745
744
|
// triggering of cursor activity due to collaborator actions.
|
|
746
|
-
if (this._editor.hasFocus
|
|
745
|
+
if (this._editor.hasFocus) {
|
|
747
746
|
const selections = this.getSelections();
|
|
748
747
|
this.model.selections.set(this.uuid, selections);
|
|
749
748
|
}
|
|
@@ -759,29 +758,6 @@ export class CodeMirrorEditor {
|
|
|
759
758
|
style: this.selectionStyle
|
|
760
759
|
};
|
|
761
760
|
}
|
|
762
|
-
/**
|
|
763
|
-
* Converts the selection style to a text marker options.
|
|
764
|
-
*/
|
|
765
|
-
_toTextMarkerOptions(style) {
|
|
766
|
-
const r = parseInt(style.color.slice(1, 3), 16);
|
|
767
|
-
const g = parseInt(style.color.slice(3, 5), 16);
|
|
768
|
-
const b = parseInt(style.color.slice(5, 7), 16);
|
|
769
|
-
const css = `background-color: rgba( ${r}, ${g}, ${b}, 0.15)`;
|
|
770
|
-
return {
|
|
771
|
-
className: style.className,
|
|
772
|
-
title: style.displayName,
|
|
773
|
-
css
|
|
774
|
-
};
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* Converts an editor selection to a code mirror selection.
|
|
778
|
-
*/
|
|
779
|
-
_toCodeMirrorSelection(selection) {
|
|
780
|
-
return {
|
|
781
|
-
anchor: this._toCodeMirrorPosition(selection.start),
|
|
782
|
-
head: this._toCodeMirrorPosition(selection.end)
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
761
|
/**
|
|
786
762
|
* Convert a code mirror position to an editor position.
|
|
787
763
|
*/
|
|
@@ -800,60 +776,16 @@ export class CodeMirrorEditor {
|
|
|
800
776
|
ch: position.column
|
|
801
777
|
};
|
|
802
778
|
}
|
|
803
|
-
/**
|
|
804
|
-
* Handle model value changes.
|
|
805
|
-
*/
|
|
806
|
-
_onValueChanged(value, args) {
|
|
807
|
-
if (this._changeGuard) {
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
this._changeGuard = true;
|
|
811
|
-
const doc = this.doc;
|
|
812
|
-
switch (args.type) {
|
|
813
|
-
case 'insert': {
|
|
814
|
-
const pos = doc.posFromIndex(args.start);
|
|
815
|
-
// Replace the range, including a '+input' origin,
|
|
816
|
-
// which indicates that CodeMirror may merge changes
|
|
817
|
-
// for undo/redo purposes.
|
|
818
|
-
doc.replaceRange(args.value, pos, pos, '+input');
|
|
819
|
-
break;
|
|
820
|
-
}
|
|
821
|
-
case 'remove': {
|
|
822
|
-
const from = doc.posFromIndex(args.start);
|
|
823
|
-
const to = doc.posFromIndex(args.end);
|
|
824
|
-
// Replace the range, including a '+input' origin,
|
|
825
|
-
// which indicates that CodeMirror may merge changes
|
|
826
|
-
// for undo/redo purposes.
|
|
827
|
-
doc.replaceRange('', from, to, '+input');
|
|
828
|
-
break;
|
|
829
|
-
}
|
|
830
|
-
case 'set':
|
|
831
|
-
doc.setValue(args.value);
|
|
832
|
-
break;
|
|
833
|
-
default:
|
|
834
|
-
break;
|
|
835
|
-
}
|
|
836
|
-
this._changeGuard = false;
|
|
837
|
-
}
|
|
838
779
|
/**
|
|
839
780
|
* Handles document changes.
|
|
840
781
|
*/
|
|
841
|
-
|
|
842
|
-
if (
|
|
843
|
-
|
|
844
|
-
}
|
|
845
|
-
this._changeGuard = true;
|
|
846
|
-
const value = this._model.value;
|
|
847
|
-
const start = doc.indexFromPos(change.from);
|
|
848
|
-
const end = doc.indexFromPos(change.to);
|
|
849
|
-
const inserted = change.text.join('\n');
|
|
850
|
-
if (end !== start) {
|
|
851
|
-
value.remove(start, end);
|
|
782
|
+
_onDocChanged(update) {
|
|
783
|
+
if (update.transactions.length && update.transactions[0].selection) {
|
|
784
|
+
this._onCursorActivity();
|
|
852
785
|
}
|
|
853
|
-
if (
|
|
854
|
-
|
|
786
|
+
if (update.docChanged) {
|
|
787
|
+
this._lastChange = update.changes;
|
|
855
788
|
}
|
|
856
|
-
this._changeGuard = false;
|
|
857
789
|
}
|
|
858
790
|
/**
|
|
859
791
|
* Handle the DOM events for the editor.
|
|
@@ -884,9 +816,6 @@ export class CodeMirrorEditor {
|
|
|
884
816
|
* Handle `focus` events for the editor.
|
|
885
817
|
*/
|
|
886
818
|
_evtFocus(event) {
|
|
887
|
-
if (this._needsRefresh) {
|
|
888
|
-
this.refresh();
|
|
889
|
-
}
|
|
890
819
|
this.host.classList.add('jp-mod-focused');
|
|
891
820
|
// Update the selections on editor gaining focus because
|
|
892
821
|
// the onCursorActivity function filters usual cursor events
|
|
@@ -918,49 +847,6 @@ export class CodeMirrorEditor {
|
|
|
918
847
|
this._caretHover = null;
|
|
919
848
|
}
|
|
920
849
|
}
|
|
921
|
-
/**
|
|
922
|
-
* Construct a caret element representing the position
|
|
923
|
-
* of a collaborator's cursor.
|
|
924
|
-
*/
|
|
925
|
-
_getCaret(collaborator) {
|
|
926
|
-
// FIXME-TRANS: Is this localizable?
|
|
927
|
-
const name = collaborator ? collaborator.displayName : 'Anonymous';
|
|
928
|
-
const color = collaborator
|
|
929
|
-
? collaborator.color
|
|
930
|
-
: this._selectionStyle.color;
|
|
931
|
-
const caret = document.createElement('span');
|
|
932
|
-
caret.className = COLLABORATOR_CURSOR_CLASS;
|
|
933
|
-
caret.style.borderBottomColor = color;
|
|
934
|
-
caret.onmouseenter = () => {
|
|
935
|
-
this._clearHover();
|
|
936
|
-
this._hoverId = collaborator.sessionId;
|
|
937
|
-
const rect = caret.getBoundingClientRect();
|
|
938
|
-
// Construct and place the hover box.
|
|
939
|
-
const hover = document.createElement('div');
|
|
940
|
-
hover.className = COLLABORATOR_HOVER_CLASS;
|
|
941
|
-
hover.style.left = String(rect.left) + 'px';
|
|
942
|
-
hover.style.top = String(rect.bottom) + 'px';
|
|
943
|
-
hover.textContent = name;
|
|
944
|
-
hover.style.backgroundColor = color;
|
|
945
|
-
// If the user mouses over the hover, take over the timer.
|
|
946
|
-
hover.onmouseenter = () => {
|
|
947
|
-
window.clearTimeout(this._hoverTimeout);
|
|
948
|
-
};
|
|
949
|
-
hover.onmouseleave = () => {
|
|
950
|
-
this._hoverTimeout = window.setTimeout(() => {
|
|
951
|
-
this._clearHover();
|
|
952
|
-
}, HOVER_TIMEOUT);
|
|
953
|
-
};
|
|
954
|
-
this._caretHover = hover;
|
|
955
|
-
document.body.appendChild(hover);
|
|
956
|
-
};
|
|
957
|
-
caret.onmouseleave = () => {
|
|
958
|
-
this._hoverTimeout = window.setTimeout(() => {
|
|
959
|
-
this._clearHover();
|
|
960
|
-
}, HOVER_TIMEOUT);
|
|
961
|
-
};
|
|
962
|
-
return caret;
|
|
963
|
-
}
|
|
964
850
|
/**
|
|
965
851
|
* Check for an out of sync editor.
|
|
966
852
|
*/
|
|
@@ -970,9 +856,8 @@ export class CodeMirrorEditor {
|
|
|
970
856
|
return;
|
|
971
857
|
}
|
|
972
858
|
this._lastChange = null;
|
|
973
|
-
const
|
|
974
|
-
|
|
975
|
-
if (doc.getValue() === this._model.value.text) {
|
|
859
|
+
const doc = this.doc;
|
|
860
|
+
if (doc.toString() === this._model.value.text) {
|
|
976
861
|
return;
|
|
977
862
|
}
|
|
978
863
|
void showDialog({
|
|
@@ -982,11 +867,10 @@ export class CodeMirrorEditor {
|
|
|
982
867
|
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.');
|
|
983
868
|
console.warn(JSON.stringify({
|
|
984
869
|
model: this._model.value.text,
|
|
985
|
-
view: doc.
|
|
870
|
+
view: doc.toString(),
|
|
986
871
|
selections: this.getSelections(),
|
|
987
872
|
cursor: this.getCursorPosition(),
|
|
988
|
-
lineSep:
|
|
989
|
-
mode: editor.getOption('mode'),
|
|
873
|
+
lineSep: this.state.facet(EditorState.lineSeparator),
|
|
990
874
|
change
|
|
991
875
|
}));
|
|
992
876
|
}
|
|
@@ -998,222 +882,98 @@ export class CodeMirrorEditor {
|
|
|
998
882
|
/**
|
|
999
883
|
* The default configuration options for an editor.
|
|
1000
884
|
*/
|
|
1001
|
-
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,
|
|
885
|
+
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, handlePaste: true });
|
|
1002
886
|
/**
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
* @param name - The name of the command to add.
|
|
1006
|
-
*
|
|
1007
|
-
* @param command - The command function.
|
|
887
|
+
* Indent or insert a tab as appropriate.
|
|
1008
888
|
*/
|
|
1009
|
-
function
|
|
1010
|
-
|
|
889
|
+
function indentMoreOrInsertTab(target) {
|
|
890
|
+
const arg = { state: target.state, dispatch: target.dispatch };
|
|
891
|
+
const from = target.state.selection.main.from;
|
|
892
|
+
const to = target.state.selection.main.to;
|
|
893
|
+
if (from != to) {
|
|
894
|
+
return indentMore(arg);
|
|
895
|
+
}
|
|
896
|
+
const line = target.state.doc.lineAt(from);
|
|
897
|
+
const before = target.state.doc.slice(line.from, from).toString();
|
|
898
|
+
if (/^\s*$/.test(before)) {
|
|
899
|
+
return indentMore(arg);
|
|
900
|
+
}
|
|
901
|
+
else {
|
|
902
|
+
return insertTab(arg);
|
|
903
|
+
}
|
|
1011
904
|
}
|
|
1012
|
-
CodeMirrorEditor.
|
|
905
|
+
CodeMirrorEditor.indentMoreOrInsertTab = indentMoreOrInsertTab;
|
|
1013
906
|
})(CodeMirrorEditor || (CodeMirrorEditor = {}));
|
|
1014
907
|
/**
|
|
1015
908
|
* The namespace for module private data.
|
|
1016
909
|
*/
|
|
1017
910
|
var Private;
|
|
1018
911
|
(function (Private) {
|
|
1019
|
-
function createEditor(host, config) {
|
|
1020
|
-
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
}
|
|
1026
|
-
if (fontSize) {
|
|
1027
|
-
el.style.fontSize = fontSize + 'px';
|
|
1028
|
-
}
|
|
1029
|
-
if (lineHeight) {
|
|
1030
|
-
el.style.lineHeight = lineHeight.toString();
|
|
1031
|
-
}
|
|
1032
|
-
if (readOnly) {
|
|
1033
|
-
el.classList.add(READ_ONLY_CLASS);
|
|
1034
|
-
}
|
|
1035
|
-
if (lineWrap === 'wordWrapColumn') {
|
|
1036
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1037
|
-
lines.style.width = `${wordWrapColumn}ch`;
|
|
1038
|
-
}
|
|
1039
|
-
if (lineWrap === 'bounded') {
|
|
1040
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1041
|
-
lines.style.maxWidth = `${wordWrapColumn}ch`;
|
|
1042
|
-
}
|
|
1043
|
-
host.appendChild(el);
|
|
1044
|
-
}, bareConfig);
|
|
1045
|
-
}
|
|
1046
|
-
Private.createEditor = createEditor;
|
|
1047
|
-
/**
|
|
1048
|
-
* Indent or insert a tab as appropriate.
|
|
1049
|
-
*/
|
|
1050
|
-
function indentMoreOrinsertTab(cm) {
|
|
1051
|
-
const doc = cm.getDoc();
|
|
1052
|
-
const from = doc.getCursor('from');
|
|
1053
|
-
const to = doc.getCursor('to');
|
|
1054
|
-
const sel = !posEq(from, to);
|
|
1055
|
-
if (sel) {
|
|
1056
|
-
CodeMirror.commands['indentMore'](cm);
|
|
1057
|
-
return;
|
|
1058
|
-
}
|
|
1059
|
-
// Check for start of line.
|
|
1060
|
-
const line = doc.getLine(from.line);
|
|
1061
|
-
const before = line.slice(0, from.ch);
|
|
1062
|
-
if (/^\s*$/.test(before)) {
|
|
1063
|
-
CodeMirror.commands['indentMore'](cm);
|
|
912
|
+
function createEditor(host, config, ybinding, editorConfig, additionalExtensions) {
|
|
913
|
+
var _a;
|
|
914
|
+
const extensions = editorConfig.getInitialExtensions(config);
|
|
915
|
+
if (ybinding) {
|
|
916
|
+
extensions.push(yCollab(ybinding.text, ybinding.awareness, {
|
|
917
|
+
undoManager: (_a = ybinding.undoManager) !== null && _a !== void 0 ? _a : false
|
|
918
|
+
}));
|
|
1064
919
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
}
|
|
920
|
+
extensions.push(...additionalExtensions);
|
|
921
|
+
const doc = ybinding === null || ybinding === void 0 ? void 0 : ybinding.text.toString();
|
|
922
|
+
const view = new EditorView({
|
|
923
|
+
state: EditorState.create({
|
|
924
|
+
doc,
|
|
925
|
+
extensions
|
|
926
|
+
}),
|
|
927
|
+
parent: host
|
|
928
|
+
});
|
|
929
|
+
if (config.readOnly) {
|
|
930
|
+
view.dom.classList.add(READ_ONLY_CLASS);
|
|
1072
931
|
}
|
|
932
|
+
return view;
|
|
1073
933
|
}
|
|
1074
|
-
Private.
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
934
|
+
Private.createEditor = createEditor;
|
|
935
|
+
class CaretWidget extends WidgetType {
|
|
936
|
+
constructor(collaborator, callbacks) {
|
|
937
|
+
super();
|
|
938
|
+
this.collaborator = collaborator;
|
|
939
|
+
this.callbacks = callbacks;
|
|
940
|
+
}
|
|
941
|
+
eq(other) {
|
|
942
|
+
return this.collaborator.sessionId == other.collaborator.sessionId;
|
|
943
|
+
}
|
|
944
|
+
toDOM() {
|
|
945
|
+
const caret = document.createElement('span');
|
|
946
|
+
caret.className = COLLABORATOR_CURSOR_CLASS;
|
|
947
|
+
caret.style.borderBottomColor = this.collaborator.color;
|
|
948
|
+
// TODO: check if this should be replaced with
|
|
949
|
+
// a global event handler
|
|
950
|
+
caret.onmouseenter = () => {
|
|
951
|
+
this.callbacks.setHoverId(this.collaborator.sessionId);
|
|
952
|
+
const rect = caret.getBoundingClientRect();
|
|
953
|
+
// Construct and place the hover box.
|
|
954
|
+
const hover = document.createElement('div');
|
|
955
|
+
hover.className = COLLABORATOR_HOVER_CLASS;
|
|
956
|
+
hover.style.left = `${rect.left}px`;
|
|
957
|
+
hover.style.top = `${rect.bottom}px`;
|
|
958
|
+
hover.textContent = this.collaborator.displayName;
|
|
959
|
+
hover.style.backgroundColor = this.collaborator.color;
|
|
960
|
+
// If the user mouses over the hover, take over the timer.
|
|
961
|
+
hover.onmouseenter = () => {
|
|
962
|
+
this.callbacks.clearHoverTimeout();
|
|
963
|
+
};
|
|
964
|
+
hover.onmouseleave = () => {
|
|
965
|
+
this.callbacks.setHoverTimeout();
|
|
966
|
+
};
|
|
967
|
+
};
|
|
968
|
+
caret.onmouseleave = () => {
|
|
969
|
+
this.callbacks.setHoverTimeout();
|
|
970
|
+
};
|
|
971
|
+
return caret;
|
|
1106
972
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
* Test whether two CodeMirror positions are equal.
|
|
1111
|
-
*/
|
|
1112
|
-
function posEq(a, b) {
|
|
1113
|
-
return a.line === b.line && a.ch === b.ch;
|
|
1114
|
-
}
|
|
1115
|
-
Private.posEq = posEq;
|
|
1116
|
-
/**
|
|
1117
|
-
* Get the list of active gutters
|
|
1118
|
-
*
|
|
1119
|
-
* @param config Editor configuration
|
|
1120
|
-
*/
|
|
1121
|
-
function getActiveGutters(config) {
|
|
1122
|
-
// The order of the classes will be the gutters order
|
|
1123
|
-
const classToSwitch = {
|
|
1124
|
-
'CodeMirror-linenumbers': 'lineNumbers',
|
|
1125
|
-
'CodeMirror-foldgutter': 'codeFolding'
|
|
1126
|
-
};
|
|
1127
|
-
return Object.keys(classToSwitch).filter(gutter => config[classToSwitch[gutter]]);
|
|
1128
|
-
}
|
|
1129
|
-
/**
|
|
1130
|
-
* Set a config option for the editor.
|
|
1131
|
-
*/
|
|
1132
|
-
function setOption(editor, option, value, config) {
|
|
1133
|
-
const el = editor.getWrapperElement();
|
|
1134
|
-
switch (option) {
|
|
1135
|
-
case 'cursorBlinkRate':
|
|
1136
|
-
editor.setOption(option, value);
|
|
1137
|
-
break;
|
|
1138
|
-
case 'lineWrap': {
|
|
1139
|
-
const lineWrapping = value === 'off' ? false : true;
|
|
1140
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1141
|
-
const maxWidth = value === 'bounded' ? `${config.wordWrapColumn}ch` : null;
|
|
1142
|
-
const width = value === 'wordWrapColumn' ? `${config.wordWrapColumn}ch` : null;
|
|
1143
|
-
lines.style.setProperty('max-width', maxWidth);
|
|
1144
|
-
lines.style.setProperty('width', width);
|
|
1145
|
-
editor.setOption('lineWrapping', lineWrapping);
|
|
1146
|
-
break;
|
|
1147
|
-
}
|
|
1148
|
-
case 'wordWrapColumn': {
|
|
1149
|
-
const { lineWrap } = config;
|
|
1150
|
-
if (lineWrap === 'wordWrapColumn' || lineWrap === 'bounded') {
|
|
1151
|
-
const lines = el.querySelector('.CodeMirror-lines');
|
|
1152
|
-
const prop = lineWrap === 'wordWrapColumn' ? 'width' : 'maxWidth';
|
|
1153
|
-
lines.style[prop] = `${value}ch`;
|
|
1154
|
-
}
|
|
1155
|
-
break;
|
|
1156
|
-
}
|
|
1157
|
-
case 'tabSize':
|
|
1158
|
-
editor.setOption('indentUnit', value);
|
|
1159
|
-
break;
|
|
1160
|
-
case 'insertSpaces':
|
|
1161
|
-
editor.setOption('indentWithTabs', !value);
|
|
1162
|
-
break;
|
|
1163
|
-
case 'autoClosingBrackets':
|
|
1164
|
-
editor.setOption('autoCloseBrackets', value);
|
|
1165
|
-
break;
|
|
1166
|
-
case 'rulers': {
|
|
1167
|
-
const rulers = value;
|
|
1168
|
-
editor.setOption('rulers', rulers.map(column => {
|
|
1169
|
-
return {
|
|
1170
|
-
column,
|
|
1171
|
-
className: 'jp-CodeMirror-ruler'
|
|
1172
|
-
};
|
|
1173
|
-
}));
|
|
1174
|
-
break;
|
|
1175
|
-
}
|
|
1176
|
-
case 'readOnly':
|
|
1177
|
-
el.classList.toggle(READ_ONLY_CLASS, value);
|
|
1178
|
-
editor.setOption(option, value);
|
|
1179
|
-
break;
|
|
1180
|
-
case 'fontFamily':
|
|
1181
|
-
el.style.fontFamily = value;
|
|
1182
|
-
break;
|
|
1183
|
-
case 'fontSize':
|
|
1184
|
-
el.style.setProperty('font-size', value ? value + 'px' : null);
|
|
1185
|
-
break;
|
|
1186
|
-
case 'lineHeight':
|
|
1187
|
-
el.style.lineHeight = (value ? value.toString() : null);
|
|
1188
|
-
break;
|
|
1189
|
-
case 'gutters':
|
|
1190
|
-
editor.setOption(option, getActiveGutters(config));
|
|
1191
|
-
break;
|
|
1192
|
-
case 'lineNumbers':
|
|
1193
|
-
editor.setOption(option, value);
|
|
1194
|
-
editor.setOption('gutters', getActiveGutters(config));
|
|
1195
|
-
break;
|
|
1196
|
-
case 'codeFolding':
|
|
1197
|
-
editor.setOption('foldGutter', value);
|
|
1198
|
-
editor.setOption('gutters', getActiveGutters(config));
|
|
1199
|
-
break;
|
|
1200
|
-
case 'showTrailingSpace':
|
|
1201
|
-
editor.setOption(option, value);
|
|
1202
|
-
break;
|
|
1203
|
-
default:
|
|
1204
|
-
editor.setOption(option, value);
|
|
1205
|
-
break;
|
|
973
|
+
ignoreEvent() {
|
|
974
|
+
return false;
|
|
1206
975
|
}
|
|
1207
976
|
}
|
|
1208
|
-
Private.
|
|
977
|
+
Private.CaretWidget = CaretWidget;
|
|
1209
978
|
})(Private || (Private = {}));
|
|
1210
|
-
/**
|
|
1211
|
-
* Add a CodeMirror command to delete until previous non blanking space
|
|
1212
|
-
* character or first multiple of tabsize tabstop.
|
|
1213
|
-
*/
|
|
1214
|
-
CodeMirrorEditor.addCommand('delSpaceToPrevTabStop', Private.delSpaceToPrevTabStop);
|
|
1215
|
-
/**
|
|
1216
|
-
* Add a CodeMirror command to indent or insert a tab as appropriate.
|
|
1217
|
-
*/
|
|
1218
|
-
CodeMirrorEditor.addCommand('indentMoreOrinsertTab', Private.indentMoreOrinsertTab);
|
|
1219
979
|
//# sourceMappingURL=editor.js.map
|