@theia/notebook 1.52.0 → 1.53.0-next.18

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 (86) hide show
  1. package/lib/browser/contributions/cell-operations.d.ts +1 -1
  2. package/lib/browser/contributions/cell-operations.d.ts.map +1 -1
  3. package/lib/browser/contributions/cell-operations.js +10 -2
  4. package/lib/browser/contributions/cell-operations.js.map +1 -1
  5. package/lib/browser/contributions/notebook-actions-contribution.d.ts +2 -0
  6. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-actions-contribution.js +43 -24
  8. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  9. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +4 -0
  10. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  11. package/lib/browser/contributions/notebook-cell-actions-contribution.js +61 -16
  12. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  13. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +14 -0
  14. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -0
  15. package/lib/browser/contributions/notebook-status-bar-contribution.js +75 -0
  16. package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -0
  17. package/lib/browser/contributions/notebook-undo-redo-handler.d.ts +10 -0
  18. package/lib/browser/contributions/notebook-undo-redo-handler.d.ts.map +1 -0
  19. package/lib/browser/contributions/notebook-undo-redo-handler.js +49 -0
  20. package/lib/browser/contributions/notebook-undo-redo-handler.js.map +1 -0
  21. package/lib/browser/notebook-editor-widget.d.ts +6 -0
  22. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  23. package/lib/browser/notebook-editor-widget.js +40 -4
  24. package/lib/browser/notebook-editor-widget.js.map +1 -1
  25. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  26. package/lib/browser/notebook-frontend-module.js +6 -0
  27. package/lib/browser/notebook-frontend-module.js.map +1 -1
  28. package/lib/browser/service/notebook-context-manager.d.ts +2 -1
  29. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
  30. package/lib/browser/service/notebook-context-manager.js +6 -3
  31. package/lib/browser/service/notebook-context-manager.js.map +1 -1
  32. package/lib/browser/service/notebook-options.d.ts +1 -0
  33. package/lib/browser/service/notebook-options.d.ts.map +1 -1
  34. package/lib/browser/service/notebook-options.js +1 -0
  35. package/lib/browser/service/notebook-options.js.map +1 -1
  36. package/lib/browser/service/notebook-service.d.ts +1 -0
  37. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  38. package/lib/browser/service/notebook-service.js +7 -0
  39. package/lib/browser/service/notebook-service.js.map +1 -1
  40. package/lib/browser/view/notebook-cell-editor.d.ts +8 -1
  41. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  42. package/lib/browser/view/notebook-cell-editor.js +89 -5
  43. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  44. package/lib/browser/view/notebook-cell-list-view.d.ts +3 -0
  45. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  46. package/lib/browser/view/notebook-cell-list-view.js +33 -2
  47. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  48. package/lib/browser/view/notebook-code-cell-view.d.ts +1 -1
  49. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  50. package/lib/browser/view/notebook-code-cell-view.js +19 -17
  51. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  52. package/lib/browser/view/notebook-find-widget.d.ts +63 -0
  53. package/lib/browser/view/notebook-find-widget.d.ts.map +1 -0
  54. package/lib/browser/view/notebook-find-widget.js +225 -0
  55. package/lib/browser/view/notebook-find-widget.js.map +1 -0
  56. package/lib/browser/view/notebook-markdown-cell-view.d.ts +4 -0
  57. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  58. package/lib/browser/view/notebook-markdown-cell-view.js +105 -8
  59. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  60. package/lib/browser/view-model/notebook-cell-model.d.ts +27 -1
  61. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  62. package/lib/browser/view-model/notebook-cell-model.js +76 -1
  63. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  64. package/lib/browser/view-model/notebook-model.d.ts +8 -1
  65. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  66. package/lib/browser/view-model/notebook-model.js +66 -14
  67. package/lib/browser/view-model/notebook-model.js.map +1 -1
  68. package/package.json +8 -7
  69. package/src/browser/contributions/cell-operations.ts +8 -2
  70. package/src/browser/contributions/notebook-actions-contribution.ts +47 -22
  71. package/src/browser/contributions/notebook-cell-actions-contribution.ts +65 -17
  72. package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -0
  73. package/src/browser/contributions/notebook-undo-redo-handler.ts +41 -0
  74. package/src/browser/notebook-editor-widget.tsx +50 -5
  75. package/src/browser/notebook-frontend-module.ts +9 -1
  76. package/src/browser/service/notebook-context-manager.ts +8 -4
  77. package/src/browser/service/notebook-options.ts +2 -1
  78. package/src/browser/service/notebook-service.ts +7 -1
  79. package/src/browser/style/index.css +165 -6
  80. package/src/browser/view/notebook-cell-editor.tsx +94 -5
  81. package/src/browser/view/notebook-cell-list-view.tsx +40 -4
  82. package/src/browser/view/notebook-code-cell-view.tsx +19 -17
  83. package/src/browser/view/notebook-find-widget.tsx +335 -0
  84. package/src/browser/view/notebook-markdown-cell-view.tsx +134 -17
  85. package/src/browser/view-model/notebook-cell-model.ts +101 -8
  86. package/src/browser/view-model/notebook-model.ts +77 -20
