@theia/notebook 1.42.1 → 1.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +8 -0
  2. package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
  3. package/lib/browser/contributions/notebook-cell-actions-contribution.js +9 -1
  4. package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
  5. package/lib/browser/contributions/notebook-context-keys.d.ts +3 -0
  6. package/lib/browser/contributions/notebook-context-keys.d.ts.map +1 -1
  7. package/lib/browser/contributions/notebook-context-keys.js +7 -0
  8. package/lib/browser/contributions/notebook-context-keys.js.map +1 -1
  9. package/lib/browser/index.d.ts +1 -1
  10. package/lib/browser/index.d.ts.map +1 -1
  11. package/lib/browser/index.js +1 -1
  12. package/lib/browser/index.js.map +1 -1
  13. package/lib/browser/notebook-cell-resource-resolver.d.ts +1 -4
  14. package/lib/browser/notebook-cell-resource-resolver.d.ts.map +1 -1
  15. package/lib/browser/notebook-cell-resource-resolver.js.map +1 -1
  16. package/lib/browser/notebook-editor-widget-factory.d.ts.map +1 -1
  17. package/lib/browser/notebook-editor-widget-factory.js +1 -1
  18. package/lib/browser/notebook-editor-widget-factory.js.map +1 -1
  19. package/lib/browser/notebook-editor-widget.d.ts +8 -6
  20. package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
  21. package/lib/browser/notebook-editor-widget.js +38 -27
  22. package/lib/browser/notebook-editor-widget.js.map +1 -1
  23. package/lib/browser/notebook-frontend-module.js +3 -3
  24. package/lib/browser/notebook-frontend-module.js.map +1 -1
  25. package/lib/browser/notebook-open-handler.d.ts +1 -2
  26. package/lib/browser/notebook-open-handler.d.ts.map +1 -1
  27. package/lib/browser/notebook-open-handler.js +8 -10
  28. package/lib/browser/notebook-open-handler.js.map +1 -1
  29. package/lib/browser/notebook-renderer-registry.d.ts +2 -1
  30. package/lib/browser/notebook-renderer-registry.d.ts.map +1 -1
  31. package/lib/browser/notebook-renderer-registry.js +6 -3
  32. package/lib/browser/notebook-renderer-registry.js.map +1 -1
  33. package/lib/browser/notebook-type-registry.d.ts +2 -1
  34. package/lib/browser/notebook-type-registry.d.ts.map +1 -1
  35. package/lib/browser/notebook-type-registry.js +6 -3
  36. package/lib/browser/notebook-type-registry.js.map +1 -1
  37. package/lib/browser/renderers/cell-output-webview.d.ts +1 -1
  38. package/lib/browser/renderers/cell-output-webview.d.ts.map +1 -1
  39. package/lib/browser/service/notebook-cell-context-manager.d.ts +1 -2
  40. package/lib/browser/service/notebook-cell-context-manager.d.ts.map +1 -1
  41. package/lib/browser/service/notebook-cell-context-manager.js +7 -11
  42. package/lib/browser/service/notebook-cell-context-manager.js.map +1 -1
  43. package/lib/browser/service/{notebook-editor-service.d.ts → notebook-editor-widget-service.d.ts} +5 -5
  44. package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -0
  45. package/lib/browser/service/{notebook-editor-service.js → notebook-editor-widget-service.js} +21 -13
  46. package/lib/browser/service/notebook-editor-widget-service.js.map +1 -0
  47. package/lib/browser/service/notebook-execution-service.js +1 -1
  48. package/lib/browser/service/notebook-execution-service.js.map +1 -1
  49. package/lib/browser/service/notebook-execution-state-service.d.ts +6 -18
  50. package/lib/browser/service/notebook-execution-state-service.d.ts.map +1 -1
  51. package/lib/browser/service/notebook-execution-state-service.js +24 -14
  52. package/lib/browser/service/notebook-execution-state-service.js.map +1 -1
  53. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts +1 -0
  54. package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts.map +1 -1
  55. package/lib/browser/service/notebook-kernel-quick-pick-service.js +20 -17
  56. package/lib/browser/service/notebook-kernel-quick-pick-service.js.map +1 -1
  57. package/lib/browser/service/notebook-kernel-service.d.ts +15 -14
  58. package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
  59. package/lib/browser/service/notebook-kernel-service.js +24 -18
  60. package/lib/browser/service/notebook-kernel-service.js.map +1 -1
  61. package/lib/browser/service/notebook-model-resolver-service.d.ts +3 -2
  62. package/lib/browser/service/notebook-model-resolver-service.d.ts.map +1 -1
  63. package/lib/browser/service/notebook-model-resolver-service.js +53 -35
  64. package/lib/browser/service/notebook-model-resolver-service.js.map +1 -1
  65. package/lib/browser/service/notebook-renderer-messaging-service.d.ts +7 -5
  66. package/lib/browser/service/notebook-renderer-messaging-service.d.ts.map +1 -1
  67. package/lib/browser/service/notebook-renderer-messaging-service.js +11 -10
  68. package/lib/browser/service/notebook-renderer-messaging-service.js.map +1 -1
  69. package/lib/browser/service/notebook-service.d.ts +12 -17
  70. package/lib/browser/service/notebook-service.d.ts.map +1 -1
  71. package/lib/browser/service/notebook-service.js +13 -18
  72. package/lib/browser/service/notebook-service.js.map +1 -1
  73. package/lib/browser/view/notebook-cell-editor.d.ts +2 -2
  74. package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
  75. package/lib/browser/view/notebook-cell-editor.js +2 -2
  76. package/lib/browser/view/notebook-cell-editor.js.map +1 -1
  77. package/lib/browser/view-model/notebook-cell-model.d.ts +7 -5
  78. package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
  79. package/lib/browser/view-model/notebook-cell-model.js +20 -6
  80. package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
  81. package/lib/browser/view-model/notebook-cell-output-model.d.ts +1 -0
  82. package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
  83. package/lib/browser/view-model/notebook-cell-output-model.js +31 -0
  84. package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
  85. package/lib/browser/view-model/notebook-model.d.ts +15 -12
  86. package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
  87. package/lib/browser/view-model/notebook-model.js +36 -21
  88. package/lib/browser/view-model/notebook-model.js.map +1 -1
  89. package/lib/common/notebook-common.d.ts +4 -6
  90. package/lib/common/notebook-common.d.ts.map +1 -1
  91. package/lib/common/notebook-common.js.map +1 -1
  92. package/package.json +7 -7
  93. package/src/browser/contributions/notebook-cell-actions-contribution.ts +9 -2
  94. package/src/browser/contributions/notebook-context-keys.ts +7 -0
  95. package/src/browser/index.ts +1 -1
  96. package/src/browser/notebook-cell-resource-resolver.ts +1 -5
  97. package/src/browser/notebook-editor-widget-factory.ts +2 -2
  98. package/src/browser/notebook-editor-widget.tsx +18 -13
  99. package/src/browser/notebook-frontend-module.ts +3 -3
  100. package/src/browser/notebook-open-handler.ts +3 -4
  101. package/src/browser/notebook-renderer-registry.ts +7 -3
  102. package/src/browser/notebook-type-registry.ts +7 -3
  103. package/src/browser/renderers/cell-output-webview.ts +1 -1
  104. package/src/browser/service/notebook-cell-context-manager.ts +7 -9
  105. package/src/browser/service/{notebook-editor-service.ts → notebook-editor-widget-service.ts} +19 -12
  106. package/src/browser/service/notebook-execution-service.ts +1 -1
  107. package/src/browser/service/notebook-execution-state-service.ts +26 -33
  108. package/src/browser/service/notebook-kernel-quick-pick-service.ts +10 -7
  109. package/src/browser/service/notebook-kernel-service.ts +29 -30
  110. package/src/browser/service/notebook-model-resolver-service.ts +56 -35
  111. package/src/browser/service/notebook-renderer-messaging-service.ts +21 -19
  112. package/src/browser/service/notebook-service.ts +21 -27
  113. package/src/browser/view/notebook-cell-editor.tsx +3 -3
  114. package/src/browser/view-model/notebook-cell-model.ts +26 -9
  115. package/src/browser/view-model/notebook-cell-output-model.ts +31 -1
  116. package/src/browser/view-model/notebook-model.ts +26 -16
  117. package/src/common/notebook-common.ts +4 -7
  118. package/lib/browser/service/notebook-editor-service.d.ts.map +0 -1
  119. package/lib/browser/service/notebook-editor-service.js.map +0 -1
