diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 541acb7a66d858cc3c2481dd8970329ddd2e98e8..d859f958b2e5b2617cc68eaa5f980e49869360c5 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -576,6 +576,11 @@ export interface IUpdateContentOptions { * The etag of the file. This can be used to prevent dirty writes. */ etag?: string; + + /** + * Run mkdirp before saving. + */ + mkdirp?: boolean; } export interface IResolveFileOptions { diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 6a30bb3eb8e95909dacbfb18b39b222ba6824dee..dadfcbd23dcf81b024c7fb71f2272a49a09d5180 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -305,10 +305,15 @@ export class RemoteFileService extends FileService { } } - updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): TPromise { + async updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): TPromise { if (resource.scheme === Schemas.file) { return super.updateContent(resource, value, options); } else { + if (options && options.mkdirp) { + // use the lack of options and or the lack of an etag as a hint that + // the parent directories might not exist. + await this._mkdirp(resource.with({ path: posix.dirname(resource.path) })); + } return this._withProvider(resource).then(provider => { const snapshot = typeof value === 'string' ? new StringSnapshot(value) : value; return this._writeFile(provider, resource, snapshot, options || {}, FileOpenFlags.Write); @@ -346,6 +351,27 @@ export class RemoteFileService extends FileService { }); } + private async _mkdirp(directory: URI): Promise { + let basenames: string[] = []; + while (directory.path !== '/') { + try { + let stat = await this.resolveFile(directory); + if (!stat.isDirectory) { + throw new Error(`${directory.toString()} is not a directory`); + } + } catch (e) { + // ENOENT + basenames.push(posix.basename(directory.path)); + directory = directory.with({ path: posix.dirname(directory.path) }); + } + break; + } + for (let i = basenames.length - 1; i >= 0; i--) { + directory = directory.with({ path: posix.join(directory.path, basenames[i]) }); + await this.createFolder(directory); + } + } + // --- delete del(resource: URI, useTrash?: boolean): TPromise {