@@ -22,7 +22,8 @@ import { NotebookCellModel } from '../view-model/notebook-cell-model';
22
22
  import {
23
23
  NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE,
24
24
  NotebookContextKeys, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_EDITOR_FOCUSED,
25
- NOTEBOOK_CELL_FOCUSED
25
+ NOTEBOOK_CELL_FOCUSED,
26
+ NOTEBOOK_CELL_LIST_FOCUSED
26
27
  } from './notebook-context-keys';
27
28
  import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
28
29
  import { NotebookExecutionService } from '../service/notebook-execution-service';
@@ -32,16 +33,19 @@ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-s
32
33
  import { NotebookCommands } from './notebook-actions-contribution';
33
34
  import { changeCellType } from './cell-operations';
34
35
  import { EditorLanguageQuickPickService } from '@theia/editor/lib/browser/editor-language-quick-pick-service';
36
+ import { NotebookService } from '../service/notebook-service';
35
37
 
36
38
  export namespace NotebookCellCommands {
37
39
  /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
38
40
  export const EDIT_COMMAND = Command.toDefaultLocalizedCommand({
39
41
  id: 'notebook.cell.edit',
42
+ category: 'Notebook',
40
43
  iconClass: codicon('edit')
41
44
  });
42
45
  /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
43
46
  export const STOP_EDIT_COMMAND = Command.toDefaultLocalizedCommand({
44
47
  id: 'notebook.cell.stop-edit',
48
+ category: 'Notebook',
45
49
  iconClass: codicon('check')
46
50
  });
47
51
  /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
@@ -57,11 +61,21 @@ export namespace NotebookCellCommands {
57
61
  /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
58
62
  export const EXECUTE_SINGLE_CELL_COMMAND = Command.toDefaultLocalizedCommand({
59
63
  id: 'notebook.cell.execute-cell',
64
+ category: 'Notebook',
65
+ label: nls.localizeByDefault('Execute Cell'),
60
66
  iconClass: codicon('play'),
61
67
  });
62
68
  /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
63
69
  export const EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND = Command.toDefaultLocalizedCommand({
64
70
  id: 'notebook.cell.execute-cell-and-focus-next',
71
+ label: nls.localizeByDefault('Execute Notebook Cell and Select Below'),
72
+ category: 'Notebook',
73
+ });
74
+ /** Parameters: notebookModel: NotebookModel, cell: NotebookCellModel */
75
+ export const EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND = Command.toDefaultLocalizedCommand({
76
+ id: 'notebook.cell.execute-cell-and-insert-below',
77
+ label: nls.localizeByDefault('Execute Notebook Cell and Insert Below'),
78
+ category: 'Notebook',
65
79
  });
66
80
 
67
81
  export const EXECUTE_ABOVE_CELLS_COMMAND = Command.toDefaultLocalizedCommand({
@@ -83,11 +97,13 @@ export namespace NotebookCellCommands {
83
97
  /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel */
84
98
  export const CLEAR_OUTPUTS_COMMAND = Command.toDefaultLocalizedCommand({
85
99
  id: 'notebook.cell.clear-outputs',
100
+ category: 'Notebook',
86
101
  label: 'Clear Cell Outputs',
87
102
  });
88
103
  /** Parameters: notebookModel: NotebookModel | undefined, cell: NotebookCellModel | undefined, output: NotebookCellOutputModel */
89
104
  export const CHANGE_OUTPUT_PRESENTATION_COMMAND = Command.toDefaultLocalizedCommand({
90
105
  id: 'notebook.cell.change-presentation',
106
+ category: 'Notebook',
91
107
  label: 'Change Presentation',
92
108
  });
93
109
 
@@ -112,12 +128,14 @@ export namespace NotebookCellCommands {
112
128
 
113
129
  export const TO_CODE_CELL_COMMAND = Command.toLocalizedCommand({
114
130
  id: 'notebook.cell.changeToCode',
131
+ category: 'Notebook',
115
132
  label: 'Change Cell to Code'
116
133
  });
117
134
 
118
135
  export const TO_MARKDOWN_CELL_COMMAND = Command.toLocalizedCommand({
119
136
  id: 'notebook.cell.changeToMarkdown',
120
- label: 'Change Cell to Mardown'
137
+ category: 'Notebook',
138
+ label: 'Change Cell to Markdown'
121
139
  });
122
140
 
123
141
  export const TOGGLE_CELL_OUTPUT = Command.toDefaultLocalizedCommand({
@@ -146,6 +164,9 @@ export class NotebookCellActionContribution implements MenuContribution, Command
146
164
  @inject(ContextKeyService)
147
165
  protected contextKeyService: ContextKeyService;
148
166
 
167
+ @inject(NotebookService)
168
+ protected notebookService: NotebookService;
169
+
149
170
  @inject(NotebookExecutionService)
150
171
  protected notebookExecutionService: NotebookExecutionService;
151
172
 
@@ -297,6 +318,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command
297
318
  }
298
319
  })
299
320
  );
321
+ commands.registerCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND, this.editableCellCommandHandler(
322
+ async (notebookModel, cell) => {
323
+ if (cell.cellKind === CellKind.Code) {
324
+ await commands.executeCommand(NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id, notebookModel, cell);
325
+ }
326
+ await commands.executeCommand(NotebookCellCommands.STOP_EDIT_COMMAND.id, notebookModel, cell);
327
+
328
+ if (cell.cellKind === CellKind.Code) {
329
+ await commands.executeCommand(NotebookCellCommands.INSERT_NEW_CELL_BELOW_COMMAND.id);
330
+ } else {
331
+ await commands.executeCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND.id);
332
+ }
333
+
334
+ const index = notebookModel.cells.indexOf(cell);
335
+ notebookModel.setSelectedCell(notebookModel.cells[index + 1]);
336
+ })
337
+ );
300
338
 
301
339
  commands.registerCommand(NotebookCellCommands.EXECUTE_ABOVE_CELLS_COMMAND, this.editableCellCommandHandler(
302
340
  (notebookModel, cell) => {
@@ -345,7 +383,7 @@ export class NotebookCellActionContribution implements MenuContribution, Command
345
383
  commands.registerCommand(NotebookCellCommands.INSERT_MARKDOWN_CELL_BELOW_COMMAND, insertCommand(CellKind.Markup, 'below'));
346
384
 
347
385
  commands.registerCommand(NotebookCellCommands.TO_CODE_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => {
348
- changeCellType(notebookModel, cell, CellKind.Code);
386
+ changeCellType(notebookModel, cell, CellKind.Code, this.notebookService.getCodeCellLanguage(notebookModel));
349
387
  }));
350
388
  commands.registerCommand(NotebookCellCommands.TO_MARKDOWN_CELL_COMMAND, this.editableCellCommandHandler((notebookModel, cell) => {
351
389
  changeCellType(notebookModel, cell, CellKind.Markup);
@@ -369,18 +407,23 @@ export class NotebookCellActionContribution implements MenuContribution, Command
369
407
  return;
370
408
  }
371
409
  const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language);
372
- if (!language?.value || language.value === 'autoDetect') {
410
+ if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) {
373
411
  return;
374
412
  }
375
- if (language.value.id === 'markdown') {
376
- selectedCell.language = 'markdown';
377
- changeCellType(activeNotebook, selectedCell, CellKind.Markup);
413
+ const isMarkdownCell = selectedCell.cellKind === CellKind.Markup;
414
+ const isMarkdownLanguage = language.value.id === 'markdown';
415
+ if (isMarkdownLanguage) {
416
+ changeCellType(activeNotebook, selectedCell, CellKind.Markup, language.value.id);
378
417
  } else {
379
- this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{
380
- editType: CellEditType.CellLanguage,
381
- index: activeNotebook.cells.indexOf(selectedCell),
382
- language: language.value.id
383
- }], true);
418
+ if (isMarkdownCell) {
419
+ changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id);
420
+ } else {
421
+ this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{
422
+ editType: CellEditType.CellLanguage,
423
+ index: activeNotebook.cells.indexOf(selectedCell),
424
+ language: language.value.id
425
+ }], true);
426
+ }
384
427
  }
385
428
  }
