@jupyterlab/codemirror 4.0.0-alpha.11 → 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 +50 -199
- package/lib/editor.js +385 -626
- 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 -2
- package/lib/index.js +2 -2
- 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 -55
- package/lib/mode.js +1472 -106
- package/lib/mode.js.map +1 -1
- package/lib/searchprovider.d.ts +11 -11
- package/lib/searchprovider.js +230 -267
- package/lib/searchprovider.js.map +1 -1
- package/lib/syntaxstatus.js +9 -3
- package/lib/syntaxstatus.js.map +1 -1
- package/package.json +43 -27
- package/style/base.css +13 -183
- package/style/index.css +0 -3
- package/style/index.js +0 -3
- 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
|
@@ -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,37 +306,34 @@ export class CodeMirrorEditor {
|
|
|
304
306
|
* is set.
|
|
305
307
|
*/
|
|
306
308
|
setOptions(options) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
this.setOption(k, options[k]);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
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);
|
|
316
314
|
}
|
|
317
315
|
/**
|
|
318
316
|
* Returns the content for the given line number.
|
|
319
317
|
*/
|
|
320
318
|
getLine(line) {
|
|
321
|
-
|
|
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;
|
|
322
322
|
}
|
|
323
323
|
/**
|
|
324
324
|
* Find an offset for the given position.
|
|
325
325
|
*/
|
|
326
326
|
getOffsetAt(position) {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
line: position.line
|
|
330
|
-
});
|
|
327
|
+
// TODO: CM6 remove +1 when CM6 first line number has propagated
|
|
328
|
+
return this.doc.line(position.line + 1).from + position.column;
|
|
331
329
|
}
|
|
332
330
|
/**
|
|
333
331
|
* Find a position for the given offset.
|
|
334
332
|
*/
|
|
335
333
|
getPositionAt(offset) {
|
|
336
|
-
|
|
337
|
-
|
|
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 };
|
|
338
337
|
}
|
|
339
338
|
/**
|
|
340
339
|
* Undo one edit (if any undo events are stored).
|
|
@@ -353,7 +352,7 @@ export class CodeMirrorEditor {
|
|
|
353
352
|
*/
|
|
354
353
|
clearHistory() {
|
|
355
354
|
var _a, _b;
|
|
356
|
-
(_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();
|
|
357
356
|
}
|
|
358
357
|
/**
|
|
359
358
|
* Brings browser focus to this editor text.
|
|
@@ -365,78 +364,41 @@ export class CodeMirrorEditor {
|
|
|
365
364
|
* Test whether the editor has keyboard focus.
|
|
366
365
|
*/
|
|
367
366
|
hasFocus() {
|
|
368
|
-
return this._editor.
|
|
367
|
+
return this._editor.hasFocus;
|
|
369
368
|
}
|
|
370
369
|
/**
|
|
371
370
|
* Explicitly blur the editor.
|
|
372
371
|
*/
|
|
373
372
|
blur() {
|
|
374
|
-
this._editor.
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* Repaint editor.
|
|
378
|
-
*/
|
|
379
|
-
refresh() {
|
|
380
|
-
this._editor.refresh();
|
|
381
|
-
this._needsRefresh = false;
|
|
373
|
+
this._editor.contentDOM.blur();
|
|
382
374
|
}
|
|
383
375
|
/**
|
|
384
376
|
* Refresh the editor if it is focused;
|
|
385
377
|
* otherwise postpone refreshing till focusing.
|
|
386
378
|
*/
|
|
387
379
|
resizeToFit() {
|
|
388
|
-
if (this.hasFocus()) {
|
|
389
|
-
this.refresh();
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
this._needsRefresh = true;
|
|
393
|
-
}
|
|
394
380
|
this._clearHover();
|
|
395
381
|
}
|
|
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);
|
|
408
|
-
}
|
|
409
382
|
get state() {
|
|
410
383
|
return this._editor.state;
|
|
411
384
|
}
|
|
412
|
-
operation(fn) {
|
|
413
|
-
return this._editor.operation(fn);
|
|
414
|
-
}
|
|
415
385
|
firstLine() {
|
|
416
|
-
return
|
|
386
|
+
// TODO: return 1 when CM6 first line number has propagated
|
|
387
|
+
return 0;
|
|
417
388
|
}
|
|
418
389
|
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);
|
|
390
|
+
return this.doc.lines - 1;
|
|
434
391
|
}
|
|
435
392
|
cursorCoords(where, mode) {
|
|
436
|
-
|
|
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;
|
|
437
397
|
}
|
|
438
398
|
getRange(from, to, separator) {
|
|
439
|
-
|
|
399
|
+
const fromOffset = this.getOffsetAt(this._toPosition(from));
|
|
400
|
+
const toOffset = this.getOffsetAt(this._toPosition(to));
|
|
401
|
+
return this.state.sliceDoc(fromOffset, toOffset);
|
|
440
402
|
}
|
|
441
403
|
/**
|
|
442
404
|
* Add a keydown handler to the editor.
|
|
@@ -451,41 +413,31 @@ export class CodeMirrorEditor {
|
|
|
451
413
|
ArrayExt.removeAllWhere(this._keydownHandlers, val => val === handler);
|
|
452
414
|
});
|
|
453
415
|
}
|
|
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;
|
|
465
|
-
}
|
|
466
416
|
/**
|
|
467
417
|
* Reveal the given position in the editor.
|
|
468
418
|
*/
|
|
469
419
|
revealPosition(position) {
|
|
470
|
-
const
|
|
471
|
-
this._editor.
|
|
420
|
+
const offset = this.getOffsetAt(position);
|
|
421
|
+
this._editor.dispatch({
|
|
422
|
+
effects: EditorView.scrollIntoView(offset)
|
|
423
|
+
});
|
|
472
424
|
}
|
|
473
425
|
/**
|
|
474
426
|
* Reveal the given selection in the editor.
|
|
475
427
|
*/
|
|
476
428
|
revealSelection(selection) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
+
});
|
|
482
434
|
}
|
|
483
435
|
/**
|
|
484
436
|
* Get the window coordinates given a cursor position.
|
|
485
437
|
*/
|
|
486
438
|
getCoordinateForPosition(position) {
|
|
487
|
-
const
|
|
488
|
-
const rect = this.editor.
|
|
439
|
+
const offset = this.getOffsetAt(position);
|
|
440
|
+
const rect = this.editor.coordsAtPos(offset);
|
|
489
441
|
return rect;
|
|
490
442
|
}
|
|
491
443
|
/**
|
|
@@ -497,14 +449,18 @@ export class CodeMirrorEditor {
|
|
|
497
449
|
* contained in the editor.
|
|
498
450
|
*/
|
|
499
451
|
getPositionForCoordinate(coordinate) {
|
|
500
|
-
|
|
452
|
+
const offset = this.editor.posAtCoords({
|
|
453
|
+
x: coordinate.left,
|
|
454
|
+
y: coordinate.top
|
|
455
|
+
});
|
|
456
|
+
return this.getPositionAt(offset) || null;
|
|
501
457
|
}
|
|
502
458
|
/**
|
|
503
459
|
* Returns the primary position of the cursor, never `null`.
|
|
504
460
|
*/
|
|
505
461
|
getCursorPosition() {
|
|
506
|
-
const
|
|
507
|
-
return this.
|
|
462
|
+
const offset = this.state.selection.main.head;
|
|
463
|
+
return this.getPositionAt(offset);
|
|
508
464
|
}
|
|
509
465
|
/**
|
|
510
466
|
* Set the primary position of the cursor.
|
|
@@ -513,12 +469,15 @@ export class CodeMirrorEditor {
|
|
|
513
469
|
* This will remove any secondary cursors.
|
|
514
470
|
*/
|
|
515
471
|
setCursorPosition(position, options) {
|
|
516
|
-
const
|
|
517
|
-
this.
|
|
472
|
+
const offset = this.getOffsetAt(position);
|
|
473
|
+
this.editor.dispatch({
|
|
474
|
+
selection: { anchor: offset },
|
|
475
|
+
scrollIntoView: true
|
|
476
|
+
});
|
|
518
477
|
// If the editor does not have focus, this cursor change
|
|
519
478
|
// will get screened out in _onCursorsChanged(). Make an
|
|
520
479
|
// exception for this method.
|
|
521
|
-
if (!this.editor.hasFocus
|
|
480
|
+
if (!this.editor.hasFocus) {
|
|
522
481
|
this.model.selections.set(this.uuid, this.getSelections());
|
|
523
482
|
}
|
|
524
483
|
}
|
|
@@ -538,11 +497,15 @@ export class CodeMirrorEditor {
|
|
|
538
497
|
* Gets the selections for all the cursors, never `null` or empty.
|
|
539
498
|
*/
|
|
540
499
|
getSelections() {
|
|
541
|
-
const selections = this.
|
|
500
|
+
const selections = this.state.selection.ranges; //= [{anchor: number, head: number}]
|
|
542
501
|
if (selections.length > 0) {
|
|
543
|
-
|
|
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));
|
|
544
507
|
}
|
|
545
|
-
const cursor = this.
|
|
508
|
+
const cursor = this._toCodeMirrorPosition(this.getPositionAt(this.state.selection.main.head));
|
|
546
509
|
const selection = this._toSelection({ anchor: cursor, head: cursor });
|
|
547
510
|
return [selection];
|
|
548
511
|
}
|
|
@@ -552,8 +515,10 @@ export class CodeMirrorEditor {
|
|
|
552
515
|
* Passing an empty array resets a cursor position to the start of a document.
|
|
553
516
|
*/
|
|
554
517
|
setSelections(selections) {
|
|
555
|
-
const
|
|
556
|
-
|
|
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) });
|
|
557
522
|
}
|
|
558
523
|
/**
|
|
559
524
|
* Replaces the current selection with the given text.
|
|
@@ -561,41 +526,62 @@ export class CodeMirrorEditor {
|
|
|
561
526
|
* @param text The text to be inserted.
|
|
562
527
|
*/
|
|
563
528
|
replaceSelection(text) {
|
|
564
|
-
this.
|
|
529
|
+
this.editor.dispatch(this.state.replaceSelection(text));
|
|
565
530
|
}
|
|
566
531
|
/**
|
|
567
532
|
* Get a list of tokens for the current editor text content.
|
|
568
533
|
*/
|
|
569
534
|
getTokens() {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
+
});
|
|
578
548
|
}
|
|
579
549
|
return tokens;
|
|
580
550
|
}
|
|
581
551
|
/**
|
|
582
552
|
* Get the token at a given editor position.
|
|
583
553
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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);
|
|
593
576
|
}
|
|
594
577
|
/**
|
|
595
578
|
* Insert a new indented line at the current cursor position.
|
|
596
579
|
*/
|
|
597
580
|
newIndentedLine() {
|
|
598
|
-
|
|
581
|
+
insertNewlineAndIndent({
|
|
582
|
+
state: this.state,
|
|
583
|
+
dispatch: this.editor.dispatch
|
|
584
|
+
});
|
|
599
585
|
}
|
|
600
586
|
/**
|
|
601
587
|
* Execute a codemirror command on the editor.
|
|
@@ -603,31 +589,28 @@ export class CodeMirrorEditor {
|
|
|
603
589
|
* @param command - The name of the command to execute.
|
|
604
590
|
*/
|
|
605
591
|
execCommand(command) {
|
|
606
|
-
this.
|
|
592
|
+
command(this.editor);
|
|
607
593
|
}
|
|
608
594
|
/**
|
|
609
595
|
* Handle keydown events from the editor.
|
|
610
596
|
*/
|
|
611
597
|
onKeydown(event) {
|
|
612
|
-
const position = this.
|
|
613
|
-
|
|
614
|
-
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) {
|
|
615
600
|
if (!event.shiftKey) {
|
|
616
601
|
this.edgeRequested.emit('top');
|
|
617
602
|
}
|
|
618
603
|
return false;
|
|
619
604
|
}
|
|
620
|
-
|
|
605
|
+
const line = this.doc.lineAt(position).number;
|
|
606
|
+
if (line === 1 && event.keyCode === UP_ARROW) {
|
|
621
607
|
if (!event.shiftKey) {
|
|
622
608
|
this.edgeRequested.emit('topLine');
|
|
623
609
|
}
|
|
624
610
|
return false;
|
|
625
611
|
}
|
|
626
|
-
const
|
|
627
|
-
|
|
628
|
-
if (line === lastLine &&
|
|
629
|
-
column === lastCh &&
|
|
630
|
-
event.keyCode === DOWN_ARROW) {
|
|
612
|
+
const length = this.doc.length;
|
|
613
|
+
if (position === length && event.keyCode === DOWN_ARROW) {
|
|
631
614
|
if (!event.shiftKey) {
|
|
632
615
|
this.edgeRequested.emit('bottom');
|
|
633
616
|
}
|
|
@@ -635,36 +618,16 @@ export class CodeMirrorEditor {
|
|
|
635
618
|
}
|
|
636
619
|
return false;
|
|
637
620
|
}
|
|
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
621
|
/**
|
|
649
622
|
* Handles a mime type change.
|
|
650
623
|
*/
|
|
651
624
|
_onMimeTypeChanged() {
|
|
652
625
|
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
626
|
// TODO: should we provide a hook for when the mode is done being set?
|
|
665
627
|
void Mode.ensure(mime).then(spec => {
|
|
666
|
-
|
|
667
|
-
|
|
628
|
+
if (spec) {
|
|
629
|
+
this._editorConfig.reconfigureExtension(this._editor, 'language', spec.support);
|
|
630
|
+
}
|
|
668
631
|
});
|
|
669
632
|
}
|
|
670
633
|
/**
|
|
@@ -683,19 +646,15 @@ export class CodeMirrorEditor {
|
|
|
683
646
|
* Clean selections for the given uuid.
|
|
684
647
|
*/
|
|
685
648
|
_cleanSelections(uuid) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
})
|
|
691
|
-
}
|
|
692
|
-
delete this.selectionMarkers[uuid];
|
|
649
|
+
this.editor.dispatch({
|
|
650
|
+
effects: this._removeMark.of({
|
|
651
|
+
uuid: uuid,
|
|
652
|
+
decorations: this._selectionMarkers[uuid]
|
|
653
|
+
})
|
|
654
|
+
});
|
|
693
655
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
*/
|
|
697
|
-
_markSelections(uuid, selections) {
|
|
698
|
-
const markers = [];
|
|
656
|
+
_buildMarkDecoration(uuid, selections) {
|
|
657
|
+
const decorations = [];
|
|
699
658
|
// If we are marking selections corresponding to an active hover,
|
|
700
659
|
// remove it.
|
|
701
660
|
if (uuid === this._hoverId) {
|
|
@@ -709,34 +668,73 @@ export class CodeMirrorEditor {
|
|
|
709
668
|
}
|
|
710
669
|
// Style each selection for the uuid.
|
|
711
670
|
selections.forEach(selection => {
|
|
671
|
+
const from = selection.from;
|
|
672
|
+
const to = selection.to;
|
|
712
673
|
// Only render selections if the start is not equal to the end.
|
|
713
674
|
// In that case, we don't need to render the cursor.
|
|
714
|
-
if (
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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));
|
|
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));
|
|
731
682
|
}
|
|
732
683
|
else if (collaborator) {
|
|
733
|
-
const caret =
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
684
|
+
const caret = Decoration.widget({
|
|
685
|
+
widget: this._getCaret(collaborator)
|
|
686
|
+
});
|
|
687
|
+
decorations.push(caret.range(from));
|
|
737
688
|
}
|
|
738
689
|
});
|
|
739
|
-
|
|
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
|
+
});
|
|
740
738
|
}
|
|
741
739
|
/**
|
|
742
740
|
* Handles a cursor activity event.
|
|
@@ -744,7 +742,7 @@ export class CodeMirrorEditor {
|
|
|
744
742
|
_onCursorActivity() {
|
|
745
743
|
// Only add selections if the editor has focus. This avoids unwanted
|
|
746
744
|
// triggering of cursor activity due to collaborator actions.
|
|
747
|
-
if (this._editor.hasFocus
|
|
745
|
+
if (this._editor.hasFocus) {
|
|
748
746
|
const selections = this.getSelections();
|
|
749
747
|
this.model.selections.set(this.uuid, selections);
|
|
750
748
|
}
|
|
@@ -760,29 +758,6 @@ export class CodeMirrorEditor {
|
|
|
760
758
|
style: this.selectionStyle
|
|
761
759
|
};
|
|
762
760
|
}
|
|
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)
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
761
|
/**
|
|
787
762
|
* Convert a code mirror position to an editor position.
|
|
788
763
|
*/
|
|
@@ -801,60 +776,16 @@ export class CodeMirrorEditor {
|
|
|
801
776
|
ch: position.column
|
|
802
777
|
};
|
|
803
778
|
}
|
|
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
779
|
/**
|
|
840
780
|
* Handles document changes.
|
|
841
781
|
*/
|
|
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);
|
|
782
|
+
_onDocChanged(update) {
|
|
783
|
+
if (update.transactions.length && update.transactions[0].selection) {
|
|
784
|
+
this._onCursorActivity();
|
|
853
785
|
}
|
|
854
|
-
if (
|
|
855
|
-
|
|
786
|
+
if (update.docChanged) {
|
|
787
|
+
this._lastChange = update.changes;
|
|
856
788
|
}
|
|
857
|
-
this._changeGuard = false;
|
|
858
789
|
}
|
|
859
790
|
/**
|
|
860
791
|
* Handle the DOM events for the editor.
|
|
@@ -885,9 +816,6 @@ export class CodeMirrorEditor {
|
|
|
885
816
|
* Handle `focus` events for the editor.
|
|
886
817
|
*/
|
|
887
818
|
_evtFocus(event) {
|
|
888
|
-
if (this._needsRefresh) {
|
|
889
|
-
this.refresh();
|
|
890
|
-
}
|
|
891
819
|
this.host.classList.add('jp-mod-focused');
|
|
892
820
|
// Update the selections on editor gaining focus because
|
|
893
821
|
// the onCursorActivity function filters usual cursor events
|
|
@@ -919,49 +847,6 @@ export class CodeMirrorEditor {
|
|
|
919
847
|
this._caretHover = null;
|
|
920
848
|
}
|
|
921
849
|
}
|
|
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
850
|
/**
|
|
966
851
|
* Check for an out of sync editor.
|
|
967
852
|
*/
|
|
@@ -971,9 +856,8 @@ export class CodeMirrorEditor {
|
|
|
971
856
|
return;
|
|
972
857
|
}
|
|
973
858
|
this._lastChange = null;
|
|
974
|
-
const
|
|
975
|
-
|
|
976
|
-
if (doc.getValue() === this._model.value.text) {
|
|
859
|
+
const doc = this.doc;
|
|
860
|
+
if (doc.toString() === this._model.value.text) {
|
|
977
861
|
return;
|
|
978
862
|
}
|
|
979
863
|
void showDialog({
|
|
@@ -983,11 +867,10 @@ export class CodeMirrorEditor {
|
|
|
983
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.');
|
|
984
868
|
console.warn(JSON.stringify({
|
|
985
869
|
model: this._model.value.text,
|
|
986
|
-
view: doc.
|
|
870
|
+
view: doc.toString(),
|
|
987
871
|
selections: this.getSelections(),
|
|
988
872
|
cursor: this.getCursorPosition(),
|
|
989
|
-
lineSep:
|
|
990
|
-
mode: editor.getOption('mode'),
|
|
873
|
+
lineSep: this.state.facet(EditorState.lineSeparator),
|
|
991
874
|
change
|
|
992
875
|
}));
|
|
993
876
|
}
|
|
@@ -999,222 +882,98 @@ export class CodeMirrorEditor {
|
|
|
999
882
|
/**
|
|
1000
883
|
* The default configuration options for an editor.
|
|
1001
884
|
*/
|
|
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,
|
|
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 });
|
|
1003
886
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
* @param name - The name of the command to add.
|
|
1007
|
-
*
|
|
1008
|
-
* @param command - The command function.
|
|
887
|
+
* Indent or insert a tab as appropriate.
|
|
1009
888
|
*/
|
|
1010
|
-
function
|
|
1011
|
-
|
|
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
|
+
}
|
|
1012
904
|
}
|
|
1013
|
-
CodeMirrorEditor.
|
|
905
|
+
CodeMirrorEditor.indentMoreOrInsertTab = indentMoreOrInsertTab;
|
|
1014
906
|
})(CodeMirrorEditor || (CodeMirrorEditor = {}));
|
|
1015
907
|
/**
|
|
1016
908
|
* The namespace for module private data.
|
|
1017
909
|
*/
|
|
1018
910
|
var Private;
|
|
1019
911
|
(function (Private) {
|
|
1020
|
-
function createEditor(host, config) {
|
|
1021
|
-
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
}
|
|
1027
|
-
if (fontSize) {
|
|
1028
|
-
el.style.fontSize = fontSize + 'px';
|
|
1029
|
-
}
|
|
1030
|
-
if (lineHeight) {
|
|
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);
|
|
1046
|
-
}
|
|
1047
|
-
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);
|
|
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
|
+
}));
|
|
1065
919
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
}
|
|
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);
|
|
1073
931
|
}
|
|
932
|
+
return view;
|
|
1074
933
|
}
|
|
1075
|
-
Private.
|
|
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
|
-
|
|
1106
|
-
|
|
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;
|
|
1107
972
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
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;
|
|
973
|
+
ignoreEvent() {
|
|
974
|
+
return false;
|
|
1207
975
|
}
|
|
1208
976
|
}
|
|
1209
|
-
Private.
|
|
977
|
+
Private.CaretWidget = CaretWidget;
|
|
1210
978
|
})(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
979
|
//# sourceMappingURL=editor.js.map
|