vscode-apollo 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +1 -1
- package/.vscode/launch.json +5 -1
- package/CHANGELOG.md +41 -0
- package/package.json +9 -4
- package/renovate.json +2 -1
- package/sampleWorkspace/localSchema/src/test.js +3 -0
- package/sampleWorkspace/rover/apollo.config.js +3 -0
- package/sampleWorkspace/rover/src/test.graphql +14 -0
- package/sampleWorkspace/rover/src/test.js +30 -0
- package/sampleWorkspace/sampleWorkspace.code-workspace +25 -19
- package/src/language-server/__tests__/document.test.ts +161 -3
- package/src/language-server/__tests__/fixtures/TypeScript.tmLanguage.json +5749 -0
- package/src/language-server/__tests__/fixtures/documents/commentWithTemplate.ts +41 -0
- package/src/language-server/__tests__/fixtures/documents/commentWithTemplate.ts.snap +185 -0
- package/src/language-server/__tests__/fixtures/documents/functionCall.ts +93 -0
- package/src/language-server/__tests__/fixtures/documents/functionCall.ts.snap +431 -0
- package/src/language-server/__tests__/fixtures/documents/taggedTemplate.ts +80 -0
- package/src/language-server/__tests__/fixtures/documents/taggedTemplate.ts.snap +353 -0
- package/src/language-server/__tests__/fixtures/documents/templateWithComment.ts +38 -0
- package/src/language-server/__tests__/fixtures/documents/templateWithComment.ts.snap +123 -0
- package/src/language-server/config/__tests__/loadConfig.ts +28 -16
- package/src/language-server/config/config.ts +50 -12
- package/src/language-server/config/loadConfig.ts +2 -1
- package/src/language-server/config/which.d.ts +19 -0
- package/src/language-server/document.ts +86 -53
- package/src/language-server/fileSet.ts +8 -6
- package/src/language-server/project/base.ts +64 -315
- package/src/language-server/project/client.ts +731 -21
- package/src/language-server/project/internal.ts +354 -0
- package/src/language-server/project/rover/DocumentSynchronization.ts +385 -0
- package/src/language-server/project/rover/__tests__/DocumentSynchronization.test.ts +302 -0
- package/src/language-server/project/rover/project.ts +341 -0
- package/src/language-server/server.ts +187 -98
- package/src/language-server/utilities/__tests__/source.test.ts +162 -0
- package/src/language-server/utilities/languageIdForExtension.ts +39 -0
- package/src/language-server/utilities/source.ts +38 -3
- package/src/language-server/workspace.ts +61 -12
- package/src/languageServerClient.ts +13 -15
- package/src/tools/utilities/getLanguageInformation.ts +41 -0
- package/src/tools/utilities/languageInformation.ts +41 -0
- package/syntaxes/graphql.js.json +18 -21
- package/src/language-server/languageProvider.ts +0 -795
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
WorkspaceFolder,
|
|
3
3
|
NotificationHandler,
|
|
4
4
|
PublishDiagnosticsParams,
|
|
5
|
+
ClientCapabilities,
|
|
5
6
|
} from "vscode-languageserver/node";
|
|
6
7
|
import { QuickPickItem } from "vscode";
|
|
7
8
|
import { GraphQLProject, DocumentUri } from "./project/base";
|
|
@@ -15,6 +16,8 @@ import { URI } from "vscode-uri";
|
|
|
15
16
|
import { Debug } from "./utilities";
|
|
16
17
|
import type { EngineDecoration } from "../messages";
|
|
17
18
|
import { equal } from "@wry/equality";
|
|
19
|
+
import { isRoverConfig, RoverProject } from "./project/rover/project";
|
|
20
|
+
import { VSCodeConnection } from "./server";
|
|
18
21
|
|
|
19
22
|
export interface WorkspaceConfig {
|
|
20
23
|
clientIdentity: ClientIdentity;
|
|
@@ -26,12 +29,14 @@ export class GraphQLWorkspace {
|
|
|
26
29
|
private _onSchemaTags?: NotificationHandler<[ServiceID, SchemaTag[]]>;
|
|
27
30
|
private _onConfigFilesFound?: NotificationHandler<(ApolloConfig | Error)[]>;
|
|
28
31
|
private _projectForFileCache: Map<string, GraphQLProject> = new Map();
|
|
32
|
+
public capabilities?: ClientCapabilities;
|
|
29
33
|
|
|
30
34
|
private projectsByFolderUri: Map<string, GraphQLProject[]> = new Map();
|
|
31
35
|
|
|
32
36
|
constructor(
|
|
33
37
|
private LanguageServerLoadingHandler: LanguageServerLoadingHandler,
|
|
34
38
|
private config: WorkspaceConfig,
|
|
39
|
+
private whenConnectionInitialized: Promise<VSCodeConnection>,
|
|
35
40
|
) {}
|
|
36
41
|
|
|
37
42
|
onDiagnostics(handler: NotificationHandler<PublishDiagnosticsParams>) {
|
|
@@ -65,8 +70,15 @@ export class GraphQLWorkspace {
|
|
|
65
70
|
configFolderURI: URI.parse(folder.uri),
|
|
66
71
|
clientIdentity,
|
|
67
72
|
})
|
|
73
|
+
: isRoverConfig(config)
|
|
74
|
+
? new RoverProject({
|
|
75
|
+
config,
|
|
76
|
+
loadingHandler: this.LanguageServerLoadingHandler,
|
|
77
|
+
configFolderURI: URI.parse(folder.uri),
|
|
78
|
+
capabilities: this.capabilities!, // TODO?
|
|
79
|
+
})
|
|
68
80
|
: (() => {
|
|
69
|
-
throw new Error("
|
|
81
|
+
throw new Error("Impossible config!");
|
|
70
82
|
})();
|
|
71
83
|
|
|
72
84
|
project.onDiagnostics((params) => {
|
|
@@ -86,8 +98,13 @@ export class GraphQLWorkspace {
|
|
|
86
98
|
// after a project has loaded, we do an initial validation to surface errors
|
|
87
99
|
// on the start of the language server. Instead of doing this in the
|
|
88
100
|
// base class which is used by codegen and other tools
|
|
89
|
-
project.whenReady.then(() => project.validate());
|
|
101
|
+
project.whenReady.then(() => project.validate?.());
|
|
90
102
|
|
|
103
|
+
if (project.onVSCodeConnectionInitialized) {
|
|
104
|
+
this.whenConnectionInitialized.then(
|
|
105
|
+
project.onVSCodeConnectionInitialized.bind(project),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
91
108
|
return project;
|
|
92
109
|
}
|
|
93
110
|
|
|
@@ -157,12 +174,24 @@ export class GraphQLWorkspace {
|
|
|
157
174
|
this.projectsByFolderUri.forEach((projects, uri) => {
|
|
158
175
|
this.projectsByFolderUri.set(
|
|
159
176
|
uri,
|
|
160
|
-
projects.map((
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
177
|
+
projects.map((oldProject) => {
|
|
178
|
+
oldProject.clearAllDiagnostics();
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const newProject = this.createProject({
|
|
182
|
+
config: oldProject.config,
|
|
183
|
+
folder: { uri } as WorkspaceFolder,
|
|
184
|
+
});
|
|
185
|
+
if (
|
|
186
|
+
oldProject instanceof RoverProject &&
|
|
187
|
+
newProject instanceof RoverProject
|
|
188
|
+
) {
|
|
189
|
+
newProject.restoreFromPreviousProject(oldProject);
|
|
190
|
+
}
|
|
191
|
+
return newProject;
|
|
192
|
+
} finally {
|
|
193
|
+
oldProject.dispose?.();
|
|
194
|
+
}
|
|
166
195
|
}),
|
|
167
196
|
);
|
|
168
197
|
});
|
|
@@ -179,7 +208,7 @@ export class GraphQLWorkspace {
|
|
|
179
208
|
error = e;
|
|
180
209
|
}
|
|
181
210
|
|
|
182
|
-
const project = this.
|
|
211
|
+
const project = this.projectForConfigFile(configUri);
|
|
183
212
|
|
|
184
213
|
if (this._onConfigFilesFound) {
|
|
185
214
|
this._onConfigFilesFound([config || error]);
|
|
@@ -227,7 +256,10 @@ export class GraphQLWorkspace {
|
|
|
227
256
|
removeProjectsInFolder(folder: WorkspaceFolder) {
|
|
228
257
|
const projects = this.projectsByFolderUri.get(folder.uri);
|
|
229
258
|
if (projects) {
|
|
230
|
-
projects.forEach((project) =>
|
|
259
|
+
projects.forEach((project) => {
|
|
260
|
+
project.clearAllDiagnostics();
|
|
261
|
+
project.dispose?.();
|
|
262
|
+
});
|
|
231
263
|
this.projectsByFolderUri.delete(folder.uri);
|
|
232
264
|
}
|
|
233
265
|
}
|
|
@@ -236,14 +268,31 @@ export class GraphQLWorkspace {
|
|
|
236
268
|
return Array.from(this.projectsByFolderUri.values()).flat();
|
|
237
269
|
}
|
|
238
270
|
|
|
239
|
-
|
|
271
|
+
projectForConfigFile(configUri: DocumentUri): GraphQLProject | undefined {
|
|
272
|
+
for (const projects of this.projectsByFolderUri.values()) {
|
|
273
|
+
const project = projects.find((project) =>
|
|
274
|
+
project.isConfiguredBy(configUri),
|
|
275
|
+
);
|
|
276
|
+
if (project) {
|
|
277
|
+
return project;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
projectForFile(
|
|
284
|
+
uri: DocumentUri,
|
|
285
|
+
languageId?: string,
|
|
286
|
+
): GraphQLProject | undefined {
|
|
240
287
|
const cachedResult = this._projectForFileCache.get(uri);
|
|
241
288
|
if (cachedResult) {
|
|
242
289
|
return cachedResult;
|
|
243
290
|
}
|
|
244
291
|
|
|
245
292
|
for (const projects of this.projectsByFolderUri.values()) {
|
|
246
|
-
const project = projects.find((project) =>
|
|
293
|
+
const project = projects.find((project) =>
|
|
294
|
+
project.includesFile(uri, languageId),
|
|
295
|
+
);
|
|
247
296
|
if (project) {
|
|
248
297
|
this._projectForFileCache.set(uri, project);
|
|
249
298
|
return project;
|
|
@@ -6,9 +6,17 @@ import {
|
|
|
6
6
|
RevealOutputChannelOn,
|
|
7
7
|
} from "vscode-languageclient/node";
|
|
8
8
|
import { workspace, OutputChannel } from "vscode";
|
|
9
|
+
import { supportedLanguageIds } from "./tools/utilities/languageInformation";
|
|
10
|
+
import type { InitializationOptions } from "./language-server/server";
|
|
11
|
+
import { getLangugageInformation } from "./tools/utilities/getLanguageInformation";
|
|
9
12
|
|
|
10
13
|
const { version, referenceID } = require("../package.json");
|
|
11
14
|
|
|
15
|
+
const languageIdExtensionMap = getLangugageInformation();
|
|
16
|
+
const supportedExtensions = supportedLanguageIds.flatMap(
|
|
17
|
+
(id) => languageIdExtensionMap[id],
|
|
18
|
+
);
|
|
19
|
+
|
|
12
20
|
export function getLanguageServerClient(
|
|
13
21
|
serverModule: string,
|
|
14
22
|
outputChannel: OutputChannel,
|
|
@@ -41,25 +49,12 @@ export function getLanguageServerClient(
|
|
|
41
49
|
};
|
|
42
50
|
|
|
43
51
|
const clientOptions: LanguageClientOptions = {
|
|
44
|
-
documentSelector:
|
|
45
|
-
"graphql",
|
|
46
|
-
"javascript",
|
|
47
|
-
"typescript",
|
|
48
|
-
"javascriptreact",
|
|
49
|
-
"typescriptreact",
|
|
50
|
-
"vue",
|
|
51
|
-
"svelte",
|
|
52
|
-
"python",
|
|
53
|
-
"ruby",
|
|
54
|
-
"dart",
|
|
55
|
-
"reason",
|
|
56
|
-
"elixir",
|
|
57
|
-
],
|
|
52
|
+
documentSelector: supportedLanguageIds,
|
|
58
53
|
synchronize: {
|
|
59
54
|
fileEvents: [
|
|
60
55
|
workspace.createFileSystemWatcher("**/.env?(.local)"),
|
|
61
56
|
workspace.createFileSystemWatcher(
|
|
62
|
-
"
|
|
57
|
+
"**/*{" + supportedExtensions.join(",") + "}",
|
|
63
58
|
),
|
|
64
59
|
],
|
|
65
60
|
},
|
|
@@ -69,6 +64,9 @@ export function getLanguageServerClient(
|
|
|
69
64
|
.get("debug.revealOutputOnLanguageServerError")
|
|
70
65
|
? RevealOutputChannelOn.Error
|
|
71
66
|
: RevealOutputChannelOn.Never,
|
|
67
|
+
initializationOptions: {
|
|
68
|
+
languageIdExtensionMap,
|
|
69
|
+
} satisfies InitializationOptions,
|
|
72
70
|
};
|
|
73
71
|
|
|
74
72
|
return new LanguageClient(
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as vscode from "vscode";
|
|
2
|
+
import {
|
|
3
|
+
LanguageIdExtensionMap,
|
|
4
|
+
minimumKnownExtensions,
|
|
5
|
+
} from "./languageInformation";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @returns An object with language identifiers as keys and file extensions as values.
|
|
9
|
+
* see https://github.com/microsoft/vscode/issues/109919
|
|
10
|
+
*/
|
|
11
|
+
export function getLangugageInformation(): LanguageIdExtensionMap {
|
|
12
|
+
const allKnownExtensions = vscode.extensions.all
|
|
13
|
+
.map(
|
|
14
|
+
(i) =>
|
|
15
|
+
i.packageJSON?.contributes?.languages as (
|
|
16
|
+
| undefined
|
|
17
|
+
| {
|
|
18
|
+
id?: string;
|
|
19
|
+
extensions?: string[];
|
|
20
|
+
}
|
|
21
|
+
)[],
|
|
22
|
+
)
|
|
23
|
+
.flat()
|
|
24
|
+
.filter(
|
|
25
|
+
(i): i is { id: string; extensions: `.${string}`[] } =>
|
|
26
|
+
!!(i && i.id && i.extensions?.length),
|
|
27
|
+
)
|
|
28
|
+
.reduce<Record<string, Set<`.${string}`>>>(
|
|
29
|
+
(acc, i) => {
|
|
30
|
+
if (!acc[i.id]) acc[i.id] = new Set();
|
|
31
|
+
for (const ext of i.extensions) acc[i.id].add(ext);
|
|
32
|
+
return acc;
|
|
33
|
+
},
|
|
34
|
+
Object.fromEntries(
|
|
35
|
+
Object.entries(minimumKnownExtensions).map(([k, v]) => [k, new Set(v)]),
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
Object.entries(allKnownExtensions).map(([k, v]) => [k, [...v]] as const),
|
|
40
|
+
) as LanguageIdExtensionMap;
|
|
41
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const _supportedDocumentTypes = [
|
|
2
|
+
"graphql",
|
|
3
|
+
"javascript",
|
|
4
|
+
"typescript",
|
|
5
|
+
"javascriptreact",
|
|
6
|
+
"typescriptreact",
|
|
7
|
+
"vue",
|
|
8
|
+
"svelte",
|
|
9
|
+
"python",
|
|
10
|
+
"ruby",
|
|
11
|
+
"dart",
|
|
12
|
+
"reason",
|
|
13
|
+
"elixir",
|
|
14
|
+
] as const;
|
|
15
|
+
export type SupportedLanguageIds = (typeof _supportedDocumentTypes)[number];
|
|
16
|
+
export const supportedLanguageIds =
|
|
17
|
+
// remove the `readonly` we get from using `as const`
|
|
18
|
+
_supportedDocumentTypes as any as SupportedLanguageIds[];
|
|
19
|
+
|
|
20
|
+
export type FileExtension = `.${string}`;
|
|
21
|
+
|
|
22
|
+
export const minimumKnownExtensions: Record<
|
|
23
|
+
SupportedLanguageIds,
|
|
24
|
+
FileExtension[]
|
|
25
|
+
> = {
|
|
26
|
+
graphql: [".gql", ".graphql", ".graphqls"],
|
|
27
|
+
javascript: [".js", ".mjs", ".cjs"],
|
|
28
|
+
typescript: [".ts", ".mts", ".cts"],
|
|
29
|
+
javascriptreact: [".jsx"],
|
|
30
|
+
typescriptreact: [".tsx"],
|
|
31
|
+
vue: [".vue"],
|
|
32
|
+
svelte: [".svelte"],
|
|
33
|
+
python: [".py"],
|
|
34
|
+
ruby: [".rb"],
|
|
35
|
+
dart: [".dart"],
|
|
36
|
+
reason: [".re"],
|
|
37
|
+
elixir: [".ex", ".exs"],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type LanguageIdExtensionMap = Record<string, `.${string}`[]> &
|
|
41
|
+
typeof minimumKnownExtensions;
|
package/syntaxes/graphql.js.json
CHANGED
|
@@ -3,43 +3,40 @@
|
|
|
3
3
|
"injectionSelector": "L:source -string -comment",
|
|
4
4
|
"patterns": [
|
|
5
5
|
{
|
|
6
|
-
"name": "taggedTemplates",
|
|
7
6
|
"contentName": "meta.embedded.block.graphql",
|
|
8
|
-
"begin": "(Relay\\.QL|gql|graphql(\\.experimental)?)\\(
|
|
7
|
+
"begin": "(?:Relay\\.QL|gql|graphql(\\.experimental)?)\\s*(?:<.*?>\\s*)?`",
|
|
8
|
+
"end": "`",
|
|
9
|
+
"patterns": [{ "include": "source.graphql" }]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"contentName": "meta.embedded.block.graphql",
|
|
13
|
+
"begin": "`(\\s*#[ ]*(gql|graphql|GraphQL))",
|
|
9
14
|
"beginCaptures": {
|
|
10
15
|
"1": {
|
|
11
|
-
"name": "
|
|
16
|
+
"name": "meta.embedded.block.graphql comment.line.graphql.js"
|
|
12
17
|
},
|
|
13
18
|
"2": {
|
|
14
|
-
"name": "
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
"end": "`\\)?",
|
|
18
|
-
"endCaptures": {
|
|
19
|
-
"0": {
|
|
20
|
-
"name": "punctuation.definition.string.template.end.js"
|
|
19
|
+
"name": "markup.italic"
|
|
21
20
|
}
|
|
22
21
|
},
|
|
22
|
+
"end": "`",
|
|
23
23
|
"patterns": [{ "include": "source.graphql" }]
|
|
24
24
|
},
|
|
25
25
|
{
|
|
26
|
-
"name": "taggedTemplates",
|
|
27
26
|
"contentName": "meta.embedded.block.graphql",
|
|
28
|
-
"begin": "(
|
|
27
|
+
"begin": "(?:gql|graphql)\\s*(?:<.*?>\\s*)?\\(\\s*`",
|
|
28
|
+
"end": "`\\s*\\)",
|
|
29
|
+
"patterns": [{ "include": "source.graphql" }]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"contentName": "meta.embedded.block.graphql",
|
|
33
|
+
"begin": "(?:\\/\\*[\\s\\*]*(gql|graphql|GraphQL)\\s*\\*\\/)\\s*(`)",
|
|
29
34
|
"beginCaptures": {
|
|
30
35
|
"1": {
|
|
31
|
-
"name": "
|
|
32
|
-
},
|
|
33
|
-
"2": {
|
|
34
|
-
"name": "comment.line.graphql.js"
|
|
36
|
+
"name": "markup.italic"
|
|
35
37
|
}
|
|
36
38
|
},
|
|
37
39
|
"end": "`",
|
|
38
|
-
"endCaptures": {
|
|
39
|
-
"0": {
|
|
40
|
-
"name": "punctuation.definition.string.template.end.js"
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
40
|
"patterns": [{ "include": "source.graphql" }]
|
|
44
41
|
}
|
|
45
42
|
],
|