diff --git a/src/harness/fakesHosts.ts b/src/harness/fakesHosts.ts index dd4b727dd08..846dc1241f1 100644 --- a/src/harness/fakesHosts.ts +++ b/src/harness/fakesHosts.ts @@ -63,6 +63,7 @@ namespace fakes { } } + function indentedText(indent: number, text: string) { if (!indent) return text; let indentText = ""; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 0c7f116bc18..98a9581753b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -758,6 +758,7 @@ namespace ts.server { readonly toCanonicalFileName: (f: string) => string; public readonly host: ServerHost; + public fs: vfs.FileSystem | undefined; public readonly logger: Logger; public readonly cancellationToken: HostCancellationToken; public readonly useSingleInferredProject: boolean; @@ -3216,7 +3217,7 @@ namespace ts.server { /** * Open file whose contents is managed by the client - * @param filename is absolute pathname + * @param fileName is absolute pathname * @param fileContent is a known version of the file content that is more up to date than the one on disk */ openClientFile(fileName: string, fileContent?: string, scriptKind?: ScriptKind, projectRootPath?: string): OpenConfiguredProjectResult { @@ -3650,7 +3651,7 @@ namespace ts.server { /** * Close file whose contents is managed by the client - * @param filename is absolute pathname + * @param uncheckedFileName is absolute pathname */ closeClientFile(uncheckedFileName: string): void; /*@internal*/ @@ -3689,58 +3690,48 @@ namespace ts.server { updateFileSystem(createdFiles: Iterator | undefined, updatedFiles?: Iterator, deletedFiles?: string[]) { // TODO: Maybe it is somehow gauche or verboten to use protocol types but the translation in applyChangesInOpenFiles seems stupid // It is also WEIRD that we use iterators here not arrays - // TODO: Probably copy what vfsUtil does to hook up a virtual filesystem here. Maybe. - // ugggggggggg have to copy over all that stuff to here - // (though I probably need to create a vfs project anyway, so why not) - - // const fs = new vfs.FileSystem(/*ignoreCase*/ true, { - // files: { - // [builtFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "built/local"), resolver), - // [testLibFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/lib"), resolver), - // [projectsFolder]: new Mount(vpath.resolve(host.getWorkspaceRoot(), "tests/projects"), resolver), - // [srcFolder]: {} - // }, - // cwd: srcFolder, - // meta: { defaultLibLocation: builtFolder } - // }) - // if (!this.fs) - // this.fs = fs - // if (createdFiles) { - // for (const document of Array.from(createdFiles)) { - // fs.mkdirpSync(vpath.dirname(document.file)); - // fs.writeFileSync(document.file, document.text, "utf8"); - // fs.filemeta(document.file).set("document", document); - // // Add symlinks - // const symlink = document.meta.get("symlink"); - // if (symlink) { - // for (const link of symlink.split(",").map(link => link.trim())) { - // fs.mkdirpSync(vpath.dirname(link)); - // fs.symlinkSync(vpath.resolve(fs.cwd(), document.file), link); - // } - // } - // } - // } - // 1. set some internal tsserver state for mocked FS (if it hasn't already been set, this might not be the first message) // - change this.host at least this.host // no, it's readonly, we get this from a parent object // - ScriptInfo instances might need to update their host -- what is textStorage? // - they deffo have FileWatcher instances // - TextStorage.host needs to update - // 2. create - // 3. update - // 4. delete - // - closeClientFile -> closeOpenFile -> ScriptInfo.close - // 5. ??? - // 6. success! - if (createdFiles) { + if (!this.fs) { + this.fs = new vfs.FileSystem(/*ignoreCase*/ true, { + files: { + "/": {} + }, + cwd: "/", // maybe not needed + meta: { } // probably not needed + }) + ;(this.session as any).host = new fakes.FakeCompilerHost(this.fs, {}) // TODO: Grab the real current options } + if (createdFiles) { + let it + while (!(it = createdFiles.next()).done) { + const document = it.value + if (document.fileContent) { + this.fs.mkdirpSync(vpath.dirname(document.file)); + this.fs.writeFileSync(document.file, document.fileContent, "utf8"); + this.fs.filemeta(document.file).set("document", document); + } + } + } + if (updatedFiles) { + const fileset: vfs.FileSet = {} + let it + while (!(it = updatedFiles.next()).done) { + fileset[it.value.file] = it.value.fileContent + } + this.fs.apply(fileset) } if (deletedFiles) { + // TODO: - closeClientFile -> closeOpenFile -> ScriptInfo.close + // (maybe -- it may be enough to just delete it from the filesystem) for (const file of deletedFiles) { this.closeClientFile(file) // maybe copy stuff from applyChanges -- mostly I just want to leave a pointer to more code - + this.fs.rimrafSync(file) } } } diff --git a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts index 03ca5c29f74..46bf7b475b9 100644 --- a/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts +++ b/src/testRunner/unittests/tsserver/applyChangesToOpenFiles.ts @@ -17,26 +17,36 @@ namespace ts.projectSystem { file: "/a/b/app.ts", fileContent: "import { xyz } from './file3'; let x = xyz" }; + const appFile: File = { + path: "/a/b/app.ts", + content: "import { xyz } from './file3'; let x = xyz" + }; function fileContentWithComment(file: protocol.FileSystemRequestArgs | File) { return `// some copy right notice ${'content' in file ? file.content : file.fileContent}`; } function verify({ applyChangesToOpen, openFile1Again }: Verify) { // TODO: Replace with createMemfsServerHost - const host = createServerHost([commonFile1, commonFile2, libFile]); + const host = createServerHost([appFile, file3File, commonFile1, commonFile2, libFile, configFile]); + // const host = createServerHost([app, file3, commonFile1, commonFile2, libFile, configFile]); const session = createSession(host); session.executeCommandSeq({ command: protocol.CommandTypes.UpdateFileSystem, arguments:{ fileSystem: 'memfs', - created: [configFile, file3, app], + created: [app, file3, file1, file2, libFile, configFile], deleted: [], // string[]; updated: [], //FileSystemRequestArgs[]; } }); + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, + arguments: { file: app.file } + }); const service = session.getProjectService(); - // session.host const project = service.configuredProjects.get(configFile.file)!; + const vfs = (session as any).host.vfs + assert.isDefined(vfs); assert.isDefined(project); verifyProjectVersion(project, 1); session.executeCommandSeq({