@theia/notebook 1.47.1 → 1.48.2

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 (112) hide show
  1. package/lib/browser/contributions/cell-operations.d.ts +8 -0
  2. package/lib/browser/contributions/cell-operations.d.ts.map +1 -0
  3. package/lib/browser/contributions/cell-operations.js +37 -0
  4. package/lib/browser/contributions/cell-operations.js.map +1 -0
  5. package/lib/browser/contributions/notebook-actions-contribution.d.ts +10 -2
  6. package/lib/browser/contributions/notebook-actions-contribution.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-actions-contribution.js +61 -7
  8. package/lib/browser/contributions/notebook-actions-contribution.js.map +1 -1
  9. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +16 -1
  10. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  11. package/lib/browser/contributions/notebook-cell-actions-contribution.js +154 -17
  12. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  13. package/lib/browser/notebook-editor-widget.d.ts +8 -2
  14. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  15. package/lib/browser/notebook-editor-widget.js +38 -13
  16. package/lib/browser/notebook-editor-widget.js.map +1 -1
  17. package/lib/browser/notebook-frontend-module.d.ts.map +1 -1
  18. package/lib/browser/notebook-frontend-module.js +6 -9
  19. package/lib/browser/notebook-frontend-module.js.map +1 -1
  20. package/lib/browser/notebook-types.d.ts +10 -2
  21. package/lib/browser/notebook-types.d.ts.map +1 -1
  22. package/lib/browser/notebook-types.js.map +1 -1
  23. package/lib/browser/service/notebook-context-manager.d.ts +24 -0
  24. package/lib/browser/service/notebook-context-manager.d.ts.map +1 -0
  25. package/lib/browser/service/notebook-context-manager.js +112 -0
  26. package/lib/browser/service/notebook-context-manager.js.map +1 -0
  27. package/lib/browser/service/notebook-editor-widget-service.d.ts +3 -0
  28. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -1
  29. package/lib/browser/service/notebook-editor-widget-service.js +21 -11
  30. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -1
  31. package/lib/browser/service/notebook-kernel-service.d.ts +1 -0
  32. package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
  33. package/lib/browser/service/notebook-kernel-service.js +6 -3
  34. package/lib/browser/service/notebook-kernel-service.js.map +1 -1
  35. package/lib/browser/service/notebook-monaco-text-model-service.d.ts +16 -0
  36. package/lib/browser/service/notebook-monaco-text-model-service.d.ts.map +1 -0
  37. package/lib/browser/service/notebook-monaco-text-model-service.js +49 -0
  38. package/lib/browser/service/notebook-monaco-text-model-service.js.map +1 -0
  39. package/lib/browser/service/notebook-service.d.ts +10 -2
  40. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  41. package/lib/browser/service/notebook-service.js +19 -6
  42. package/lib/browser/service/notebook-service.js.map +1 -1
  43. package/lib/browser/view/notebook-cell-editor.d.ts +7 -0
  44. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  45. package/lib/browser/view/notebook-cell-editor.js +43 -3
  46. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  47. package/lib/browser/view/notebook-cell-list-view.d.ts.map +1 -1
  48. package/lib/browser/view/notebook-cell-list-view.js +6 -3
  49. package/lib/browser/view/notebook-cell-list-view.js.map +1 -1
  50. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts +2 -0
  51. package/lib/browser/view/notebook-cell-toolbar-factory.d.ts.map +1 -1
  52. package/lib/browser/view/notebook-cell-toolbar-factory.js +12 -6
  53. package/lib/browser/view/notebook-cell-toolbar-factory.js.map +1 -1
  54. package/lib/browser/view/notebook-cell-toolbar.js +1 -1
  55. package/lib/browser/view/notebook-cell-toolbar.js.map +1 -1
  56. package/lib/browser/view/notebook-code-cell-view.d.ts +11 -0
  57. package/lib/browser/view/notebook-code-cell-view.d.ts.map +1 -1
  58. package/lib/browser/view/notebook-code-cell-view.js +52 -4
  59. package/lib/browser/view/notebook-code-cell-view.js.map +1 -1
  60. package/lib/browser/view/notebook-main-toolbar.d.ts +6 -1
  61. package/lib/browser/view/notebook-main-toolbar.d.ts.map +1 -1
  62. package/lib/browser/view/notebook-main-toolbar.js +23 -5
  63. package/lib/browser/view/notebook-main-toolbar.js.map +1 -1
  64. package/lib/browser/view/notebook-markdown-cell-view.d.ts +2 -0
  65. package/lib/browser/view/notebook-markdown-cell-view.d.ts.map +1 -1
  66. package/lib/browser/view/notebook-markdown-cell-view.js +9 -4
  67. package/lib/browser/view/notebook-markdown-cell-view.js.map +1 -1
  68. package/lib/browser/view/notebook-viewport-service.d.ts +17 -0
  69. package/lib/browser/view/notebook-viewport-service.d.ts.map +1 -0
  70. package/lib/browser/view/notebook-viewport-service.js +60 -0
  71. package/lib/browser/view/notebook-viewport-service.js.map +1 -0
  72. package/lib/browser/view-model/notebook-cell-model.d.ts +9 -15
  73. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  74. package/lib/browser/view-model/notebook-cell-model.js +22 -23
  75. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  76. package/lib/browser/view-model/notebook-model.d.ts +7 -4
  77. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  78. package/lib/browser/view-model/notebook-model.js +50 -6
  79. package/lib/browser/view-model/notebook-model.js.map +1 -1
  80. package/lib/common/notebook-common.d.ts +7 -0
  81. package/lib/common/notebook-common.d.ts.map +1 -1
  82. package/lib/common/notebook-common.js +12 -1
  83. package/lib/common/notebook-common.js.map +1 -1
  84. package/package.json +8 -7
  85. package/src/browser/contributions/cell-operations.ts +38 -0
  86. package/src/browser/contributions/notebook-actions-contribution.ts +66 -6
  87. package/src/browser/contributions/notebook-cell-actions-contribution.ts +194 -22
  88. package/src/browser/notebook-editor-widget.tsx +43 -17
  89. package/src/browser/notebook-frontend-module.ts +8 -11
  90. package/src/browser/notebook-types.ts +13 -1
  91. package/src/browser/service/notebook-context-manager.ts +127 -0
  92. package/src/browser/service/notebook-editor-widget-service.ts +20 -10
  93. package/src/browser/service/notebook-kernel-service.ts +6 -2
  94. package/src/browser/service/notebook-monaco-text-model-service.ts +48 -0
  95. package/src/browser/service/notebook-service.ts +26 -5
  96. package/src/browser/style/index.css +25 -8
  97. package/src/browser/view/notebook-cell-editor.tsx +52 -10
  98. package/src/browser/view/notebook-cell-list-view.tsx +8 -4
  99. package/src/browser/view/notebook-cell-toolbar-factory.tsx +9 -4
  100. package/src/browser/view/notebook-cell-toolbar.tsx +1 -1
  101. package/src/browser/view/notebook-code-cell-view.tsx +64 -8
  102. package/src/browser/view/notebook-main-toolbar.tsx +26 -5
  103. package/src/browser/view/notebook-markdown-cell-view.tsx +9 -4
  104. package/src/browser/view/notebook-viewport-service.ts +61 -0
  105. package/src/browser/view-model/notebook-cell-model.ts +26 -33
  106. package/src/browser/view-model/notebook-model.ts +70 -7
  107. package/src/common/notebook-common.ts +13 -0
  108. package/lib/browser/service/notebook-cell-context-manager.d.ts +0 -16
  109. package/lib/browser/service/notebook-cell-context-manager.d.ts.map +0 -1
  110. package/lib/browser/service/notebook-cell-context-manager.js +0 -74
  111. package/lib/browser/service/notebook-cell-context-manager.js.map +0 -1
  112. package/src/browser/service/notebook-cell-context-manager.ts +0 -72
