Skip to content

Commit 5dd084f

Browse files
committed
Enable remote save behind REMOTE EH and Experimental setting
1 parent 818d499 commit 5dd084f

File tree

7 files changed

+79
-64
lines changed

7 files changed

+79
-64
lines changed

src/vs/workbench/api/browser/mainThreadNotebook.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,13 @@ export class MainThreadNotebooks implements MainThreadNotebookShape {
7373
});
7474
return result;
7575
},
76-
save: (uri, versionId, options, token) => {
77-
return this._proxy.$saveNotebook(handle, uri, versionId, options, token);
76+
save: async (uri, versionId, options, token) => {
77+
const stat = await this._proxy.$saveNotebook(handle, uri, versionId, options, token);
78+
return {
79+
...stat,
80+
children: undefined,
81+
resource: uri
82+
};
7883
},
7984
}));
8085

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2274,13 +2274,15 @@ export interface NotebookCellDto {
22742274
internalMetadata?: notebookCommon.NotebookCellInternalMetadata;
22752275
}
22762276

2277+
export type INotebookPartialFileStatsWithMetadata = Omit<files.IFileStatWithMetadata, 'resource' | 'children'>;
2278+
22772279
export interface ExtHostNotebookShape extends ExtHostNotebookDocumentsAndEditorsShape {
22782280
$provideNotebookCellStatusBarItems(handle: number, uri: UriComponents, index: number, token: CancellationToken): Promise<INotebookCellStatusBarListDto | undefined>;
22792281
$releaseNotebookCellStatusBarItems(id: number): void;
22802282

22812283
$dataToNotebook(handle: number, data: VSBuffer, token: CancellationToken): Promise<SerializableObjectWithBuffers<NotebookDataDto>>;
22822284
$notebookToData(handle: number, data: SerializableObjectWithBuffers<NotebookDataDto>, token: CancellationToken): Promise<VSBuffer>;
2283-
$saveNotebook(handle: number, uri: UriComponents, versionId: number, options: files.IWriteFileOptions, token: CancellationToken): Promise<files.IStat>;
2285+
$saveNotebook(handle: number, uri: UriComponents, versionId: number, options: files.IWriteFileOptions, token: CancellationToken): Promise<INotebookPartialFileStatsWithMetadata>;
22842286
}
22852287

