Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/browser/mainThreadNotebook.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { VSBuffer } from '../../../base/common/buffer.js';
7
import { CancellationToken } from '../../../base/common/cancellation.js';
8
import { Emitter } from '../../../base/common/event.js';
9
import { DisposableStore, dispose, IDisposable } from '../../../base/common/lifecycle.js';
10
import { StopWatch } from '../../../base/common/stopwatch.js';
11
import { assertType } from '../../../base/common/types.js';
12
import { URI } from '../../../base/common/uri.js';
13
import { CommandsRegistry } from '../../../platform/commands/common/commands.js';
14
import { ILogService } from '../../../platform/log/common/log.js';
15
import { NotebookDto } from './mainThreadNotebookDto.js';
16
import { INotebookCellStatusBarService } from '../../contrib/notebook/common/notebookCellStatusBarService.js';
17
import { INotebookCellStatusBarItemProvider, INotebookContributionData, INotebookExclusiveDocumentFilter, NotebookData, NotebookExtensionDescription, TransientOptions } from '../../contrib/notebook/common/notebookCommon.js';
18
import { INotebookService, SimpleNotebookProviderInfo } from '../../contrib/notebook/common/notebookService.js';
19
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
20
import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js';
21
import { ExtHostContext, ExtHostNotebookShape, MainContext, MainThreadNotebookShape } from '../common/extHost.protocol.js';
22
import { IRelativePattern } from '../../../base/common/glob.js';
23
import { revive } from '../../../base/common/marshalling.js';
24
import { INotebookFileMatchNoModel } from '../../contrib/search/common/searchNotebookHelpers.js';
25
import { NotebookPriorityInfo } from '../../contrib/search/common/search.js';
26
import { coalesce } from '../../../base/common/arrays.js';
27
import { FileOperationError } from '../../../platform/files/common/files.js';
28
29
@extHostNamedCustomer(MainContext.MainThreadNotebook)
30
export class MainThreadNotebooks implements MainThreadNotebookShape {
31
32
private readonly _disposables = new DisposableStore();
33
34
private readonly _proxy: ExtHostNotebookShape;
35
private readonly _notebookSerializer = new Map<number, IDisposable>();
36
private readonly _notebookCellStatusBarRegistrations = new Map<number, IDisposable>();
37
38
constructor(
39
extHostContext: IExtHostContext,
40
@INotebookService private readonly _notebookService: INotebookService,
41
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
42
@ILogService private readonly _logService: ILogService,
43
) {
44
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
45
}
46
47
dispose(): void {
48
this._disposables.dispose();
49
dispose(this._notebookSerializer.values());
50
}
51
52
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions, data: INotebookContributionData | undefined): void {
53
const disposables = new DisposableStore();
54
55
disposables.add(this._notebookService.registerNotebookSerializer(viewType, extension, {
56
options,
57
dataToNotebook: async (data: VSBuffer): Promise<NotebookData> => {
58
const sw = new StopWatch();
59
let result: NotebookData;
60
if (data.byteLength === 0 && viewType === 'interactive') {
61
// we don't want any starting cells for an empty interactive window.
62
result = NotebookDto.fromNotebookDataDto({ cells: [], metadata: {} });
63
} else {
64
const dto = await this._proxy.$dataToNotebook(handle, data, CancellationToken.None);
65
result = NotebookDto.fromNotebookDataDto(dto.value);
66
}
67
this._logService.trace(`[NotebookSerializer] dataToNotebook DONE after ${sw.elapsed()}ms`, {
68
viewType,
69
extensionId: extension.id.value,
70
});
71
return result;
72
},
73
notebookToData: (data: NotebookData): Promise<VSBuffer> => {
74
const sw = new StopWatch();
75
const result = this._proxy.$notebookToData(handle, new SerializableObjectWithBuffers(NotebookDto.toNotebookDataDto(data)), CancellationToken.None);
76
this._logService.trace(`[NotebookSerializer] notebookToData DONE after ${sw.elapsed()}`, {
77
viewType,
78
extensionId: extension.id.value,
79
});
80
return result;
81
},
82
save: async (uri, versionId, options, token) => {
83
const stat = await this._proxy.$saveNotebook(handle, uri, versionId, options, token);
84
if (isFileOperationError(stat)) {
85
throw new FileOperationError(stat.message, stat.fileOperationResult, stat.options);
86
}
87
return {
88
...stat,
89
children: undefined,
90
resource: uri
91
};
92
},
93
searchInNotebooks: async (textQuery, token, allPriorityInfo): Promise<{ results: INotebookFileMatchNoModel<URI>[]; limitHit: boolean }> => {
94
const contributedType = this._notebookService.getContributedNotebookType(viewType);
95
if (!contributedType) {
96
return { results: [], limitHit: false };
97
}
98
const fileNames = contributedType.selectors;
99
100
const includes = fileNames.map((selector) => {
101
const globPattern = (selector as INotebookExclusiveDocumentFilter).include || selector as IRelativePattern | string;
102
return globPattern.toString();
103
});
104
105
if (!includes.length) {
106
return {
107
results: [], limitHit: false
108
};
109
}
110
111
const thisPriorityInfo = coalesce<NotebookPriorityInfo>([{ isFromSettings: false, filenamePatterns: includes }, ...allPriorityInfo.get(viewType) ?? []]);
112
const otherEditorsPriorityInfo = Array.from(allPriorityInfo.keys())
113
.flatMap(key => {
114
if (key !== viewType) {
115
return allPriorityInfo.get(key) ?? [];
116
}
117
return [];
118
});
119
120
const searchComplete = await this._proxy.$searchInNotebooks(handle, textQuery, thisPriorityInfo, otherEditorsPriorityInfo, token);
121
const revivedResults: INotebookFileMatchNoModel<URI>[] = searchComplete.results.map(result => {
122
const resource = URI.revive(result.resource);
123
return {
124
resource,
125
cellResults: result.cellResults.map(e => revive(e))
126
};
127
});
128
return { results: revivedResults, limitHit: searchComplete.limitHit };
129
}
130
}));
131
132
if (data) {
133
disposables.add(this._notebookService.registerContributedNotebookType(viewType, data));
134
}
135
this._notebookSerializer.set(handle, disposables);
136
137
this._logService.trace('[NotebookSerializer] registered notebook serializer', {
138
viewType,
139
extensionId: extension.id.value,
140
});
141
}
142
143
$unregisterNotebookSerializer(handle: number): void {
144
this._notebookSerializer.get(handle)?.dispose();
145
this._notebookSerializer.delete(handle);
146
}
147
148
$emitCellStatusBarEvent(eventHandle: number): void {
149
const emitter = this._notebookCellStatusBarRegistrations.get(eventHandle);
150
if (emitter instanceof Emitter) {
151
emitter.fire(undefined);
152
}
153
}
154
155
async $registerNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined, viewType: string): Promise<void> {
156
const that = this;
157
const provider: INotebookCellStatusBarItemProvider = {
158
async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken) {
159
const result = await that._proxy.$provideNotebookCellStatusBarItems(handle, uri, index, token);
160
return {
161
items: result?.items ?? [],
162
dispose() {
163
if (result) {
164
that._proxy.$releaseNotebookCellStatusBarItems(result.cacheId);
165
}
166
}
167
};
168
},
169
viewType
170
};
171
172
if (typeof eventHandle === 'number') {
173
const emitter = new Emitter<void>();
174
this._notebookCellStatusBarRegistrations.set(eventHandle, emitter);
175
provider.onDidChangeStatusBarItems = emitter.event;
176
}
177
178
const disposable = this._cellStatusBarService.registerCellStatusBarItemProvider(provider);
179
this._notebookCellStatusBarRegistrations.set(handle, disposable);
180
}
181
182
async $unregisterNotebookCellStatusBarItemProvider(handle: number, eventHandle: number | undefined): Promise<void> {
183
const unregisterThing = (handle: number) => {
184
const entry = this._notebookCellStatusBarRegistrations.get(handle);
185
if (entry) {
186
this._notebookCellStatusBarRegistrations.get(handle)?.dispose();
187
this._notebookCellStatusBarRegistrations.delete(handle);
188
}
189
};
190
unregisterThing(handle);
191
if (typeof eventHandle === 'number') {
192
unregisterThing(eventHandle);
193
}
194
}
195
}
196
197
CommandsRegistry.registerCommand('_executeDataToNotebook', async (accessor, ...args) => {
198
199
const [notebookType, bytes] = args;
200
assertType(typeof notebookType === 'string', 'string');
201
assertType(bytes instanceof VSBuffer, 'VSBuffer');
202
203
const notebookService = accessor.get(INotebookService);
204
const info = await notebookService.withNotebookDataProvider(notebookType);
205
if (!(info instanceof SimpleNotebookProviderInfo)) {
206
return;
207
}
208
209
const dto = await info.serializer.dataToNotebook(bytes);
210
return new SerializableObjectWithBuffers(NotebookDto.toNotebookDataDto(dto));
211
});
212
213
CommandsRegistry.registerCommand('_executeNotebookToData', async (accessor, ...args) => {
214
215
const [notebookType, dto] = args;
216
assertType(typeof notebookType === 'string', 'string');
217
assertType(typeof dto === 'object');
218
219
const notebookService = accessor.get(INotebookService);
220
const info = await notebookService.withNotebookDataProvider(notebookType);
221
if (!(info instanceof SimpleNotebookProviderInfo)) {
222
return;
223
}
224
225
const data = NotebookDto.fromNotebookDataDto(dto.value);
226
const bytes = await info.serializer.notebookToData(data);
227
return bytes;
228
});
229
230
function isFileOperationError(error: any): error is FileOperationError {
231
return typeof error.fileOperationResult === 'number' && typeof error.message === 'string';
232
}
233
234