@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/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 { JSONExt, UUID } from '@lumino/coreutils';
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 CodeMirror from 'codemirror';
26
- import 'codemirror/addon/comment/comment.js';
27
- import 'codemirror/addon/display/rulers.js';
28
- import 'codemirror/addon/edit/closebrackets.js';
29
- import 'codemirror/addon/edit/matchbrackets.js';
30
- import 'codemirror/addon/fold/brace-fold.js';
31
- import 'codemirror/addon/fold/comment-fold.js';
32
- import 'codemirror/addon/fold/foldcode.js';
33
- import 'codemirror/addon/fold/foldgutter.js';
34
- import 'codemirror/addon/fold/indent-fold.js';
35
- import 'codemirror/addon/fold/markdown-fold.js';
36
- import 'codemirror/addon/fold/xml-fold.js';
37
- import 'codemirror/addon/mode/simple';
38
- import 'codemirror/addon/scroll/scrollpastend.js';
39
- import 'codemirror/addon/search/jump-to-line';
40
- import 'codemirror/addon/search/search';
41
- import 'codemirror/addon/search/searchcursor';
42
- import 'codemirror/addon/selection/active-line';
43
- import 'codemirror/addon/selection/mark-selection';
44
- import 'codemirror/addon/selection/selection-pointer';
45
- import 'codemirror/addon/edit/trailingspace.js';
46
- import 'codemirror/keymap/emacs.js';
47
- import 'codemirror/keymap/sublime.js';
48
- import { CodemirrorBinding } from 'y-codemirror';
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
- // import 'codemirror/keymap/vim.js'; lazy loading of vim mode is available in ../codemirror-extension/index.ts
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.selectionMarkers = {};
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
- const opts = sharedModel.undoManager
199
- ? { yUndoManager: sharedModel.undoManager }
200
- : {};
201
- const awareness = sharedModel.awareness;
202
- this._yeditorBinding = new CodemirrorBinding(sharedModel.ysource, this.editor, awareness, opts);
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.getDoc();
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.lineCount();
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.defaultTextHeight();
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.defaultCharWidth();
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
- Private.setOption(this.editor, option, value, this._config);
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
- const editor = this._editor;
308
- editor.startOperation();
309
- for (let key in options) {
310
- editor.operation(() => {
311
- this.setOption(key, options[key]);
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
- return this.doc.getLine(line);
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
- return this.doc.indexFromPos({
327
- ch: position.column,
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
- const { ch, line } = this.doc.posFromIndex(offset);
336
- return { line, column: ch };
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.yUndoManager) === null || _b === void 0 ? void 0 : _b.clear();
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.getWrapperElement().contains(document.activeElement);
367
+ return this._editor.hasFocus;
368
368
  }
369
369
  /**
370
370
  * Explicitly blur the editor.
371
371
  */
372
372
  blur() {
373
- this._editor.getInputField().blur();
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 this._editor.getDoc().firstLine();
386
+ // TODO: return 1 when CM6 first line number has propagated
387
+ return 0;
416
388
  }
417
389
  lastLine() {
418
- return this._editor.getDoc().lastLine();
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
- return this._editor.cursorCoords(where, mode);
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
- return this._editor.getDoc().getRange(from, to, separator);
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 cmPosition = this._toCodeMirrorPosition(position);
470
- this._editor.scrollIntoView(cmPosition);
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 range = {
477
- from: this._toCodeMirrorPosition(selection.start),
478
- to: this._toCodeMirrorPosition(selection.end)
479
- };
480
- this._editor.scrollIntoView(range);
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 pos = this._toCodeMirrorPosition(position);
487
- const rect = this.editor.charCoords(pos, 'page');
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
- return this._toPosition(this.editor.coordsChar(coordinate)) || null;
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 cursor = this.doc.getCursor();
506
- return this._toPosition(cursor);
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 cursor = this._toCodeMirrorPosition(position);
516
- this.doc.setCursor(cursor, undefined, options);
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.doc.listSelections();
500
+ const selections = this.state.selection.ranges; //= [{anchor: number, head: number}]
541
501
  if (selections.length > 0) {
542
- return selections.map(selection => this._toSelection(selection));
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.doc.getCursor();
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 cmSelections = this._toCodeMirrorSelections(selections);
555
- this.doc.setSelections(cmSelections, 0);
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.doc.replaceSelection(text);
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
- let tokens = [];
570
- for (let i = 0; i < this.lineCount; ++i) {
571
- const lineTokens = this.editor.getLineTokens(i).map(t => ({
572
- offset: this.getOffsetAt({ column: t.start, line: i }),
573
- value: t.string,
574
- type: t.type || ''
575
- }));
576
- tokens = tokens.concat(lineTokens);
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
- getTokenForPosition(position) {
584
- var _a;
585
- const cursor = this._toCodeMirrorPosition(position);
586
- const token = this.editor.getTokenAt(cursor);
587
- return {
588
- offset: this.getOffsetAt({ column: token.start, line: cursor.line }),
589
- value: token.string,
590
- type: (_a = token.type) !== null && _a !== void 0 ? _a : undefined
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
- this.execCommand('newlineAndIndent');
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._editor.execCommand(command);
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.getCursorPosition();
612
- const { line, column } = position;
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
- if (line === 0 && event.keyCode === UP_ARROW) {
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 lastLine = this.lineCount - 1;
626
- const lastCh = this.getLine(lastLine).length;
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
- var _a;
666
- this.setOption('mode', (_a = spec === null || spec === void 0 ? void 0 : spec.mime) !== null && _a !== void 0 ? _a : 'null');
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
- const markers = this.selectionMarkers[uuid];
686
- if (markers) {
687
- markers.forEach(marker => {
688
- marker.clear();
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
- * Marks selections.
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 (!JSONExt.deepEqual(selection.start, selection.end)) {
714
- // Selections only appear to render correctly if the anchor
715
- // is before the head in the document. That is, reverse selections
716
- // do not appear as intended.
717
- const forward = selection.start.line < selection.end.line ||
718
- (selection.start.line === selection.end.line &&
719
- selection.start.column <= selection.end.column);
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 = this._getCaret(collaborator);
733
- markers.push(this.doc.setBookmark(this._toCodeMirrorPosition(selection.end), {
734
- widget: caret
735
- }));
684
+ const caret = Decoration.widget({
685
+ widget: this._getCaret(collaborator)
686
+ });
687
+ decorations.push(caret.range(from));
736
688
  }
737
689
  });
738
- this.selectionMarkers[uuid] = markers;
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
- _beforeDocChanged(doc, change) {
842
- if (this._changeGuard) {
843
- return;
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 (inserted) {
854
- value.insert(start, inserted);
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 editor = this._editor;
974
- const doc = editor.getDoc();
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.getValue(),
870
+ view: doc.toString(),
986
871
  selections: this.getSelections(),
987
872
  cursor: this.getCursorPosition(),
988
- lineSep: editor.getOption('lineSeparator'),
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, rulers: [], foldGutter: false, handlePaste: true });
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
- * Add a command to CodeMirror.
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 addCommand(name, command) {
1010
- CodeMirror.commands[name] = command;
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.addCommand = addCommand;
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
- const { autoClosingBrackets, fontFamily, fontSize, insertSpaces, lineHeight, lineWrap, wordWrapColumn, tabSize, readOnly } = config, otherOptions = __rest(config, ["autoClosingBrackets", "fontFamily", "fontSize", "insertSpaces", "lineHeight", "lineWrap", "wordWrapColumn", "tabSize", "readOnly"]);
1021
- const bareConfig = Object.assign({ autoCloseBrackets: autoClosingBrackets ? {} : false, indentUnit: tabSize, indentWithTabs: !insertSpaces, lineWrapping: lineWrap === 'off' ? false : true, readOnly }, otherOptions);
1022
- return CodeMirror(el => {
1023
- if (fontFamily) {
1024
- el.style.fontFamily = fontFamily;
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
- else {
1066
- if (cm.getOption('indentWithTabs')) {
1067
- CodeMirror.commands['insertTab'](cm);
1068
- }
1069
- else {
1070
- CodeMirror.commands['insertSoftTab'](cm);
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.indentMoreOrinsertTab = indentMoreOrinsertTab;
1075
- /**
1076
- * Delete spaces to the previous tab stop in a codemirror editor.
1077
- */
1078
- function delSpaceToPrevTabStop(cm) {
1079
- var _a;
1080
- const doc = cm.getDoc();
1081
- // default tabsize is 2, according to codemirror docs: https://codemirror.net/doc/manual.html#config
1082
- const tabSize = (_a = cm.getOption('indentUnit')) !== null && _a !== void 0 ? _a : 2;
1083
- const ranges = doc.listSelections(); // handle multicursor
1084
- for (let i = ranges.length - 1; i >= 0; i--) {
1085
- // iterate reverse so any deletions don't overlap
1086
- const head = ranges[i].head;
1087
- const anchor = ranges[i].anchor;
1088
- const isSelection = !posEq(head, anchor);
1089
- if (isSelection) {
1090
- doc.replaceRange('', anchor, head);
1091
- }
1092
- else {
1093
- const line = doc.getLine(head.line).substring(0, head.ch);
1094
- if (line.match(/^\ +$/) !== null) {
1095
- // delete tabs
1096
- const prevTabStop = (Math.ceil(head.ch / tabSize) - 1) * tabSize;
1097
- const from = CodeMirror.Pos(head.line, prevTabStop);
1098
- doc.replaceRange('', from, head);
1099
- }
1100
- else {
1101
- // delete non-tabs
1102
- const from = cm.findPosH(head, -1, 'char', false);
1103
- doc.replaceRange('', from, head);
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
- Private.delSpaceToPrevTabStop = delSpaceToPrevTabStop;
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.setOption = setOption;
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