• 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
126    describe("unittests:: tsserver:: dynamicFiles:: ", () => {
127        it("dynamic file without external project", () => {
128            const file: File = {
129                path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
130                content: "var x = 10;"
131            };
132            const host = createServerHost([libFile], { useCaseSensitiveFileNames: true });
133            const projectService = createProjectService(host);
134            projectService.setCompilerOptionsForInferredProjects({
135                module: ModuleKind.CommonJS,
136                allowJs: true,
137                allowSyntheticDefaultImports: true,
138                allowNonTsExtensions: true
139            });
140            projectService.openClientFile(file.path, "var x = 10;");
141
142            projectService.checkNumberOfProjects({ inferredProjects: 1 });
143            const project = projectService.inferredProjects[0];
144            checkProjectRootFiles(project, [file.path]);
145            checkProjectActualFiles(project, [file.path, libFile.path]);
146            verifyDynamic(projectService, `/${file.path}`);
147
148            assert.strictEqual(projectService.ensureDefaultProjectForFile(server.toNormalizedPath(file.path)), project);
149            const indexOfX = file.content.indexOf("x");
150            assert.deepEqual(project.getLanguageService(/*ensureSynchronized*/ true).getQuickInfoAtPosition(file.path, indexOfX), {
151                kind: ScriptElementKind.variableElement,
152                kindModifiers: "",
153                textSpan: { start: indexOfX, length: 1 },
154                displayParts: [
155                    { text: "var", kind: "keyword" },
156                    { text: " ", kind: "space" },
157                    { text: "x", kind: "localName" },
158                    { text: ":", kind: "punctuation" },
159                    { text: " ", kind: "space" },
160                    { text: "number", kind: "keyword" }
161                ],
162                documentation: [],
163                tags: undefined,
164            });
165        });
166
167        it("dynamic file with reference paths without external project", () => {
168            verifyPathRecognizedAsDynamic("^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js");
169        });
170
171        describe("dynamic file with projectRootPath", () => {
172            const file: File = {
173                path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js",
174                content: "var x = 10;"
175            };
176            const configFile: File = {
177                path: `${tscWatch.projectRoot}/tsconfig.json`,
178                content: "{}"
179            };
180            const configProjectFile: File = {
181                path: `${tscWatch.projectRoot}/a.ts`,
182                content: "let y = 10;"
183            };
184            it("with useInferredProjectPerProjectRoot", () => {
185                const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
186                const session = createSession(host, { useInferredProjectPerProjectRoot: true });
187                openFilesForSession([{ file: file.path, projectRootPath: tscWatch.projectRoot }], session);
188
189                const projectService = session.getProjectService();
190                checkNumberOfProjects(projectService, { inferredProjects: 1 });
191                checkProjectActualFiles(projectService.inferredProjects[0], [file.path, libFile.path]);
192                verifyDynamic(projectService, `${tscWatch.projectRoot}/${file.path}`);
193
194                session.executeCommandSeq<protocol.OutliningSpansRequest>({
195                    command: protocol.CommandTypes.GetOutliningSpans,
196                    arguments: {
197                        file: file.path
198                    }
199                });
200
201                // Without project root
202                const file2Path = file.path.replace("#1", "#2");
203                projectService.openClientFile(file2Path, file.content);
204                checkNumberOfProjects(projectService, { inferredProjects: 2 });
205                checkProjectActualFiles(projectService.inferredProjects[0], [file.path, libFile.path]);
206                checkProjectActualFiles(projectService.inferredProjects[1], [file2Path, libFile.path]);
207            });
208
209            it("fails when useInferredProjectPerProjectRoot is false", () => {
210                const host = createServerHost([libFile, configFile, configProjectFile], { useCaseSensitiveFileNames: true });
211                const projectService = createProjectService(host);
212                try {
213                    projectService.openClientFile(file.path, file.content, /*scriptKind*/ undefined, tscWatch.projectRoot);
214                }
215                catch (e) {
216                    assert.strictEqual(
217                        e.message.replace(/\r?\n/, "\n"),
218                        `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.`
219                    );
220                }
221                const file2Path = file.path.replace("#1", "#2");
222                projectService.openClientFile(file2Path, file.content);
223                projectService.checkNumberOfProjects({ inferredProjects: 1 });
224                checkProjectActualFiles(projectService.inferredProjects[0], [file2Path, libFile.path]);
225            });
226        });
227
228        describe("verify accepts known schemas as dynamic file", () => {
229            it("walkThroughSnippet", () => {
230                verifyPathRecognizedAsDynamic("walkThroughSnippet:/usr/share/code/resources/app/out/vs/workbench/contrib/welcome/walkThrough/browser/editor/^vs_code_editor_walkthrough.md#1.ts");
231            });
232
233            it("untitled", () => {
234                verifyPathRecognizedAsDynamic("untitled:/Users/matb/projects/san/^newFile.ts");
235            });
236        });
237    });
238}
239