@theia/notebook 1.51.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 (91) 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 +63 -10
  12. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  13. package/lib/browser/contributions/notebook-preferences.d.ts +3 -0
  14. package/lib/browser/contributions/notebook-preferences.d.ts.map +1 -1
  15. package/lib/browser/contributions/notebook-preferences.js +9 -1
  16. package/lib/browser/contributions/notebook-preferences.js.map +1 -1
  17. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts +14 -0
  18. package/lib/browser/contributions/notebook-status-bar-contribution.d.ts.map +1 -0
  19. package/lib/browser/contributions/notebook-status-bar-contribution.js +75 -0
  20. package/lib/browser/contributions/notebook-status-bar-contribution.js.map +1 -0
  21. package/lib/browser/contributions/notebook-undo-redo-handler.d.ts +10 -0
  22. package/lib/browser/contributions/notebook-undo-redo-handler.d.ts.map +1 -0
  23. package/lib/browser/contributions/notebook-undo-redo-handler.js +49 -0
  24. package/lib/browser/contributions/notebook-undo-redo-handler.js.map +1 -0
  25. package/lib/browser/notebook-editor-widget.d.ts +6 -0
  26. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  27. package/lib/browser/notebook-editor-widget.js +40 -4
  28. package/lib/browser/notebook-editor-widget.js.map +1 -1
  29. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  30. package/lib/browser/notebook-frontend-module.js +7 -1
  31. package/lib/browser/notebook-frontend-module.js.map +1 -1
  32. package/lib/browser/service/notebook-context-manager.d.ts +2 -1
  33. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -1
  34. package/lib/browser/service/notebook-context-manager.js +6 -3
  35. package/lib/browser/service/notebook-context-manager.js.map +1 -1
  36. package/lib/browser/service/notebook-options.d.ts +1 -0
  37. package/lib/browser/service/notebook-options.d.ts.map +1 -1
  38. package/lib/browser/service/notebook-options.js +1 -0
  39. package/lib/browser/service/notebook-options.js.map +1 -1
  40. package/lib/browser/service/notebook-service.d.ts +1 -0
  41. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  42. package/lib/browser/service/notebook-service.js +7 -0
  43. package/lib/browser/service/notebook-service.js.map +1 -1
  44. package/lib/browser/view/notebook-cell-editor.d.ts +8 -1
  45. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  46. package/lib/browser/view/notebook-cell-editor.js +89 -5
  47. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  48. package/lib/browser/view/notebook-cell-list-view.d.ts +3 -0
  49. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  50. package/lib/browser/view/notebook-cell-list-view.js +33 -2
  51. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  52. package/lib/browser/view/notebook-code-cell-view.d.ts +1 -1
  53. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  54. package/lib/browser/view/notebook-code-cell-view.js +19 -17
  55. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  56. package/lib/browser/view/notebook-find-widget.d.ts +63 -0
  57. package/lib/browser/view/notebook-find-widget.d.ts.map +1 -0
  58. package/lib/browser/view/notebook-find-widget.js +225 -0
  59. package/lib/browser/view/notebook-find-widget.js.map +1 -0
  60. package/lib/browser/view/notebook-markdown-cell-view.d.ts +4 -0
  61. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  62. package/lib/browser/view/notebook-markdown-cell-view.js +105 -8
  63. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  64. package/lib/browser/view-model/notebook-cell-model.d.ts +27 -1
  65. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  66. package/lib/browser/view-model/notebook-cell-model.js +76 -1
  67. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  68. package/lib/browser/view-model/notebook-model.d.ts +8 -1
  69. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  70. package/lib/browser/view-model/notebook-model.js +66 -14
  71. package/lib/browser/view-model/notebook-model.js.map +1 -1
  72. package/package.json +9 -8
  73. package/src/browser/contributions/cell-operations.ts +8 -2
  74. package/src/browser/contributions/notebook-actions-contribution.ts +47 -22
  75. package/src/browser/contributions/notebook-cell-actions-contribution.ts +66 -11
  76. package/src/browser/contributions/notebook-preferences.ts +10 -1
  77. package/src/browser/contributions/notebook-status-bar-contribution.ts +77 -0
  78. package/src/browser/contributions/notebook-undo-redo-handler.ts +41 -0
  79. package/src/browser/notebook-editor-widget.tsx +50 -5
  80. package/src/browser/notebook-frontend-module.ts +11 -3
  81. package/src/browser/service/notebook-context-manager.ts +8 -4
  82. package/src/browser/service/notebook-options.ts +2 -1
  83. package/src/browser/service/notebook-service.ts +7 -1
  84. package/src/browser/style/index.css +178 -7
  85. package/src/browser/view/notebook-cell-editor.tsx +94 -5
  86. package/src/browser/view/notebook-cell-list-view.tsx +40 -4
  87. package/src/browser/view/notebook-code-cell-view.tsx +19 -17
  88. package/src/browser/view/notebook-find-widget.tsx +335 -0
  89. package/src/browser/view/notebook-markdown-cell-view.tsx +134 -17
  90. package/src/browser/view-model/notebook-cell-model.ts +101 -8
  91. package/src/browser/view-model/notebook-model.ts +77 -20