22862288
export interface ExtHostNotebookDocumentSaveParticipantShape {

src/vs/workbench/api/common/extHostNotebook.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
1616
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
1717
import * as files from 'vs/platform/files/common/files';
1818
import { Cache } from 'vs/workbench/api/common/cache';
19-
import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape, NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol';
19+
import { ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookCellStatusBarListDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookPartialFileStatsWithMetadata, MainContext, MainThreadNotebookDocumentsShape, MainThreadNotebookEditorsShape, MainThreadNotebookShape, NotebookDataDto } from 'vs/workbench/api/common/extHost.protocol';
2020
import { ApiCommand, ApiCommandArgument, ApiCommandResult, CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
2121
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
2222
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
@@ -29,8 +29,8 @@ import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument'
2929
import { ExtHostNotebookEditor } from './extHostNotebookEditor';
3030
import { onUnexpectedExternalError } from 'vs/base/common/errors';
3131
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
32-
// import { filter } from 'vs/base/common/objects';
33-
// import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
32+
import { basename } from 'vs/base/common/resources';
33+
import { filter } from 'vs/base/common/objects';
3434

3535

3636

@@ -268,14 +268,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
268268
// --- serialize/deserialize
269269

270270
private _handlePool = 0;
271-
private readonly _notebookSerializer = new Map<number, vscode.NotebookSerializer>();
271+
private readonly _notebookSerializer = new Map<number, { viewType: string; serializer: vscode.NotebookSerializer; options: vscode.NotebookDocumentContentOptions | undefined }>();
272272

273273
registerNotebookSerializer(extension: IExtensionDescription, viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions, registration?: vscode.NotebookRegistrationData): vscode.Disposable {
274274
if (isFalsyOrWhitespace(viewType)) {
275275
throw new Error(`viewType cannot be empty or just whitespace`);
276276
}
277277
const handle = this._handlePool++;
278-
this._notebookSerializer.set(handle, serializer);
278+
this._notebookSerializer.set(handle, { viewType, serializer, options });
279279
this._notebookProxy.$registerNotebookSerializer(
280280
handle,
281281
{ id: extension.identifier, location: extension.extensionLocation },
@@ -293,7 +293,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
293293
if (!serializer) {
294294
throw new Error('NO serializer found');
295295
}
296-
const data = await serializer.deserializeNotebook(bytes.buffer, token);
296+
const data = await serializer.serializer.deserializeNotebook(bytes.buffer, token);
297297
return new SerializableObjectWithBuffers(typeConverters.NotebookData.from(data));
298298
}
299299

@@ -302,11 +302,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
302302
if (!serializer) {
303303
throw new Error('NO serializer found');
304304
}
305-
const bytes = await serializer.serializeNotebook(typeConverters.NotebookData.to(data.value), token);
305+
const bytes = await serializer.serializer.serializeNotebook(typeConverters.NotebookData.to(data.value), token);
306306
return VSBuffer.wrap(bytes);
307307
}
308308

309-
async $saveNotebook(handle: number, uriComponents: UriComponents, versionId: number, options: files.IWriteFileOptions, token: CancellationToken): Promise<files.IStat> {
309+
async $saveNotebook(handle: number, uriComponents: UriComponents, versionId: number, options: files.IWriteFileOptions, token: CancellationToken): Promise<INotebookPartialFileStatsWithMetadata> {
310310
const uri = URI.revive(uriComponents);
311311
const serializer = this._notebookSerializer.get(handle);
312312
if (!serializer) {
@@ -323,7 +323,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
323323
}
324324

325325
const data: vscode.NotebookData = {
326-
metadata: document.apiNotebook.metadata, // filter(document.apiNotebook.metadata, key => !serializer.options.transientDocumentMetadata[key]),
326+
metadata: filter(document.apiNotebook.metadata, key => !(serializer.options?.transientDocumentMetadata ?? {})[key]),
327327
cells: [],
328328
};
329329

@@ -333,23 +333,36 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
333333
cell.document.getText(),
334334
cell.document.languageId,
335335
cell.mime,
336-
[...cell.outputs],
336+
!(serializer.options?.transientOutputs) ? [...cell.outputs] : [],
337337
cell.metadata,
338338
cell.executionSummary
339339
);
340340

341-
// cellData.outputs = !serializer.options.transientOutputs ? cell.outputs : [];
342-
// cellData.metadata = filter(cell.metadata, key => !serializer.options.transientCellMetadata[key]);
343-
341+
cellData.metadata = filter(cell.metadata, key => !(serializer.options?.transientCellMetadata ?? {})[key]);
344342
data.cells.push(cellData);
345343
}
346344

347-
const bytes = await serializer.serializeNotebook(data, token);
345+
const bytes = await serializer.serializer.serializeNotebook(data, token);
348346
await this._extHostFileSystem.value.writeFile(uri, bytes);
347+
const stat = await this._extHostFileSystem.value.stat(uri);
348+
349+
const fileStats = {
350+
name: basename(uri), // providerExtUri.basename(resource)
351+
isFile: (stat.type & files.FileType.File) !== 0,
352+
isDirectory: (stat.type & files.FileType.Directory) !== 0,
353+
isSymbolicLink: (stat.type & files.FileType.SymbolicLink) !== 0,
354+
mtime: stat.mtime,
355+
ctime: stat.ctime,
356+
size: stat.size,
357+
readonly: Boolean((stat.permissions ?? 0) & files.FilePermission.Readonly) || !this._extHostFileSystem.value.isWritableFileSystem(uri.scheme),
358+
locked: Boolean((stat.permissions ?? 0) & files.FilePermission.Locked),
359+
etag: files.etag({ mtime: stat.mtime, size: stat.size }),
360+
children: undefined
361+
};
349362

350-
const stats = await this._extHostFileSystem.value.stat(uri);
351-
return stats;
363+
return fileStats;
352364
}
365+
353366
// --- open, save, saveAs, backup
354367

355368

src/vs/workbench/contrib/notebook/common/notebookEditorModel.ts

Lines changed: 21 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
1111
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
1212
import { Schemas } from 'vs/base/common/network';
1313
import { filter } from 'vs/base/common/objects';
14-
import { basename } from 'vs/base/common/resources';
1514
import { assertType } from 'vs/base/common/types';
1615
import { URI } from 'vs/base/common/uri';
17-
import { IWriteFileOptions, IFileStatWithMetadata, FileType, FilePermission, etag } from 'vs/platform/files/common/files';
16+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
17+
import { IWriteFileOptions, IFileStatWithMetadata } from 'vs/platform/files/common/files';
1818
import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor';
1919
import { EditorModel } from 'vs/workbench/common/editor/editorModel';
2020
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
@@ -177,10 +177,12 @@ export class NotebookFileWorkingCopyModel extends Disposable implements IStoredF
177177
readonly onWillDispose: Event<void>;
178178

179179
readonly configuration: IFileWorkingCopyModelConfiguration | undefined = undefined;
180+
save: ((options: IWriteFileOptions, token: CancellationToken) => Promise<IFileStatWithMetadata>) | undefined;
180181

181182
constructor(
182183
private readonly _notebookModel: NotebookTextModel,
183-
private readonly _notebookService: INotebookService
184+
private readonly _notebookService: INotebookService,
185+
private readonly _configurationService: IConfigurationService
184186
) {
185187
super();
186188

@@ -211,6 +213,20 @@ export class NotebookFileWorkingCopyModel extends Disposable implements IStoredF
211213
// remote hosts the extension of the notebook with the contents truth
212214
backupDelay: 10000
213215
};
216+
217+
// Override save behavior to avoid transferring the buffer across the wire 3 times
218+
if (this._configurationService.getValue('notebook.experimental.remoteSave')) {
219+
this.save = async (options: IWriteFileOptions, token: CancellationToken) => {
220+
const serializer = await this.getNotebookSerializer();
221+
222+
if (token.isCancellationRequested) {
223+
throw new CancellationError();
224+
}
225+
226+
const stat = await serializer.save(this._notebookModel.uri, this._notebookModel.versionId, options, token);
227+
return stat;
228+
};
229+
}
214230
}
215231
}
216232