386
429
  });
@@ -418,12 +461,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
418
461
  {
419
462
  command: NotebookCellCommands.EDIT_COMMAND.id,
420
463
  keybinding: 'Enter',
421
- when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
464
+ when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
422
465
  },
423
466
  {
424
467
  command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
425
- keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
426
- when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`,
468
+ keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt, KeyModifier.CtrlCmd] }).toString(),
469
+ when: `editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'markdown'`,
427
470
  },
428
471
  {
429
472
  command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
@@ -433,12 +476,17 @@ export class NotebookCellActionContribution implements MenuContribution, Command
433
476
  {
434
477
  command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id,
435
478
  keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(),
436
- when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`,
479
+ when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED} && ${NOTEBOOK_CELL_TYPE} == 'code'`,
437
480
  },
438
481
  {
439
482
  command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id,
440
483
  keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(),
441
- when: `${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
484
+ when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
485
+ },
486
+ {
487
+ command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_INSERT_BELOW_COMMAND.id,
488
+ keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
489
+ when: `${NOTEBOOK_CELL_LIST_FOCUSED} && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
442
490
  },
443
491
  {
444
492
  command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
@@ -0,0 +1,77 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 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
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
+ import { FrontendApplicationContribution, StatusBar, StatusBarAlignment } from '@theia/core/lib/browser';
19
+ import { Disposable } from '@theia/core/lib/common';
20
+ import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
21
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
22
+ import { nls } from '@theia/core';
23
+ import { NotebookCommands } from './notebook-actions-contribution';
24
+
25
+ export const NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID = 'notebook-cell-selection-position';
26
+
27
+ @injectable()
28
+ export class NotebookStatusBarContribution implements FrontendApplicationContribution {
29
+
30
+ @inject(StatusBar) protected readonly statusBar: StatusBar;
31
+ @inject(NotebookEditorWidgetService) protected readonly editorWidgetService: NotebookEditorWidgetService;
32
+
33
+ protected currentCellSelectionListener: Disposable | undefined;
34
+ protected lastFocusedEditor: NotebookEditorWidget | undefined;
35
+
36
+ onStart(): void {
37
+ this.editorWidgetService.onDidChangeFocusedEditor(editor => {
38
+ this.currentCellSelectionListener?.dispose();
39
+ this.currentCellSelectionListener = editor?.model?.onDidChangeSelectedCell(() =>
40
+ this.updateStatusbar(editor)
41
+ );
42
+ editor?.onDidDispose(() => {
43
+ this.lastFocusedEditor = undefined;
44
+ this.updateStatusbar();
45
+ });
46
+ this.updateStatusbar(editor);
47
+ this.lastFocusedEditor = editor;
48
+ });
49
+ if (this.editorWidgetService.focusedEditor) {
50
+ this.updateStatusbar();
51
+ }
52
+ }
53
+
54
+ protected async updateStatusbar(editor?: NotebookEditorWidget): Promise<void> {
55
+ if ((!editor && !this.lastFocusedEditor?.isVisible) || editor?.model?.cells.length === 0) {
56
+ this.statusBar.removeElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID);
57
+ return;
58
+ }
59
+
60
+ await editor?.ready;
61
+ if (!editor?.model) {
62
+ return;
63
+ }
64
+
65
+ const selectedCellIndex = editor.model.selectedCell ? editor.model.cells.indexOf(editor.model.selectedCell) + 1 : '';
66
+
67
+ this.statusBar.setElement(NOTEBOOK_CELL_SELECTION_STATUS_BAR_ID, {
68
+ text: nls.localizeByDefault('Cell {0} of {1}', selectedCellIndex, editor.model.cells.length),
69
+ alignment: StatusBarAlignment.RIGHT,
70
+ priority: 100,
71
+ command: NotebookCommands.CENTER_ACTIVE_CELL.id,
72
+ arguments: [editor]
73
+ });
74
+
75
+ }
76
+
77
+ }
@@ -0,0 +1,41 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2024 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
+ import { inject, injectable } from '@theia/core/shared/inversify';
18
+ import { ApplicationShell, UndoRedoHandler } from '@theia/core/lib/browser';
19
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
20
+
21
+ @injectable()
22
+ export class NotebookUndoRedoHandler implements UndoRedoHandler<NotebookEditorWidget> {
23
+
24
+ @inject(ApplicationShell)
25
+ protected readonly applicationShell: ApplicationShell;
26
+
27
+ priority = 200;
28
+ select(): NotebookEditorWidget | undefined {
29
+ const current = this.applicationShell.currentWidget;
30
+ if (current instanceof NotebookEditorWidget) {
31
+ return current;
32
+ }
33
+ return undefined;
34
+ }
35
+ undo(item: NotebookEditorWidget): void {
36
+ item.undo();
37
+ }
38
+ redo(item: NotebookEditorWidget): void {
39
+ item.redo();
40
+ }
41
+ }
@@ -33,6 +33,8 @@ import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
33
33
  import { NotebookContextManager } from './service/notebook-context-manager';
34
34
  import { NotebookViewportService } from './view/notebook-viewport-service';
35
35
  import { NotebookCellCommands } from './contributions/notebook-cell-actions-contribution';
36
+ import { NotebookFindWidget } from './view/notebook-find-widget';
37
+ import debounce = require('lodash/debounce');
36
38
  const PerfectScrollbar = require('react-perfect-scrollbar');
37
39
 
38
40
  export const NotebookEditorWidgetContainerFactory = Symbol('NotebookEditorWidgetContainerFactory');
@@ -126,7 +128,16 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
126
128
  protected readonly renderers = new Map<CellKind, CellRenderer>();
127
129
  protected _model?: NotebookModel;
128
130
  protected _ready: Deferred<NotebookModel> = new Deferred();
131
+ protected _findWidgetVisible = false;
132
+ protected _findWidgetRef = React.createRef<NotebookFindWidget>();
129
133
  protected scrollBarRef = React.createRef<{ updateScroll(): void }>();
134
+ protected debounceFind = debounce(() => {
135
+ this._findWidgetRef.current?.search({});
136
+ }, 30, {
137
+ trailing: true,
138
+ maxWait: 100,
139
+ leading: false
140
+ });
130
141
 
131
142
  get notebookType(): string {
132
143
  return this.props.notebookType;
@@ -143,7 +154,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
143
154
  @postConstruct()
144
155
  protected init(): void {
145
156
  this.id = NOTEBOOK_EDITOR_ID_PREFIX + this.props.uri.toString();
146
- this.node.tabIndex = -1;
147
157
 
148
158
  this.scrollOptions = {
149
159
  suppressScrollY: true
@@ -163,8 +173,6 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
163
173
  this.commandRegistry.executeCommand(NotebookCellCommands.EDIT_COMMAND.id, model, model.cells[0]);
164
174
  model.setSelectedCell(model.cells[0]);
165
175
  }
166
- model.cells.forEach(cell => cell.onWillBlurCellEditor(() => this.node.focus()));
167
- model.onDidAddOrRemoveCell(e => e.newCellIds?.forEach(cellId => model.cells.find(cell => cell.handle === cellId)?.onWillBlurCellEditor(() => this.node.focus())));
168
176
  });
169
177
  }
170
178
 
@@ -177,6 +185,11 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
177
185
  // Wait one frame to ensure that the content has been rendered
178
186
  animationFrame().then(() => this.scrollBarRef.current?.updateScroll());
179
187
  }));
188
+ this.toDispose.push(this._model.onContentChanged(() => {
189
+ if (this._findWidgetVisible) {
190
+ this.debounceFind();
191
+ }
192
+ }));
180
193
  this.toDispose.push(this._model.onDidChangeReadOnly(readOnly => {
181
194
  if (readOnly) {
182
195
  lock(this.title);
@@ -191,6 +204,7 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
191
204
  }
192
205
  // Ensure that the model is loaded before adding the editor
193
206
  this.notebookEditorService.addNotebookEditor(this);
207
+ this._model.selectedCell = this._model.cells[0];
194
208
  this.update();
195
209
  this.notebookContextManager.init(this);
196
210
  return this._model;
@@ -220,18 +234,41 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
220
234
  protected render(): ReactNode {
221
235
  if (this._model) {
222
236
  return <div className='theia-notebook-main-container'>
237
+ <div className='theia-notebook-overlay'>
238
+ <NotebookFindWidget
239
+ ref={this._findWidgetRef}
240
+ hidden={!this._findWidgetVisible}
241
+ onClose={() => {
242
+ this._findWidgetVisible = false;
243
+ this._model?.findMatches({
244
+ activeFilters: [],
245
+ matchCase: false,
246
+ regex: false,
247
+ search: '',
248
+ wholeWord: false
249
+ });
250
+ this.update();
251
+ }}
252
+ onSearch={options => this._model?.findMatches(options) ?? []}
253
+ onReplace={(matches, replaceText) => this._model?.replaceAll(matches, replaceText)}
254
+ />
255
+ </div>
223
256
  {this.notebookMainToolbarRenderer.render(this._model, this.node)}
224
- <div className='theia-notebook-viewport' ref={(ref: HTMLDivElement) => this.viewportService.viewportElement = ref}>
257
+ <div
258
+ className='theia-notebook-viewport'
259
+ ref={(ref: HTMLDivElement) => this.viewportService.viewportElement = ref}
260
+ >
225
261
  <PerfectScrollbar className='theia-notebook-scroll-container'
226
262
  ref={this.scrollBarRef}
227
263
  onScrollY={(e: HTMLDivElement) => this.viewportService.onScroll(e)}>
228
264
  <NotebookCellListView renderers={this.renderers}
229
265
  notebookModel={this._model}
266
+ notebookContext={this.notebookContextManager}
230
267
  toolbarRenderer={this.cellToolbarFactory}
231
268
  commandRegistry={this.commandRegistry} />
232
269
  </PerfectScrollbar>
233
270
  </div>
234
- </div >;
271
+ </div>;
235
272
  } else {
236
273
  return <div className='theia-notebook-main-container'>
237
274
  <div className='theia-notebook-main-loading-indicator'></div>
@@ -260,6 +297,14 @@ export class NotebookEditorWidget extends ReactWidget implements Navigatable, Sa
260
297
  this.onDidChangeOutputInputFocusEmitter.fire(focused);
261
298
  }
262
299
 
300
+ showFindWidget(): void {
301
+ if (!this._findWidgetVisible) {
302
+ this._findWidgetVisible = true;
303
+ this.update();
304
+ }
305
+ this._findWidgetRef.current?.focusSearch(this._model?.selectedText);
306
+ }
307
+
263
308
  override dispose(): void {
264
309
  this.notebookContextManager.dispose();
265
310
  this.onDidChangeModelEmitter.dispose();
@@ -16,7 +16,7 @@
16
16
  import '../../src/browser/style/index.css';
17
17
 
18
18
  import { ContainerModule } from '@theia/core/shared/inversify';
19
- import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, WidgetFactory } from '@theia/core/lib/browser';
19
+ import { FrontendApplicationContribution, KeybindingContribution, LabelProviderContribution, OpenHandler, UndoRedoHandler, WidgetFactory } from '@theia/core/lib/browser';
20
20
  import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
21
21
  import { NotebookOpenHandler } from './notebook-open-handler';
22
22
  import { CommandContribution, MenuContribution, ResourceResolver, } from '@theia/core';
@@ -46,6 +46,8 @@ import { NotebookOutputActionContribution } from './contributions/notebook-outpu
46
46
  import { NotebookClipboardService } from './service/notebook-clipboard-service';
47
47
  import { bindNotebookPreferences } from './contributions/notebook-preferences';
48
48
  import { NotebookOptionsService } from './service/notebook-options';
49
+ import { NotebookUndoRedoHandler } from './contributions/notebook-undo-redo-handler';
50
+ import { NotebookStatusBarContribution } from './contributions/notebook-status-bar-contribution';
49
51
 
50
52
  export default new ContainerModule((bind, unbind, isBound, rebind) => {
51
53
  bind(NotebookColorContribution).toSelf().inSingletonScope();
@@ -108,4 +110,10 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
108
110
 
109
111
  bindNotebookPreferences(bind);
110
112
  bind(NotebookOptionsService).toSelf().inSingletonScope();
113
+
114
+ bind(NotebookUndoRedoHandler).toSelf().inSingletonScope();
115
+ bind(UndoRedoHandler).toService(NotebookUndoRedoHandler);
116
+
117
+ bind(NotebookStatusBarContribution).toSelf().inSingletonScope();
118
+ bind(FrontendApplicationContribution).toService(NotebookStatusBarContribution);
111
119
  });
@@ -21,7 +21,7 @@ import { NotebookKernelService } from './notebook-kernel-service';
21
21
  import {
22
22
  NOTEBOOK_CELL_EDITABLE,
23
23
  NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE,
24
- NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
24
+ NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE,
25
25
  NOTEBOOK_CELL_TYPE, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_SELECTED,
26
26
  NOTEBOOK_OUTPUT_INPUT_FOCUSED,
27
27
  NOTEBOOK_VIEW_TYPE
@@ -85,7 +85,7 @@ export class NotebookContextManager {
85
85
 
86
86
  this.scopedStore.setContext(NOTEBOOK_HAS_OUTPUTS, !!widget.model?.cells.find(cell => cell.outputs.length > 0));
87
87
 
88
- // Cell Selection realted keys
88
+ // Cell Selection related keys
89
89
  this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, !!widget.model?.selectedCell);
90
90
  widget.model?.onDidChangeSelectedCell(e => {
91
91
  this.selectedCellChanged(e.cell);
@@ -144,8 +144,12 @@ export class NotebookContextManager {
144
144
  return this.contextKeyService.createOverlay(Object.entries(this.cellContexts.get(cellHandle) ?? {}));
145
145
  }
146
146
 
147
- onDidEditorTextFocus(focus: boolean): void {
148
- this.scopedStore.setContext('inputFocus', focus);
147
+ changeCellFocus(focus: boolean): void {
148
+ this.scopedStore.setContext(NOTEBOOK_CELL_FOCUSED, focus);
149
+ }
150
+
151
+ changeCellListFocus(focus: boolean): void {
152
+ this.scopedStore.setContext(NOTEBOOK_CELL_LIST_FOCUSED, focus);
149
153
  }
150
154
 
151
155
  createContextKeyChangedEvent(affectedKeys: string[]): ContextKeyChangeEvent {
@@ -37,7 +37,7 @@ const notebookOutputOptionsRelevantPreferences = [
37
37
 
38
38
  export interface NotebookOutputOptions {
39
39
  // readonly outputNodePadding: number;
40
- // readonly outputNodeLeftPadding: number;
40
+ readonly outputNodeLeftPadding: number;
41
41
  // readonly previewNodePadding: number;
42
42
  // readonly markdownLeftMargin: number;
43
43
  // readonly leftMargin: number;
@@ -95,6 +95,7 @@ export class NotebookOptionsService {
95
95
  fontSize,
96
96
  outputFontSize: outputFontSize,
97
97
  fontFamily: this.preferenceService.get<string>('editor.fontFamily')!,
98
+ outputNodeLeftPadding: 8,
98
99
  outputFontFamily: this.getNotebookPreferenceWithDefault<string>(NotebookPreferences.OUTPUT_FONT_FAMILY),
99
100
  outputLineHeight: this.computeOutputLineHeight(outputLineHeight, outputFontSize ?? fontSize),
100
101
  outputScrolling: this.getNotebookPreferenceWithDefault<boolean>(NotebookPreferences.OUTPUT_SCROLLING)!,
@@ -17,7 +17,7 @@
17
17
  import { Disposable, DisposableCollection, Emitter, Resource, URI } from '@theia/core';
18
18
  import { inject, injectable } from '@theia/core/shared/inversify';
19
19
  import { BinaryBuffer } from '@theia/core/lib/common/buffer';
20
- import { NotebookData, TransientOptions } from '../../common';
20
+ import { CellKind, NotebookData, TransientOptions } from '../../common';
21
21
  import { NotebookModel, NotebookModelFactory, NotebookModelProps } from '../view-model/notebook-model';
22
22
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
23
23
  import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from '../view-model/notebook-cell-model';
@@ -206,4 +206,10 @@ export class NotebookService implements Disposable {
206
206
  return false;
207
207
  }
208
208
  }
209
+
210
+ getCodeCellLanguage(model: NotebookModel): string {
211
+ const firstCodeCell = model.cells.find(cellModel => cellModel.cellKind === CellKind.Code);
212
+ const cellLanguage = firstCodeCell?.language ?? 'plaintext';
213
+ return cellLanguage;
214
+ }
209
215
  }