@@ -16,17 +16,17 @@
16
16
 
17
17
  import { Command, CommandContribution, CommandHandler, CommandRegistry, CompoundMenuNodeRole, MenuContribution, MenuModelRegistry, nls, URI } from '@theia/core';
18
18
  import { inject, injectable } from '@theia/core/shared/inversify';
19
- import { ApplicationShell, codicon, CommonCommands, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser';
19
+ import { ApplicationShell, codicon, KeybindingContribution, KeybindingRegistry } from '@theia/core/lib/browser';
20
20
  import { NotebookModel } from '../view-model/notebook-model';
21
21
  import { NotebookService } from '../service/notebook-service';
22
22
  import { CellEditType, CellKind, NotebookCommand } from '../../common';
23
23
  import { NotebookKernelQuickPickService } from '../service/notebook-kernel-quick-pick-service';
24
24
  import { NotebookExecutionService } from '../service/notebook-execution-service';
25
- import { NotebookEditorWidget } from '../notebook-editor-widget';
26
25
  import { NotebookEditorWidgetService } from '../service/notebook-editor-widget-service';
27
26
  import { NOTEBOOK_CELL_CURSOR_FIRST_LINE, NOTEBOOK_CELL_CURSOR_LAST_LINE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS } from './notebook-context-keys';
28
27
  import { NotebookClipboardService } from '../service/notebook-clipboard-service';
29
28
  import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
29
+ import { NotebookEditorWidget } from '../notebook-editor-widget';
30
30
 
31
31
  export namespace NotebookCommands {
32
32
  export const ADD_NEW_CELL_COMMAND = Command.toDefaultLocalizedCommand({
@@ -83,6 +83,16 @@ export namespace NotebookCommands {
83
83
  id: 'notebook.cell.paste',
84
84
  category: 'Notebook',
85
85
  });
86
+
87
+ export const NOTEBOOK_FIND = Command.toDefaultLocalizedCommand({
88
+ id: 'notebook.find',
89
+ category: 'Notebook',
90
+ });
91
+
92
+ export const CENTER_ACTIVE_CELL = Command.toDefaultLocalizedCommand({
93
+ id: 'notebook.centerActiveCell',
94
+ category: 'Notebook',
95
+ });
86
96
  }
87
97
 
88
98
  export enum CellChangeDirection {
@@ -129,8 +139,7 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
129
139
 
130
140
  let cellLanguage: string = 'markdown';
131
141
  if (cellKind === CellKind.Code) {
132
- const firstCodeCell = notebookModel.cells.find(cell => cell.cellKind === CellKind.Code);
133
- cellLanguage = firstCodeCell?.language ?? 'plaintext';
142
+ cellLanguage = this.notebookService.getCodeCellLanguage(notebookModel);
134
143
  }
135
144
 
136
145
  notebookModel.applyEdits([{
@@ -184,12 +193,14 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
184
193
 
185
194
  if (change === CellChangeDirection.Up && currentIndex > 0) {
186
195
  model.setSelectedCell(model.cells[currentIndex - 1]);
187
- if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) {
196
+ if ((model.selectedCell?.cellKind === CellKind.Code
197
+ || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) {
188
198
  model.selectedCell.requestFocusEditor('lastLine');
189
199
  }
190
200
  } else if (change === CellChangeDirection.Down && currentIndex < model.cells.length - 1) {
191
201
  model.setSelectedCell(model.cells[currentIndex + 1]);
192
- if (model.selectedCell?.cellKind === CellKind.Code && shouldFocusEditor) {
202
+ if ((model.selectedCell?.cellKind === CellKind.Code
203
+ || (model.selectedCell?.cellKind === CellKind.Markup && model.selectedCell?.editing)) && shouldFocusEditor) {
193
204
  model.selectedCell.requestFocusEditor();
194
205
  }
195
206
  }
@@ -202,20 +213,11 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
202
213
  }
203
214
  }
204
215
  );
205
-
206
- commands.registerHandler(CommonCommands.UNDO.id, {
207
- isEnabled: () => {
208
- const widget = this.shell.activeWidget;
209
- return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly);
210
- },
211
- execute: () => (this.shell.activeWidget as NotebookEditorWidget).undo()
216
+ commands.registerCommand({ id: 'list.focusUp' }, {
217
+ execute: () => commands.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, CellChangeDirection.Up)
212
218
  });
213
- commands.registerHandler(CommonCommands.REDO.id, {
214
- isEnabled: () => {
215
- const widget = this.shell.activeWidget;
216
- return widget instanceof NotebookEditorWidget && !Boolean(widget.model?.readOnly);
217
- },
218
- execute: () => (this.shell.activeWidget as NotebookEditorWidget).redo()
219
+ commands.registerCommand({ id: 'list.focusDown' }, {
220
+ execute: () => commands.executeCommand(NotebookCommands.CHANGE_SELECTED_CELL.id, CellChangeDirection.Down)
219
221
  });
220
222
 
221
223
  commands.registerCommand(NotebookCommands.CUT_SELECTED_CELL, this.editableCommandHandler(
@@ -251,6 +253,19 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
251
253
  }
252
254
  });
253
255
 
256
+ commands.registerCommand(NotebookCommands.NOTEBOOK_FIND, {
257
+ execute: () => {
258
+ this.notebookEditorWidgetService.focusedEditor?.showFindWidget();
259
+ }
260
+ });
261
+
262
+ commands.registerCommand(NotebookCommands.CENTER_ACTIVE_CELL, {
263
+ execute: (editor?: NotebookEditorWidget) => {
264
+ const model = editor ? editor.model : this.notebookEditorWidgetService.focusedEditor?.model;
265
+ model?.selectedCell?.requestCenterEditor();
266
+ }
267
+ });
268
+
254
269
  }
255
270
 
256
271
  protected editableCommandHandler(execute: (notebookModel: NotebookModel) => void): CommandHandler {
@@ -326,18 +341,28 @@ export class NotebookActionsContribution implements CommandContribution, MenuCon
326
341
  {
327
342
  command: NotebookCommands.CUT_SELECTED_CELL.id,
328
343
  keybinding: 'ctrlcmd+x',
329
- when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`
344
+ when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus`
330
345
  },
331
346
  {
332
347
  command: NotebookCommands.COPY_SELECTED_CELL.id,
333
348
  keybinding: 'ctrlcmd+c',
334
- when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`
349
+ when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus`
335
350
  },
336
351
  {
337
352
  command: NotebookCommands.PASTE_CELL.id,
338
353
  keybinding: 'ctrlcmd+v',
339
- when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED}`
354
+ when: `${NOTEBOOK_EDITOR_FOCUSED} && !inputFocus`
355
+ },
356
+ {
357
+ command: NotebookCommands.NOTEBOOK_FIND.id,
358
+ keybinding: 'ctrlcmd+f',
359
+ when: `${NOTEBOOK_EDITOR_FOCUSED}`
340
360
  },
361
+ {
362
+ command: NotebookCommands.CENTER_ACTIVE_CELL.id,
363
+ keybinding: 'ctrlcmd+l',
364
+ when: `${NOTEBOOK_EDITOR_FOCUSED}`
365
+ }
341
366
  );
342
367
  }
343
368
 
@@ -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);
@@ -365,9 +403,21 @@ export class NotebookCellActionContribution implements MenuContribution, Command
365
403
  execute: async (notebook?: NotebookModel, cell?: NotebookCellModel) => {
366
404
  const selectedCell = cell ?? this.notebookEditorWidgetService.focusedEditor?.model?.selectedCell;
367
405
  const activeNotebook = notebook ?? this.notebookEditorWidgetService.focusedEditor?.model;
368
- if (selectedCell && activeNotebook) {
369
- const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language);
370
- if (language?.value && language.value !== 'autoDetect') {
406
+ if (!selectedCell || !activeNotebook) {
407
+ return;
408
+ }
409
+ const language = await this.languageQuickPickService.pickEditorLanguage(selectedCell.language);
410
+ if (!language?.value || language.value === 'autoDetect' || language.value.id === selectedCell.language) {
411
+ return;
412
+ }
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);
417
+ } else {
418
+ if (isMarkdownCell) {
419
+ changeCellType(activeNotebook, selectedCell, CellKind.Code, language.value.id);
420
+ } else {
371
421
  this.notebookEditorWidgetService.focusedEditor?.model?.applyEdits([{
372
422
  editType: CellEditType.CellLanguage,
373
423
  index: activeNotebook.cells.indexOf(selectedCell),
@@ -411,12 +461,12 @@ export class NotebookCellActionContribution implements MenuContribution, Command
411
461
  {
412
462
  command: NotebookCellCommands.EDIT_COMMAND.id,
413
463
  keybinding: 'Enter',
414
- when: `!editorTextFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
464
+ when: `!editorTextFocus && !inputFocus && ${NOTEBOOK_EDITOR_FOCUSED} && ${NOTEBOOK_CELL_FOCUSED}`,
415
465
  },
416
466
  {
417
467
  command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
418
- keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Alt] }).toString(),
419
- 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'`,
420
470
  },
421
471
  {
422
472
  command: NotebookCellCommands.STOP_EDIT_COMMAND.id,
@@ -426,12 +476,17 @@ export class NotebookCellActionContribution implements MenuContribution, Command
426
476
  {
427
477
  command: NotebookCellCommands.EXECUTE_SINGLE_CELL_COMMAND.id,
428
478
  keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.CtrlCmd] }).toString(),
429
- 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'`,
430
480
  },
