@jupyterlab/console 4.0.0-alpha.9 → 4.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/foreign.js +5 -2
- package/lib/foreign.js.map +1 -1
- package/lib/history.js +8 -8
- package/lib/history.js.map +1 -1
- package/lib/panel.d.ts +4 -4
- package/lib/panel.js +17 -21
- package/lib/panel.js.map +1 -1
- package/lib/widget.d.ts +25 -22
- package/lib/widget.js +83 -53
- package/lib/widget.js.map +1 -1
- package/package.json +28 -25
- package/src/foreign.ts +194 -0
- package/src/history.ts +370 -0
- package/src/index.ts +12 -0
- package/src/panel.ts +336 -0
- package/src/tokens.ts +18 -0
- package/src/widget.ts +1075 -0
package/src/widget.ts
ADDED
|
@@ -0,0 +1,1075 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Prec } from '@codemirror/state';
|
|
5
|
+
import { EditorView } from '@codemirror/view';
|
|
6
|
+
import { createStandaloneCell, ISharedRawCell } from '@jupyter/ydoc';
|
|
7
|
+
import { ISessionContext } from '@jupyterlab/apputils';
|
|
8
|
+
import {
|
|
9
|
+
AttachmentsCellModel,
|
|
10
|
+
Cell,
|
|
11
|
+
CellDragUtils,
|
|
12
|
+
CodeCell,
|
|
13
|
+
CodeCellModel,
|
|
14
|
+
ICodeCellModel,
|
|
15
|
+
IRawCellModel,
|
|
16
|
+
isCodeCellModel,
|
|
17
|
+
RawCell,
|
|
18
|
+
RawCellModel
|
|
19
|
+
} from '@jupyterlab/cells';
|
|
20
|
+
import { IEditorMimeTypeService } from '@jupyterlab/codeeditor';
|
|
21
|
+
import * as nbformat from '@jupyterlab/nbformat';
|
|
22
|
+
import { IObservableList, ObservableList } from '@jupyterlab/observables';
|
|
23
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
24
|
+
import { KernelMessage } from '@jupyterlab/services';
|
|
25
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
26
|
+
import { JSONObject, MimeData } from '@lumino/coreutils';
|
|
27
|
+
import { Drag } from '@lumino/dragdrop';
|
|
28
|
+
import { Message } from '@lumino/messaging';
|
|
29
|
+
import { ISignal, Signal } from '@lumino/signaling';
|
|
30
|
+
import { Panel, PanelLayout, Widget } from '@lumino/widgets';
|
|
31
|
+
import { ConsoleHistory, IConsoleHistory } from './history';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The data attribute added to a widget that has an active kernel.
|
|
35
|
+
*/
|
|
36
|
+
const KERNEL_USER = 'jpKernelUser';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The data attribute added to a widget can run code.
|
|
40
|
+
*/
|
|
41
|
+
const CODE_RUNNER = 'jpCodeRunner';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The class name added to console widgets.
|
|
45
|
+
*/
|
|
46
|
+
const CONSOLE_CLASS = 'jp-CodeConsole';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The class added to console cells
|
|
50
|
+
*/
|
|
51
|
+
const CONSOLE_CELL_CLASS = 'jp-Console-cell';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The class name added to the console banner.
|
|
55
|
+
*/
|
|
56
|
+
const BANNER_CLASS = 'jp-CodeConsole-banner';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The class name of the active prompt cell.
|
|
60
|
+
*/
|
|
61
|
+
const PROMPT_CLASS = 'jp-CodeConsole-promptCell';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The class name of the panel that holds cell content.
|
|
65
|
+
*/
|
|
66
|
+
const CONTENT_CLASS = 'jp-CodeConsole-content';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The class name of the panel that holds prompts.
|
|
70
|
+
*/
|
|
71
|
+
const INPUT_CLASS = 'jp-CodeConsole-input';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The timeout in ms for execution requests to the kernel.
|
|
75
|
+
*/
|
|
76
|
+
const EXECUTION_TIMEOUT = 250;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The mimetype used for Jupyter cell data.
|
|
80
|
+
*/
|
|
81
|
+
const JUPYTER_CELL_MIME = 'application/vnd.jupyter.cells';
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A widget containing a Jupyter console.
|
|
85
|
+
*
|
|
86
|
+
* #### Notes
|
|
87
|
+
* The CodeConsole class is intended to be used within a ConsolePanel
|
|
88
|
+
* instance. Under most circumstances, it is not instantiated by user code.
|
|
89
|
+
*/
|
|
90
|
+
export class CodeConsole extends Widget {
|
|
91
|
+
/**
|
|
92
|
+
* Construct a console widget.
|
|
93
|
+
*/
|
|
94
|
+
constructor(options: CodeConsole.IOptions) {
|
|
95
|
+
super();
|
|
96
|
+
this._translator = options.translator ?? nullTranslator;
|
|
97
|
+
this.addClass(CONSOLE_CLASS);
|
|
98
|
+
this.node.dataset[KERNEL_USER] = 'true';
|
|
99
|
+
this.node.dataset[CODE_RUNNER] = 'true';
|
|
100
|
+
this.node.tabIndex = -1; // Allow the widget to take focus.
|
|
101
|
+
|
|
102
|
+
// Create the panels that hold the content and input.
|
|
103
|
+
const layout = (this.layout = new PanelLayout());
|
|
104
|
+
this._cells = new ObservableList<Cell>();
|
|
105
|
+
this._content = new Panel();
|
|
106
|
+
this._input = new Panel();
|
|
107
|
+
|
|
108
|
+
this.contentFactory = options.contentFactory;
|
|
109
|
+
this.modelFactory = options.modelFactory ?? CodeConsole.defaultModelFactory;
|
|
110
|
+
this.rendermime = options.rendermime;
|
|
111
|
+
this.sessionContext = options.sessionContext;
|
|
112
|
+
this._mimeTypeService = options.mimeTypeService;
|
|
113
|
+
|
|
114
|
+
// Add top-level CSS classes.
|
|
115
|
+
this._content.addClass(CONTENT_CLASS);
|
|
116
|
+
this._input.addClass(INPUT_CLASS);
|
|
117
|
+
|
|
118
|
+
// Insert the content and input panes into the widget.
|
|
119
|
+
layout.addWidget(this._content);
|
|
120
|
+
layout.addWidget(this._input);
|
|
121
|
+
|
|
122
|
+
this._history = new ConsoleHistory({
|
|
123
|
+
sessionContext: this.sessionContext
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
void this._onKernelChanged();
|
|
127
|
+
|
|
128
|
+
this.sessionContext.kernelChanged.connect(this._onKernelChanged, this);
|
|
129
|
+
this.sessionContext.statusChanged.connect(
|
|
130
|
+
this._onKernelStatusChanged,
|
|
131
|
+
this
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A signal emitted when the console finished executing its prompt cell.
|
|
137
|
+
*/
|
|
138
|
+
get executed(): ISignal<this, Date> {
|
|
139
|
+
return this._executed;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* A signal emitted when a new prompt cell is created.
|
|
144
|
+
*/
|
|
145
|
+
get promptCellCreated(): ISignal<this, CodeCell> {
|
|
146
|
+
return this._promptCellCreated;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The content factory used by the console.
|
|
151
|
+
*/
|
|
152
|
+
readonly contentFactory: CodeConsole.IContentFactory;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The model factory for the console widget.
|
|
156
|
+
*/
|
|
157
|
+
readonly modelFactory: CodeConsole.IModelFactory;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The rendermime instance used by the console.
|
|
161
|
+
*/
|
|
162
|
+
readonly rendermime: IRenderMimeRegistry;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The client session used by the console.
|
|
166
|
+
*/
|
|
167
|
+
readonly sessionContext: ISessionContext;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The configuration options for the text editor widget.
|
|
171
|
+
*/
|
|
172
|
+
editorConfig: Record<string, any> = CodeConsole.defaultEditorConfig;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The list of content cells in the console.
|
|
176
|
+
*
|
|
177
|
+
* #### Notes
|
|
178
|
+
* This list does not include the current banner or the prompt for a console.
|
|
179
|
+
* It may include previous banners as raw cells.
|
|
180
|
+
*/
|
|
181
|
+
get cells(): IObservableList<Cell> {
|
|
182
|
+
return this._cells;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/*
|
|
186
|
+
* The console input prompt cell.
|
|
187
|
+
*/
|
|
188
|
+
get promptCell(): CodeCell | null {
|
|
189
|
+
const inputLayout = this._input.layout as PanelLayout;
|
|
190
|
+
return (inputLayout.widgets[0] as CodeCell) || null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Add a new cell to the content panel.
|
|
195
|
+
*
|
|
196
|
+
* @param cell - The code cell widget being added to the content panel.
|
|
197
|
+
*
|
|
198
|
+
* @param msgId - The optional execution message id for the cell.
|
|
199
|
+
*
|
|
200
|
+
* #### Notes
|
|
201
|
+
* This method is meant for use by outside classes that want to add cells to a
|
|
202
|
+
* console. It is distinct from the `inject` method in that it requires
|
|
203
|
+
* rendered code cell widgets and does not execute them (though it can store
|
|
204
|
+
* the execution message id).
|
|
205
|
+
*/
|
|
206
|
+
addCell(cell: CodeCell, msgId?: string): void {
|
|
207
|
+
cell.addClass(CONSOLE_CELL_CLASS);
|
|
208
|
+
this._content.addWidget(cell);
|
|
209
|
+
this._cells.push(cell);
|
|
210
|
+
if (msgId) {
|
|
211
|
+
this._msgIds.set(msgId, cell);
|
|
212
|
+
this._msgIdCells.set(cell, msgId);
|
|
213
|
+
}
|
|
214
|
+
cell.disposed.connect(this._onCellDisposed, this);
|
|
215
|
+
this.update();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Add a banner cell.
|
|
220
|
+
*/
|
|
221
|
+
addBanner(): void {
|
|
222
|
+
if (this._banner) {
|
|
223
|
+
// An old banner just becomes a normal cell now.
|
|
224
|
+
const cell = this._banner;
|
|
225
|
+
this._cells.push(this._banner);
|
|
226
|
+
cell.disposed.connect(this._onCellDisposed, this);
|
|
227
|
+
}
|
|
228
|
+
// Create the banner.
|
|
229
|
+
const model = this.modelFactory.createRawCell({
|
|
230
|
+
sharedModel: createStandaloneCell({
|
|
231
|
+
cell_type: 'raw',
|
|
232
|
+
source: '...'
|
|
233
|
+
}) as ISharedRawCell
|
|
234
|
+
});
|
|
235
|
+
const banner = (this._banner = new RawCell({
|
|
236
|
+
model,
|
|
237
|
+
contentFactory: this.contentFactory,
|
|
238
|
+
placeholder: false,
|
|
239
|
+
editorConfig: {
|
|
240
|
+
autoClosingBrackets: false,
|
|
241
|
+
codeFolding: false,
|
|
242
|
+
highlightActiveLine: false,
|
|
243
|
+
highlightTrailingWhitespace: false,
|
|
244
|
+
highlightWhitespace: false,
|
|
245
|
+
indentUnit: '2',
|
|
246
|
+
lineNumbers: false,
|
|
247
|
+
lineWrap: true,
|
|
248
|
+
matchBrackets: false,
|
|
249
|
+
readOnly: true,
|
|
250
|
+
rulers: [],
|
|
251
|
+
scrollPastEnd: false,
|
|
252
|
+
smartIndent: false,
|
|
253
|
+
tabSize: 4,
|
|
254
|
+
theme: 'jupyter'
|
|
255
|
+
}
|
|
256
|
+
})).initializeState();
|
|
257
|
+
banner.addClass(BANNER_CLASS);
|
|
258
|
+
banner.readOnly = true;
|
|
259
|
+
this._content.addWidget(banner);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Clear the code cells.
|
|
264
|
+
*/
|
|
265
|
+
clear(): void {
|
|
266
|
+
// Dispose all the content cells
|
|
267
|
+
const cells = this._cells;
|
|
268
|
+
while (cells.length > 0) {
|
|
269
|
+
cells.get(0).dispose();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Create a new cell with the built-in factory.
|
|
275
|
+
*/
|
|
276
|
+
createCodeCell(): CodeCell {
|
|
277
|
+
const factory = this.contentFactory;
|
|
278
|
+
const options = this._createCodeCellOptions();
|
|
279
|
+
const cell = factory.createCodeCell(options);
|
|
280
|
+
cell.readOnly = true;
|
|
281
|
+
cell.model.mimeType = this._mimetype;
|
|
282
|
+
return cell;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Dispose of the resources held by the widget.
|
|
287
|
+
*/
|
|
288
|
+
dispose(): void {
|
|
289
|
+
// Do nothing if already disposed.
|
|
290
|
+
if (this.isDisposed) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
this._msgIdCells = null!;
|
|
294
|
+
this._msgIds = null!;
|
|
295
|
+
this._history.dispose();
|
|
296
|
+
super.dispose();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Execute the current prompt.
|
|
301
|
+
*
|
|
302
|
+
* @param force - Whether to force execution without checking code
|
|
303
|
+
* completeness.
|
|
304
|
+
*
|
|
305
|
+
* @param timeout - The length of time, in milliseconds, that the execution
|
|
306
|
+
* should wait for the API to determine whether code being submitted is
|
|
307
|
+
* incomplete before attempting submission anyway. The default value is `250`.
|
|
308
|
+
*/
|
|
309
|
+
async execute(force = false, timeout = EXECUTION_TIMEOUT): Promise<void> {
|
|
310
|
+
if (this.sessionContext.session?.kernel?.status === 'dead') {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const promptCell = this.promptCell;
|
|
315
|
+
if (!promptCell) {
|
|
316
|
+
throw new Error('Cannot execute without a prompt cell');
|
|
317
|
+
}
|
|
318
|
+
promptCell.model.trusted = true;
|
|
319
|
+
|
|
320
|
+
if (force) {
|
|
321
|
+
// Create a new prompt cell before kernel execution to allow typeahead.
|
|
322
|
+
this.newPromptCell();
|
|
323
|
+
await this._execute(promptCell);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Check whether we should execute.
|
|
328
|
+
const shouldExecute = await this._shouldExecute(timeout);
|
|
329
|
+
if (this.isDisposed) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (shouldExecute) {
|
|
333
|
+
// Create a new prompt cell before kernel execution to allow typeahead.
|
|
334
|
+
this.newPromptCell();
|
|
335
|
+
this.promptCell!.editor!.focus();
|
|
336
|
+
await this._execute(promptCell);
|
|
337
|
+
} else {
|
|
338
|
+
// add a newline if we shouldn't execute
|
|
339
|
+
promptCell.editor!.newIndentedLine();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get a cell given a message id.
|
|
345
|
+
*
|
|
346
|
+
* @param msgId - The message id.
|
|
347
|
+
*/
|
|
348
|
+
getCell(msgId: string): CodeCell | undefined {
|
|
349
|
+
return this._msgIds.get(msgId);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Inject arbitrary code for the console to execute immediately.
|
|
354
|
+
*
|
|
355
|
+
* @param code - The code contents of the cell being injected.
|
|
356
|
+
*
|
|
357
|
+
* @returns A promise that indicates when the injected cell's execution ends.
|
|
358
|
+
*/
|
|
359
|
+
inject(code: string, metadata: JSONObject = {}): Promise<void> {
|
|
360
|
+
const cell = this.createCodeCell();
|
|
361
|
+
cell.model.sharedModel.setSource(code);
|
|
362
|
+
for (const key of Object.keys(metadata)) {
|
|
363
|
+
cell.model.setMetadata(key, metadata[key]);
|
|
364
|
+
}
|
|
365
|
+
this.addCell(cell);
|
|
366
|
+
return this._execute(cell);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Insert a line break in the prompt cell.
|
|
371
|
+
*/
|
|
372
|
+
insertLinebreak(): void {
|
|
373
|
+
const promptCell = this.promptCell;
|
|
374
|
+
if (!promptCell) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
promptCell.editor!.newIndentedLine();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Replaces the selected text in the prompt cell.
|
|
382
|
+
*
|
|
383
|
+
* @param text - The text to replace the selection.
|
|
384
|
+
*/
|
|
385
|
+
replaceSelection(text: string): void {
|
|
386
|
+
const promptCell = this.promptCell;
|
|
387
|
+
if (!promptCell) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
promptCell.editor!.replaceSelection?.(text);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Serialize the output.
|
|
395
|
+
*
|
|
396
|
+
* #### Notes
|
|
397
|
+
* This only serializes the code cells and the prompt cell if it exists, and
|
|
398
|
+
* skips any old banner cells.
|
|
399
|
+
*/
|
|
400
|
+
serialize(): nbformat.ICodeCell[] {
|
|
401
|
+
const cells: nbformat.ICodeCell[] = [];
|
|
402
|
+
for (const cell of this._cells) {
|
|
403
|
+
const model = cell.model;
|
|
404
|
+
if (isCodeCellModel(model)) {
|
|
405
|
+
cells.push(model.toJSON());
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (this.promptCell) {
|
|
410
|
+
cells.push(this.promptCell.model.toJSON());
|
|
411
|
+
}
|
|
412
|
+
return cells;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Handle `mousedown` events for the widget.
|
|
417
|
+
*/
|
|
418
|
+
private _evtMouseDown(event: MouseEvent): void {
|
|
419
|
+
const { button, shiftKey } = event;
|
|
420
|
+
|
|
421
|
+
// We only handle main or secondary button actions.
|
|
422
|
+
if (
|
|
423
|
+
!(button === 0 || button === 2) ||
|
|
424
|
+
// Shift right-click gives the browser default behavior.
|
|
425
|
+
(shiftKey && button === 2)
|
|
426
|
+
) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
let target = event.target as HTMLElement;
|
|
431
|
+
const cellFilter = (node: HTMLElement) =>
|
|
432
|
+
node.classList.contains(CONSOLE_CELL_CLASS);
|
|
433
|
+
let cellIndex = CellDragUtils.findCell(target, this._cells, cellFilter);
|
|
434
|
+
|
|
435
|
+
if (cellIndex === -1) {
|
|
436
|
+
// `event.target` sometimes gives an orphaned node in
|
|
437
|
+
// Firefox 57, which can have `null` anywhere in its parent line. If we fail
|
|
438
|
+
// to find a cell using `event.target`, try again using a target
|
|
439
|
+
// reconstructed from the position of the click event.
|
|
440
|
+
target = document.elementFromPoint(
|
|
441
|
+
event.clientX,
|
|
442
|
+
event.clientY
|
|
443
|
+
) as HTMLElement;
|
|
444
|
+
cellIndex = CellDragUtils.findCell(target, this._cells, cellFilter);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (cellIndex === -1) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const cell = this._cells.get(cellIndex);
|
|
452
|
+
|
|
453
|
+
const targetArea: CellDragUtils.ICellTargetArea =
|
|
454
|
+
CellDragUtils.detectTargetArea(cell, event.target as HTMLElement);
|
|
455
|
+
|
|
456
|
+
if (targetArea === 'prompt') {
|
|
457
|
+
this._dragData = {
|
|
458
|
+
pressX: event.clientX,
|
|
459
|
+
pressY: event.clientY,
|
|
460
|
+
index: cellIndex
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
this._focusedCell = cell;
|
|
464
|
+
|
|
465
|
+
document.addEventListener('mouseup', this, true);
|
|
466
|
+
document.addEventListener('mousemove', this, true);
|
|
467
|
+
event.preventDefault();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Handle `mousemove` event of widget
|
|
473
|
+
*/
|
|
474
|
+
private _evtMouseMove(event: MouseEvent) {
|
|
475
|
+
const data = this._dragData;
|
|
476
|
+
if (
|
|
477
|
+
data &&
|
|
478
|
+
CellDragUtils.shouldStartDrag(
|
|
479
|
+
data.pressX,
|
|
480
|
+
data.pressY,
|
|
481
|
+
event.clientX,
|
|
482
|
+
event.clientY
|
|
483
|
+
)
|
|
484
|
+
) {
|
|
485
|
+
void this._startDrag(data.index, event.clientX, event.clientY);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Start a drag event
|
|
491
|
+
*/
|
|
492
|
+
private _startDrag(
|
|
493
|
+
index: number,
|
|
494
|
+
clientX: number,
|
|
495
|
+
clientY: number
|
|
496
|
+
): Promise<void> {
|
|
497
|
+
const cellModel = this._focusedCell!.model as ICodeCellModel;
|
|
498
|
+
const selected: nbformat.ICell[] = [cellModel.toJSON()];
|
|
499
|
+
|
|
500
|
+
const dragImage = CellDragUtils.createCellDragImage(
|
|
501
|
+
this._focusedCell!,
|
|
502
|
+
selected
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
this._drag = new Drag({
|
|
506
|
+
mimeData: new MimeData(),
|
|
507
|
+
dragImage,
|
|
508
|
+
proposedAction: 'copy',
|
|
509
|
+
supportedActions: 'copy',
|
|
510
|
+
source: this
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
this._drag.mimeData.setData(JUPYTER_CELL_MIME, selected);
|
|
514
|
+
const textContent = cellModel.sharedModel.getSource();
|
|
515
|
+
this._drag.mimeData.setData('text/plain', textContent);
|
|
516
|
+
|
|
517
|
+
this._focusedCell = null;
|
|
518
|
+
|
|
519
|
+
document.removeEventListener('mousemove', this, true);
|
|
520
|
+
document.removeEventListener('mouseup', this, true);
|
|
521
|
+
return this._drag.start(clientX, clientY).then(() => {
|
|
522
|
+
if (this.isDisposed) {
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
this._drag = null;
|
|
526
|
+
this._dragData = null;
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Handle the DOM events for the widget.
|
|
532
|
+
*
|
|
533
|
+
* @param event -The DOM event sent to the widget.
|
|
534
|
+
*
|
|
535
|
+
* #### Notes
|
|
536
|
+
* This method implements the DOM `EventListener` interface and is
|
|
537
|
+
* called in response to events on the notebook panel's node. It should
|
|
538
|
+
* not be called directly by user code.
|
|
539
|
+
*/
|
|
540
|
+
handleEvent(event: Event): void {
|
|
541
|
+
switch (event.type) {
|
|
542
|
+
case 'keydown':
|
|
543
|
+
this._evtKeyDown(event as KeyboardEvent);
|
|
544
|
+
break;
|
|
545
|
+
case 'mousedown':
|
|
546
|
+
this._evtMouseDown(event as MouseEvent);
|
|
547
|
+
break;
|
|
548
|
+
case 'mousemove':
|
|
549
|
+
this._evtMouseMove(event as MouseEvent);
|
|
550
|
+
break;
|
|
551
|
+
case 'mouseup':
|
|
552
|
+
this._evtMouseUp(event as MouseEvent);
|
|
553
|
+
break;
|
|
554
|
+
default:
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Handle `after_attach` messages for the widget.
|
|
561
|
+
*/
|
|
562
|
+
protected onAfterAttach(msg: Message): void {
|
|
563
|
+
const node = this.node;
|
|
564
|
+
node.addEventListener('keydown', this, true);
|
|
565
|
+
node.addEventListener('click', this);
|
|
566
|
+
node.addEventListener('mousedown', this);
|
|
567
|
+
// Create a prompt if necessary.
|
|
568
|
+
if (!this.promptCell) {
|
|
569
|
+
this.newPromptCell();
|
|
570
|
+
} else {
|
|
571
|
+
this.promptCell.editor!.focus();
|
|
572
|
+
this.update();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Handle `before-detach` messages for the widget.
|
|
578
|
+
*/
|
|
579
|
+
protected onBeforeDetach(msg: Message): void {
|
|
580
|
+
const node = this.node;
|
|
581
|
+
node.removeEventListener('keydown', this, true);
|
|
582
|
+
node.removeEventListener('click', this);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Handle `'activate-request'` messages.
|
|
587
|
+
*/
|
|
588
|
+
protected onActivateRequest(msg: Message): void {
|
|
589
|
+
const editor = this.promptCell && this.promptCell.editor;
|
|
590
|
+
if (editor) {
|
|
591
|
+
editor.focus();
|
|
592
|
+
}
|
|
593
|
+
this.update();
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Make a new prompt cell.
|
|
598
|
+
*/
|
|
599
|
+
protected newPromptCell(): void {
|
|
600
|
+
let promptCell = this.promptCell;
|
|
601
|
+
const input = this._input;
|
|
602
|
+
|
|
603
|
+
// Make the last prompt read-only, clear its signals, and move to content.
|
|
604
|
+
if (promptCell) {
|
|
605
|
+
promptCell.readOnly = true;
|
|
606
|
+
promptCell.removeClass(PROMPT_CLASS);
|
|
607
|
+
Signal.clearData(promptCell.editor);
|
|
608
|
+
const child = input.widgets[0];
|
|
609
|
+
child.parent = null;
|
|
610
|
+
this.addCell(promptCell);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Create the new prompt cell.
|
|
614
|
+
const factory = this.contentFactory;
|
|
615
|
+
const options = this._createCodeCellOptions();
|
|
616
|
+
promptCell = factory.createCodeCell(options);
|
|
617
|
+
promptCell.model.mimeType = this._mimetype;
|
|
618
|
+
promptCell.addClass(PROMPT_CLASS);
|
|
619
|
+
|
|
620
|
+
// Add the prompt cell to the DOM, making `this.promptCell` valid again.
|
|
621
|
+
this._input.addWidget(promptCell);
|
|
622
|
+
|
|
623
|
+
this._history.editor = promptCell.editor;
|
|
624
|
+
this._promptCellCreated.emit(promptCell);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Handle `update-request` messages.
|
|
629
|
+
*/
|
|
630
|
+
protected onUpdateRequest(msg: Message): void {
|
|
631
|
+
Private.scrollToBottom(this._content.node);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Handle the `'keydown'` event for the widget.
|
|
636
|
+
*/
|
|
637
|
+
private _evtKeyDown(event: KeyboardEvent): void {
|
|
638
|
+
const editor = this.promptCell && this.promptCell.editor;
|
|
639
|
+
if (!editor) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (event.keyCode === 13 && !editor.hasFocus()) {
|
|
643
|
+
event.preventDefault();
|
|
644
|
+
editor.focus();
|
|
645
|
+
} else if (event.keyCode === 27 && editor.hasFocus()) {
|
|
646
|
+
// Set to command mode
|
|
647
|
+
event.preventDefault();
|
|
648
|
+
event.stopPropagation();
|
|
649
|
+
this.node.focus();
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Handle the `'mouseup'` event for the widget.
|
|
655
|
+
*/
|
|
656
|
+
private _evtMouseUp(event: MouseEvent): void {
|
|
657
|
+
if (
|
|
658
|
+
this.promptCell &&
|
|
659
|
+
this.promptCell.node.contains(event.target as HTMLElement)
|
|
660
|
+
) {
|
|
661
|
+
this.promptCell.editor!.focus();
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Execute the code in the current prompt cell.
|
|
667
|
+
*/
|
|
668
|
+
private _execute(cell: CodeCell): Promise<void> {
|
|
669
|
+
const source = cell.model.sharedModel.getSource();
|
|
670
|
+
this._history.push(source);
|
|
671
|
+
// If the source of the console is just "clear", clear the console as we
|
|
672
|
+
// do in IPython or QtConsole.
|
|
673
|
+
if (source === 'clear' || source === '%clear') {
|
|
674
|
+
this.clear();
|
|
675
|
+
return Promise.resolve(void 0);
|
|
676
|
+
}
|
|
677
|
+
cell.model.contentChanged.connect(this.update, this);
|
|
678
|
+
const onSuccess = (value: KernelMessage.IExecuteReplyMsg) => {
|
|
679
|
+
if (this.isDisposed) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (value && value.content.status === 'ok') {
|
|
683
|
+
const content = value.content;
|
|
684
|
+
// Use deprecated payloads for backwards compatibility.
|
|
685
|
+
if (content.payload && content.payload.length) {
|
|
686
|
+
const setNextInput = content.payload.filter(i => {
|
|
687
|
+
return (i as any).source === 'set_next_input';
|
|
688
|
+
})[0];
|
|
689
|
+
if (setNextInput) {
|
|
690
|
+
const text = (setNextInput as any).text;
|
|
691
|
+
// Ignore the `replace` value and always set the next cell.
|
|
692
|
+
cell.model.sharedModel.setSource(text);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} else if (value && value.content.status === 'error') {
|
|
696
|
+
for (const cell of this._cells) {
|
|
697
|
+
if ((cell.model as ICodeCellModel).executionCount === null) {
|
|
698
|
+
cell.setPrompt('');
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
cell.model.contentChanged.disconnect(this.update, this);
|
|
703
|
+
this.update();
|
|
704
|
+
this._executed.emit(new Date());
|
|
705
|
+
};
|
|
706
|
+
const onFailure = () => {
|
|
707
|
+
if (this.isDisposed) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
cell.model.contentChanged.disconnect(this.update, this);
|
|
711
|
+
this.update();
|
|
712
|
+
};
|
|
713
|
+
return CodeCell.execute(cell, this.sessionContext).then(
|
|
714
|
+
onSuccess,
|
|
715
|
+
onFailure
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* Update the console based on the kernel info.
|
|
721
|
+
*/
|
|
722
|
+
private _handleInfo(info: KernelMessage.IInfoReplyMsg['content']): void {
|
|
723
|
+
if (info.status !== 'ok') {
|
|
724
|
+
this._banner!.model.sharedModel.setSource(
|
|
725
|
+
'Error in getting kernel banner'
|
|
726
|
+
);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
this._banner!.model.sharedModel.setSource(info.banner);
|
|
730
|
+
const lang = info.language_info as nbformat.ILanguageInfoMetadata;
|
|
731
|
+
this._mimetype = this._mimeTypeService.getMimeTypeByLanguage(lang);
|
|
732
|
+
if (this.promptCell) {
|
|
733
|
+
this.promptCell.model.mimeType = this._mimetype;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Create the options used to initialize a code cell widget.
|
|
739
|
+
*/
|
|
740
|
+
private _createCodeCellOptions(): CodeCell.IOptions {
|
|
741
|
+
const contentFactory = this.contentFactory;
|
|
742
|
+
const modelFactory = this.modelFactory;
|
|
743
|
+
const model = modelFactory.createCodeCell({});
|
|
744
|
+
const rendermime = this.rendermime;
|
|
745
|
+
const editorConfig = this.editorConfig;
|
|
746
|
+
|
|
747
|
+
// Suppress the default "Enter" key handling.
|
|
748
|
+
const onKeyDown = EditorView.domEventHandlers({
|
|
749
|
+
keydown: (event: KeyboardEvent, view: EditorView) => {
|
|
750
|
+
if (event.keyCode === 13) {
|
|
751
|
+
event.preventDefault();
|
|
752
|
+
return true;
|
|
753
|
+
}
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
return {
|
|
759
|
+
model,
|
|
760
|
+
rendermime,
|
|
761
|
+
contentFactory,
|
|
762
|
+
editorConfig,
|
|
763
|
+
editorExtensions: [Prec.high(onKeyDown)],
|
|
764
|
+
placeholder: false,
|
|
765
|
+
translator: this._translator
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Handle cell disposed signals.
|
|
771
|
+
*/
|
|
772
|
+
private _onCellDisposed(sender: Cell, args: void): void {
|
|
773
|
+
if (!this.isDisposed) {
|
|
774
|
+
this._cells.removeValue(sender);
|
|
775
|
+
const msgId = this._msgIdCells.get(sender as CodeCell);
|
|
776
|
+
if (msgId) {
|
|
777
|
+
this._msgIdCells.delete(sender as CodeCell);
|
|
778
|
+
this._msgIds.delete(msgId);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Test whether we should execute the prompt cell.
|
|
785
|
+
*/
|
|
786
|
+
private _shouldExecute(timeout: number): Promise<boolean> {
|
|
787
|
+
const promptCell = this.promptCell;
|
|
788
|
+
if (!promptCell) {
|
|
789
|
+
return Promise.resolve(false);
|
|
790
|
+
}
|
|
791
|
+
const model = promptCell.model;
|
|
792
|
+
const code = model.sharedModel.getSource();
|
|
793
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
794
|
+
const timer = setTimeout(() => {
|
|
795
|
+
resolve(true);
|
|
796
|
+
}, timeout);
|
|
797
|
+
const kernel = this.sessionContext.session?.kernel;
|
|
798
|
+
if (!kernel) {
|
|
799
|
+
resolve(false);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
kernel
|
|
803
|
+
.requestIsComplete({ code })
|
|
804
|
+
.then(isComplete => {
|
|
805
|
+
clearTimeout(timer);
|
|
806
|
+
if (this.isDisposed) {
|
|
807
|
+
resolve(false);
|
|
808
|
+
}
|
|
809
|
+
if (isComplete.content.status !== 'incomplete') {
|
|
810
|
+
resolve(true);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
resolve(false);
|
|
814
|
+
})
|
|
815
|
+
.catch(() => {
|
|
816
|
+
resolve(true);
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Handle a change to the kernel.
|
|
823
|
+
*/
|
|
824
|
+
private async _onKernelChanged(): Promise<void> {
|
|
825
|
+
this.clear();
|
|
826
|
+
if (this._banner) {
|
|
827
|
+
this._banner.dispose();
|
|
828
|
+
this._banner = null;
|
|
829
|
+
}
|
|
830
|
+
this.addBanner();
|
|
831
|
+
if (this.sessionContext.session?.kernel) {
|
|
832
|
+
this._handleInfo(await this.sessionContext.session.kernel.info);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Handle a change to the kernel status.
|
|
838
|
+
*/
|
|
839
|
+
private async _onKernelStatusChanged(): Promise<void> {
|
|
840
|
+
const kernel = this.sessionContext.session?.kernel;
|
|
841
|
+
if (kernel?.status === 'restarting') {
|
|
842
|
+
this.addBanner();
|
|
843
|
+
this._handleInfo(await kernel?.info);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
private _banner: RawCell | null = null;
|
|
848
|
+
private _cells: IObservableList<Cell>;
|
|
849
|
+
private _content: Panel;
|
|
850
|
+
private _executed = new Signal<this, Date>(this);
|
|
851
|
+
private _history: IConsoleHistory;
|
|
852
|
+
private _input: Panel;
|
|
853
|
+
private _mimetype = 'text/x-ipython';
|
|
854
|
+
private _mimeTypeService: IEditorMimeTypeService;
|
|
855
|
+
private _msgIds = new Map<string, CodeCell>();
|
|
856
|
+
private _msgIdCells = new Map<CodeCell, string>();
|
|
857
|
+
private _promptCellCreated = new Signal<this, CodeCell>(this);
|
|
858
|
+
private _dragData: {
|
|
859
|
+
pressX: number;
|
|
860
|
+
pressY: number;
|
|
861
|
+
index: number;
|
|
862
|
+
} | null = null;
|
|
863
|
+
private _drag: Drag | null = null;
|
|
864
|
+
private _focusedCell: Cell | null = null;
|
|
865
|
+
private _translator: ITranslator;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
/**
|
|
869
|
+
* A namespace for CodeConsole statics.
|
|
870
|
+
*/
|
|
871
|
+
export namespace CodeConsole {
|
|
872
|
+
/**
|
|
873
|
+
* The initialization options for a console widget.
|
|
874
|
+
*/
|
|
875
|
+
export interface IOptions {
|
|
876
|
+
/**
|
|
877
|
+
* The content factory for the console widget.
|
|
878
|
+
*/
|
|
879
|
+
contentFactory: IContentFactory;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* The model factory for the console widget.
|
|
883
|
+
*/
|
|
884
|
+
modelFactory?: IModelFactory;
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* The mime renderer for the console widget.
|
|
888
|
+
*/
|
|
889
|
+
rendermime: IRenderMimeRegistry;
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* The client session for the console widget.
|
|
893
|
+
*/
|
|
894
|
+
sessionContext: ISessionContext;
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* The service used to look up mime types.
|
|
898
|
+
*/
|
|
899
|
+
mimeTypeService: IEditorMimeTypeService;
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* The application language translator.
|
|
903
|
+
*/
|
|
904
|
+
translator?: ITranslator;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Default console editor configuration
|
|
909
|
+
*/
|
|
910
|
+
export const defaultEditorConfig: Record<string, any> = {
|
|
911
|
+
codeFolding: false,
|
|
912
|
+
lineNumbers: false
|
|
913
|
+
};
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* A content factory for console children.
|
|
917
|
+
*/
|
|
918
|
+
export interface IContentFactory extends Cell.IContentFactory {
|
|
919
|
+
/**
|
|
920
|
+
* Create a new code cell widget.
|
|
921
|
+
*/
|
|
922
|
+
createCodeCell(options: CodeCell.IOptions): CodeCell;
|
|
923
|
+
|
|
924
|
+
/**
|
|
925
|
+
* Create a new raw cell widget.
|
|
926
|
+
*/
|
|
927
|
+
createRawCell(options: RawCell.IOptions): RawCell;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Default implementation of `IContentFactory`.
|
|
932
|
+
*/
|
|
933
|
+
export class ContentFactory
|
|
934
|
+
extends Cell.ContentFactory
|
|
935
|
+
implements IContentFactory
|
|
936
|
+
{
|
|
937
|
+
/**
|
|
938
|
+
* Create a new code cell widget.
|
|
939
|
+
*
|
|
940
|
+
* #### Notes
|
|
941
|
+
* If no cell content factory is passed in with the options, the one on the
|
|
942
|
+
* notebook content factory is used.
|
|
943
|
+
*/
|
|
944
|
+
createCodeCell(options: CodeCell.IOptions): CodeCell {
|
|
945
|
+
return new CodeCell(options).initializeState();
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Create a new raw cell widget.
|
|
950
|
+
*
|
|
951
|
+
* #### Notes
|
|
952
|
+
* If no cell content factory is passed in with the options, the one on the
|
|
953
|
+
* notebook content factory is used.
|
|
954
|
+
*/
|
|
955
|
+
createRawCell(options: RawCell.IOptions): RawCell {
|
|
956
|
+
return new RawCell(options).initializeState();
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* A namespace for the code console content factory.
|
|
962
|
+
*/
|
|
963
|
+
export namespace ContentFactory {
|
|
964
|
+
/**
|
|
965
|
+
* An initialize options for `ContentFactory`.
|
|
966
|
+
*/
|
|
967
|
+
export interface IOptions extends Cell.IContentFactory {}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* A model factory for a console widget.
|
|
972
|
+
*/
|
|
973
|
+
export interface IModelFactory {
|
|
974
|
+
/**
|
|
975
|
+
* The factory for code cell content.
|
|
976
|
+
*/
|
|
977
|
+
readonly codeCellContentFactory: CodeCellModel.IContentFactory;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Create a new code cell.
|
|
981
|
+
*
|
|
982
|
+
* @param options - The options used to create the cell.
|
|
983
|
+
*
|
|
984
|
+
* @returns A new code cell. If a source cell is provided, the
|
|
985
|
+
* new cell will be initialized with the data from the source.
|
|
986
|
+
*/
|
|
987
|
+
createCodeCell(options: CodeCellModel.IOptions): ICodeCellModel;
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Create a new raw cell.
|
|
991
|
+
*
|
|
992
|
+
* @param options - The options used to create the cell.
|
|
993
|
+
*
|
|
994
|
+
* @returns A new raw cell. If a source cell is provided, the
|
|
995
|
+
* new cell will be initialized with the data from the source.
|
|
996
|
+
*/
|
|
997
|
+
createRawCell(
|
|
998
|
+
options: AttachmentsCellModel.IOptions<ISharedRawCell>
|
|
999
|
+
): IRawCellModel;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* The default implementation of an `IModelFactory`.
|
|
1004
|
+
*/
|
|
1005
|
+
export class ModelFactory {
|
|
1006
|
+
/**
|
|
1007
|
+
* Create a new cell model factory.
|
|
1008
|
+
*/
|
|
1009
|
+
constructor(options: IModelFactoryOptions = {}) {
|
|
1010
|
+
this.codeCellContentFactory =
|
|
1011
|
+
options.codeCellContentFactory || CodeCellModel.defaultContentFactory;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* The factory for output area models.
|
|
1016
|
+
*/
|
|
1017
|
+
readonly codeCellContentFactory: CodeCellModel.IContentFactory;
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Create a new code cell.
|
|
1021
|
+
* @param options - The data to use for the original source data.
|
|
1022
|
+
* @returns A new code cell. If a source cell is provided, the
|
|
1023
|
+
new cell will be initialized with the data from the source.
|
|
1024
|
+
If the contentFactory is not provided, the instance
|
|
1025
|
+
`codeCellContentFactory` will be used.
|
|
1026
|
+
*/
|
|
1027
|
+
createCodeCell(options: CodeCellModel.IOptions = {}): ICodeCellModel {
|
|
1028
|
+
if (!options.contentFactory) {
|
|
1029
|
+
options.contentFactory = this.codeCellContentFactory;
|
|
1030
|
+
}
|
|
1031
|
+
return new CodeCellModel(options);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Create a new raw cell.
|
|
1036
|
+
* @param options - The data to use for the original source data.
|
|
1037
|
+
* @returns A new raw cell. If a source cell is provided, the
|
|
1038
|
+
new cell will be initialized with the data from the source.
|
|
1039
|
+
*/
|
|
1040
|
+
createRawCell(
|
|
1041
|
+
options: AttachmentsCellModel.IOptions<ISharedRawCell>
|
|
1042
|
+
): IRawCellModel {
|
|
1043
|
+
return new RawCellModel(options);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* The options used to initialize a `ModelFactory`.
|
|
1049
|
+
*/
|
|
1050
|
+
export interface IModelFactoryOptions {
|
|
1051
|
+
/**
|
|
1052
|
+
* The factory for output area models.
|
|
1053
|
+
*/
|
|
1054
|
+
codeCellContentFactory?: CodeCellModel.IContentFactory;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* The default `ModelFactory` instance.
|
|
1059
|
+
*/
|
|
1060
|
+
export const defaultModelFactory = new ModelFactory({});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* A namespace for console widget private data.
|
|
1065
|
+
*/
|
|
1066
|
+
namespace Private {
|
|
1067
|
+
/**
|
|
1068
|
+
* Jump to the bottom of a node.
|
|
1069
|
+
*
|
|
1070
|
+
* @param node - The scrollable element.
|
|
1071
|
+
*/
|
|
1072
|
+
export function scrollToBottom(node: HTMLElement): void {
|
|
1073
|
+
node.scrollTop = node.scrollHeight - node.clientHeight;
|
|
1074
|
+
}
|
|
1075
|
+
}
|