@theia/notebook 1.53.0-next.55 → 1.53.0-next.64

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.
Files changed (78) hide show
  1. package/README.md +30 -30
  2. package/lib/browser/index.d.ts +1 -0
  3. package/lib/browser/index.d.ts.map +1 -1
  4. package/lib/browser/index.js +1 -0
  5. package/lib/browser/index.js.map +1 -1
  6. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  7. package/lib/browser/notebook-frontend-module.js +2 -0
  8. package/lib/browser/notebook-frontend-module.js.map +1 -1
  9. package/lib/browser/service/notebook-cell-editor-service.d.ts +16 -0
  10. package/lib/browser/service/notebook-cell-editor-service.d.ts.map +1 -0
  11. package/lib/browser/service/notebook-cell-editor-service.js +53 -0
  12. package/lib/browser/service/notebook-cell-editor-service.js.map +1 -0
  13. package/lib/browser/view/notebook-cell-editor.d.ts +2 -0
  14. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  15. package/lib/browser/view/notebook-cell-editor.js +12 -1
  16. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  17. package/lib/browser/view/notebook-code-cell-view.d.ts +2 -0
  18. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  19. package/lib/browser/view/notebook-code-cell-view.js +6 -1
  20. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  21. package/lib/browser/view/notebook-markdown-cell-view.d.ts +2 -0
  22. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  23. package/lib/browser/view/notebook-markdown-cell-view.js +8 -3
  24. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  25. package/package.json +7 -7
  26. package/src/browser/contributions/cell-operations.ts +44 -44
  27. package/src/browser/contributions/notebook-actions-contribution.ts +379 -379
  28. package/src/browser/contributions/notebook-cell-actions-contribution.ts +525 -525
  29. package/src/browser/contributions/notebook-color-contribution.ts +268 -268
  30. package/src/browser/contributions/notebook-context-keys.ts +113 -113
  31. package/src/browser/contributions/notebook-label-provider-contribution.ts +85 -85
  32. package/src/browser/contributions/notebook-outline-contribution.ts +114 -114
  33. package/src/browser/contributions/notebook-output-action-contribution.ts +82 -82
  34. package/src/browser/contributions/notebook-preferences.ts +92 -92
  35. package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -77
  36. package/src/browser/contributions/notebook-undo-redo-handler.ts +41 -41
  37. package/src/browser/index.ts +28 -27
  38. package/src/browser/notebook-cell-resource-resolver.ts +130 -130
  39. package/src/browser/notebook-editor-widget-factory.ts +82 -82
  40. package/src/browser/notebook-editor-widget.tsx +330 -330
  41. package/src/browser/notebook-frontend-module.ts +121 -119
  42. package/src/browser/notebook-open-handler.ts +120 -120
  43. package/src/browser/notebook-output-utils.ts +119 -119
  44. package/src/browser/notebook-renderer-registry.ts +85 -85
  45. package/src/browser/notebook-type-registry.ts +54 -54
  46. package/src/browser/notebook-types.ts +186 -186
  47. package/src/browser/renderers/cell-output-webview.ts +33 -33
  48. package/src/browser/service/notebook-cell-editor-service.ts +56 -0
  49. package/src/browser/service/notebook-clipboard-service.ts +43 -43
  50. package/src/browser/service/notebook-context-manager.ts +162 -162
  51. package/src/browser/service/notebook-editor-widget-service.ts +101 -101
  52. package/src/browser/service/notebook-execution-service.ts +139 -139
  53. package/src/browser/service/notebook-execution-state-service.ts +311 -311
  54. package/src/browser/service/notebook-kernel-history-service.ts +124 -124
  55. package/src/browser/service/notebook-kernel-quick-pick-service.ts +479 -479
  56. package/src/browser/service/notebook-kernel-service.ts +357 -357
  57. package/src/browser/service/notebook-model-resolver-service.ts +160 -160
  58. package/src/browser/service/notebook-monaco-text-model-service.ts +48 -48
  59. package/src/browser/service/notebook-options.ts +155 -155
  60. package/src/browser/service/notebook-renderer-messaging-service.ts +121 -121
  61. package/src/browser/service/notebook-service.ts +215 -215
  62. package/src/browser/style/index.css +484 -483
  63. package/src/browser/view/notebook-cell-editor.tsx +276 -263
  64. package/src/browser/view/notebook-cell-list-view.tsx +279 -279
  65. package/src/browser/view/notebook-cell-toolbar-factory.tsx +102 -102
  66. package/src/browser/view/notebook-cell-toolbar.tsx +74 -74
  67. package/src/browser/view/notebook-code-cell-view.tsx +355 -350
  68. package/src/browser/view/notebook-find-widget.tsx +335 -335
  69. package/src/browser/view/notebook-main-toolbar.tsx +235 -235
  70. package/src/browser/view/notebook-markdown-cell-view.tsx +215 -208
  71. package/src/browser/view/notebook-viewport-service.ts +61 -61
  72. package/src/browser/view-model/notebook-cell-model.ts +473 -473
  73. package/src/browser/view-model/notebook-cell-output-model.ts +100 -100
  74. package/src/browser/view-model/notebook-model.ts +550 -550
  75. package/src/common/index.ts +18 -18
  76. package/src/common/notebook-common.ts +337 -337
  77. package/src/common/notebook-protocol.ts +35 -35
  78. package/src/common/notebook-range.ts +30 -30
