• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.projectSystem {
2    export function verifyDynamic(service: server.ProjectService, path: string) {
3        const info = Debug.checkDefined(service.filenameToScriptInfo.get(path), `Expected ${path} in :: ${JSON.stringify(arrayFrom(service.filenameToScriptInfo.entries(), ([key, f]) => ({ key, fileName: f.fileName, path: f.path })))}`);
4        assert.isTrue(info.isDynamic);
5    }
6
7    function verifyPathRecognizedAsDynamic(path: string) {
8        const file: File = {
9            path,
10            content: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />
11/// <reference path="../../../../../../typings/@epic/Shell.d.ts" />
12var x = 10;`
13        };
14        const host = createServerHost([libFile]);
15        const projectService = createProjectService(host);
16        projectService.openClientFile(file.path, file.content);
17        verifyDynamic(projectService, projectService.toPath(file.path));
18
19        projectService.checkNumberOfProjects({ inferredProjects: 1 });
20        const project = projectService.inferredProjects[0];
21        checkProjectRootFiles(project, [file.path]);
22        checkProjectActualFiles(project, [file.path, libFile.path]);
23    }
24
25    describe("unittests:: tsserver:: dynamicFiles:: Untitled files", () => {
26        const untitledFile = "untitled:^Untitled-1";
27        it("Can convert positions to locations", () => {
28            const aTs: File = { path: "/proj/a.ts", content: "" };
29            const tsconfig: File = { path: "/proj/tsconfig.json", content: "{}" };
30            const session = createSession(createServerHost([aTs, tsconfig]), { useInferredProjectPerProjectRoot: true });
31
32            openFilesForSession([aTs], session);
33
34            executeSessionRequestNoResponse<protocol.OpenRequest>(session, protocol.CommandTypes.Open, {
35                file: untitledFile,
36                fileContent: `/// <reference path="../../../../../../typings/@epic/Core.d.ts" />\nlet foo = 1;\nfooo/**/`,
37                scriptKindName: "TS",
38                projectRootPath: "/proj",
39            });
40            verifyDynamic(session.getProjectService(), `/proj/untitled:^untitled-1`);
41            const response = executeSessionRequest<protocol.CodeFixRequest, protocol.CodeFixResponse>(session, protocol.CommandTypes.GetCodeFixes, {
42                file: untitledFile,
43                startLine: 3,
44                startOffset: 1,
45                endLine: 3,
46                endOffset: 5,
47                errorCodes: [Diagnostics.Cannot_find_name_0_Did_you_mean_1.code],
48            });
49            assert.deepEqual<readonly protocol.CodeFixAction[] | undefined>(response, [
50                {
51                    description: "Change spelling to 'foo'",
52                    fixName: "spelling",
53                    changes: [{
54                        fileName: untitledFile,
55                        textChanges: [{
56                            start: { line: 3, offset: 1 },
57                            end: { line: 3, offset: 5 },
58                            newText: "foo",
59                        }],
60                    }],
61                    commands: undefined,
62                    fixId: undefined,
63                    fixAllDescription: undefined
64                },
65            ]);
66        });
67
68        it("opening untitled files", () => {
69            const config: File = {
70                path: `${tscWatch.projectRoot}/tsconfig.json`,
71                content: "{}"
72            };
73            const host = createServerHost([config, libFile], { useCaseSensitiveFileNames: true, currentDirectory: tscWatch.projectRoot });
74            const service = createProjectService(host);
75            service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, tscWatch.projectRoot);
76            checkNumberOfProjects(service, { inferredProjects: 1 });
77            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
78            verifyDynamic(service, `${tscWatch.projectRoot}/${untitledFile}`);
79
80            const untitled: File = {
81                path: `${tscWatch.projectRoot}/Untitled-1.ts`,
82                content: "const x = 10;"
83            };
84            host.writeFile(untitled.path, untitled.content);
85            host.checkTimeoutQueueLength(0);
86            service.openClientFile(untitled.path, untitled.content, /*scriptKind*/ undefined, tscWatch.projectRoot);
87            checkNumberOfProjects(service, { configuredProjects: 1, inferredProjects: 1 });
88            checkProjectActualFiles(service.configuredProjects.get(config.path)!, [untitled.path, libFile.path, config.path]);
89            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
90
91            service.closeClientFile(untitledFile);
92            checkProjectActualFiles(service.configuredProjects.get(config.path)!, [untitled.path, libFile.path, config.path]);
93            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
94
95            service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, tscWatch.projectRoot);
96            verifyDynamic(service, `${tscWatch.projectRoot}/${untitledFile}`);
97            checkProjectActualFiles(service.configuredProjects.get(config.path)!, [untitled.path, libFile.path, config.path]);
98            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
99        });
100
101        it("opening and closing untitled files when projectRootPath is different from currentDirectory", () => {
102            const config: File = {
103                path: `${tscWatch.projectRoot}/tsconfig.json`,
104                content: "{}"
105            };
106            const file: File = {
107                path: `${tscWatch.projectRoot}/file.ts`,
108                content: "const y = 10"
109            };
110            const host = createServerHost([config, file, libFile], { useCaseSensitiveFileNames: true });
111            const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
112            service.openClientFile(untitledFile, "const x = 10;", /*scriptKind*/ undefined, tscWatch.projectRoot);
113            checkNumberOfProjects(service, { inferredProjects: 1 });
114            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
115            verifyDynamic(service, `${tscWatch.projectRoot}/${untitledFile}`);
116
117            // Close untitled file
118            service.closeClientFile(untitledFile);
119
120            // Open file from configured project which should collect inferredProject
121            service.openClientFile(file.path);
122            checkNumberOfProjects(service, { configuredProjects: 1 });
123        });
124
125        it("when changing scriptKind of the untitled files", () => {
126            const host = createServerHost([libFile], { useCaseSensitiveFileNames: true });
127            const service = createProjectService(host, { useInferredProjectPerProjectRoot: true });
128            service.openClientFile(untitledFile, "const x = 10;", ScriptKind.TS, tscWatch.projectRoot);
129            checkNumberOfProjects(service, { inferredProjects: 1 });
130            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
131            const program = service.inferredProjects[0].getCurrentProgram()!;
132            const sourceFile = program.getSourceFile(untitledFile)!;
133
134            // Close untitled file
135            service.closeClientFile(untitledFile);
136
137            // Open untitled file with different mode
138            service.openClientFile(untitledFile, "const x = 10;", ScriptKind.TSX, tscWatch.projectRoot);
139            checkNumberOfProjects(service, { inferredProjects: 1 });
140            checkProjectActualFiles(service.inferredProjects[0], [untitledFile, libFile.path]);
141            const newProgram = service.inferredProjects[0].getCurrentProgram()!;
142            const newSourceFile = newProgram.getSourceFile(untitledFile)!;
143            assert.notStrictEqual(newProgram, program);
144            assert.notStrictEqual(newSourceFile, sourceFile);
145        });
146    });
147
148    describe("unittests:: tsserver:: dynamicFiles:: ", () => {
149        it("dynamic file without external project", () => {
150            const file: File = {
151                path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
152                content: "var x = 10;"
153            };
154            const host = createServerHost([libFile], { useCaseSensitiveFileNames: true });
155            const projectService = createProjectService(host);
156            projectService.setCompilerOptionsForInferredProjects({
157                module: ModuleKind.CommonJS,
158                allowJs: true,
159                allowSyntheticDefaultImports: true,
160                allowNonTsExtensions: true
161            });
162            projectService.openClientFile(file.path, "var x = 10;");
163
164            projectService.checkNumberOfProjects({ inferredProjects: 1 });
165            const project = projectService.inferredProjects[0];
166            checkProjectRootFiles(project, [file.path]);
167            checkProjectActualFiles(project, [file.path, libFile.path]);
168            verifyDynamic(projectService, `/${file.path}`);
169
170            assert.strictEqual(projectService.ensureDefaultProjectForFile(server.toNormalizedPath(file.path)), project);
171            const indexOfX = file.content.indexOf("x");
172            assert.deepEqual(project.getLanguageService(/*ensureSynchronized*/ true).getQuickInfoAtPosition(file.path, indexOfX), {
173                kind: ScriptElementKind.variableElement,
174                kindModifiers: "",
175                textSpan: { start: indexOfX, length: 1 },
176                displayParts: [
177                    { text: "var", kind: "keyword" },
178                    { text: " ", kind: "space" },
179                    { text: "x", kind: "localName" },
180                    { text: ":", kind: "punctuation" },
181                    { text: " ", kind: "space" },
182                    { text: "number", kind: "keyword" }
183                ],
184                documentation: [],
185                tags: undefined,
186            });
187        });
188
189        it("dynamic file with reference paths without external project", () => {
190            verifyPathRecognizedAsDynamic("^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js");
191        });
192
193        describe("dynamic file with projectRootPath", () => {
194            const file: File = {
195                path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
196                content: "var x = 10;"
197            };
198            const configFile: File = {
199                path: `${tscWatch.projectRoot}/tsconfig.json`,
200                content: "{}"
201            };
202            const configProjectFile: File = {
203                path: `${tscWatch.projectRoot}/a.ts`,
204                content: "let y = 10;"
205            };
206            it("with useInferredProjectPerProjectRoot", () => {
207                const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
208                const session = createSession(host, { useInferredProjectPerProjectRoot: true });
209                openFilesForSession([{ file: file.path, projectRootPath: tscWatch.projectRoot }], session);
210
211                const projectService = session.getProjectService();
212                checkNumberOfProjects(projectService, { inferredProjects: 1 });
213                checkProjectActualFiles(projectService.inferredProjects[0], [file.path, libFile.path]);
214                verifyDynamic(projectService, `${tscWatch.projectRoot}/${file.path}`);
215
216                session.executeCommandSeq<protocol.OutliningSpansRequest>({
217                    command: protocol.CommandTypes.GetOutliningSpans,
218                    arguments: {
219                        file: file.path
220                    }
221                });
222
223                // Without project root
224                const file2Path = file.path.replace("#1", "#2");
225                projectService.openClientFile(file2Path, file.content);
226                checkNumberOfProjects(projectService, { inferredProjects: 2 });
227                checkProjectActualFiles(projectService.inferredProjects[0], [file.path, libFile.path]);
228                checkProjectActualFiles(projectService.inferredProjects[1], [file2Path, libFile.path]);
229            });
230
231            it("fails when useInferredProjectPerProjectRoot is false", () => {
232                const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
233                const projectService = createProjectService(host);
234                try {
235                    projectService.openClientFile(file.path, file.content, /*scriptKind*/ undefined, tscWatch.projectRoot);
236                }
237                catch (e) {
238                    assert.strictEqual(
239                        e.message.replace(/\r?\n/, "\n"),
240                        `Debug Failure. False expression.\nVerbose Debug Information: {"fileName":"^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js","currentDirectory":"/user/username/projects/myproject","hostCurrentDirectory":"/","openKeys":[]}\nDynamic files must always be opened with service's current directory or service should support inferred project per projectRootPath.`
241                    );
242                }
243                const file2Path = file.path.replace("#1", "#2");
244                projectService.openClientFile(file2Path, file.content);
245                projectService.checkNumberOfProjects({ inferredProjects: 1 });
246                checkProjectActualFiles(projectService.inferredProjects[0], [file2Path, libFile.path]);
247            });
248        });
249
250        describe("verify accepts known schemas as dynamic file", () => {
251            it("walkThroughSnippet", () => {
252                verifyPathRecognizedAsDynamic("walkThroughSnippet:/usr/share/code/resources/app/out/vs/workbench/contrib/welcome/walkThrough/browser/editor/^vs_code_editor_walkthrough.md#1.ts");
253            });
254
255            it("untitled", () => {
256                verifyPathRecognizedAsDynamic("untitled:/Users/matb/projects/san/^newFile.ts");
257            });
258        });
259    });
260}
261