@@ -29,6 +29,11 @@ import { CellExecution, NotebookExecutionStateService } from '../service/noteboo
29
29
  import { codicon } from '@theia/core/lib/browser';
30
30
  import { NotebookCellExecutionState } from '../../common';
31
31
  import { DisposableCollection } from '@theia/core';
32
+ import { NotebookContextManager } from '../service/notebook-context-manager';
33
+ import { NotebookViewportService } from './notebook-viewport-service';
34
+ import { EditorPreferences } from '@theia/editor/lib/browser';
35
+ import { BareFontInfo } from '@theia/monaco-editor-core/esm/vs/editor/common/config/fontInfo';
36
+ import { PixelRatio } from '@theia/monaco-editor-core/esm/vs/base/browser/browser';
32
37
 
33
38
  @injectable()
34
39
  export class NotebookCodeCellRenderer implements CellRenderer {
@@ -47,19 +52,33 @@ export class NotebookCodeCellRenderer implements CellRenderer {
47
52
  @inject(NotebookExecutionStateService)
48
53
  protected readonly executionStateService: NotebookExecutionStateService;
49
54
 
55
+ @inject(NotebookContextManager)
56
+ protected readonly notebookContextManager: NotebookContextManager;
57
+
58
+ @inject(NotebookViewportService)
59
+ protected readonly notebookViewportService: NotebookViewportService;
60
+
61
+ @inject(EditorPreferences)
62
+ protected readonly editorPreferences: EditorPreferences;
63
+
64
+ protected fontInfo: BareFontInfo | undefined;
65
+
50
66
  render(notebookModel: NotebookModel, cell: NotebookCellModel, handle: number): React.ReactNode {
51
67
  return <div>
52
68
  <div className='theia-notebook-cell-with-sidebar'>
53
- <div>
69
+ <div className='theia-notebook-cell-sidebar'>
54
70
  {this.notebookCellToolbarFactory.renderSidebar(NotebookCellActionContribution.CODE_CELL_SIDEBAR_MENU, notebookModel, cell)}
55
- {/* cell-execution-order needs an own component. Could be a little more complicated
56
- <p className='theia-notebook-code-cell-execution-order'>{`[${cell.exec ?? ' '}]`}</p> */}
71
+ <CodeCellExecutionOrder cell={cell} />
57
72
  </div>
58
73
  <div className='theia-notebook-cell-editor-container'>
59
- <CellEditor notebookModel={notebookModel} cell={cell} monacoServices={this.monacoServices} />
60
- <NotebookCodeCellStatus cell={cell} executionStateService={this.executionStateService}></NotebookCodeCellStatus>
61
- </div>
62
- </div>
74
+ <CellEditor notebookModel={notebookModel} cell={cell}
75
+ monacoServices={this.monacoServices}
76
+ notebookContextManager={this.notebookContextManager}
77
+ notebookViewportService={this.notebookViewportService}
78
+ fontInfo={this.getOrCreateMonacoFontInfo()} />
79
+ <NotebookCodeCellStatus cell={cell} executionStateService={this.executionStateService} onClick={() => cell.requestFocusEditor()}></NotebookCodeCellStatus>
80
+ </div >
81
+ </div >
63
82
  <div className='theia-notebook-cell-with-sidebar'>
64
83
  <NotebookCodeCellOutputs cell={cell} notebook={notebookModel} outputWebviewFactory={this.cellOutputWebviewFactory}
65
84
  renderSidebar={() =>
@@ -67,11 +86,31 @@ export class NotebookCodeCellRenderer implements CellRenderer {
67
86
  </div>
68
87
  </div >;
69
88
  }
89
+
90
+ protected getOrCreateMonacoFontInfo(): BareFontInfo {
91
+ if (!this.fontInfo) {
92
+ this.fontInfo = this.createFontInfo();
93
+ this.editorPreferences.onPreferenceChanged(e => this.fontInfo = this.createFontInfo());
94
+ }
95
+ return this.fontInfo;
96
+ }
97
+
98
+ protected createFontInfo(): BareFontInfo {
99
+ return BareFontInfo.createFromRawSettings({
100
+ fontFamily: this.editorPreferences['editor.fontFamily'],
101
+ fontWeight: String(this.editorPreferences['editor.fontWeight']),
102
+ fontSize: this.editorPreferences['editor.fontSize'],
103
+ fontLigatures: this.editorPreferences['editor.fontLigatures'],
104
+ lineHeight: this.editorPreferences['editor.lineHeight'],
105
+ letterSpacing: this.editorPreferences['editor.letterSpacing'],
106
+ }, PixelRatio.value);
107
+ }
70
108
  }
71
109
 
72
110
  export interface NotebookCodeCellStatusProps {
73
111
  cell: NotebookCellModel;
74
112
  executionStateService: NotebookExecutionStateService;
113
+ onClick: () => void;
75
114
  }
76
115
 
77
116
  export interface NotebookCodeCellStatusState {
@@ -114,7 +153,7 @@ export class NotebookCodeCellStatus extends React.Component<NotebookCodeCellStat
114
153
  }
115
154
 
116
155
  override render(): React.ReactNode {
117
- return <div className='notebook-cell-status'>
156
+ return <div className='notebook-cell-status' onClick={() => this.props.onClick()}>
118
157
  <div className='notebook-cell-status-left'>
119
158
  {this.renderExecutionState()}
120
159
  </div>
@@ -230,3 +269,20 @@ export class NotebookCodeCellOutputs extends React.Component<NotebookCellOutputP
230
269
  }
231
270
 
232
271
  }
272
+
273
+ interface NotebookCellExecutionOrderProps {
274
+ cell: NotebookCellModel;
275
+ }
276
+
277
+ function CodeCellExecutionOrder({ cell }: NotebookCellExecutionOrderProps): React.JSX.Element {
278
+ const [executionOrder, setExecutionOrder] = React.useState(cell.internalMetadata.executionOrder ?? ' ');
279
+
280
+ React.useEffect(() => {
281
+ const listener = cell.onDidChangeInternalMetadata(e => {
282
+ setExecutionOrder(cell.internalMetadata.executionOrder ?? ' ');
283
+ });
284
+ return () => listener.dispose();
285
+ }, []);
286
+
287
+ return <span className='theia-notebook-code-cell-execution-order'>{`[${executionOrder}]`}</span>;
288
+ }
@@ -22,6 +22,7 @@ import { NotebookKernelService } from '../service/notebook-kernel-service';
22
22
  import { inject, injectable } from '@theia/core/shared/inversify';
23
23
  import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
24
24
  import { NotebookCommand } from '../../common';
25
+ import { NotebookContextManager } from '../service/notebook-context-manager';
25
26
 
26
27
  export interface NotebookMainToolbarProps {
27
28
  notebookModel: NotebookModel
@@ -29,6 +30,8 @@ export interface NotebookMainToolbarProps {
29
30
  notebookKernelService: NotebookKernelService;
30
31
  commandRegistry: CommandRegistry;
31
32
  contextKeyService: ContextKeyService;
33
+ editorNode: HTMLElement;
34
+ notebookContextManager: NotebookContextManager;
32
35
  }
33
36
 
34
37
  @injectable()
@@ -37,14 +40,16 @@ export class NotebookMainToolbarRenderer {
37
40
  @inject(CommandRegistry) protected readonly commandRegistry: CommandRegistry;
38
41
  @inject(MenuModelRegistry) protected readonly menuRegistry: MenuModelRegistry;
39
42
  @inject(ContextKeyService) protected readonly contextKeyService: ContextKeyService;
43
+ @inject(NotebookContextManager) protected readonly notebookContextManager: NotebookContextManager;
40
44
 
41
- render(notebookModel: NotebookModel): React.ReactNode {
45
+ render(notebookModel: NotebookModel, editorNode: HTMLElement): React.ReactNode {
42
46
  return <NotebookMainToolbar notebookModel={notebookModel}
43
47
  menuRegistry={this.menuRegistry}
44
48
  notebookKernelService={this.notebookKernelService}
45
49
  commandRegistry={this.commandRegistry}
46
50
  contextKeyService={this.contextKeyService}
47
- />;
51
+ editorNode={editorNode}
52
+ notebookContextManager={this.notebookContextManager} />;
48
53
  }
49
54
  }
50
55
 
@@ -70,12 +75,18 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
70
75
 
71
76
  // TODO maybe we need a mechanism to check for changes in the menu to update this toolbar
72
77
  const contextKeys = new Set<string>();
73
- this.getMenuItems().filter(item => item.when).forEach(item => props.contextKeyService.parseKeys(item.when!)?.forEach(key => contextKeys.add(key)));
78
+ this.getAllContextKeys(this.getMenuItems(), contextKeys);
79
+ props.notebookContextManager.onDidChangeContext(e => {
80
+ if (e.affects(contextKeys)) {
81
+ this.forceUpdate();
82
+ }
83
+ });
74
84
  props.contextKeyService.onDidChange(e => {
75
85
  if (e.affects(contextKeys)) {
76
86
  this.forceUpdate();
77
87
  }
78
88
  });
89
+
79
90
  }
80
91
 
81
92
  override componentWillUnmount(): void {
@@ -103,7 +114,7 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
103
114
  {itemNodes}
104
115
  {itemNodes && itemNodes.length > 0 && <span key={`${item.id}-separator`} className='theia-notebook-toolbar-separator'></span>}
105
116
  </React.Fragment>;
106
- } else if (!item.when || this.props.contextKeyService.match(item.when)) {
117
+ } else if (!item.when || this.props.contextKeyService.match(item.when, this.props.editorNode)) {
107
118
  const visibleCommand = Boolean(this.props.commandRegistry.getVisibleHandler(item.command ?? '', this.props.notebookModel));
108
119
  if (!visibleCommand) {
109
120
  return undefined;
@@ -125,6 +136,16 @@ export class NotebookMainToolbar extends React.Component<NotebookMainToolbarProp
125
136
  private getMenuItems(): readonly MenuNode[] {
126
137
  const menuPath = NotebookMenus.NOTEBOOK_MAIN_TOOLBAR;
127
138
  const pluginCommands = this.props.menuRegistry.getMenuNode(menuPath).children;
128
- return this.props.menuRegistry.getMenu([menuPath]).children.concat(pluginCommands);
139
+ const theiaCommands = this.props.menuRegistry.getMenu([menuPath]).children;
140
+ // TODO add specifc arguments to commands
141
+ return theiaCommands.concat(pluginCommands);
142
+ }
143
+
144
+ private getAllContextKeys(menus: readonly MenuNode[], keySet: Set<string>): void {
145
+ menus.filter(item => item.when)
146
+ .forEach(item => this.props.contextKeyService.parseKeys(item.when!)?.forEach(key => keySet.add(key)));
147
+
148
+ menus.filter(item => item.children && item.children.length > 0)
149
+ .forEach(item => this.getAllContextKeys(item.children!, keySet));
129
150
  }
130
151
  }
@@ -24,6 +24,7 @@ import { CellEditor } from './notebook-cell-editor';
24
24
  import { inject, injectable } from '@theia/core/shared/inversify';
25
25
  import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor';
26
26
  import { nls } from '@theia/core';
27
+ import { NotebookContextManager } from '../service/notebook-context-manager';
27
28
 
28
29
  @injectable()
29
30
  export class NotebookMarkdownCellRenderer implements CellRenderer {
@@ -33,9 +34,12 @@ export class NotebookMarkdownCellRenderer implements CellRenderer {
33
34
  @inject(MonacoEditorServices)
34
35
  protected readonly monacoServices: MonacoEditorServices;
35
36
 
37
+ @inject(NotebookContextManager)
38
+ protected readonly notebookContextManager: NotebookContextManager;
39
+
36
40
  render(notebookModel: NotebookModel, cell: NotebookCellModel): React.ReactNode {
37
41
  return <MarkdownCell markdownRenderer={this.markdownRenderer} monacoServices={this.monacoServices}
38
- cell={cell} notebookModel={notebookModel} />;
42
+ cell={cell} notebookModel={notebookModel} notebookContextManager={this.notebookContextManager} />;
39
43
  }
40
44
 
41
45
  }
@@ -46,10 +50,11 @@ interface MarkdownCellProps {
46
50
 
47
51
  cell: NotebookCellModel,
48
52
  notebookModel: NotebookModel
53
+ notebookContextManager: NotebookContextManager;
49
54
  }
50
55
 
51
- function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel }: MarkdownCellProps): React.JSX.Element {
52
- const [editMode, setEditMode] = React.useState(false);
56
+ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel, notebookContextManager }: MarkdownCellProps): React.JSX.Element {
57
+ const [editMode, setEditMode] = React.useState(cell.editing);
53
58
 
54
59
  React.useEffect(() => {
55
60
  const listener = cell.onDidRequestCellEditChange(cellEdit => setEditMode(cellEdit));
@@ -62,7 +67,7 @@ function MarkdownCell({ markdownRenderer, monacoServices, cell, notebookModel }:
62
67
  }
63
68
 
64
69
  return editMode ?
65
- <CellEditor cell={cell} notebookModel={notebookModel} monacoServices={monacoServices} /> :
70
+ <CellEditor cell={cell} notebookModel={notebookModel} monacoServices={monacoServices} notebookContextManager={notebookContextManager} /> :
66
71
  <div className='theia-notebook-markdown-content'
67
72
  onDoubleClick={() => cell.requestEdit()}
68
73
  // This sets the non React HTML node from the markdown renderers output as a child node to this react component
@@ -0,0 +1,61 @@
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 { Disposable } from '@theia/core';
18
+ import { injectable } from '@theia/core/shared/inversify';
19
+ import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol';
20
+
21
+ /**
22
+ * this service is for managing the viewport and scroll state of a notebook editor.
23
+ * its used both for restoring scroll state after reopening an editor and for cell to check if they are in the viewport.
24
+ */
25
+ @injectable()
26
+ export class NotebookViewportService implements Disposable {
27
+
28
+ protected onDidChangeViewportEmitter = new Emitter<void>();
29
+ readonly onDidChangeViewport = this.onDidChangeViewportEmitter.event;
30
+
31
+ protected _viewportElement: HTMLDivElement | undefined;
32
+
33
+ protected resizeObserver: ResizeObserver;
34
+
35
+ set viewportElement(element: HTMLDivElement | undefined) {
36
+ this._viewportElement = element;
37
+ if (element) {
38
+ this.onDidChangeViewportEmitter.fire();
39
+ this.resizeObserver?.disconnect();
40
+ this.resizeObserver = new ResizeObserver(() => this.onDidChangeViewportEmitter.fire());
41
+ this.resizeObserver.observe(element);
42
+ }
43
+ }
44
+
45
+ isElementInViewport(element: HTMLElement): boolean {
46
+ if (this._viewportElement) {
47
+ const rect = element.getBoundingClientRect();
48
+ const viewRect = this._viewportElement.getBoundingClientRect();
49
+ return rect.top < viewRect.top ? rect.bottom > viewRect.top : rect.top < viewRect.bottom;
50
+ }
51
+ return false;
52
+ }
53
+
54
+ onScroll(e: HTMLDivElement): void {
55
+ this.onDidChangeViewportEmitter.fire();
56
+ }
57
+
58
+ dispose(): void {
59
+ this.resizeObserver.disconnect();
60
+ }
61
+ }
@@ -20,38 +20,27 @@
20
20
 
21
21
  import { Disposable, DisposableCollection, Emitter, Event, URI } from '@theia/core';
22
22
  import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
23
- import { ContextKeyChangeEvent } from '@theia/core/lib/browser/context-key-service';
24
23
  import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
25
- import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
26
24
  import {
27
25
  CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
28
26
  NotebookCellMetadata, CellOutput, CellData, CellOutputItem
29
27
  } from '../../common';
30
28
  import { NotebookCellOutputsSplice } from '../notebook-types';
29
+ import { NotebookMonacoTextModelService } from '../service/notebook-monaco-text-model-service';
31
30
  import { NotebookCellOutputModel } from './notebook-cell-output-model';
32
31
 
33
32
  export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
34
33
  export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
35
34
 
36
- export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps,
37
- notebookCellContextManager: new (...args: never[]) => unknown): interfaces.Container {
35
+ export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps): interfaces.Container {
38
36
  const child = parent.createChild();
39
37
 
40
38
  child.bind(NotebookCellModelProps).toConstantValue(props);
41
- // We need the constructor as property here to avoid circular dependencies for the context manager
42
- child.bind(NotebookCellContextManager).to(notebookCellContextManager).inSingletonScope();
43
39
  child.bind(NotebookCellModel).toSelf();
44
40
 
45
41
  return child;
46
42
  }
47
43
 
48
- const NotebookCellContextManager = Symbol('NotebookCellContextManager');
49
- interface NotebookCellContextManager {
50
- updateCellContext(cell: NotebookCellModel, context: HTMLElement): void;
51
- dispose(): void;
52
- onDidChangeContext: Event<ContextKeyChangeEvent>;
53
- }
54
-
55
44
  export interface CellInternalMetadataChangedEvent {
56
45
  readonly lastRunSuccessChanged?: boolean;
57
46
  }
@@ -111,13 +100,14 @@ export class NotebookCellModel implements NotebookCell, Disposable {
111
100
  protected readonly onDidRequestCellEditChangeEmitter = new Emitter<boolean>();
112
101
  readonly onDidRequestCellEditChange = this.onDidRequestCellEditChangeEmitter.event;
113
102
 
114
- @inject(NotebookCellContextManager)
115
- readonly notebookCellContextManager: NotebookCellContextManager;
103
+ protected readonly onWillFocusCellEditorEmitter = new Emitter<void>();
104
+ readonly onWillFocusCellEditor = this.onWillFocusCellEditorEmitter.event;
116
105
 
117
106
  @inject(NotebookCellModelProps)
118
107
  protected readonly props: NotebookCellModelProps;
119
- @inject(MonacoTextModelService)
120
- protected readonly textModelService: MonacoTextModelService;
108
+
109
+ @inject(NotebookMonacoTextModelService)
110
+ protected readonly textModelService: NotebookMonacoTextModelService;
121
111
 
122
112
  get outputs(): NotebookCellOutputModel[] {
123
113
  return this._outputs;
@@ -129,6 +119,11 @@ export class NotebookCellModel implements NotebookCell, Disposable {
129
119
  return this._metadata;
130
120
  }
131
121
 
122
+ set metadata(newMetadata: NotebookCellMetadata) {
123
+ this._metadata = newMetadata;
124
+ this.onDidChangeMetadataEmitter.fire();
125
+ }
126
+
132
127
  protected _metadata: NotebookCellMetadata;
133
128
 
134
129
  protected toDispose = new DisposableCollection();
@@ -152,14 +147,8 @@ export class NotebookCellModel implements NotebookCell, Disposable {
152
147
 
153
148
  protected textModel?: MonacoEditorModel;
154
149
 
155
- protected htmlContext: HTMLLIElement;
156
-
157
- get context(): HTMLLIElement {
158
- return this.htmlContext;
159
- }
160
-
161
150
  get text(): string {
162
- return this.textModel ? this.textModel.getText() : this.source;
151
+ return this.textModel && !this.textModel.isDisposed() ? this.textModel.getText() : this.source;
163
152
  }
164
153
 
165
154
  get source(): string {
@@ -198,6 +187,11 @@ export class NotebookCellModel implements NotebookCell, Disposable {
198
187
  return this.props.cellKind;
199
188
  }
200
189
 
190
+ protected _editing: boolean = false;
191
+ get editing(): boolean {
192
+ return this._editing;
193
+ }
194
+
201
195
  @postConstruct()
202
196
  protected init(): void {
203
197
  this._outputs = this.props.outputs.map(op => new NotebookCellOutputModel(op));
@@ -205,13 +199,6 @@ export class NotebookCellModel implements NotebookCell, Disposable {
205
199
  this._internalMetadata = this.props.internalMetadata ?? {};
206
200
  }
207
201
 
208
- refChanged(node: HTMLLIElement): void {
209
- if (node) {
210
- this.htmlContext = node;
211
- this.notebookCellContextManager.updateCellContext(this, node);
212
- }
213
- }
214
-
215
202
  dispose(): void {
216
203
  this.onDidChangeOutputsEmitter.dispose();
217
204
  this.onDidChangeOutputItemsEmitter.dispose();
@@ -219,21 +206,27 @@ export class NotebookCellModel implements NotebookCell, Disposable {
219
206
  this.onDidChangeMetadataEmitter.dispose();
220
207
  this.onDidChangeInternalMetadataEmitter.dispose();
221
208
  this.onDidChangeLanguageEmitter.dispose();
222
- this.notebookCellContextManager.dispose();
223
209
  this.textModel?.dispose();
224
210
  this.toDispose.dispose();
225
211
  }
226
212
 
227
213
  requestEdit(): void {
228
214
  if (!this.textModel || !this.textModel.readOnly) {
215
+ this._editing = true;
229
216
  this.onDidRequestCellEditChangeEmitter.fire(true);
230
217
  }
231
218
  }
232
219
 
233
220
  requestStopEdit(): void {
221
+ this._editing = false;
234
222
  this.onDidRequestCellEditChangeEmitter.fire(false);
235
223
  }
236
224
 
225
+ requestFocusEditor(): void {
226
+ this.requestEdit();
227
+ this.onWillFocusCellEditorEmitter.fire();
228
+ }
229
+
237
230
  spliceNotebookCellOutputs(splice: NotebookCellOutputsSplice): void {
238
231
  if (splice.deleteCount > 0 && splice.newOutputs.length > 0) {
239
232
  const commonLen = Math.min(splice.deleteCount, splice.newOutputs.length);
@@ -298,7 +291,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
298
291
  return this.textModel;
299
292
  }
300
293
 
301
- const ref = await this.textModelService.createModelReference(this.uri);
294
+ const ref = await this.textModelService.getOrCreateNotebookCellModelReference(this.uri);
302
295
  this.textModel = ref.object;
303
296
  this.textModel.onDidChangeContent(e => {
304
297
  this.props.source = e.model.getText();
@@ -18,14 +18,18 @@ import { Disposable, Emitter, Event, Resource, URI } from '@theia/core';
18
18
  import { Saveable, SaveOptions } from '@theia/core/lib/browser';
19
19
  import {
20
20
  CellData, CellEditType, CellUri, NotebookCellInternalMetadata,
21
+ NotebookCellMetadata,
21
22
  NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookData,
22
23
  NotebookDocumentMetadata,
23
24
  } from '../../common';
24
- import { NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent, CellEditOperation, NullablePartialNotebookCellInternalMetadata } from '../notebook-types';
25
+ import {
26
+ NotebookContentChangedEvent, NotebookModelWillAddRemoveEvent,
27
+ CellEditOperation, NullablePartialNotebookCellInternalMetadata,
28
+ NullablePartialNotebookCellMetadata
29
+ } from '../notebook-types';
25
30
  import { NotebookSerializer } from '../service/notebook-service';
26
31
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
27
32
  import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model';
28
- import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
29
33
  import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
30
34
  import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service';
31
35
  import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
@@ -64,6 +68,9 @@ export class NotebookModel implements Saveable, Disposable {
64
68
  protected readonly onDidChangeContentEmitter = new Emitter<NotebookContentChangedEvent[]>();
65
69
  readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
66
70
 
71
+ protected readonly onDidChangeSelectedCellEmitter = new Emitter<NotebookCellModel | undefined>();
72
+ readonly onDidChangeSelectedCell = this.onDidChangeSelectedCellEmitter.event;
73
+
67
74
  get onDidChangeReadOnly(): Event<boolean | MarkdownString> {
68
75
  return this.props.resource.onDidChangeReadOnly ?? Event.None;
69
76
  }
@@ -77,9 +84,6 @@ export class NotebookModel implements Saveable, Disposable {
77
84
  @inject(NotebookModelProps)
78
85
  protected props: NotebookModelProps;
79
86
 
80
- @inject(MonacoTextModelService)
81
- protected modelService: MonacoTextModelService;
82
-
83
87
  @inject(NotebookCellModelFactory)
84
88
  protected cellModelFactory: NotebookCellModelFactory;
85
89
  readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange';
@@ -232,6 +236,7 @@ export class NotebookModel implements Saveable, Disposable {
232
236
 
233
237
  setSelectedCell(cell: NotebookCellModel): void {
234
238
  this.selectedCell = cell;
239
+ this.onDidChangeSelectedCellEmitter.fire(cell);
235
240
  }
236
241
 
237
242
  private addCellOutputListeners(cells: NotebookCellModel[]): void {
@@ -285,7 +290,10 @@ export class NotebookModel implements Saveable, Disposable {
285
290
  cell.changeOutputItems(edit.outputId, !!edit.append, edit.items);
286
291
  break;
287
292
  case CellEditType.Metadata:
288
- this.updateNotebookMetadata(edit.metadata, computeUndoRedo);
293
+ this.changeCellMetadata(this.cells[cellIndex], edit.metadata, computeUndoRedo);
294
+ break;
295
+ case CellEditType.PartialMetadata:
296
+ this.changeCellMetadataPartial(this.cells[cellIndex], edit.metadata, computeUndoRedo);
289
297
  break;
290
298
  case CellEditType.PartialInternalMetadata:
291
299
  this.changeCellInternalMetadataPartial(this.cells[cellIndex], edit.internalMetadata);
@@ -294,13 +302,18 @@ export class NotebookModel implements Saveable, Disposable {
294
302
  this.changeCellLanguage(this.cells[cellIndex], edit.language, computeUndoRedo);
295
303
  break;
296
304
  case CellEditType.DocumentMetadata:
305
+ this.updateNotebookMetadata(edit.metadata, computeUndoRedo);
297
306
  break;
298
307
  case CellEditType.Move:
299
308
  this.moveCellToIndex(cellIndex, edit.length, edit.newIdx, computeUndoRedo);
300
309
  break;
301
-
310
+ }
311
+ // if selected cell is affected update it because it can potentially have been replaced
312
+ if (cell === this.selectedCell) {
313
+ this.setSelectedCell(this.cells[cellIndex]);
302
314
  }
303
315
  }
316
+
304
317
  }
305
318
 
306
319
  protected async replaceCells(start: number, deleteCount: number, newCells: CellData[], computeUndoRedo: boolean): Promise<void> {
@@ -340,6 +353,10 @@ export class NotebookModel implements Saveable, Disposable {
340
353
 
341
354
  this.onDidAddOrRemoveCellEmitter.fire({ rawEvent: { kind: NotebookCellsChangeType.ModelChange, changes }, newCellIds: cells.map(cell => cell.handle) });
342
355
  this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ModelChange, changes }]);
356
+ if (cells.length > 0) {
357
+ this.setSelectedCell(cells[cells.length - 1]);
358
+ cells[cells.length - 1].requestEdit();
359
+ }
343
360
  }
344
361
 
345
362
  protected changeCellInternalMetadataPartial(cell: NotebookCellModel, internalMetadata: NullablePartialNotebookCellInternalMetadata): void {
@@ -371,6 +388,38 @@ export class NotebookModel implements Saveable, Disposable {
371
388
  this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeDocumentMetadata, metadata: this.metadata }]);
372
389
  }
373
390
 
391
+ protected changeCellMetadataPartial(cell: NotebookCellModel, metadata: NullablePartialNotebookCellMetadata, computeUndoRedo: boolean): void {
392
+ const newMetadata: NotebookCellMetadata = {
393
+ ...cell.metadata
394
+ };
395
+ let k: keyof NullablePartialNotebookCellMetadata;
396
+ // eslint-disable-next-line guard-for-in
397
+ for (k in metadata) {
398
+ const value = metadata[k] ?? undefined;
399
+ newMetadata[k] = value as unknown;
400
+ }
401
+
402
+ this.changeCellMetadata(cell, newMetadata, computeUndoRedo);
403
+ }
404
+
405
+ protected changeCellMetadata(cell: NotebookCellModel, metadata: NotebookCellMetadata, computeUndoRedo: boolean): void {
406
+ const triggerDirtyChange = this.isCellMetadataChanged(cell.metadata, metadata);
407
+
408
+ if (triggerDirtyChange) {
409
+ if (computeUndoRedo) {
410
+ const oldMetadata = cell.metadata;
411
+ cell.metadata = metadata;
412
+ this.undoRedoService.pushElement(this.uri,
413
+ async () => { cell.metadata = oldMetadata; },
414
+ async () => { cell.metadata = metadata; }
415
+ );
416
+ }
417
+ }
418
+
419
+ cell.metadata = metadata;
420
+ this.onDidChangeContentEmitter.fire([{ kind: NotebookCellsChangeType.ChangeCellMetadata, index: this.cells.indexOf(cell), metadata: cell.metadata }]);
421
+ }
422
+
374
423
  protected changeCellLanguage(cell: NotebookCellModel, languageId: string, computeUndoRedo: boolean): void {
375
424
  if (cell.language === languageId) {
376
425
  return;
@@ -399,4 +448,18 @@ export class NotebookModel implements Saveable, Disposable {
399
448
  protected getCellIndexByHandle(handle: number): number {
400
449
  return this.cells.findIndex(c => c.handle === handle);
401
450
  }
451
+
452
+ protected isCellMetadataChanged(a: NotebookCellMetadata, b: NotebookCellMetadata): boolean {
453
+ const keys = new Set([...Object.keys(a || {}), ...Object.keys(b || {})]);
454
+ for (const key of keys) {
455
+ if (
456
+ (a[key as keyof NotebookCellMetadata] !== b[key as keyof NotebookCellMetadata])
457
+ ) {
458
+ return true;
459
+ }
460
+ }
461
+
462
+ return false;
463
+ }
464
+
402
465
  }
@@ -179,6 +179,19 @@ export namespace NotebookModelResource {
179
179
  }
180
180
  }
181
181
 
182
+ export interface NotebookCellModelResource {
183
+ notebookCellModelUri: URI;
184
+ }
185
+
186
+ export namespace NotebookCellModelResource {
187
+ export function is(item: unknown): item is NotebookCellModelResource {
188
+ return isObject<NotebookCellModelResource>(item) && item.notebookCellModelUri instanceof URI;
189
+ }
190
+ export function create(uri: URI): NotebookCellModelResource {
191
+ return { notebookCellModelUri: uri };
192
+ }
193
+ }
194
+
182
195
  export enum NotebookCellExecutionState {
183
196
  Unconfirmed = 1,
184
197
  Pending = 2,
@@ -1,16 +0,0 @@
1
- import { ContextKeyChangeEvent, ContextKeyService, ScopedValueStore } from '@theia/core/lib/browser/context-key-service';
2
- import { NotebookCellModel } from '../view-model/notebook-cell-model';
3
- import { Disposable, DisposableCollection, Emitter } from '@theia/core';
4
- import { NotebookExecutionStateService } from '../service/notebook-execution-state-service';
5
- export declare class NotebookCellContextManager implements NotebookCellContextManager, Disposable {
6
- protected contextKeyService: ContextKeyService;
7
- protected readonly executionStateService: NotebookExecutionStateService;
8
- protected readonly toDispose: DisposableCollection;
9
- protected currentStore: ScopedValueStore;
10
- protected currentContext: HTMLLIElement;
11
- protected readonly onDidChangeContextEmitter: Emitter<ContextKeyChangeEvent>;
12
- readonly onDidChangeContext: import("@theia/core").Event<ContextKeyChangeEvent>;
13
- updateCellContext(cell: NotebookCellModel, newHtmlContext: HTMLLIElement): void;
14
- dispose(): void;
15
- }
16
- //# sourceMappingURL=notebook-cell-context-manager.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"notebook-cell-context-manager.d.ts","sourceRoot":"","sources":["../../../src/browser/service/notebook-cell-context-manager.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AACzH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6CAA6C,CAAC;AAE5F,qBACa,0BAA2B,YAAW,0BAA0B,EAAE,UAAU;IAC1D,SAAS,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAG1E,SAAS,CAAC,QAAQ,CAAC,qBAAqB,EAAE,6BAA6B,CAAC;IAExE,SAAS,CAAC,QAAQ,CAAC,SAAS,uBAA8B;IAE1D,SAAS,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACzC,SAAS,CAAC,cAAc,EAAE,aAAa,CAAC;IAExC,SAAS,CAAC,QAAQ,CAAC,yBAAyB,iCAAwC;IACpF,QAAQ,CAAC,kBAAkB,qDAAwC;IAEnE,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,GAAG,IAAI;IA4B/E,OAAO,IAAI,IAAI;CAIlB"}