@@ -254,38 +270,6 @@ export class NotebookFileWorkingCopyModel extends Disposable implements IStoredF
254270
return bufferToStream(bytes);
255271
}
256272

257-
async save(options: IWriteFileOptions, token: CancellationToken): Promise<IFileStatWithMetadata> {
258-
const serializer = await this.getNotebookSerializer();
259-
260-
if (token.isCancellationRequested) {
261-
throw new CancellationError();
262-
}
263-
264-
// const stat = await this.validateWriteFile(provider, resource, options);
265-
// const handle = await provider.open(resource, { create: true, unlock: options?.unlock ?? false });
266-
267-
268-
const stat = await serializer.save(this._notebookModel.uri, this._notebookModel.versionId, options, token);
269-
const fileStat: IFileStatWithMetadata = {
270-
resource: this._notebookModel.uri,
271-
// name: providerExtUri.basename(resource),
272-
name: basename(this._notebookModel.uri),
273-
isFile: (stat.type & FileType.File) !== 0,
274-
isDirectory: (stat.type & FileType.Directory) !== 0,
275-
isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
276-
mtime: stat.mtime,
277-
ctime: stat.ctime,
278-
size: stat.size,
279-
readonly: Boolean((stat.permissions ?? 0) & FilePermission.Readonly), // || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
280-
// readonly: Boolean((stat.permissions ?? 0) & FilePermission.Readonly) || Boolean(provider.capabilities & FileSystemProviderCapabilities.Readonly),
281-
locked: Boolean((stat.permissions ?? 0) & FilePermission.Locked),
282-
etag: etag({ mtime: stat.mtime, size: stat.size }),
283-
children: undefined
284-
};
285-
286-
return fileStat;
287-
}
288-
289273
async update(stream: VSBufferReadableStream, token: CancellationToken): Promise<void> {
290274
const serializer = await this.getNotebookSerializer();
291275

@@ -321,6 +305,7 @@ export class NotebookFileWorkingCopyModelFactory implements IStoredFileWorkingCo
321305
constructor(
322306
private readonly _viewType: string,
323307
@INotebookService private readonly _notebookService: INotebookService,
308+
@IConfigurationService private readonly _configurationService: IConfigurationService,
324309
) { }
325310

326311
async createModel(resource: URI, stream: VSBufferReadableStream, token: CancellationToken): Promise<NotebookFileWorkingCopyModel> {
@@ -338,7 +323,7 @@ export class NotebookFileWorkingCopyModelFactory implements IStoredFileWorkingCo
338323
}
339324

340325
const notebookModel = this._notebookService.createNotebookTextModel(info.viewType, resource, data, info.serializer.options);
341-
return new NotebookFileWorkingCopyModel(notebookModel, this._notebookService);
326+
return new NotebookFileWorkingCopyModel(notebookModel, this._notebookService, this._configurationService);
342327
}
343328
}
344329

