@theia/notebook 1.42.0 → 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.
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts +8 -0
- package/lib/browser/contributions/notebook-cell-actions-contribution.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js +9 -1
- package/lib/browser/contributions/notebook-cell-actions-contribution.js.map +1 -1
- package/lib/browser/contributions/notebook-context-keys.d.ts +3 -0
- package/lib/browser/contributions/notebook-context-keys.d.ts.map +1 -1
- package/lib/browser/contributions/notebook-context-keys.js +7 -0
- package/lib/browser/contributions/notebook-context-keys.js.map +1 -1
- package/lib/browser/index.d.ts +1 -1
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +1 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/notebook-cell-resource-resolver.d.ts +1 -4
- package/lib/browser/notebook-cell-resource-resolver.d.ts.map +1 -1
- package/lib/browser/notebook-cell-resource-resolver.js.map +1 -1
- package/lib/browser/notebook-editor-widget-factory.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget-factory.js +1 -1
- package/lib/browser/notebook-editor-widget-factory.js.map +1 -1
- package/lib/browser/notebook-editor-widget.d.ts +8 -6
- package/lib/browser/notebook-editor-widget.d.ts.map +1 -1
- package/lib/browser/notebook-editor-widget.js +38 -27
- package/lib/browser/notebook-editor-widget.js.map +1 -1
- package/lib/browser/notebook-frontend-module.js +3 -3
- package/lib/browser/notebook-frontend-module.js.map +1 -1
- package/lib/browser/notebook-open-handler.d.ts +1 -2
- package/lib/browser/notebook-open-handler.d.ts.map +1 -1
- package/lib/browser/notebook-open-handler.js +8 -10
- package/lib/browser/notebook-open-handler.js.map +1 -1
- package/lib/browser/notebook-renderer-registry.d.ts +2 -1
- package/lib/browser/notebook-renderer-registry.d.ts.map +1 -1
- package/lib/browser/notebook-renderer-registry.js +6 -3
- package/lib/browser/notebook-renderer-registry.js.map +1 -1
- package/lib/browser/notebook-type-registry.d.ts +2 -1
- package/lib/browser/notebook-type-registry.d.ts.map +1 -1
- package/lib/browser/notebook-type-registry.js +6 -3
- package/lib/browser/notebook-type-registry.js.map +1 -1
- package/lib/browser/renderers/cell-output-webview.d.ts +1 -1
- package/lib/browser/renderers/cell-output-webview.d.ts.map +1 -1
- package/lib/browser/service/notebook-cell-context-manager.d.ts +1 -2
- package/lib/browser/service/notebook-cell-context-manager.d.ts.map +1 -1
- package/lib/browser/service/notebook-cell-context-manager.js +7 -11
- package/lib/browser/service/notebook-cell-context-manager.js.map +1 -1
- package/lib/browser/service/{notebook-editor-service.d.ts → notebook-editor-widget-service.d.ts} +5 -5
- package/lib/browser/service/notebook-editor-widget-service.d.ts.map +1 -0
- package/lib/browser/service/{notebook-editor-service.js → notebook-editor-widget-service.js} +21 -13
- package/lib/browser/service/notebook-editor-widget-service.js.map +1 -0
- package/lib/browser/service/notebook-execution-service.js +1 -1
- package/lib/browser/service/notebook-execution-service.js.map +1 -1
- package/lib/browser/service/notebook-execution-state-service.d.ts +6 -18
- package/lib/browser/service/notebook-execution-state-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-execution-state-service.js +24 -14
- package/lib/browser/service/notebook-execution-state-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts +1 -0
- package/lib/browser/service/notebook-kernel-quick-pick-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-kernel-quick-pick-service.js +20 -17
- package/lib/browser/service/notebook-kernel-quick-pick-service.js.map +1 -1
- package/lib/browser/service/notebook-kernel-service.d.ts +15 -14
- package/lib/browser/service/notebook-kernel-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-kernel-service.js +24 -18
- package/lib/browser/service/notebook-kernel-service.js.map +1 -1
- package/lib/browser/service/notebook-model-resolver-service.d.ts +3 -2
- package/lib/browser/service/notebook-model-resolver-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-model-resolver-service.js +53 -35
- package/lib/browser/service/notebook-model-resolver-service.js.map +1 -1
- package/lib/browser/service/notebook-renderer-messaging-service.d.ts +7 -5
- package/lib/browser/service/notebook-renderer-messaging-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-renderer-messaging-service.js +11 -10
- package/lib/browser/service/notebook-renderer-messaging-service.js.map +1 -1
- package/lib/browser/service/notebook-service.d.ts +12 -17
- package/lib/browser/service/notebook-service.d.ts.map +1 -1
- package/lib/browser/service/notebook-service.js +13 -18
- package/lib/browser/service/notebook-service.js.map +1 -1
- package/lib/browser/view/notebook-cell-editor.d.ts +2 -2
- package/lib/browser/view/notebook-cell-editor.d.ts.map +1 -1
- package/lib/browser/view/notebook-cell-editor.js +2 -2
- package/lib/browser/view/notebook-cell-editor.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.d.ts +7 -5
- package/lib/browser/view-model/notebook-cell-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-model.js +20 -6
- package/lib/browser/view-model/notebook-cell-model.js.map +1 -1
- package/lib/browser/view-model/notebook-cell-output-model.d.ts +1 -0
- package/lib/browser/view-model/notebook-cell-output-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-cell-output-model.js +31 -0
- package/lib/browser/view-model/notebook-cell-output-model.js.map +1 -1
- package/lib/browser/view-model/notebook-model.d.ts +15 -12
- package/lib/browser/view-model/notebook-model.d.ts.map +1 -1
- package/lib/browser/view-model/notebook-model.js +36 -21
- package/lib/browser/view-model/notebook-model.js.map +1 -1
- package/lib/common/notebook-common.d.ts +4 -6
- package/lib/common/notebook-common.d.ts.map +1 -1
- package/lib/common/notebook-common.js.map +1 -1
- package/package.json +7 -7
- package/src/browser/contributions/notebook-cell-actions-contribution.ts +9 -2
- package/src/browser/contributions/notebook-context-keys.ts +7 -0
- package/src/browser/index.ts +1 -1
- package/src/browser/notebook-cell-resource-resolver.ts +1 -5
- package/src/browser/notebook-editor-widget-factory.ts +2 -2
- package/src/browser/notebook-editor-widget.tsx +18 -13
- package/src/browser/notebook-frontend-module.ts +3 -3
- package/src/browser/notebook-open-handler.ts +3 -4
- package/src/browser/notebook-renderer-registry.ts +7 -3
- package/src/browser/notebook-type-registry.ts +7 -3
- package/src/browser/renderers/cell-output-webview.ts +1 -1
- package/src/browser/service/notebook-cell-context-manager.ts +7 -9
- package/src/browser/service/{notebook-editor-service.ts → notebook-editor-widget-service.ts} +19 -12
- package/src/browser/service/notebook-execution-service.ts +1 -1
- package/src/browser/service/notebook-execution-state-service.ts +26 -33
- package/src/browser/service/notebook-kernel-quick-pick-service.ts +10 -7
- package/src/browser/service/notebook-kernel-service.ts +29 -30
- package/src/browser/service/notebook-model-resolver-service.ts +56 -35
- package/src/browser/service/notebook-renderer-messaging-service.ts +21 -19
- package/src/browser/service/notebook-service.ts +21 -27
- package/src/browser/view/notebook-cell-editor.tsx +3 -3
- package/src/browser/view-model/notebook-cell-model.ts +26 -9
- package/src/browser/view-model/notebook-cell-output-model.ts +31 -1
- package/src/browser/view-model/notebook-model.ts +26 -16
- package/src/common/notebook-common.ts +4 -7
- package/lib/browser/service/notebook-editor-service.d.ts.map +0 -1
- 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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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.
|
|
124
|
+
const notebook = await dataProvider.serializer.toNotebook(file.value);
|
|
110
125
|
|
|
111
126
|
return notebook;
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
|
|
115
|
-
protected
|
|
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
|
|
144
|
+
const candidate = typeof selector === 'string' ? selector : selector.filenamePattern;
|
|
130
145
|
|
|
131
146
|
if (candidate) {
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
return
|
|
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
|
|
25
|
+
interface RendererMessage {
|
|
26
26
|
editorId: string;
|
|
27
27
|
rendererId: string;
|
|
28
28
|
message: unknown
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export interface
|
|
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
|
-
|
|
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<
|
|
48
|
-
readonly
|
|
47
|
+
private readonly postMessageEmitter = new Emitter<RendererMessage>();
|
|
48
|
+
readonly onPostMessage = this.postMessageEmitter.event;
|
|
49
49
|
|
|
50
|
-
private readonly
|
|
51
|
-
|
|
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.
|
|
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)?.
|
|
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:
|
|
70
|
+
const queue: RendererMessage[] = [];
|
|
68
71
|
this.activations.set(rendererId, queue);
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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):
|
|
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:
|
|
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,
|
|
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
|
|
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
|
-
|
|
38
|
-
|
|
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
|
|
57
|
-
readonly
|
|
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,
|
|
60
|
+
protected readonly notebookProviders = new Map<string, NotebookProviderInfo>();
|
|
62
61
|
protected readonly notebookModels = new Map<string, NotebookModel>();
|
|
63
62
|
|
|
64
|
-
protected readonly
|
|
65
|
-
readonly
|
|
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(
|
|
96
|
-
if (this.notebookProviders.has(
|
|
97
|
-
throw new Error(`notebook provider for viewtype '${
|
|
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(
|
|
101
|
-
this.
|
|
95
|
+
this.notebookProviders.set(viewType, { notebookType: viewType, serializer });
|
|
96
|
+
this.didRegisterNotebookSerializerEmitter.fire(viewType);
|
|
102
97
|
|
|
103
98
|
return Disposable.create(() => {
|
|
104
|
-
this.notebookProviders.delete(
|
|
105
|
-
this.didRemoveViewTypeEmitter.fire(
|
|
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<
|
|
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<
|
|
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
|
-
|
|
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.
|
|
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 {
|
|
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?:
|
|
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
|
|
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<
|
|
73
|
-
readonly onDidChangeOutputItems: 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
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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 {
|
|
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
|
|
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
|
-
|
|
56
|
+
protected readonly onDirtyChangedEmitter = new Emitter<void>();
|
|
57
57
|
readonly onDirtyChanged = this.onDirtyChangedEmitter.event;
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
protected readonly onDidSaveNotebookEmitter = new Emitter<void>();
|
|
60
60
|
readonly onDidSaveNotebook = this.onDidSaveNotebookEmitter.event;
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
protected readonly onDidAddOrRemoveCellEmitter = new Emitter<NotebookModelWillAddRemoveEvent>();
|
|
63
63
|
readonly onDidAddOrRemoveCell = this.onDidAddOrRemoveCellEmitter.event;
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
protected readonly onDidChangeContentEmitter = new Emitter<NotebookTextModelChangedEvent>();
|
|
66
66
|
readonly onDidChangeContent = this.onDidChangeContentEmitter.event;
|
|
67
67
|
|
|
68
68
|
@inject(FileService)
|
|
69
|
-
|
|
69
|
+
protected readonly fileService: FileService;
|
|
70
70
|
|
|
71
71
|
@inject(UndoRedoService)
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
-
|
|
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.
|
|
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);
|