@@ -23,6 +23,7 @@ import { NotebookModel } from '../view-model/notebook-model';
23
23
  import { NotebookService } from './notebook-service';
24
24
  import { NotebookTypeRegistry } from '../notebook-type-registry';
25
25
  import { NotebookFileSelector } from '../../common/notebook-protocol';
26
+ import { match } from '@theia/core/lib/common/glob';
26
27
 
27
28
  export interface UntitledResource {
28
29
  untitledResource: URI | undefined
@@ -44,40 +45,21 @@ export class NotebookModelResolverService {
44
45
  protected onDidSaveNotebookEmitter = new Emitter<UriComponents>();
45
46
  readonly onDidSaveNotebook = this.onDidSaveNotebookEmitter.event;
46
47
 
47
- async resolve(resource: URI, viewType?: string): Promise<NotebookModel>;
48
- async resolve(resource: UntitledResource, viewType: string): Promise<NotebookModel>;
49
- async resolve(arg: URI | UntitledResource, viewType: string): Promise<NotebookModel> {
50
- let resource: URI;
51
- // let hasAssociatedFilePath = false;
52
- if (arg instanceof URI) {
53
- resource = arg;
54
- } else {
55
- arg = arg as UntitledResource;
56
- if (!arg.untitledResource) {
57
- const notebookTypeInfo = this.notebookTypeRegistry.notebookTypes.find(info => info.type === viewType);
58
- if (!notebookTypeInfo) {
59
- throw new Error('UNKNOWN view type: ' + viewType);
60
- }
48
+ async resolve(resource: URI, viewType?: string): Promise<NotebookModel> {
61
49
 
62
- const suffix = this.getPossibleFileEndings(notebookTypeInfo.selector ?? []) ?? '';
63
- for (let counter = 1; ; counter++) {
64
- const candidate = new URI()
65
- .withScheme('untitled')
66
- .withPath(`Untitled-notebook-${counter}${suffix}`)
67
- .withQuery(viewType);
68
- if (!this.notebookService.getNotebookEditorModel(candidate)) {
69
- resource = candidate;
70
- break;
71
- }
72
- }
73
- } else if (arg.untitledResource.scheme === 'untitled') {
74
- resource = arg.untitledResource;
50
+ if (!viewType) {
51
+ const existingViewType = this.notebookService.getNotebookEditorModel(resource)?.viewType;
52
+ if (existingViewType) {
53
+ viewType = existingViewType;
75
54
  } else {
76
- resource = arg.untitledResource.withScheme('untitled');
77
- // hasAssociatedFilePath = true;
55
+ viewType = this.findViewTypeForResource(resource);
78
56
  }
79
57
  }
80
58
 
59
+ if (!viewType) {
60
+ throw new Error(`Missing viewType for '${resource}'`);
61
+ }
62
+
81
63
  const notebookData = await this.resolveExistingNotebookData(resource, viewType!);
82
64
 
83
65
  const notebookModel = await this.notebookService.createNotebookModel(notebookData, viewType, resource);
@@ -88,6 +70,39 @@ export class NotebookModelResolverService {
88
70
  return notebookModel;
89
71
  }
90
72
 
73
+ async resolveUntitledResource(arg: UntitledResource, viewType: string): Promise<NotebookModel> {
74
+ let resource: URI;
75
+ // let hasAssociatedFilePath = false;
76
+ arg = arg as UntitledResource;
77
+ if (!arg.untitledResource) {
78
+ const notebookTypeInfo = this.notebookTypeRegistry.notebookTypes.find(info => info.type === viewType);
79
+ if (!notebookTypeInfo) {
80
+ throw new Error('UNKNOWN view type: ' + viewType);
81
+ }
82
+
83
+ const suffix = this.getPossibleFileEnding(notebookTypeInfo.selector ?? []) ?? '';
84
+ for (let counter = 1; ; counter++) {
85
+ const candidate = new URI()
86
+ .withScheme('untitled')
87
+ .withPath(`Untitled-notebook-${counter}${suffix}`)
88
+ .withQuery(viewType);
89
+ if (!this.notebookService.getNotebookEditorModel(candidate)) {
90
+ resource = candidate;
91
+ break;
92
+ }
93
+ }
94
+ } else if (arg.untitledResource.scheme === 'untitled') {
95
+ resource = arg.untitledResource;
96
+ } else {
97
+ throw new Error('Invalid untitled resource: ' + arg.untitledResource.toString() + ' untitled resources with associated file path are not supported yet');
98
+ // TODO implement associated file path support
99
+ // resource = arg.untitledResource.withScheme('untitled');
100
+ // hasAssociatedFilePath = true;
101
+ }
102
+
103
+ return this.resolve(resource, viewType);
104
+ }
105
+
91
106
  protected async resolveExistingNotebookData(resource: URI, viewType: string): Promise<NotebookData> {
92
107
  if (resource.scheme === 'untitled') {
93
108
 
@@ -106,13 +121,13 @@ export class NotebookModelResolverService {
106
121
  const file = await this.fileService.readFile(resource);
107
122
 
108
123
  const dataProvider = await this.notebookService.getNotebookDataProvider(viewType);
109
- const notebook = await dataProvider.serializer.dataToNotebook(file.value);
124
+ const notebook = await dataProvider.serializer.toNotebook(file.value);
110
125
 
111
126
  return notebook;
112
127
  }
113
128
  }
114
129
 
115
- protected getPossibleFileEndings(selectors: readonly NotebookFileSelector[]): string | undefined {
130
+ protected getPossibleFileEnding(selectors: readonly NotebookFileSelector[]): string | undefined {
116
131
  for (const selector of selectors) {
117
132
  const ending = this.possibleFileEnding(selector);
118
133
  if (ending) {
@@ -126,16 +141,22 @@ export class NotebookModelResolverService {
126
141
 
127
142
  const pattern = /^.*(\.[a-zA-Z0-9_-]+)$/;
128
143
 
129
- const candidate: string | undefined = typeof selector === 'string' ? selector : selector.filenamePattern;
144
+ const candidate = typeof selector === 'string' ? selector : selector.filenamePattern;
130
145
 
131
146
  if (candidate) {
132
- const match = pattern.exec(candidate);
133
- if (match) {
134
- return match[1];
147
+ const matches = pattern.exec(candidate);
148
+ if (matches) {
149
+ return matches[1];
135
150
  }
136
151
  }
137
152
 
138
153
  return undefined;
139
154
  }
140
155
 
156
+ protected findViewTypeForResource(resource: URI): string | undefined {
157
+ return this.notebookTypeRegistry.notebookTypes.find(info =>
158
+ info.selector?.some(selector => selector.filenamePattern && match(selector.filenamePattern, resource.path.name + resource.path.ext))
159
+ )?.type;
160
+ }
161
+
141
162
  }
@@ -22,18 +22,18 @@ import { Emitter } from '@theia/core';
22
22
  import { injectable } from '@theia/core/shared/inversify';
23
23
  import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
24
24
 
25
- interface MessageToSend {
25
+ interface RendererMessage {
26
26
  editorId: string;
27
27
  rendererId: string;
28
28
  message: unknown
29
29
  };
30
30
 
31
- export interface ScopedRendererMessaging extends Disposable {
31
+ export interface RendererMessaging extends Disposable {
32
32
  /**
33
33
  * Method called when a message is received. Should return a boolean
34
34
  * indicating whether a renderer received it.
35
35
  */
36
- receiveMessageHandler?: (rendererId: string, message: unknown) => Promise<boolean>;
36
+ receiveMessage?: (rendererId: string, message: unknown) => Promise<boolean>;
37
37
 
38
38
  /**
39
39
  * Sends a message to an extension from a renderer.
@@ -44,19 +44,22 @@ export interface ScopedRendererMessaging extends Disposable {
44
44
  @injectable()
45
45
  export class NotebookRendererMessagingService implements Disposable {
46
46
 
47
- private readonly postMessageEmitter = new Emitter<MessageToSend>();
48
- readonly onShouldPostMessage = this.postMessageEmitter.event;
47
+ private readonly postMessageEmitter = new Emitter<RendererMessage>();
48
+ readonly onPostMessage = this.postMessageEmitter.event;
49
49
 
50
- private readonly activations = new Map<string /* rendererId */, undefined | MessageToSend[]>();
51
- private readonly scopedMessaging = new Map<string /* editorId */, ScopedRendererMessaging>();
50
+ private readonly willActivateRendererEmitter = new Emitter<string>();
51
+ readonly onWillActivateRenderer = this.willActivateRendererEmitter.event;
52
+
53
+ private readonly activations = new Map<string /* rendererId */, undefined | RendererMessage[]>();
54
+ private readonly scopedMessaging = new Map<string /* editorId */, RendererMessaging>();
52
55
 
53
56
  receiveMessage(editorId: string | undefined, rendererId: string, message: unknown): Promise<boolean> {
54
57
  if (editorId === undefined) {
55
- const sends = [...this.scopedMessaging.values()].map(e => e.receiveMessageHandler?.(rendererId, message));
58
+ const sends = [...this.scopedMessaging.values()].map(e => e.receiveMessage?.(rendererId, message));
56
59
  return Promise.all(sends).then(values => values.some(value => !!value));
57
60
  }
58
61
 
59
- return this.scopedMessaging.get(editorId)?.receiveMessageHandler?.(rendererId, message) ?? Promise.resolve(false);
62
+ return this.scopedMessaging.get(editorId)?.receiveMessage?.(rendererId, message) ?? Promise.resolve(false);
60
63
  }
61
64
 
62
65
  prepare(rendererId: string): void {
@@ -64,25 +67,24 @@ export class NotebookRendererMessagingService implements Disposable {
64
67
  return;
65
68
  }
66
69
 
67
- const queue: MessageToSend[] = [];
70
+ const queue: RendererMessage[] = [];
68
71
  this.activations.set(rendererId, queue);
69
72
 
70
- // activate renderer
71
- // this.extensionService.activateByEvent(`onRenderer:${rendererId}`).then(() => {
72
- // for (const message of queue) {
73
- // this.postMessageEmitter.fire(message);
74
- // }
75
- // this.activations.set(rendererId, undefined);
76
- // });
73
+ Promise.all(this.willActivateRendererEmitter.fire(rendererId)).then(() => {
74
+ for (const message of queue) {
75
+ this.postMessageEmitter.fire(message);
76
+ }
77
+ this.activations.set(rendererId, undefined);
78
+ });
77
79
  }
78
80
 
79
- public getScoped(editorId: string): ScopedRendererMessaging {
81
+ public getScoped(editorId: string): RendererMessaging {
80
82
  const existing = this.scopedMessaging.get(editorId);
81
83
  if (existing) {
82
84
  return existing;
83
85
  }
84
86
 
85
- const messaging: ScopedRendererMessaging = {
87
+ const messaging: RendererMessaging = {
86
88
  postMessage: (rendererId, message) => this.postMessage(editorId, rendererId, message),
87
89
  dispose: () => this.scopedMessaging.delete(editorId),
88
90
  };
@@ -17,7 +17,7 @@
17
17
  import { Disposable, DisposableCollection, Emitter, 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, NotebookExtensionDescription, TransientOptions } from '../../common';
20
+ import { 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 { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
@@ -26,16 +26,15 @@ import { Deferred } from '@theia/core/lib/common/promise-util';
26
26
 
27
27
  export const NotebookProvider = Symbol('notebook provider');
28
28
 
29
- export interface SimpleNotebookProviderInfo {
29
+ export interface NotebookProviderInfo {
30
30
  readonly notebookType: string,
31
31
  readonly serializer: NotebookSerializer,
32
- readonly extensionData: NotebookExtensionDescription
33
32
  }
34
33
 
35
34
  export interface NotebookSerializer {
36
35
  options: TransientOptions;
37
- dataToNotebook(data: BinaryBuffer): Promise<NotebookData>;
38
- notebookToData(data: NotebookData): Promise<BinaryBuffer>;
36
+ toNotebook(data: BinaryBuffer): Promise<NotebookData>;
37
+ fromNotebook(data: NotebookData): Promise<BinaryBuffer>;
39
38
  }
40
39
 
41
40
  @injectable()
@@ -53,16 +52,16 @@ export class NotebookService implements Disposable {
53
52
  @inject(NotebookCellModelFactory)
54
53
  protected notebookCellModelFactory: (props: NotebookCellModelProps) => NotebookCellModel;
55
54
 
56
- protected notebookSerializerEmitter = new Emitter<string>();
57
- readonly onNotebookSerializer = this.notebookSerializerEmitter.event;
55
+ protected willUseNotebookSerializerEmitter = new Emitter<string>();
56
+ readonly onWillUseNotebookSerializer = this.willUseNotebookSerializerEmitter.event;
58
57
 
59
58
  protected readonly disposables = new DisposableCollection();
60
59
 
61
- protected readonly notebookProviders = new Map<string, SimpleNotebookProviderInfo>();
60
+ protected readonly notebookProviders = new Map<string, NotebookProviderInfo>();
62
61
  protected readonly notebookModels = new Map<string, NotebookModel>();
63
62
 
64
- protected readonly didAddViewTypeEmitter = new Emitter<string>();
65
- readonly onDidAddViewType = this.didAddViewTypeEmitter.event;
63
+ protected readonly didRegisterNotebookSerializerEmitter = new Emitter<string>();
64
+ readonly onDidRegisterNotebookSerializer = this.didRegisterNotebookSerializerEmitter.event;
66
65
 
67
66
  protected readonly didRemoveViewTypeEmitter = new Emitter<string>();
68
67
  readonly onDidRemoveViewType = this.didRemoveViewTypeEmitter.event;
@@ -70,12 +69,8 @@ export class NotebookService implements Disposable {
70
69
  protected readonly willOpenNotebookTypeEmitter = new Emitter<string>();
71
70
  readonly onWillOpenNotebook = this.willOpenNotebookTypeEmitter.event;
72
71
 
73
- protected readonly willAddNotebookDocumentEmitter = new Emitter<URI>();
74
- readonly onWillAddNotebookDocument = this.willAddNotebookDocumentEmitter.event;
75
72
  protected readonly didAddNotebookDocumentEmitter = new Emitter<NotebookModel>();
76
73
  readonly onDidAddNotebookDocument = this.didAddNotebookDocumentEmitter.event;
77
- protected readonly willRemoveNotebookDocumentEmitter = new Emitter<NotebookModel>();
78
- readonly onWillRemoveNotebookDocument = this.willRemoveNotebookDocumentEmitter.event;
79
74
  protected readonly didRemoveNotebookDocumentEmitter = new Emitter<NotebookModel>();
80
75
  readonly onDidRemoveNotebookDocument = this.didRemoveNotebookDocumentEmitter.event;
81
76
 
@@ -92,17 +87,17 @@ export class NotebookService implements Disposable {
92
87
  this.ready.resolve();
93
88
  }
94
89
 
95
- registerNotebookSerializer(notebookType: string, extensionData: NotebookExtensionDescription, serializer: NotebookSerializer): Disposable {
96
- if (this.notebookProviders.has(notebookType)) {
97
- throw new Error(`notebook provider for viewtype '${notebookType}' already exists`);
90
+ registerNotebookSerializer(viewType: string, serializer: NotebookSerializer): Disposable {
91
+ if (this.notebookProviders.has(viewType)) {
92
+ throw new Error(`notebook provider for viewtype '${viewType}' already exists`);
98
93
  }
99
94
 
100
- this.notebookProviders.set(notebookType, { notebookType: notebookType, serializer, extensionData });
101
- this.didAddViewTypeEmitter.fire(notebookType);
95
+ this.notebookProviders.set(viewType, { notebookType: viewType, serializer });
96
+ this.didRegisterNotebookSerializerEmitter.fire(viewType);
102
97
 
103
98
  return Disposable.create(() => {
104
- this.notebookProviders.delete(notebookType);
105
- this.didRemoveViewTypeEmitter.fire(notebookType);
99
+ this.notebookProviders.delete(viewType);
100
+ this.didRemoveViewTypeEmitter.fire(viewType);
106
101
  });
107
102
  }
108
103
 
@@ -112,7 +107,6 @@ export class NotebookService implements Disposable {
112
107
  throw new Error('no notebook serializer for ' + viewType);
113
108
  }
114
109
 
115
- this.willAddNotebookDocumentEmitter.fire(uri);
116
110
  const model = this.notebookModelFactory({ data, uri, viewType, serializer });
117
111
  this.notebookModels.set(uri.toString(), model);
118
112
  // Resolve cell text models right after creating the notebook model
@@ -122,9 +116,8 @@ export class NotebookService implements Disposable {
122
116
  return model;
123
117
  }
124
118
 
125
- async getNotebookDataProvider(viewType: string): Promise<SimpleNotebookProviderInfo> {
119
+ async getNotebookDataProvider(viewType: string): Promise<NotebookProviderInfo> {
126
120
  await this.ready.promise;
127
- await this.notebookSerializerEmitter.sequence(async listener => listener(`onNotebookSerializer:${viewType}`));
128
121
 
129
122
  const result = await this.waitForNotebookProvider(viewType);
130
123
  if (!result) {
@@ -138,14 +131,15 @@ export class NotebookService implements Disposable {
138
131
  * It takes a few seconds for the plugin host to start so that notebook data providers can be registered.
139
132
  * This methods waits until the notebook provider is registered.
140
133
  */
141
- protected async waitForNotebookProvider(type: string): Promise<SimpleNotebookProviderInfo | undefined> {
134
+ protected async waitForNotebookProvider(type: string): Promise<NotebookProviderInfo | undefined> {
142
135
  if (this.notebookProviders.has(type)) {
143
136
  return this.notebookProviders.get(type);
144
137
  }
145
- const deferred = new Deferred<SimpleNotebookProviderInfo | undefined>();
138
+ await Promise.all(this.willUseNotebookSerializerEmitter.fire(type));
139
+ const deferred = new Deferred<NotebookProviderInfo | undefined>();
146
140
  // 20 seconds of timeout
147
141
  const timeoutDuration = 20_000;
148
- const disposable = this.onDidAddViewType(viewType => {
142
+ const disposable = this.onDidRegisterNotebookSerializer(viewType => {
149
143
  if (viewType === type) {
150
144
  clearTimeout(timeout);
151
145
  disposable.dispose();
@@ -17,7 +17,7 @@
17
17
  import * as React from '@theia/core/shared/react';
18
18
  import { NotebookModel } from '../view-model/notebook-model';
19
19
  import { NotebookCellModel } from '../view-model/notebook-cell-model';
20
- import { MonacoCodeEditor } from '@theia/monaco/lib/browser/monaco-code-editor';
20
+ import { SimpleMonacoEditor } from '@theia/monaco/lib/browser/simple-monaco-editor';
21
21
  import { MonacoEditorServices } from '@theia/monaco/lib/browser/monaco-editor';
22
22
  import { MonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
23
23
  import { DisposableCollection } from '@theia/core';
@@ -40,7 +40,7 @@ const DEFAULT_EDITOR_OPTIONS = {
40
40
 
41
41
  export class CellEditor extends React.Component<CellEditorProps, {}> {
42
42
 
43
- protected editor?: MonacoCodeEditor;
43
+ protected editor?: SimpleMonacoEditor;
44
44
  protected toDispose = new DisposableCollection();
45
45
  protected container?: HTMLDivElement;
46
46
 
@@ -64,7 +64,7 @@ export class CellEditor extends React.Component<CellEditorProps, {}> {
64
64
  const editorNode = this.container;
65
65
  const editorModel = await cell.resolveTextModel();
66
66
  const uri = cell.uri;
67
- this.editor = new MonacoCodeEditor(uri,
67
+ this.editor = new SimpleMonacoEditor(uri,
68
68
  editorModel,
69
69
  editorNode,
70
70
  monacoServices,
@@ -24,11 +24,12 @@ import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model
24
24
  import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
25
25
  import {
26
26
  CellInternalMetadataChangedEvent, CellKind, NotebookCellCollapseState, NotebookCellInternalMetadata,
27
- NotebookCellMetadata, NotebookCellOutputsSplice, CellOutput, CellData, NotebookCell
27
+ NotebookCellMetadata, NotebookCellOutputsSplice, CellOutput, CellData, NotebookCell, CellOutputItem
28
28
  } from '../../common';
29
29
  import { NotebookCellOutputModel } from './notebook-cell-output-model';
30
30
 
31
31
  export const NotebookCellModelFactory = Symbol('NotebookModelFactory');
32
+ export type NotebookCellModelFactory = (props: NotebookCellModelProps) => NotebookCellModel;
32
33
 
33
34
  export function createNotebookCellModelContainer(parent: interfaces.Container, props: NotebookCellModelProps,
34
35
  notebookCellContextManager: new (...args: never[]) => unknown): interfaces.Container {
@@ -69,8 +70,8 @@ export class NotebookCellModel implements NotebookCell, Disposable {
69
70
  protected readonly onDidChangeOutputsEmitter = new Emitter<NotebookCellOutputsSplice>();
70
71
  readonly onDidChangeOutputs: Event<NotebookCellOutputsSplice> = this.onDidChangeOutputsEmitter.event;
71
72
 
72
- protected readonly onDidChangeOutputItemsEmitter = new Emitter<void>();
73
- readonly onDidChangeOutputItems: Event<void> = this.onDidChangeOutputItemsEmitter.event;
73
+ protected readonly onDidChangeOutputItemsEmitter = new Emitter<CellOutput>();
74
+ readonly onDidChangeOutputItems: Event<CellOutput> = this.onDidChangeOutputItemsEmitter.event;
74
75
 
75
76
  protected readonly onDidChangeContentEmitter = new Emitter<'content' | 'language' | 'mime'>();
76
77
  readonly onDidChangeContent: Event<'content' | 'language' | 'mime'> = this.onDidChangeContentEmitter.event;
@@ -134,7 +135,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
134
135
  return this.htmlContext;
135
136
  }
136
137
 
137
- get textBuffer(): string {
138
+ get text(): string {
138
139
  return this.textModel ? this.textModel.getText() : this.source;
139
140
  }
140
141
 
@@ -215,7 +216,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
215
216
  const currentOutput = this.outputs[splice.start + i];
216
217
  const newOutput = splice.newOutputs[i];
217
218
 
218
- this.replaceOutputItems(currentOutput.outputId, newOutput);
219
+ this.replaceOutputData(currentOutput.outputId, newOutput);
219
220
  }
220
221
 
221
222
  this.outputs.splice(splice.start + commonLen, splice.deleteCount - commonLen, ...splice.newOutputs.slice(commonLen).map(op => new NotebookCellOutputModel(op)));
@@ -226,15 +227,31 @@ export class NotebookCellModel implements NotebookCell, Disposable {
226
227
  }
227
228
  }
228
229
 
229
- replaceOutputItems(outputId: string, newOutputItem: CellOutput): boolean {
230
+ replaceOutputData(outputId: string, newOutputData: CellOutput): boolean {
230
231
  const output = this.outputs.find(out => out.outputId === outputId);
231
232
 
232
233
  if (!output) {
233
234
  return false;
234
235
  }
235
236
 
236
- output.replaceData(newOutputItem);
237
- this.onDidChangeOutputItemsEmitter.fire();
237
+ output.replaceData(newOutputData);
238
+ this.onDidChangeOutputItemsEmitter.fire(output);
239
+ return true;
240
+ }
241
+
242
+ changeOutputItems(outputId: string, append: boolean, items: CellOutputItem[]): boolean {
243
+ const output = this.outputs.find(out => out.outputId === outputId);
244
+
245
+ if (!output) {
246
+ return false;
247
+ }
248
+
249
+ if (append) {
250
+ output.appendData(items);
251
+ } else {
252
+ output.replaceData({ outputId: outputId, outputs: items, metadata: output.metadata });
253
+ }
254
+ this.onDidChangeOutputItemsEmitter.fire(output);
238
255
  return true;
239
256
  }
240
257
 
@@ -243,7 +260,7 @@ export class NotebookCellModel implements NotebookCell, Disposable {
243
260
  cellKind: this.cellKind,
244
261
  language: this.language,
245
262
  outputs: this.outputs.map(output => output.getData()),
246
- source: this.textBuffer,
263
+ source: this.text,
247
264
  collapseState: this.props.collapseState,
248
265
  internalMetadata: this.internalMetadata,
249
266
  metadata: this.metadata
@@ -15,7 +15,8 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { Disposable, Emitter } from '@theia/core';
18
- import { CellOutput, CellOutputItem } from '../../common';
18
+ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
19
+ import { CellOutput, CellOutputItem, isTextStreamMime } from '../../common';
19
20
 
20
21
  export class NotebookCellOutputModel implements Disposable {
21
22
 
@@ -41,11 +42,13 @@ export class NotebookCellOutputModel implements Disposable {
41
42
 
42
43
  replaceData(rawData: CellOutput): void {
43
44
  this.rawOutput = rawData;
45
+ this.optimizeOutputItems();
44
46
  this.didChangeDataEmitter.fire();
45
47
  }
46
48
 
47
49
  appendData(items: CellOutputItem[]): void {
48
50
  this.rawOutput.outputs.push(...items);
51
+ this.optimizeOutputItems();
49
52
  this.didChangeDataEmitter.fire();
50
53
  }
51
54
 
@@ -66,4 +69,31 @@ export class NotebookCellOutputModel implements Disposable {
66
69
  };
67
70
  }
68
71
 
72
+ private optimizeOutputItems(): void {
73
+ if (this.outputs.length > 1 && this.outputs.every(item => isTextStreamMime(item.mime))) {
74
+ // Look for the mimes in the items, and keep track of their order.
75
+ // Merge the streams into one output item, per mime type.
76
+ const mimeOutputs = new Map<string, BinaryBuffer[]>();
77
+ const mimeTypes: string[] = [];
78
+ this.outputs.forEach(item => {
79
+ let items: BinaryBuffer[];
80
+ if (mimeOutputs.has(item.mime)) {
81
+ items = mimeOutputs.get(item.mime)!;
82
+ } else {
83
+ items = [];
84
+ mimeOutputs.set(item.mime, items);
85
+ mimeTypes.push(item.mime);
86
+ }
87
+ items.push(item.data);
88
+ });
89
+ this.outputs.length = 0;
90
+ mimeTypes.forEach(mime => {
91
+ this.outputs.push({
92
+ mime,
93
+ data: BinaryBuffer.concat(mimeOutputs.get(mime)!)
94
+ });
95
+ });
96
+ }
97
+ }
98
+
69
99
  }
@@ -25,9 +25,9 @@ import {
25
25
  } from '../../common';
26
26
  import { NotebookSerializer } from '../service/notebook-service';
27
27
  import { FileService } from '@theia/filesystem/lib/browser/file-service';
28
- import { NotebookCellModel, NotebookCellModelFactory, NotebookCellModelProps } from './notebook-cell-model';
28
+ import { NotebookCellModel, NotebookCellModelFactory } from './notebook-cell-model';
29
29
  import { MonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
30
- import { inject, injectable, interfaces } from '@theia/core/shared/inversify';
30
+ import { inject, injectable, interfaces, postConstruct } from '@theia/core/shared/inversify';
31
31
  import { NotebookKernel } from '../service/notebook-kernel-service';
32
32
  import { UndoRedoService } from '@theia/editor/lib/browser/undo-redo-service';
33
33
 
@@ -53,24 +53,32 @@ export interface NotebookModelProps {
53
53
  @injectable()
54
54
  export class NotebookModel implements Saveable, Disposable {
55
55
 
56
- private readonly onDirtyChangedEmitter = new Emitter<void>();
56
+ protected readonly onDirtyChangedEmitter = new Emitter<void>();
57
57
  readonly onDirtyChanged = this.onDirtyChangedEmitter.event;
58
58
 
59
- private readonly onDidSaveNotebookEmitter = new Emitter<void>();
59
+ protected readonly onDidSaveNotebookEmitter = new Emitter<void>();
60
60
  readonly onDidSaveNotebook = this.onDidSaveNotebookEmitter.event;
61
61
 
62
- private readonly onDidAddOrRemoveCellEmitter = new Emitter<NotebookModelWillAddRemoveEvent>();
62
+ protected readonly onDidAddOrRemoveCellEmitter = new Emitter<NotebookModelWillAddRemoveEvent>();
63
63
  readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event;
64
64
 
65
- private readonly onDidChangeContentEmitter = new Emitter<NotebookTextModelChangedEvent>();
65
+ protected readonly onDidChangeContentEmitter = new Emitter<NotebookTextModelChangedEvent>();
66
66
  readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
67
67
 
68
68
  @inject(FileService)
69
- private readonly fileService: FileService;
69
+ protected readonly fileService: FileService;
70
70
 
71
71
  @inject(UndoRedoService)
72
- private readonly undoRedoService: UndoRedoService;
72
+ protected readonly undoRedoService: UndoRedoService;
73
73
 
74
+ @inject(NotebookModelProps)
75
+ protected props: NotebookModelProps;
76
+
77
+ @inject(MonacoTextModelService)
78
+ protected modelService: MonacoTextModelService;
79
+
80
+ @inject(NotebookCellModelFactory)
81
+ protected cellModelFactory: NotebookCellModelFactory;
74
82
  readonly autoSave: 'off' | 'afterDelay' | 'onFocusChange' | 'onWindowChange';
75
83
 
76
84
  nextHandle: number = 0;
@@ -79,7 +87,7 @@ export class NotebookModel implements Saveable, Disposable {
79
87
 
80
88
  dirty: boolean;
81
89
  selectedCell?: NotebookCellModel;
82
- private dirtyCells: NotebookCellModel[] = [];
90
+ protected dirtyCells: NotebookCellModel[] = [];
83
91
 
84
92
  cells: NotebookCellModel[];
85
93
 
@@ -93,13 +101,12 @@ export class NotebookModel implements Saveable, Disposable {
93
101
 
94
102
  metadata: NotebookDocumentMetadata = {};
95
103
 
96
- constructor(@inject(NotebookModelProps) private props: NotebookModelProps,
97
- @inject(MonacoTextModelService) modelService: MonacoTextModelService,
98
- @inject(NotebookCellModelFactory) private cellModelFactory: (props: NotebookCellModelProps) => NotebookCellModel) {
104
+ @postConstruct()
105
+ initialize(): void {
99
106
  this.dirty = false;
100
107
 
101
- this.cells = props.data.cells.map((cell, index) => cellModelFactory({
102
- uri: CellUri.generate(props.uri, index),
108
+ this.cells = this.props.data.cells.map((cell, index) => this.cellModelFactory({
109
+ uri: CellUri.generate(this.props.uri, index),
103
110
  handle: index,
104
111
  source: cell.source,
105
112
  language: cell.language,
@@ -114,7 +121,7 @@ export class NotebookModel implements Saveable, Disposable {
114
121
 
115
122
  this.metadata = this.metadata;
116
123
 
117
- modelService.onDidCreate(editorModel => {
124
+ this.modelService.onDidCreate(editorModel => {
118
125
  const modelUri = new URI(editorModel.uri);
119
126
  if (modelUri.scheme === CellUri.scheme) {
120
127
  const cellUri = CellUri.parse(modelUri);
@@ -142,7 +149,7 @@ export class NotebookModel implements Saveable, Disposable {
142
149
  this.dirty = false;
143
150
  this.onDirtyChangedEmitter.fire();
144
151
 
145
- const serializedNotebook = await this.props.serializer.notebookToData({
152
+ const serializedNotebook = await this.props.serializer.fromNotebook({
146
153
  cells: this.cells.map(cell => cell.getData()),
147
154
  metadata: this.metadata
148
155
  });
@@ -216,6 +223,8 @@ export class NotebookModel implements Saveable, Disposable {
216
223
  cellIndex = edit.index;
217
224
  } else if ('handle' in edit) {
218
225
  cellIndex = this.getCellIndexByHandle(edit.handle);
226
+ } else if ('outputId' in edit) {
227
+ cellIndex = this.cells.findIndex(cell => cell.outputs.some(output => output.outputId === edit.outputId));
219
228
  }
220
229
 
221
230
  return {
@@ -247,6 +256,7 @@ export class NotebookModel implements Saveable, Disposable {
247
256
  break;
248
257
  }
249
258
  case CellEditType.OutputItems:
259
+ cell.changeOutputItems(edit.outputId, !!edit.append, edit.items);
250
260
  break;
251
261
  case CellEditType.Metadata:
252
262
  this.updateNotebookMetadata(edit.metadata, computeUndoRedo);