431
481
  {
432
482
  command: NotebookCellCommands.EXECUTE_SINGLE_CELL_AND_FOCUS_NEXT_COMMAND.id,
433
483
  keybinding: KeyCode.createKeyCode({ first: Key.ENTER, modifiers: [KeyModifier.Shift] }).toString(),
434
- 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}`,
435
490
  },
436
491
  {
437
492
  command: NotebookCellCommands.CLEAR_OUTPUTS_COMMAND.id,
@@ -19,7 +19,8 @@
19
19
  *--------------------------------------------------------------------------------------------*/
20
20
 
21
21
  import { nls } from '@theia/core';
22
- import { PreferenceSchema } from '@theia/core/lib/browser';
22
+ import { interfaces } from '@theia/core/shared/inversify';
23
+ import { PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser';
23
24
 
24
25
  export namespace NotebookPreferences {
25
26
  export const NOTEBOOK_LINE_NUMBERS = 'notebook.lineNumbers';
@@ -81,3 +82,11 @@ export const notebookPreferenceSchema: PreferenceSchema = {
81
82
 
82
83
  }
83
84
  };
85
+
86
+ export const NotebookPreferenceContribution = Symbol('NotebookPreferenceContribution');
87
+
88
+ export function bindNotebookPreferences(bind: interfaces.Bind): void {
89
+ // We don't need a NotebookPreferenceConfiguration class, so there's no preference proxy to bind
90
+ bind(NotebookPreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema });
91
+ bind(PreferenceContribution).toService(NotebookPreferenceContribution);
92
+ }
@@ -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, PreferenceContribution, 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';
@@ -44,8 +44,10 @@ import { NotebookOutlineContribution } from './contributions/notebook-outline-co
44
44
  import { NotebookLabelProviderContribution } from './contributions/notebook-label-provider-contribution';
45
45
  import { NotebookOutputActionContribution } from './contributions/notebook-output-action-contribution';
46
46
  import { NotebookClipboardService } from './service/notebook-clipboard-service';
47
- import { notebookPreferenceSchema } from './contributions/notebook-preferences';
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();
@@ -106,6 +108,12 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
106
108
  bind(NotebookLabelProviderContribution).toSelf().inSingletonScope();
107
109
  bind(LabelProviderContribution).toService(NotebookLabelProviderContribution);
108
110
 
109
- bind(PreferenceContribution).toConstantValue({ schema: notebookPreferenceSchema });
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 {