src/vs/workbench/contrib/notebook/common/notebookEditorModelResolverServiceImpl.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Schemas } from 'vs/base/common/network';
2020
import { NotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookProvider';
2121
import { assertIsDefined } from 'vs/base/common/types';
2222
import { CancellationToken } from 'vs/base/common/cancellation';
23+
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2324

2425
class NotebookModelReferenceCollection extends ReferenceCollection<Promise<IResolvedNotebookEditorModel>> {
2526

@@ -40,6 +41,7 @@ class NotebookModelReferenceCollection extends ReferenceCollection<Promise<IReso
4041
@IInstantiationService private readonly _instantiationService: IInstantiationService,
4142
@INotebookService private readonly _notebookService: INotebookService,
4243
@ILogService private readonly _logService: ILogService,
44+
@IConfigurationService private readonly _configurationService: IConfigurationService,
4345
) {
4446
super();
4547
}
@@ -65,7 +67,7 @@ class NotebookModelReferenceCollection extends ReferenceCollection<Promise<IReso
6567
const workingCopyTypeId = NotebookWorkingCopyTypeIdentifier.create(viewType);
6668
let workingCopyManager = this._workingCopyManagers.get(workingCopyTypeId);
6769
if (!workingCopyManager) {
68-
const factory = new NotebookFileWorkingCopyModelFactory(viewType, this._notebookService);
70+
const factory = new NotebookFileWorkingCopyModelFactory(viewType, this._notebookService, this._configurationService);
6971
workingCopyManager = <IFileWorkingCopyManager<NotebookFileWorkingCopyModel, NotebookFileWorkingCopyModel>><any>this._instantiationService.createInstance(
7072
FileWorkingCopyManager,
7173
workingCopyTypeId,

src/vs/workbench/contrib/notebook/common/notebookService.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode
1414
import { IDisposable } from 'vs/base/common/lifecycle';
1515
import { VSBuffer } from 'vs/base/common/buffer';
1616
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
17-
import { IStat, IWriteFileOptions } from 'vs/platform/files/common/files';
17+
import { IFileStatWithMetadata, IWriteFileOptions } from 'vs/platform/files/common/files';
1818

1919

2020
export const INotebookService = createDecorator<INotebookService>('notebookService');
@@ -30,7 +30,7 @@ export interface INotebookSerializer {
3030
options: TransientOptions;
3131
dataToNotebook(data: VSBuffer): Promise<NotebookData>;
3232
notebookToData(data: NotebookData): Promise<VSBuffer>;
33-
save(uri: URI, versionId: number, options: IWriteFileOptions, token: CancellationToken): Promise<IStat>;
33+
save(uri: URI, versionId: number, options: IWriteFileOptions, token: CancellationToken): Promise<IFileStatWithMetadata>;
3434
}
3535

3636
export interface INotebookRawData {

src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
1010
import { Mimes } from 'vs/base/common/mime';
1111
import { URI } from 'vs/base/common/uri';
1212
import { mock } from 'vs/base/test/common/mock';
13+
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
1314
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
1415
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
1516
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
@@ -22,6 +23,7 @@ suite('NotebookFileWorkingCopyModel', function () {
2223

2324
let disposables: DisposableStore;
2425
let instantiationService: TestInstantiationService;
26+
const configurationService = new TestConfigurationService();
2527

2628
suiteSetup(() => {
2729
disposables = new DisposableStore();
@@ -54,7 +56,8 @@ suite('NotebookFileWorkingCopyModel', function () {
5456
return VSBuffer.fromString('');
5557
}
5658
}
57-
)
59+
),
60+
configurationService
5861
);
5962

6063
await model.snapshot(CancellationToken.None);
@@ -75,7 +78,8 @@ suite('NotebookFileWorkingCopyModel', function () {
7578
return VSBuffer.fromString('');
7679
}
7780
}
78-
)
81+
),
82+
configurationService
7983
);
8084
await model.snapshot(CancellationToken.None);
8185
assert.strictEqual(callCount, 1);
@@ -106,7 +110,8 @@ suite('NotebookFileWorkingCopyModel', function () {
106110
return VSBuffer.fromString('');
107111
}
108112
}
109-
)
113+
),
114+
configurationService
110115
);
111116

112117
await model.snapshot(CancellationToken.None);
@@ -127,7 +132,8 @@ suite('NotebookFileWorkingCopyModel', function () {
127132
return VSBuffer.fromString('');
128133
}
129134
}
130-
)
135+
),
136+
configurationService
131137
);
132138
await model.snapshot(CancellationToken.None);
133139
assert.strictEqual(callCount, 1);
@@ -158,7 +164,8 @@ suite('NotebookFileWorkingCopyModel', function () {
158164
return VSBuffer.fromString('');
159165
}
160166
}
161-
)
167+
),
168+
configurationService
162169
);
163170

164171
await model.snapshot(CancellationToken.None);
@@ -179,7 +186,8 @@ suite('NotebookFileWorkingCopyModel', function () {
179186
return VSBuffer.fromString('');
180187
}
181188
}
182-
)
189+
),
190+
configurationService
183191
);
184192
await model.snapshot(CancellationToken.None);
185193
assert.strictEqual(callCount, 1);

0 commit comments

Comments
 (0)