vscode-apollo 1.19.3 → 2.0.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/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.circleci/config.yml +91 -0
- package/.eslintrc.js +10 -0
- package/.git-blame-ignore-revs +2 -0
- package/.gitattributes +1 -0
- package/.github/workflows/release.yml +95 -0
- package/.gitleaks.toml +26 -0
- package/.nvmrc +1 -0
- package/.prettierrc +5 -0
- package/.vscode/launch.json +66 -0
- package/.vscode/settings.json +16 -0
- package/.vscode/tasks.json +60 -0
- package/.vscodeignore +28 -1
- package/CHANGELOG.md +250 -1
- package/CODEOWNERS +4 -0
- package/LICENSE +2 -2
- package/README.md +104 -55
- package/codegen.yml +12 -0
- package/graphql.configuration.json +5 -1
- package/images/IconRun.svg +8 -0
- package/images/marketplace/apollo-wordmark.png +0 -0
- package/jest.config.ts +21 -0
- package/jest.e2e.config.js +17 -0
- package/package.json +102 -23
- package/renovate.json +30 -0
- package/sampleWorkspace/clientSchema/apollo.config.cjs +10 -0
- package/sampleWorkspace/clientSchema/src/clientSchema.js +16 -0
- package/sampleWorkspace/clientSchema/src/test.js +18 -0
- package/sampleWorkspace/fixtures/starwarsSchema.graphql +299 -0
- package/sampleWorkspace/httpSchema/apollo.config.ts +8 -0
- package/sampleWorkspace/httpSchema/src/test.js +9 -0
- package/sampleWorkspace/localSchema/apollo.config.js +8 -0
- package/sampleWorkspace/localSchema/src/test.js +8 -0
- package/sampleWorkspace/localSchemaArray/apollo.config.js +12 -0
- package/sampleWorkspace/localSchemaArray/planets.graphql +20 -0
- package/sampleWorkspace/localSchemaArray/src/test.js +12 -0
- package/sampleWorkspace/sampleWorkspace.code-workspace +20 -0
- package/sampleWorkspace/spotifyGraph/apollo.config.mjs +5 -0
- package/sampleWorkspace/spotifyGraph/src/test.js +11 -0
- package/src/__e2e__/mockServer.js +117 -0
- package/src/__e2e__/mocks.js +13094 -0
- package/src/__e2e__/run.js +23 -0
- package/src/__e2e__/runTests.js +44 -0
- package/src/__e2e__/setup.js +1 -0
- package/src/__e2e__/vscode-environment.js +16 -0
- package/src/__e2e__/vscode.js +1 -0
- package/src/__mocks__/fs.js +3 -0
- package/src/__tests__/statusBar.test.ts +8 -7
- package/src/build.js +57 -0
- package/src/debug.ts +2 -5
- package/src/env/index.ts +1 -0
- package/src/env/typescript-utility-types.ts +2 -0
- package/src/extension.ts +265 -170
- package/src/language-server/__e2e__/clientSchema.e2e.ts +147 -0
- package/src/language-server/__e2e__/httpSchema.e2e.ts +21 -0
- package/src/language-server/__e2e__/localSchema.e2e.ts +25 -0
- package/src/language-server/__e2e__/localSchemaArray.e2e.ts +31 -0
- package/src/language-server/__e2e__/studioGraph.e2e.ts +65 -0
- package/src/language-server/__e2e__/utils.ts +151 -0
- package/src/language-server/__tests__/diagnostics.test.ts +86 -0
- package/src/language-server/__tests__/document.test.ts +187 -0
- package/src/language-server/__tests__/fileSet.test.ts +46 -0
- package/src/language-server/__tests__/fixtures/starwarsSchema.ts +1917 -0
- package/src/language-server/config/__tests__/config.ts +54 -0
- package/src/language-server/config/__tests__/loadConfig.ts +384 -0
- package/src/language-server/config/__tests__/utils.ts +99 -0
- package/src/language-server/config/config.ts +284 -0
- package/src/language-server/config/index.ts +3 -0
- package/src/language-server/config/loadConfig.ts +101 -0
- package/src/language-server/config/utils.ts +45 -0
- package/src/language-server/diagnostics.ts +118 -0
- package/src/language-server/document.ts +277 -0
- package/src/language-server/engine/index.ts +123 -0
- package/src/language-server/engine/operations/frontendUrlRoot.ts +15 -0
- package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +32 -0
- package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +225 -0
- package/src/language-server/errors/logger.ts +58 -0
- package/src/language-server/errors/validation.ts +274 -0
- package/src/language-server/fileSet.ts +63 -0
- package/src/language-server/format.ts +48 -0
- package/src/language-server/graphqlTypes.ts +16741 -0
- package/src/language-server/index.ts +28 -0
- package/src/language-server/languageProvider.ts +795 -0
- package/src/language-server/loadingHandler.ts +47 -0
- package/src/language-server/project/base.ts +406 -0
- package/src/language-server/project/client.ts +568 -0
- package/src/language-server/project/defaultClientSchema.ts +70 -0
- package/src/language-server/providers/schema/__tests__/file.ts +191 -0
- package/src/language-server/providers/schema/base.ts +15 -0
- package/src/language-server/providers/schema/endpoint.ts +138 -0
- package/src/language-server/providers/schema/engine.ts +204 -0
- package/src/language-server/providers/schema/file.ts +176 -0
- package/src/language-server/providers/schema/index.ts +59 -0
- package/src/language-server/server.ts +274 -0
- package/src/language-server/typings/graphql.d.ts +27 -0
- package/src/language-server/utilities/__tests__/graphql.test.ts +399 -0
- package/src/language-server/utilities/__tests__/uri.ts +55 -0
- package/src/language-server/utilities/debouncer.ts +8 -0
- package/src/language-server/utilities/debug.ts +90 -0
- package/src/language-server/utilities/graphql.ts +433 -0
- package/src/language-server/utilities/index.ts +3 -0
- package/src/language-server/utilities/source.ts +182 -0
- package/src/language-server/utilities/uri.ts +19 -0
- package/src/language-server/workspace.ts +254 -0
- package/src/languageServerClient.ts +22 -15
- package/src/messages.ts +75 -0
- package/src/statusBar.ts +5 -5
- package/src/tools/__tests__/buildServiceDefinition.test.ts +491 -0
- package/src/tools/__tests__/snapshotSerializers/astSerializer.ts +19 -0
- package/src/tools/__tests__/snapshotSerializers/graphQLTypeSerializer.ts +14 -0
- package/src/tools/buildServiceDefinition.ts +241 -0
- package/src/tools/index.ts +6 -0
- package/src/tools/schema/index.ts +2 -0
- package/src/tools/schema/resolveObject.ts +18 -0
- package/src/tools/schema/resolverMap.ts +23 -0
- package/src/tools/utilities/graphql.ts +22 -0
- package/src/tools/utilities/index.ts +3 -0
- package/src/tools/utilities/invariant.ts +5 -0
- package/src/tools/utilities/predicates.ts +5 -0
- package/src/utils.ts +7 -21
- package/syntaxes/graphql.dart.json +2 -4
- package/syntaxes/graphql.ex.json +1 -4
- package/syntaxes/graphql.js.json +3 -3
- package/syntaxes/graphql.json +13 -9
- package/syntaxes/graphql.lua.json +51 -0
- package/syntaxes/graphql.rb.json +1 -1
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +22 -7
- package/create-server-symlink.js +0 -8
- package/lib/debug.d.ts +0 -11
- package/lib/debug.d.ts.map +0 -1
- package/lib/debug.js +0 -48
- package/lib/debug.js.map +0 -1
- package/lib/extension.d.ts +0 -4
- package/lib/extension.d.ts.map +0 -1
- package/lib/extension.js +0 -187
- package/lib/extension.js.map +0 -1
- package/lib/languageServerClient.d.ts +0 -4
- package/lib/languageServerClient.d.ts.map +0 -1
- package/lib/languageServerClient.js +0 -57
- package/lib/languageServerClient.js.map +0 -1
- package/lib/statusBar.d.ts +0 -24
- package/lib/statusBar.d.ts.map +0 -1
- package/lib/statusBar.js +0 -46
- package/lib/statusBar.js.map +0 -1
- package/lib/testRunner/index.d.ts +0 -3
- package/lib/testRunner/index.d.ts.map +0 -1
- package/lib/testRunner/index.js +0 -49
- package/lib/testRunner/index.js.map +0 -1
- package/lib/testRunner/jest-config.d.ts +0 -14
- package/lib/testRunner/jest-config.d.ts.map +0 -1
- package/lib/testRunner/jest-config.js +0 -18
- package/lib/testRunner/jest-config.js.map +0 -1
- package/lib/testRunner/jest-vscode-environment.d.ts +0 -2
- package/lib/testRunner/jest-vscode-environment.d.ts.map +0 -1
- package/lib/testRunner/jest-vscode-environment.js +0 -19
- package/lib/testRunner/jest-vscode-environment.js.map +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.d.ts +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.d.ts.map +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.js +0 -3
- package/lib/testRunner/jest-vscode-framework-setup.js.map +0 -1
- package/lib/testRunner/vscode-test-script.d.ts +0 -2
- package/lib/testRunner/vscode-test-script.d.ts.map +0 -1
- package/lib/testRunner/vscode-test-script.js +0 -23
- package/lib/testRunner/vscode-test-script.js.map +0 -1
- package/lib/utils.d.ts +0 -18
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js +0 -52
- package/lib/utils.js.map +0 -1
- package/src/testRunner/README.md +0 -23
- package/src/testRunner/index.ts +0 -72
- package/src/testRunner/jest-config.ts +0 -17
- package/src/testRunner/jest-vscode-environment.ts +0 -25
- package/src/testRunner/jest-vscode-framework-setup.ts +0 -10
- package/src/testRunner/jest.d.ts +0 -37
- package/src/testRunner/vscode-test-script.ts +0 -38
- package/tsconfig.test.json +0 -4
- package/tsconfig.tsbuildinfo +0 -2486
package/src/extension.ts
CHANGED
|
@@ -9,21 +9,28 @@ import {
|
|
|
9
9
|
commands,
|
|
10
10
|
QuickPickItem,
|
|
11
11
|
Disposable,
|
|
12
|
-
OutputChannel
|
|
12
|
+
OutputChannel,
|
|
13
|
+
MarkdownString,
|
|
14
|
+
Range,
|
|
13
15
|
} from "vscode";
|
|
14
16
|
import StatusBar from "./statusBar";
|
|
15
17
|
import { getLanguageServerClient } from "./languageServerClient";
|
|
16
|
-
import { LanguageClient
|
|
18
|
+
import { LanguageClient } from "vscode-languageclient/node";
|
|
19
|
+
import {
|
|
20
|
+
type EngineDecoration,
|
|
21
|
+
LanguageServerCommands as LSCommands,
|
|
22
|
+
LanguageServerNotifications as LSNotifications,
|
|
23
|
+
LanguageServerRequests as LSRequests,
|
|
24
|
+
} from "./messages";
|
|
17
25
|
import {
|
|
18
26
|
printNoFileOpenMessage,
|
|
19
|
-
printStatsToClientOutputChannel
|
|
27
|
+
printStatsToClientOutputChannel,
|
|
20
28
|
} from "./utils";
|
|
21
29
|
import { Debug } from "./debug";
|
|
22
30
|
|
|
23
31
|
const { version } = require("../package.json");
|
|
24
32
|
|
|
25
|
-
let
|
|
26
|
-
let clientDisposable: Disposable;
|
|
33
|
+
let globalClient: LanguageClient | null = null;
|
|
27
34
|
let statusBar: StatusBar;
|
|
28
35
|
let outputChannel: OutputChannel;
|
|
29
36
|
let schemaTagItems: QuickPickItem[] = [];
|
|
@@ -41,215 +48,303 @@ function isError(response: any): response is ErrorShape {
|
|
|
41
48
|
);
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
export
|
|
51
|
+
export interface VSCodeGraphQLExtension {
|
|
52
|
+
outputChannel: OutputChannel;
|
|
53
|
+
client: LanguageClient;
|
|
54
|
+
LanguageServerCommands: typeof LSCommands;
|
|
55
|
+
LanguageServerNotifications: typeof LSNotifications;
|
|
56
|
+
LanguageServerRequests: typeof LSRequests;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function activate(
|
|
60
|
+
context: ExtensionContext,
|
|
61
|
+
): Promise<VSCodeGraphQLExtension> {
|
|
45
62
|
const serverModule = context.asAbsolutePath(
|
|
46
|
-
join("
|
|
63
|
+
join("lib/language-server", "server.js"),
|
|
47
64
|
);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
outputChannel ||= window.createOutputChannel("Apollo GraphQL");
|
|
66
|
+
const client = getLanguageServerClient(serverModule, outputChannel);
|
|
67
|
+
globalClient = client;
|
|
51
68
|
client.registerProposedFeatures();
|
|
52
69
|
|
|
53
70
|
// Initialize disposables
|
|
54
71
|
statusBar = new StatusBar({
|
|
55
|
-
hasActiveTextEditor: Boolean(window.activeTextEditor)
|
|
72
|
+
hasActiveTextEditor: Boolean(window.activeTextEditor),
|
|
56
73
|
});
|
|
57
|
-
outputChannel = window.createOutputChannel("Apollo GraphQL");
|
|
58
74
|
Debug.SetOutputConsole(outputChannel);
|
|
59
|
-
clientDisposable = client.start();
|
|
60
|
-
|
|
61
75
|
// Handoff disposables for cleanup
|
|
62
|
-
context.subscriptions.push(statusBar, outputChannel
|
|
63
|
-
|
|
64
|
-
var serverDebugMessage: NotificationType<
|
|
65
|
-
{ type: string; message: string; stack?: string },
|
|
66
|
-
any
|
|
67
|
-
> = new NotificationType("serverDebugMessage");
|
|
68
|
-
|
|
69
|
-
// Once client is ready, we can send messages and add listeners for various notifications
|
|
70
|
-
client.onReady().then(() => {
|
|
71
|
-
client.onNotification(serverDebugMessage, message => {
|
|
72
|
-
switch (message.type) {
|
|
73
|
-
case "info":
|
|
74
|
-
Debug.info(message.message, message.stack);
|
|
75
|
-
break;
|
|
76
|
-
case "error":
|
|
77
|
-
Debug.error(message.message, message.stack);
|
|
78
|
-
break;
|
|
79
|
-
case "warning":
|
|
80
|
-
Debug.warning(message.message, message.stack);
|
|
81
|
-
break;
|
|
82
|
-
default:
|
|
83
|
-
Debug.info(message.message, message.stack);
|
|
84
|
-
break;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
76
|
+
context.subscriptions.push(statusBar, outputChannel);
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
78
|
+
client.onNotification(LSNotifications.ServerDebugMessage, (message) => {
|
|
79
|
+
switch (message.type) {
|
|
80
|
+
case "info":
|
|
81
|
+
Debug.info(message.message, message.stack);
|
|
82
|
+
break;
|
|
83
|
+
case "error":
|
|
84
|
+
Debug.error(message.message, message.stack);
|
|
85
|
+
break;
|
|
86
|
+
case "warning":
|
|
87
|
+
Debug.warning(message.message, message.stack);
|
|
88
|
+
break;
|
|
89
|
+
default:
|
|
90
|
+
Debug.info(message.message, message.stack);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
commands.registerCommand("apollographql/showStats", () => {
|
|
96
|
+
const fileUri = window.activeTextEditor
|
|
97
|
+
? window.activeTextEditor.document.uri.fsPath
|
|
98
|
+
: null;
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
client.outputChannel.show();
|
|
104
|
-
});
|
|
100
|
+
// if no editor is open, but an output channel is, vscode returns something like
|
|
101
|
+
// output:extension-output-%234. If an editor IS open, this is something like file://Users/...
|
|
102
|
+
// This check is just for either a / or a \ anywhere in a fileUri
|
|
103
|
+
// TODO: this should probably be `registerTextEditorCommand` instead of `registerCommand`
|
|
104
|
+
const fileOpen = fileUri && /[\/\\]/.test(fileUri);
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
client
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
106
|
+
if (fileOpen) {
|
|
107
|
+
client.sendNotification(LSCommands.GetStats, {
|
|
108
|
+
uri: fileUri,
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
printNoFileOpenMessage(client, version);
|
|
113
|
+
client.outputChannel.show();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
client.onNotification(LSNotifications.StatsLoaded, (params) => {
|
|
117
|
+
printStatsToClientOutputChannel(client, params, version);
|
|
118
|
+
client.outputChannel.show();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
commands.registerCommand("apollographql/fileStats", (uri: string) => {
|
|
122
|
+
// this has been introduced to check the LSP loading status in tests right now
|
|
123
|
+
// TODO: "apollographql/showStats" should use this request as well instead of notifications
|
|
124
|
+
return client.sendRequest(LSRequests.FileStats, { uri });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// For some reason, non-strings can only be sent in one direction. For now, messages
|
|
128
|
+
// coming from the language server just need to be stringified and parsed.
|
|
129
|
+
client.onNotification(LSNotifications.ConfigFilesFound, (params: string) => {
|
|
130
|
+
const response = JSON.parse(params) as Array<any> | ErrorShape;
|
|
131
|
+
|
|
132
|
+
const hasActiveTextEditor = Boolean(window.activeTextEditor);
|
|
133
|
+
if (Array.isArray(response)) {
|
|
134
|
+
const errors = response.filter((item) => isError(item));
|
|
135
|
+
if (errors.length) {
|
|
136
|
+
errors.forEach((response) => {
|
|
119
137
|
statusBar.showWarningState({
|
|
120
138
|
hasActiveTextEditor,
|
|
121
|
-
tooltip: "Configuration Error"
|
|
139
|
+
tooltip: "Configuration Error",
|
|
122
140
|
});
|
|
123
|
-
outputChannel.
|
|
141
|
+
outputChannel.appendLine("---\n" + response.stack + "\n---");
|
|
124
142
|
|
|
125
143
|
const infoButtonText = "More Info";
|
|
126
144
|
window
|
|
127
145
|
.showInformationMessage(response.message, infoButtonText)
|
|
128
|
-
.then(clicked => {
|
|
146
|
+
.then((clicked) => {
|
|
129
147
|
if (clicked === infoButtonText) {
|
|
130
148
|
outputChannel.show();
|
|
131
149
|
}
|
|
132
150
|
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
} else {
|
|
143
|
-
Debug.error(
|
|
144
|
-
`Invalid response type in message apollographql/configFilesFound:\n${JSON.stringify(
|
|
145
|
-
response
|
|
146
|
-
)}`
|
|
147
|
-
);
|
|
148
|
-
}
|
|
151
|
+
});
|
|
152
|
+
} else if (response.length === 0) {
|
|
153
|
+
statusBar.showWarningState({
|
|
154
|
+
hasActiveTextEditor,
|
|
155
|
+
tooltip: "No apollo.config.js file found",
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
statusBar.showLoadedState({ hasActiveTextEditor });
|
|
149
159
|
}
|
|
150
|
-
|
|
160
|
+
} else {
|
|
161
|
+
Debug.error(
|
|
162
|
+
`Invalid response type in message apollographql/configFilesFound:\n${JSON.stringify(
|
|
163
|
+
response,
|
|
164
|
+
)}`,
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
151
168
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
169
|
+
commands.registerCommand("apollographql/reloadService", () => {
|
|
170
|
+
// wipe out tags when reloading
|
|
171
|
+
// XXX we should clean up this handling
|
|
172
|
+
schemaTagItems = [];
|
|
173
|
+
client.sendNotification(LSCommands.ReloadService);
|
|
174
|
+
});
|
|
158
175
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
schemaTagItems = [...items, ...schemaTagItems];
|
|
170
|
-
});
|
|
176
|
+
// For some reason, non-strings can only be sent in one direction. For now, messages
|
|
177
|
+
// coming from the language server just need to be stringified and parsed.
|
|
178
|
+
client.onNotification(LSNotifications.TagsLoaded, (params) => {
|
|
179
|
+
const [serviceID, tags]: [string, string[]] = JSON.parse(params);
|
|
180
|
+
const items = tags.map((tag) => ({
|
|
181
|
+
label: tag,
|
|
182
|
+
description: "",
|
|
183
|
+
detail: serviceID,
|
|
184
|
+
}));
|
|
171
185
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (selection) {
|
|
175
|
-
client.sendNotification("apollographql/tagSelected", selection);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
186
|
+
schemaTagItems = [...items, ...schemaTagItems];
|
|
187
|
+
});
|
|
178
188
|
|
|
179
|
-
|
|
189
|
+
commands.registerCommand("apollographql/selectSchemaTag", async () => {
|
|
190
|
+
const selection = await window.showQuickPick(schemaTagItems);
|
|
191
|
+
if (selection) {
|
|
192
|
+
client.sendNotification(LSCommands.TagSelected, selection);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
180
195
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (inMap) {
|
|
187
|
-
inMap();
|
|
188
|
-
currentLoadingResolve.delete(token);
|
|
189
|
-
}
|
|
196
|
+
let currentLoadingResolve: Map<number, () => void> = new Map();
|
|
197
|
+
|
|
198
|
+
client.onNotification(LSNotifications.LoadingComplete, (token) => {
|
|
199
|
+
statusBar.showLoadedState({
|
|
200
|
+
hasActiveTextEditor: Boolean(window.activeTextEditor),
|
|
190
201
|
});
|
|
202
|
+
const inMap = currentLoadingResolve.get(token);
|
|
203
|
+
if (inMap) {
|
|
204
|
+
inMap();
|
|
205
|
+
currentLoadingResolve.delete(token);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
client.onNotification(LSNotifications.Loading, ({ message, token }) => {
|
|
210
|
+
window.withProgress(
|
|
211
|
+
{
|
|
212
|
+
location: ProgressLocation.Notification,
|
|
213
|
+
title: message,
|
|
214
|
+
cancellable: false,
|
|
215
|
+
},
|
|
216
|
+
() => {
|
|
217
|
+
return new Promise<void>((resolve) => {
|
|
218
|
+
currentLoadingResolve.set(token, resolve);
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const runIconOnDiskPath = Uri.file(
|
|
225
|
+
join(context.extensionPath, "images", "IconRun.svg"),
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const textDecorationType = window.createTextEditorDecorationType({});
|
|
229
|
+
const runGlyphDecorationType = window.createTextEditorDecorationType({});
|
|
230
|
+
let latestDecorations: EngineDecoration[] | undefined = undefined;
|
|
191
231
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
const updateDecorations = () => {
|
|
233
|
+
if (window.activeTextEditor && latestDecorations) {
|
|
234
|
+
const editor = window.activeTextEditor!;
|
|
235
|
+
|
|
236
|
+
const decorationsForDocument = latestDecorations.filter(
|
|
237
|
+
(decoration) =>
|
|
238
|
+
decoration.document ===
|
|
239
|
+
window.activeTextEditor!.document.uri.toString(),
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const textDecorations = decorationsForDocument.flatMap(
|
|
243
|
+
(decoration): DecorationOptions | DecorationOptions[] => {
|
|
244
|
+
if (decoration.type !== "text") {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
range: editor.document.lineAt(decoration.range.start.line).range,
|
|
250
|
+
renderOptions: {
|
|
251
|
+
after: {
|
|
252
|
+
contentText: decoration.message,
|
|
253
|
+
textDecoration: "none; padding-left: 15px; opacity: .5",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
};
|
|
198
257
|
},
|
|
199
|
-
() => {
|
|
200
|
-
return new Promise<void>(resolve => {
|
|
201
|
-
currentLoadingResolve.set(token, resolve);
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
258
|
);
|
|
205
|
-
});
|
|
206
259
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
renderOptions: {
|
|
221
|
-
after: {
|
|
222
|
-
contentText: `${dec.message}`,
|
|
223
|
-
textDecoration: "none; padding-left: 15px; opacity: .5"
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
});
|
|
260
|
+
const runGlyphDecorations = decorationsForDocument.flatMap(
|
|
261
|
+
(decoration): DecorationOptions | DecorationOptions[] => {
|
|
262
|
+
if (decoration.type !== "runGlyph") {
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const hoverMessage =
|
|
267
|
+
decoration.hoverMessage === undefined
|
|
268
|
+
? undefined
|
|
269
|
+
: new MarkdownString(decoration.hoverMessage);
|
|
270
|
+
if (hoverMessage) {
|
|
271
|
+
hoverMessage.isTrusted = true;
|
|
272
|
+
}
|
|
228
273
|
|
|
229
|
-
|
|
274
|
+
const endOfLinePosition = editor.document.lineAt(
|
|
275
|
+
decoration.range.start.line,
|
|
276
|
+
).range.end;
|
|
277
|
+
return {
|
|
278
|
+
// Hover range of just the end of the line (and the icon) so the hover shows above the icon,
|
|
279
|
+
// not over at the start of the line
|
|
280
|
+
range: new Range(endOfLinePosition, endOfLinePosition),
|
|
281
|
+
renderOptions: {
|
|
282
|
+
after: {
|
|
283
|
+
contentIconPath: runIconOnDiskPath,
|
|
284
|
+
textDecoration:
|
|
285
|
+
"none; border-radius: .20rem; margin-left: 8px; text-align: center;",
|
|
286
|
+
backgroundColor: "#2075D6",
|
|
287
|
+
width: "18px",
|
|
288
|
+
height: "18px",
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
hoverMessage,
|
|
292
|
+
};
|
|
293
|
+
},
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
window.activeTextEditor!.setDecorations(
|
|
297
|
+
textDecorationType,
|
|
298
|
+
textDecorations,
|
|
299
|
+
);
|
|
300
|
+
if (
|
|
301
|
+
workspace
|
|
302
|
+
.getConfiguration("apollographql")
|
|
303
|
+
.get("display.showRunInStudioButton")
|
|
304
|
+
) {
|
|
305
|
+
window.activeTextEditor!.setDecorations(
|
|
306
|
+
runGlyphDecorationType,
|
|
307
|
+
runGlyphDecorations,
|
|
308
|
+
);
|
|
230
309
|
}
|
|
231
|
-
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
232
312
|
|
|
233
|
-
|
|
234
|
-
|
|
313
|
+
client.onNotification(
|
|
314
|
+
LSNotifications.EngineDecorations,
|
|
315
|
+
({ decorations }) => {
|
|
316
|
+
latestDecorations = decorations;
|
|
235
317
|
updateDecorations();
|
|
236
|
-
}
|
|
318
|
+
},
|
|
319
|
+
);
|
|
237
320
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
321
|
+
window.onDidChangeActiveTextEditor(() => {
|
|
322
|
+
updateDecorations();
|
|
323
|
+
});
|
|
241
324
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
});
|
|
325
|
+
workspace.registerTextDocumentContentProvider("graphql-schema", {
|
|
326
|
+
provideTextDocumentContent(uri: Uri) {
|
|
327
|
+
// the schema source is provided inside the URI, just return that here
|
|
328
|
+
return uri.query;
|
|
329
|
+
},
|
|
248
330
|
});
|
|
331
|
+
|
|
332
|
+
await client.start();
|
|
333
|
+
return {
|
|
334
|
+
outputChannel,
|
|
335
|
+
client,
|
|
336
|
+
LanguageServerCommands: LSCommands,
|
|
337
|
+
LanguageServerNotifications: LSNotifications,
|
|
338
|
+
LanguageServerRequests: LSRequests,
|
|
339
|
+
};
|
|
249
340
|
}
|
|
250
341
|
|
|
251
342
|
export function deactivate(): Thenable<void> | void {
|
|
252
|
-
if (
|
|
253
|
-
|
|
343
|
+
if (globalClient) {
|
|
344
|
+
try {
|
|
345
|
+
return globalClient.stop();
|
|
346
|
+
} finally {
|
|
347
|
+
globalClient = null;
|
|
348
|
+
}
|
|
254
349
|
}
|
|
255
350
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { TextEditor } from "vscode";
|
|
2
|
+
import { closeAllEditors, openEditor, testCompletion, getHover } from "./utils";
|
|
3
|
+
|
|
4
|
+
let editor: TextEditor;
|
|
5
|
+
beforeAll(async () => {
|
|
6
|
+
closeAllEditors();
|
|
7
|
+
editor = await openEditor("clientSchema/src/test.js");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("completion", async () => {
|
|
11
|
+
// dro|id
|
|
12
|
+
await testCompletion(editor, [4, 7], [["droid", "Droid"]]);
|
|
13
|
+
// na|me
|
|
14
|
+
await testCompletion(editor, [5, 8], [["name", "String!"]]);
|
|
15
|
+
// mo|del
|
|
16
|
+
await testCompletion(editor, [6, 8], [["model", "String"]]);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("hover", async () => {
|
|
20
|
+
// featu|reFlagDefer
|
|
21
|
+
expect(await getHover(editor, [3, 10])).toMatchInlineSnapshot(`
|
|
22
|
+
"\`\`\`graphql
|
|
23
|
+
Query.featureFlagDefer: Boolean!
|
|
24
|
+
\`\`\`
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
\`Client-Only Field\` \`Resolved locally\`
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
Whether to use defer"
|
|
33
|
+
`);
|
|
34
|
+
|
|
35
|
+
// @c|lient(always: false)
|
|
36
|
+
expect(await getHover(editor, [3, 24])).toMatchInlineSnapshot(`
|
|
37
|
+
"\`\`\`graphql
|
|
38
|
+
@client(always: Boolean)
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
Direct the client to resolve this field locally, either from the cache or local resolvers."
|
|
44
|
+
`);
|
|
45
|
+
|
|
46
|
+
// @client(alwa|ys: false)
|
|
47
|
+
expect(await getHover(editor, [3, 33])).toMatchInlineSnapshot(`
|
|
48
|
+
"\`\`\`graphql
|
|
49
|
+
always: Boolean
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
When true, the client will never use the cache for this value. See
|
|
55
|
+
https://www.apollographql.com/docs/react/local-state/local-resolvers/#forcing-resolvers-with-clientalways-true"
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
// @expo|rt(as: "defer")
|
|
59
|
+
expect(await getHover(editor, [3, 49])).toMatchInlineSnapshot(`
|
|
60
|
+
"\`\`\`graphql
|
|
61
|
+
@export(as: String!)
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
Export this locally resolved field as a variable to be used in the remainder of this query. See
|
|
67
|
+
https://www.apollographql.com/docs/react/local-state/local-resolvers/#using-client-fields-as-variables"
|
|
68
|
+
`);
|
|
69
|
+
expect(await getHover(editor, [3, 53])).toMatchInlineSnapshot(`
|
|
70
|
+
"\`\`\`graphql
|
|
71
|
+
as: String!
|
|
72
|
+
\`\`\`
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
The variable name to export this field as."
|
|
77
|
+
`);
|
|
78
|
+
|
|
79
|
+
// @nonre|active
|
|
80
|
+
expect(await getHover(editor, [7, 28])).toMatchInlineSnapshot(`
|
|
81
|
+
"\`\`\`graphql
|
|
82
|
+
@nonreactive
|
|
83
|
+
\`\`\`
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
The @nonreactive directive can be used to mark query fields or fragment spreads and is used to indicate that changes to the data contained within the subtrees marked @nonreactive should not trigger rerendering.
|
|
88
|
+
This allows parent components to fetch data to be rendered by their children without rerendering themselves when the data corresponding with fields marked as @nonreactive change.
|
|
89
|
+
https://www.apollographql.com/docs/react/data/directives#nonreactive"
|
|
90
|
+
`);
|
|
91
|
+
|
|
92
|
+
// @def|er(if: $defer, label: "fc")
|
|
93
|
+
expect(await getHover(editor, [8, 14])).toMatchInlineSnapshot(`
|
|
94
|
+
"\`\`\`graphql
|
|
95
|
+
@defer(if: Boolean, label: String)
|
|
96
|
+
\`\`\`
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
This directive enables your queries to receive data for specific fields incrementally, instead of receiving all field data at the same time.
|
|
101
|
+
This is helpful whenever some fields in a query take much longer to resolve than others.
|
|
102
|
+
https://www.apollographql.com/docs/react/data/directives#defer"
|
|
103
|
+
`);
|
|
104
|
+
//@defer(i|f: $defer, label: "fc")
|
|
105
|
+
expect(await getHover(editor, [8, 18])).toMatchInlineSnapshot(`
|
|
106
|
+
"\`\`\`graphql
|
|
107
|
+
if: Boolean
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
When true fragment may be deferred, if omitted defaults to true."
|
|
113
|
+
`);
|
|
114
|
+
//@defer(if: $defer, labe|l: "fc")
|
|
115
|
+
expect(await getHover(editor, [8, 33])).toMatchInlineSnapshot(`
|
|
116
|
+
"\`\`\`graphql
|
|
117
|
+
label: String
|
|
118
|
+
\`\`\`
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
A unique label across all @defer and @stream directives in an operation.
|
|
123
|
+
This label should be used by GraphQL clients to identify the data from patch responses and associate it with the correct fragment.
|
|
124
|
+
If provided, the GraphQL Server must add it to the payload."
|
|
125
|
+
`);
|
|
126
|
+
// @connec|tion(key: "feed")
|
|
127
|
+
expect(await getHover(editor, [9, 53])).toMatchInlineSnapshot(`
|
|
128
|
+
"\`\`\`graphql
|
|
129
|
+
@connection(key: String!, filter: [String!])
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
Specify a custom store key for this result. See
|
|
135
|
+
https://www.apollographql.com/docs/react/caching/advanced-topics/#the-connection-directive"
|
|
136
|
+
`);
|
|
137
|
+
// @connection(ke|y: "feed")
|
|
138
|
+
expect(await getHover(editor, [9, 61])).toMatchInlineSnapshot(`
|
|
139
|
+
"\`\`\`graphql
|
|
140
|
+
key: String!
|
|
141
|
+
\`\`\`
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
Specify the store key."
|
|
146
|
+
`);
|
|
147
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { TextEditor } from "vscode";
|
|
2
|
+
import { closeAllEditors, openEditor, testCompletion, getHover } from "./utils";
|
|
3
|
+
|
|
4
|
+
let editor: TextEditor;
|
|
5
|
+
beforeAll(async () => {
|
|
6
|
+
closeAllEditors();
|
|
7
|
+
editor = await openEditor("httpSchema/src/test.js");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("completion", async () => {
|
|
11
|
+
await testCompletion(editor, [3, 7], [["books", "[Book]"]]);
|
|
12
|
+
await testCompletion(editor, [5, 9], [["author", "String"]]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("hover", async () => {
|
|
16
|
+
expect(await getHover(editor, [5, 9])).toMatchInlineSnapshot(`
|
|
17
|
+
"\`\`\`graphql
|
|
18
|
+
Book.author: String
|
|
19
|
+
\`\`\`"
|
|
20
|
+
`);
|
|
21
|
+
});
|