@@ -1,473 +1,473 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2023 TypeFox and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
- /*---------------------------------------------------------------------------------------------
17
- * Copyright (c) Microsoft Corporation. All rights reserved.
18
- * Licensed under the MIT License. See License.txt in the project root for license information.
19
- *--------------------------------------------------------------------------------------------*/
20
-
21
- import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core';
22
- import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
23
- import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
24
- import { type MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
25
- import {
26
- CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
27
- NotebookCellMetadata, CellOutput, CellData, CellOutputItem
28
- } from '../../common';
29
- import { NotebookCellOutputsSplice } from '../notebook-types';
30
- import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service';
31
- import { NotebookCellOutputModel } from './notebook-cell-output-model';
32
- import { PreferenceService } from '@theia/core/lib/browser';
33
- import { NotebookPreferences } from '../contributions/notebook-preferences';
34
- import { LanguageService } from '@theia/core/lib/browser/language-service';
35
- import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from '../view/notebook-find-widget';
36
- import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
37
-
38
- export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
39
- export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
40
-
41
- export type CellEditorFocusRequest = number | 'lastLine' | undefined;
42
-
43
- export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container {
44
- const child = parent.createChild();
45
-
46
- child.bind(NotebookCellModelProps).toConstantValue(props);
47
- child.bind(NotebookCellModel).toSelf();
48
-
49
- return child;
50
- }
51
-
52
- export interface CellInternalMetadataChangedEvent {
53
- readonly lastRunSuccessChanged?: boolean;
54
- }
55
-
56
- export interface NotebookCell {
57
- readonly uri: URI;
58
- handle: number;
59
- language: string;
60
- cellKind: CellKind;
61
- outputs: CellOutput[];
62
- metadata: NotebookCellMetadata;
63
- internalMetadata: NotebookCellInternalMetadata;
64
- text: string;
65
- onDidChangeOutputs?: Event<NotebookCellOutputsSplice>;
66
- onDidChangeOutputItems?: Event<CellOutput>;
67
- onDidChangeLanguage: Event<string>;
68
- onDidChangeMetadata: Event<void>;
69
- onDidChangeInternalMetadata: Event<CellInternalMetadataChangedEvent>;
70
-
71
- }
72
-
73
- const NotebookCellModelProps = Symbol('NotebookModelProps');
74
- export interface NotebookCellModelProps {
75
- readonly uri: URI,
76
- readonly handle: number,
77
- source: string,
78
- language: string,
79
- readonly cellKind: CellKind,
80
- outputs: CellOutput[],
81
- metadata?: NotebookCellMetadata | undefined,
82
- internalMetadata?: NotebookCellInternalMetadata | undefined,
83
- readonly collapseState?: NotebookCellCollapseState | undefined,
84
-
85
- }
86
-
87
- @injectable()
88
- export class NotebookCellModel implements NotebookCell, Disposable {
89
-
90
- protected readonly onDidChangeOutputsEmitter = new Emitter<NotebookCellOutputsSplice>();
91
- readonly onDidChangeOutputs = this.onDidChangeOutputsEmitter.event;
92
-
93
- protected readonly onDidChangeOutputItemsEmitter = new Emitter<CellOutput>();
94
- readonly onDidChangeOutputItems = this.onDidChangeOutputItemsEmitter.event;
95
-
96
- protected readonly onDidChangeContentEmitter = new Emitter<'content' | 'language' | 'mime'>();
97
- readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
98
-
99
- protected readonly onDidChangeMetadataEmitter = new Emitter<void>();
100
- readonly onDidChangeMetadata = this.onDidChangeMetadataEmitter.event;
101
-
102
- protected readonly onDidChangeInternalMetadataEmitter = new Emitter<CellInternalMetadataChangedEvent>();
103
- readonly onDidChangeInternalMetadata = this.onDidChangeInternalMetadataEmitter.event;
104
-
105
- protected readonly onDidChangeLanguageEmitter = new Emitter<string>();
106
- readonly onDidChangeLanguage = this.onDidChangeLanguageEmitter.event;
107
-
108
- protected readonly onDidRequestCellEditChangeEmitter = new Emitter<boolean>();
109
- readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event;
110
-
111
- protected readonly onWillFocusCellEditorEmitter = new Emitter<CellEditorFocusRequest>();
112
- readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event;
113
-
114
- protected readonly onWillBlurCellEditorEmitter = new Emitter<void>();
115
- readonly onWillBlurCellEditor = this.onWillBlurCellEditorEmitter.event;
116
-
117
- protected readonly onDidChangeEditorOptionsEmitter = new Emitter<MonacoEditor.IOptions>();
118
- readonly onDidChangeEditorOptions = this.onDidChangeEditorOptionsEmitter.event;
119
-
120
- protected readonly outputVisibilityChangeEmitter = new Emitter<boolean>();
121
- readonly onDidChangeOutputVisibility = this.outputVisibilityChangeEmitter.event;
122
-
123
- protected readonly onDidFindMatchesEmitter = new Emitter<NotebookCodeEditorFindMatch[]>();
124
- readonly onDidFindMatches: Event<NotebookCodeEditorFindMatch[]> = this.onDidFindMatchesEmitter.event;
125
-
126
- protected readonly onDidSelectFindMatchEmitter = new Emitter<NotebookCodeEditorFindMatch>();
127
- readonly onDidSelectFindMatch: Event<NotebookCodeEditorFindMatch> = this.onDidSelectFindMatchEmitter.event;
128
-
129
- protected onDidRequestCenterEditorEmitter = new Emitter<void>();
130
- readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event;
131
-
132
- @inject(NotebookCellModelProps)
133
- protected readonly props: NotebookCellModelProps;
134
-
135
- @inject(NotebookMonacoTextModelService)
136
- protected readonly textModelService: NotebookMonacoTextModelService;
137
-
138
- @inject(LanguageService)
139
- protected readonly languageService: LanguageService;
140
-
141
- @inject(PreferenceService)
142
- protected readonly preferenceService: PreferenceService;
143
-
144
- get outputs(): NotebookCellOutputModel[] {
145
- return this._outputs;
146
- }
147
-
148
- protected _outputs: NotebookCellOutputModel[];
149
-
150
- get metadata(): NotebookCellMetadata {
151
- return this._metadata;
152
- }
153
-
154
- set metadata(newMetadata: NotebookCellMetadata) {
155
- this._metadata = newMetadata;
156
- this.onDidChangeMetadataEmitter.fire();
157
- }
158
-
159
- protected _metadata: NotebookCellMetadata;
160
-
161
- protected toDispose = new DisposableCollection();
162
-
163
- protected _internalMetadata: NotebookCellInternalMetadata;
164
-
165
- get internalMetadata(): NotebookCellInternalMetadata {
166
- return this._internalMetadata;
167
- }
168
-
169
- set internalMetadata(newInternalMetadata: NotebookCellInternalMetadata) {
170
- const lastRunSuccessChanged = this._internalMetadata.lastRunSuccess !== newInternalMetadata.lastRunSuccess;
171
- newInternalMetadata = {
172
- ...newInternalMetadata,
173
- ...{ runStartTimeAdjustment: computeRunStartTimeAdjustment(this._internalMetadata, newInternalMetadata) }
174
- };
175
- this._internalMetadata = newInternalMetadata;
176
- this.onDidChangeInternalMetadataEmitter.fire({ lastRunSuccessChanged });
177
-
178
- }
179
-
180
- protected textModel?: MonacoEditorModel;
181
-
182
- get text(): string {
183
- return this.textModel && !this.textModel.isDisposed() ? this.textModel.getText() : this.source;
184
- }
185
-
186
- get source(): string {
187
- return this.props.source;
188
- }
189
-
190
- set source(source: string) {
191
- this.props.source = source;
192
- this.textModel?.textEditorModel.setValue(source);
193
- }
194
-
195
- get language(): string {
196
- return this.props.language;
197
- }
198
-
199
- set language(newLanguage: string) {
200
- if (this.language === newLanguage) {
201
- return;
202
- }
203
-
204
- if (this.textModel) {
205
- this.textModel.setLanguageId(newLanguage);
206
- }
207
-
208
- this.props.language = newLanguage;
209
- this.onDidChangeLanguageEmitter.fire(newLanguage);
210
- this.onDidChangeContentEmitter.fire('language');
211
- }
212
-
213
- get languageName(): string {
214
- return this.languageService.getLanguage(this.language)?.name ?? this.language;
215
- }
216
-
217
- get uri(): URI {
218
- return this.props.uri;
219
- }
220
- get handle(): number {
221
- return this.props.handle;
222
- }
223
- get cellKind(): CellKind {
224
- return this.props.cellKind;
225
- }
226
-
227
- protected _editing: boolean = false;
228
- get editing(): boolean {
229
- return this._editing;
230
- }
231
-
232
- protected _editorOptions: MonacoEditor.IOptions = {};
233
- get editorOptions(): Readonly<MonacoEditor.IOptions> {
234
- return this._editorOptions;
235
- }
236
-
237
- set editorOptions(options: MonacoEditor.IOptions) {
238
- this._editorOptions = options;
239
- this.onDidChangeEditorOptionsEmitter.fire(options);
240
- }
241
-
242
- protected _outputVisible: boolean = true;
243
- get outputVisible(): boolean {
244
- return this._outputVisible;
245
- }
246
-
247
- set outputVisible(visible: boolean) {
248
- if (this._outputVisible !== visible) {
249
- this._outputVisible = visible;
250
- this.outputVisibilityChangeEmitter.fire(visible);
251
- }
252
- }
253
-
254
- @postConstruct()
255
- protected init(): void {
256
- this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op));
257
- this._metadata = this.props.metadata ?? {};
258
- this._internalMetadata = this.props.internalMetadata ?? {};
259
-
260
- this.editorOptions = {
261
- lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
262
- };
263
- this.toDispose.push(this.preferenceService.onPreferenceChanged(e => {
264
- if (e.preferenceName === NotebookPreferences.NOTEBOOK_LINE_NUMBERS) {
265
- this.editorOptions = {
266
- ...this.editorOptions,
267
- lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
268
- };
269
- }
270
- }));
271
- }
272
-
273
- dispose(): void {
274
- this.onDidChangeOutputsEmitter.dispose();
275
- this.onDidChangeOutputItemsEmitter.dispose();
276
- this.onDidChangeContentEmitter.dispose();
277
- this.onDidChangeMetadataEmitter.dispose();
278
- this.onDidChangeInternalMetadataEmitter.dispose();
279
- this.onDidChangeLanguageEmitter.dispose();
280
- this.toDispose.dispose();
281
- }
282
-
283
- requestEdit(): void {
284
- if (!this.textModel || !this.textModel.readOnly) {
285
- this._editing = true;
286
- this.onDidRequestCellEditChangeEmitter.fire(true);
287
- }
288
- }
289
-
290
- requestStopEdit(): void {
291
- this._editing = false;
292
- this.onDidRequestCellEditChangeEmitter.fire(false);
293
- }
294
-
295
- requestFocusEditor(focusRequest?: CellEditorFocusRequest): void {
296
- this.requestEdit();
297
- this.onWillFocusCellEditorEmitter.fire(focusRequest);
298
- }
299
-
300
- requestBlurEditor(): void {
301
- this.requestStopEdit();
302
- this.onWillBlurCellEditorEmitter.fire();
303
- }
304
-
305
- requestCenterEditor(): void {
306
- this.onDidRequestCenterEditorEmitter.fire();
307
- }
308
-
309
- spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
310
- if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
311
- const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
312
- // update
313
- for (let i = 0; i < commonLen; i++) {
314
- const currentOutput = this.outputs[splice.start + i];
315
- const newOutput = splice.newOutputs[i];
316
-
317
- this.replaceOutputData(currentOutput.outputId, newOutput);
318
- }
319
-
320
- this.outputs.splice(splice.start + commonLen, splice.deleteCount - commonLen, ...splice.newOutputs.slice(commonLen).map(op => new NotebookCellOutputModel(op)));
321
- this.onDidChangeOutputsEmitter.fire({ start: splice.start + commonLen, deleteCount: splice.deleteCount - commonLen, newOutputs: splice.newOutputs.slice(commonLen) });
322
- } else {
323
- this.outputs.splice(splice.start, splice.deleteCount, ...splice.newOutputs.map(op => new NotebookCellOutputModel(op)));
324
- this.onDidChangeOutputsEmitter.fire(splice);
325
- }
326
- }
327
-
328
- replaceOutputData(outputId: string, newOutputData: CellOutput): boolean {
329
- const output = this.outputs.find(out => out.outputId === outputId);
330
-
331
- if (!output) {
332
- return false;
333
- }
334
-
335
- output.replaceData(newOutputData);
336
- this.onDidChangeOutputItemsEmitter.fire(output);
337
- return true;
338
- }
339
-
340
- changeOutputItems(outputId: string, append: boolean, items: CellOutputItem[]): boolean {
341
- const output = this.outputs.find(out => out.outputId === outputId);
342
-
343
- if (!output) {
344
- return false;
345
- }
346
-
347
- if (append) {
348
- output.appendData(items);
349
- } else {
350
- output.replaceData({ outputId: outputId, outputs: items, metadata: output.metadata });
351
- }
352
- this.onDidChangeOutputItemsEmitter.fire(output);
353
- return true;
354
- }
355
-
356
- getData(): CellData {
357
- return {
358
- cellKind: this.cellKind,
359
- language: this.language,
360
- outputs: this.outputs.map(output => output.getData()),
361
- source: this.text,
362
- collapseState: this.props.collapseState,
363
- internalMetadata: this.internalMetadata,
364
- metadata: this.metadata
365
- };
366
- }
367
-
368
- async resolveTextModel(): Promise<MonacoEditorModel> {
369
- if (this.textModel) {
370
- return this.textModel;
371
- }
372
-
373
- const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri);
374
- this.textModel = ref.object;
375
- this.toDispose.push(ref);
376
- this.toDispose.push(this.textModel.onDidChangeContent(e => {
377
- this.props.source = e.model.getText();
378
- }));
379
- return ref.object;
380
- }
381
-
382
- restartOutputRenderer(outputId: string): void {
383
- const output = this.outputs.find(out => out.outputId === outputId);
384
- if (output) {
385
- this.onDidChangeOutputItemsEmitter.fire(output);
386
- }
387
- }
388
-
389
- onMarkdownFind: ((options: NotebookEditorFindMatchOptions) => NotebookEditorFindMatch[]) | undefined;
390
-
391
- showMatch(selected: NotebookCodeEditorFindMatch): void {
392
- this.onDidSelectFindMatchEmitter.fire(selected);
393
- }
394
-
395
- findMatches(options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] {
396
- if (this.cellKind === CellKind.Markup && !this.editing) {
397
- return this.onMarkdownFind?.(options) ?? [];
398
- }
399
- if (!this.textModel) {
400
- return [];
401
- }
402
- const matches = options.search ? this.textModel.findMatches({
403
- searchString: options.search,
404
- isRegex: options.regex,
405
- matchCase: options.matchCase,
406
- matchWholeWord: options.wholeWord
407
- }) : [];
408
- const editorFindMatches = matches.map(match => new NotebookCodeEditorFindMatch(this, match.range, this.textModel!));
409
- this.onDidFindMatchesEmitter.fire(editorFindMatches);
410
- return editorFindMatches;
411
- }
412
-
413
- replaceAll(matches: NotebookCodeEditorFindMatch[], value: string): void {
414
- const editOperations = matches.map(match => ({
415
- range: {
416
- startColumn: match.range.start.character,
417
- startLineNumber: match.range.start.line,
418
- endColumn: match.range.end.character,
419
- endLineNumber: match.range.end.line
420
- },
421
- text: value
422
- }));
423
- this.textModel?.textEditorModel.pushEditOperations(
424
- // eslint-disable-next-line no-null/no-null
425
- null,
426
- editOperations,
427
- // eslint-disable-next-line no-null/no-null
428
- () => null);
429
- }
430
- }
431
-
432
- export interface NotebookCellFindMatches {
433
- matches: NotebookEditorFindMatch[];
434
- selected: NotebookEditorFindMatch;
435
- }
436
-
437
- export class NotebookCodeEditorFindMatch implements NotebookEditorFindMatch {
438
-
439
- selected = false;
440
-
441
- constructor(readonly cell: NotebookCellModel, readonly range: Range, readonly textModel: MonacoEditorModel) {
442
- }
443
-
444
- show(): void {
445
- this.cell.showMatch(this);
446
- }
447
- replace(value: string): void {
448
- this.textModel.textEditorModel.pushEditOperations(
449
- // eslint-disable-next-line no-null/no-null
450
- null,
451
- [{
452
- range: {
453
- startColumn: this.range.start.character,
454
- startLineNumber: this.range.start.line,
455
- endColumn: this.range.end.character,
456
- endLineNumber: this.range.end.line
457
- },
458
- text: value
459
- }],
460
- // eslint-disable-next-line no-null/no-null
461
- () => null);
462
- }
463
-
464
- }
465
-
466
- function computeRunStartTimeAdjustment(oldMetadata: NotebookCellInternalMetadata, newMetadata: NotebookCellInternalMetadata): number | undefined {
467
- if (oldMetadata.runStartTime !== newMetadata.runStartTime && typeof newMetadata.runStartTime === 'number') {
468
- const offset = Date.now() - newMetadata.runStartTime;
469
- return offset < 0 ? Math.abs(offset) : 0;
470
- } else {
471
- return newMetadata.runStartTimeAdjustment;
472
- }
473
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2023 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ /*---------------------------------------------------------------------------------------------
17
+ * Copyright (c) Microsoft Corporation. All rights reserved.
18
+ * Licensed under the MIT License. See License.txt in the project root for license information.
19
+ *--------------------------------------------------------------------------------------------*/
20
+
21
+ import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core';
22
+ import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
23
+ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
24
+ import { type MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
25
+ import {
26
+ CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
27
+ NotebookCellMetadata, CellOutput, CellData, CellOutputItem
28
+ } from '../../common';
29
+ import { NotebookCellOutputsSplice } from '../notebook-types';
30
+ import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service';
31
+ import { NotebookCellOutputModel } from './notebook-cell-output-model';
32
+ import { PreferenceService } from '@theia/core/lib/browser';
33
+ import { NotebookPreferences } from '../contributions/notebook-preferences';
34
+ import { LanguageService } from '@theia/core/lib/browser/language-service';
35
+ import { NotebookEditorFindMatch, NotebookEditorFindMatchOptions } from '../view/notebook-find-widget';
36
+ import { Range } from '@theia/core/shared/vscode-languageserver-protocol';
37
+
38
+ export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
39
+ export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
40
+
41
+ export type CellEditorFocusRequest = number | 'lastLine' | undefined;
42
+
43
+ export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container {
44
+ const child = parent.createChild();
45
+
46
+ child.bind(NotebookCellModelProps).toConstantValue(props);
47
+ child.bind(NotebookCellModel).toSelf();
48
+
49
+ return child;
50
+ }
51
+
52
+ export interface CellInternalMetadataChangedEvent {
53
+ readonly lastRunSuccessChanged?: boolean;
54
+ }
55
+
56
+ export interface NotebookCell {
57
+ readonly uri: URI;
58
+ handle: number;
59
+ language: string;
60
+ cellKind: CellKind;
61
+ outputs: CellOutput[];
62
+ metadata: NotebookCellMetadata;
63
+ internalMetadata: NotebookCellInternalMetadata;
64
+ text: string;
65
+ onDidChangeOutputs?: Event<NotebookCellOutputsSplice>;
66
+ onDidChangeOutputItems?: Event<CellOutput>;
67
+ onDidChangeLanguage: Event<string>;
68
+ onDidChangeMetadata: Event<void>;
69
+ onDidChangeInternalMetadata: Event<CellInternalMetadataChangedEvent>;
70
+
71
+ }
72
+
73
+ const NotebookCellModelProps = Symbol('NotebookModelProps');
74
+ export interface NotebookCellModelProps {
75
+ readonly uri: URI,
76
+ readonly handle: number,
77
+ source: string,
78
+ language: string,
79
+ readonly cellKind: CellKind,
80
+ outputs: CellOutput[],
81
+ metadata?: NotebookCellMetadata | undefined,
82
+ internalMetadata?: NotebookCellInternalMetadata | undefined,
83
+ readonly collapseState?: NotebookCellCollapseState | undefined,
84
+
85
+ }
86
+
87
+ @injectable()
88
+ export class NotebookCellModel implements NotebookCell, Disposable {
89
+
90
+ protected readonly onDidChangeOutputsEmitter = new Emitter<NotebookCellOutputsSplice>();
91
+ readonly onDidChangeOutputs = this.onDidChangeOutputsEmitter.event;
92
+
93
+ protected readonly onDidChangeOutputItemsEmitter = new Emitter<CellOutput>();
94
+ readonly onDidChangeOutputItems = this.onDidChangeOutputItemsEmitter.event;
95
+
96
+ protected readonly onDidChangeContentEmitter = new Emitter<'content' | 'language' | 'mime'>();
97
+ readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
98
+
99
+ protected readonly onDidChangeMetadataEmitter = new Emitter<void>();
100
+ readonly onDidChangeMetadata = this.onDidChangeMetadataEmitter.event;
101
+
102
+ protected readonly onDidChangeInternalMetadataEmitter = new Emitter<CellInternalMetadataChangedEvent>();
103
+ readonly onDidChangeInternalMetadata = this.onDidChangeInternalMetadataEmitter.event;
104
+
105
+ protected readonly onDidChangeLanguageEmitter = new Emitter<string>();
106
+ readonly onDidChangeLanguage = this.onDidChangeLanguageEmitter.event;
107
+
108
+ protected readonly onDidRequestCellEditChangeEmitter = new Emitter<boolean>();
109
+ readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event;
110
+
111
+ protected readonly onWillFocusCellEditorEmitter = new Emitter<CellEditorFocusRequest>();
112
+ readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event;
113
+
114
+ protected readonly onWillBlurCellEditorEmitter = new Emitter<void>();
115
+ readonly onWillBlurCellEditor = this.onWillBlurCellEditorEmitter.event;
116
+
117
+ protected readonly onDidChangeEditorOptionsEmitter = new Emitter<MonacoEditor.IOptions>();
118
+ readonly onDidChangeEditorOptions = this.onDidChangeEditorOptionsEmitter.event;
119
+
120
+ protected readonly outputVisibilityChangeEmitter = new Emitter<boolean>();
121
+ readonly onDidChangeOutputVisibility = this.outputVisibilityChangeEmitter.event;
122
+
123
+ protected readonly onDidFindMatchesEmitter = new Emitter<NotebookCodeEditorFindMatch[]>();
124
+ readonly onDidFindMatches: Event<NotebookCodeEditorFindMatch[]> = this.onDidFindMatchesEmitter.event;
125
+
126
+ protected readonly onDidSelectFindMatchEmitter = new Emitter<NotebookCodeEditorFindMatch>();
127
+ readonly onDidSelectFindMatch: Event<NotebookCodeEditorFindMatch> = this.onDidSelectFindMatchEmitter.event;
128
+
129
+ protected onDidRequestCenterEditorEmitter = new Emitter<void>();
130
+ readonly onDidRequestCenterEditor = this.onDidRequestCenterEditorEmitter.event;
131
+
132
+ @inject(NotebookCellModelProps)
133
+ protected readonly props: NotebookCellModelProps;
134
+
135
+ @inject(NotebookMonacoTextModelService)
136
+ protected readonly textModelService: NotebookMonacoTextModelService;
137
+
138
+ @inject(LanguageService)
139
+ protected readonly languageService: LanguageService;
140
+
141
+ @inject(PreferenceService)
142
+ protected readonly preferenceService: PreferenceService;
143
+
144
+ get outputs(): NotebookCellOutputModel[] {
145
+ return this._outputs;
146
+ }
147
+
148
+ protected _outputs: NotebookCellOutputModel[];
149
+
150
+ get metadata(): NotebookCellMetadata {
151
+ return this._metadata;
152
+ }
153
+
154
+ set metadata(newMetadata: NotebookCellMetadata) {
155
+ this._metadata = newMetadata;
156
+ this.onDidChangeMetadataEmitter.fire();
157
+ }
158
+
159
+ protected _metadata: NotebookCellMetadata;
160
+
161
+ protected toDispose = new DisposableCollection();
162
+
163
+ protected _internalMetadata: NotebookCellInternalMetadata;
164
+
165
+ get internalMetadata(): NotebookCellInternalMetadata {
166
+ return this._internalMetadata;
167
+ }
168
+
169
+ set internalMetadata(newInternalMetadata: NotebookCellInternalMetadata) {
170
+ const lastRunSuccessChanged = this._internalMetadata.lastRunSuccess !== newInternalMetadata.lastRunSuccess;
171
+ newInternalMetadata = {
172
+ ...newInternalMetadata,
173
+ ...{ runStartTimeAdjustment: computeRunStartTimeAdjustment(this._internalMetadata, newInternalMetadata) }
174
+ };
175
+ this._internalMetadata = newInternalMetadata;
176
+ this.onDidChangeInternalMetadataEmitter.fire({ lastRunSuccessChanged });
177
+
178
+ }
179
+
180
+ protected textModel?: MonacoEditorModel;
181
+
182
+ get text(): string {
183
+ return this.textModel && !this.textModel.isDisposed() ? this.textModel.getText() : this.source;
184
+ }
185
+
186
+ get source(): string {
187
+ return this.props.source;
188
+ }
189
+
190
+ set source(source: string) {
191
+ this.props.source = source;
192
+ this.textModel?.textEditorModel.setValue(source);
193
+ }
194
+
195
+ get language(): string {
196
+ return this.props.language;
197
+ }
198
+
199
+ set language(newLanguage: string) {
200
+ if (this.language === newLanguage) {
201
+ return;
202
+ }
203
+
204
+ if (this.textModel) {
205
+ this.textModel.setLanguageId(newLanguage);
206
+ }
207
+
208
+ this.props.language = newLanguage;
209
+ this.onDidChangeLanguageEmitter.fire(newLanguage);
210
+ this.onDidChangeContentEmitter.fire('language');
211
+ }
212
+
213
+ get languageName(): string {
214
+ return this.languageService.getLanguage(this.language)?.name ?? this.language;
215
+ }
216
+
217
+ get uri(): URI {
218
+ return this.props.uri;
219
+ }
220
+ get handle(): number {
221
+ return this.props.handle;
222
+ }
223
+ get cellKind(): CellKind {
224
+ return this.props.cellKind;
225
+ }
226
+
227
+ protected _editing: boolean = false;
228
+ get editing(): boolean {
229
+ return this._editing;
230
+ }
231
+
232
+ protected _editorOptions: MonacoEditor.IOptions = {};
233
+ get editorOptions(): Readonly<MonacoEditor.IOptions> {
234
+ return this._editorOptions;
235
+ }
236
+
237
+ set editorOptions(options: MonacoEditor.IOptions) {
238
+ this._editorOptions = options;
239
+ this.onDidChangeEditorOptionsEmitter.fire(options);
240
+ }
241
+
242
+ protected _outputVisible: boolean = true;
243
+ get outputVisible(): boolean {
244
+ return this._outputVisible;
245
+ }
246
+
247
+ set outputVisible(visible: boolean) {
248
+ if (this._outputVisible !== visible) {
249
+ this._outputVisible = visible;
250
+ this.outputVisibilityChangeEmitter.fire(visible);
251
+ }
252
+ }
253
+
254
+ @postConstruct()
255
+ protected init(): void {
256
+ this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op));
257
+ this._metadata = this.props.metadata ?? {};
258
+ this._internalMetadata = this.props.internalMetadata ?? {};
259
+
260
+ this.editorOptions = {
261
+ lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
262
+ };
263
+ this.toDispose.push(this.preferenceService.onPreferenceChanged(e => {
264
+ if (e.preferenceName === NotebookPreferences.NOTEBOOK_LINE_NUMBERS) {
265
+ this.editorOptions = {
266
+ ...this.editorOptions,
267
+ lineNumbers: this.preferenceService.get(NotebookPreferences.NOTEBOOK_LINE_NUMBERS)
268
+ };
269
+ }
270
+ }));
271
+ }
272
+
273
+ dispose(): void {
274
+ this.onDidChangeOutputsEmitter.dispose();
275
+ this.onDidChangeOutputItemsEmitter.dispose();
276
+ this.onDidChangeContentEmitter.dispose();
277
+ this.onDidChangeMetadataEmitter.dispose();
278
+ this.onDidChangeInternalMetadataEmitter.dispose();
279
+ this.onDidChangeLanguageEmitter.dispose();
280
+ this.toDispose.dispose();
281
+ }
282
+
283
+ requestEdit(): void {
284
+ if (!this.textModel || !this.textModel.readOnly) {
285
+ this._editing = true;
286
+ this.onDidRequestCellEditChangeEmitter.fire(true);
287
+ }
288
+ }
289
+
290
+ requestStopEdit(): void {
291
+ this._editing = false;
292
+ this.onDidRequestCellEditChangeEmitter.fire(false);
293
+ }
294
+
295
+ requestFocusEditor(focusRequest?: CellEditorFocusRequest): void {
296
+ this.requestEdit();
297
+ this.onWillFocusCellEditorEmitter.fire(focusRequest);
298
+ }
299
+
300
+ requestBlurEditor(): void {
301
+ this.requestStopEdit();
302
+ this.onWillBlurCellEditorEmitter.fire();
303
+ }
304
+
305
+ requestCenterEditor(): void {
306
+ this.onDidRequestCenterEditorEmitter.fire();
307
+ }
308
+
309
+ spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
310
+ if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
311
+ const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
312
+ // update
313
+ for (let i = 0; i < commonLen; i++) {
314
+ const currentOutput = this.outputs[splice.start + i];
315
+ const newOutput = splice.newOutputs[i];
316
+
317
+ this.replaceOutputData(currentOutput.outputId, newOutput);
318
+ }
319
+
320
+ this.outputs.splice(splice.start + commonLen, splice.deleteCount - commonLen, ...splice.newOutputs.slice(commonLen).map(op => new NotebookCellOutputModel(op)));
321
+ this.onDidChangeOutputsEmitter.fire({ start: splice.start + commonLen, deleteCount: splice.deleteCount - commonLen, newOutputs: splice.newOutputs.slice(commonLen) });
322
+ } else {
323
+ this.outputs.splice(splice.start, splice.deleteCount, ...splice.newOutputs.map(op => new NotebookCellOutputModel(op)));
324
+ this.onDidChangeOutputsEmitter.fire(splice);
325
+ }
326
+ }
327
+
328
+ replaceOutputData(outputId: string, newOutputData: CellOutput): boolean {
329
+ const output = this.outputs.find(out => out.outputId === outputId);
330
+
331
+ if (!output) {
332
+ return false;
333
+ }
334
+
335
+ output.replaceData(newOutputData);
336
+ this.onDidChangeOutputItemsEmitter.fire(output);
337
+ return true;
338
+ }
339
+
340
+ changeOutputItems(outputId: string, append: boolean, items: CellOutputItem[]): boolean {
341
+ const output = this.outputs.find(out => out.outputId === outputId);
342
+
343
+ if (!output) {
344
+ return false;
345
+ }
346
+
347
+ if (append) {
348
+ output.appendData(items);
349
+ } else {
350
+ output.replaceData({ outputId: outputId, outputs: items, metadata: output.metadata });
351
+ }
352
+ this.onDidChangeOutputItemsEmitter.fire(output);
353
+ return true;
354
+ }
355
+
356
+ getData(): CellData {
357
+ return {
358
+ cellKind: this.cellKind,
359
+ language: this.language,
360
+ outputs: this.outputs.map(output => output.getData()),
361
+ source: this.text,
362
+ collapseState: this.props.collapseState,
363
+ internalMetadata: this.internalMetadata,
364
+ metadata: this.metadata
365
+ };
366
+ }
367
+
368
+ async resolveTextModel(): Promise<MonacoEditorModel> {
369
+ if (this.textModel) {
370
+ return this.textModel;
371
+ }
372
+
373
+ const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri);
374
+ this.textModel = ref.object;
375
+ this.toDispose.push(ref);
376
+ this.toDispose.push(this.textModel.onDidChangeContent(e => {
377
+ this.props.source = e.model.getText();
378
+ }));
379
+ return ref.object;
380
+ }
381
+
382
+ restartOutputRenderer(outputId: string): void {
383
+ const output = this.outputs.find(out => out.outputId === outputId);
384
+ if (output) {
385
+ this.onDidChangeOutputItemsEmitter.fire(output);
386
+ }
387
+ }
388
+
389
+ onMarkdownFind: ((options: NotebookEditorFindMatchOptions) => NotebookEditorFindMatch[]) | undefined;
390
+
391
+ showMatch(selected: NotebookCodeEditorFindMatch): void {
392
+ this.onDidSelectFindMatchEmitter.fire(selected);
393
+ }
394
+
395
+ findMatches(options: NotebookEditorFindMatchOptions): NotebookEditorFindMatch[] {
396
+ if (this.cellKind === CellKind.Markup && !this.editing) {
397
+ return this.onMarkdownFind?.(options) ?? [];
398
+ }
399
+ if (!this.textModel) {
400
+ return [];
401
+ }
402
+ const matches = options.search ? this.textModel.findMatches({
403
+ searchString: options.search,
404
+ isRegex: options.regex,
405
+ matchCase: options.matchCase,
406
+ matchWholeWord: options.wholeWord
407
+ }) : [];
408
+ const editorFindMatches = matches.map(match => new NotebookCodeEditorFindMatch(this, match.range, this.textModel!));
409
+ this.onDidFindMatchesEmitter.fire(editorFindMatches);
410
+ return editorFindMatches;
411
+ }
412
+
413
+ replaceAll(matches: NotebookCodeEditorFindMatch[], value: string): void {
414
+ const editOperations = matches.map(match => ({
415
+ range: {
416
+ startColumn: match.range.start.character,
417
+ startLineNumber: match.range.start.line,
418
+ endColumn: match.range.end.character,
419
+ endLineNumber: match.range.end.line
420
+ },
421
+ text: value
422
+ }));
423
+ this.textModel?.textEditorModel.pushEditOperations(
424
+ // eslint-disable-next-line no-null/no-null
425
+ null,
426
+ editOperations,
427
+ // eslint-disable-next-line no-null/no-null
428
+ () => null);
429
+ }
430
+ }
431
+
432
+ export interface NotebookCellFindMatches {
433
+ matches: NotebookEditorFindMatch[];
434
+ selected: NotebookEditorFindMatch;
435
+ }
436
+
437
+ export class NotebookCodeEditorFindMatch implements NotebookEditorFindMatch {
438
+
439
+ selected = false;
440
+
441
+ constructor(readonly cell: NotebookCellModel, readonly range: Range, readonly textModel: MonacoEditorModel) {
442
+ }
443
+
444
+ show(): void {
445
+ this.cell.showMatch(this);
446
+ }
447
+ replace(value: string): void {
448
+ this.textModel.textEditorModel.pushEditOperations(
449
+ // eslint-disable-next-line no-null/no-null
450
+ null,
451
+ [{
452
+ range: {
453
+ startColumn: this.range.start.character,
454
+ startLineNumber: this.range.start.line,
455
+ endColumn: this.range.end.character,
456
+ endLineNumber: this.range.end.line
457
+ },
458
+ text: value
459
+ }],
460
+ // eslint-disable-next-line no-null/no-null
461
+ () => null);
462
+ }
463
+
464
+ }
465
+
466
+ function computeRunStartTimeAdjustment(oldMetadata: NotebookCellInternalMetadata, newMetadata: NotebookCellInternalMetadata): number | undefined {
467
+ if (oldMetadata.runStartTime !== newMetadata.runStartTime && typeof newMetadata.runStartTime === 'number') {
468
+ const offset = Date.now() - newMetadata.runStartTime;
469
+ return offset < 0 ? Math.abs(offset) : 0;
470
+ } else {
471
+ return newMetadata.runStartTimeAdjustment;
472
+ }
473
+ }