Skip to content

Commit 37b67b1

Browse files
committed
feat: improve codelens support, restart on config changes
1 parent d5028be commit 37b67b1

File tree

4 files changed

+91
-34
lines changed

4 files changed

+91
-34
lines changed

packages/vscode-graphql-execution/src/extension.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,32 @@ export function activate(context: ExtensionContext) {
4242
// const settings = workspace.getConfiguration("vscode-graphql-execution")
4343
// let provider: GraphQLCodeLensProvider;
4444
const registerCodeLens = () => {
45-
context.subscriptions.push(
46-
languages.registerCodeLensProvider(
47-
[
48-
'javascript',
49-
'typescript',
50-
'javascriptreact',
51-
'typescriptreact',
52-
'graphql',
53-
],
54-
new GraphQLCodeLensProvider(outputChannel),
55-
),
45+
const provider = languages.registerCodeLensProvider(
46+
[
47+
'javascript',
48+
'typescript',
49+
'javascriptreact',
50+
'typescriptreact',
51+
'graphql',
52+
],
53+
new GraphQLCodeLensProvider(outputChannel),
5654
);
55+
context.subscriptions.push(provider);
56+
return provider
5757
};
5858

5959
// if (settings.showExecCodelens !== false) {
60-
registerCodeLens();
60+
const codeLensProvider = registerCodeLens();
61+
62+
6163
// }
6264

6365
let commandContentProvider: GraphQLContentProvider;
6466

6567
const registerContentProvider = () => {
66-
return commands.registerCommand(
68+
const provider = commands.registerCommand(
6769
'vscode-graphql-execution.contentProvider',
68-
(literal: ExtractedTemplateLiteral) => {
70+
async (literal: ExtractedTemplateLiteral) => {
6971
const uri = Uri.parse('graphql://authority/graphql');
7072

7173
const panel = window.createWebviewPanel(
@@ -81,6 +83,7 @@ export function activate(context: ExtensionContext) {
8183
literal,
8284
panel,
8385
);
86+
await commandContentProvider.loadProvider();
8487
const registration = workspace.registerTextDocumentContentProvider(
8588
'graphql',
8689
commandContentProvider,
@@ -89,23 +92,33 @@ export function activate(context: ExtensionContext) {
8992
panel.webview.html = commandContentProvider.getCurrentHtml();
9093
},
9194
);
95+
context.subscriptions.push(provider);
96+
return provider
9297
};
9398

94-
const provider = registerContentProvider();
95-
context.subscriptions.push(provider);
99+
const contentProvider = registerContentProvider();
96100

97101
// workspace.onDidChangeConfiguration(async () => {
98102
// // const newSettings = workspace.getConfiguration("vscode-graphql-execution")
99103
// // if (newSettings.showExecCodeLens !== false) {
100-
// commandContentProvider.dispose()
104+
// provider.dispose();
101105
// // }
102106
// });
107+
103108
workspace.onDidSaveTextDocument(async e => {
104109
if (
105110
e.fileName.includes('graphql.config') ||
106111
e.fileName.includes('graphqlrc')
107112
) {
108-
await commandContentProvider.loadConfig();
113+
if (contentProvider) {
114+
await contentProvider.dispose();
115+
registerContentProvider()
116+
}
117+
118+
if (codeLensProvider) {
119+
await codeLensProvider.dispose();
120+
registerCodeLens();
121+
}
109122
}
110123
});
111124
}

packages/vscode-graphql-execution/src/helpers/source.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,36 @@ export class SourceHelper {
172172
} catch {}
173173
}
174174

175+
const regExpInline = new RegExp(
176+
'`[\\n\\r\\s]*#graphql+([\\s\\S]+?)`',
177+
'mg',
178+
);
179+
180+
let inlineResult: RegExpExecArray | null;
181+
182+
while ((inlineResult = regExpInline.exec(text)) !== null) {
183+
const contents = inlineResult[1];
184+
185+
// https://regex101.com/r/KFMXFg/2
186+
if (contents.match('/${(.+)?}/g')) {
187+
// We are ignoring operations with template variables for now
188+
continue;
189+
}
190+
try {
191+
processGraphQLString(contents, inlineResult.index + 1);
192+
193+
// no-op on exception, so that non-parse-able source files
194+
// don't break the extension while editing
195+
} catch {}
196+
}
197+
175198
for (const tag of tags) {
176199
// https://regex101.com/r/Pd5PaU/2
177200
const regExpGQL = new RegExp(tag + '\\s*`([\\s\\S]+?)`', 'mg');
178-
201+
// https://regex101.com/r/FvG9qc/1
202+
179203
let result: RegExpExecArray | null;
204+
180205
while ((result = regExpGQL.exec(text)) !== null) {
181206
const contents = result[1];
182207

@@ -191,6 +216,7 @@ export class SourceHelper {
191216
// don't break the extension while editing
192217
} catch {}
193218
}
219+
194220
}
195221
return documents;
196222

@@ -199,6 +225,7 @@ export class SourceHelper {
199225
const operations = ast.definitions.filter(
200226
def => def.kind === 'OperationDefinition',
201227
);
228+
202229
for (const operation of operations) {
203230
const op = operation as any;
204231
const filteredAst = {

packages/vscode-graphql-execution/src/providers/exec-codelens.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,38 @@ import {
66
CodeLens,
77
Range,
88
Position,
9-
ProviderResult,
109
} from 'vscode';
1110

1211
import { SourceHelper, ExtractedTemplateLiteral } from '../helpers/source';
1312
import capitalize from 'capitalize';
13+
import { GraphQLContentProvider } from './exec-content';
1414

1515
export class GraphQLCodeLensProvider implements CodeLensProvider {
1616
outputChannel: OutputChannel;
1717
sourceHelper: SourceHelper;
18+
contentProvider?: GraphQLContentProvider;
1819

1920
constructor(outputChannel: OutputChannel) {
2021
this.outputChannel = outputChannel;
2122
this.sourceHelper = new SourceHelper(this.outputChannel);
2223
}
2324

24-
public provideCodeLenses(
25+
public async provideCodeLenses(
2526
document: TextDocument,
2627
_token: CancellationToken,
2728
// for some reason, ProviderResult<CodeLens[]> doesn't work here
2829
// anymore after upgrading types
29-
): ProviderResult<[]> {
30+
): Promise<CodeLens[]> {
31+
this.contentProvider = new GraphQLContentProvider(
32+
document.uri,
33+
this.outputChannel,
34+
// @ts-expect-error
35+
{ uri: document.uri.fsPath },
36+
);
37+
await this.contentProvider.loadConfig();
38+
if (!this.contentProvider.hasConfig || !await this.contentProvider.loadEndpoint()) {
39+
return [];
40+
}
3041
const literals: ExtractedTemplateLiteral[] =
3142
this.sourceHelper.extractAllTemplateLiterals(document, [
3243
'gql',
@@ -47,6 +58,6 @@ export class GraphQLCodeLensProvider implements CodeLensProvider {
4758
);
4859
});
4960

50-
return results as ProviderResult<[]>;
61+
return results;
5162
}
5263
}

packages/vscode-graphql-execution/src/providers/exec-content.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
3131
private outputChannel: OutputChannel;
3232
private networkHelper: NetworkHelper;
3333
private sourceHelper: SourceHelper;
34-
private panel: WebviewPanel;
34+
private panel?: WebviewPanel;
3535
private rootDir: WorkspaceFolder | undefined;
3636
private literal: ExtractedTemplateLiteral;
3737
private _projectConfig: GraphQLProjectConfig | undefined;
@@ -48,7 +48,13 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
4848
}
4949

5050
updatePanel() {
51-
this.panel.webview.html = this.html;
51+
if (this.panel) {
52+
this.panel.webview.html = this.html;
53+
}
54+
}
55+
56+
public get hasConfig() {
57+
return Boolean(this._projectConfig);
5258
}
5359

5460
async getVariablesFromUser(
@@ -96,7 +102,7 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
96102
uri: Uri,
97103
outputChannel: OutputChannel,
98104
literal: ExtractedTemplateLiteral,
99-
panel: WebviewPanel,
105+
panel?: WebviewPanel,
100106
) {
101107
this.uri = uri;
102108
this.outputChannel = outputChannel;
@@ -106,16 +112,17 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
106112
this.sourceHelper,
107113
);
108114
this.panel = panel;
115+
109116
this.rootDir = workspace.getWorkspaceFolder(Uri.file(literal.uri));
110117
this.literal = literal;
111-
this.panel.webview.options = {
112-
enableScripts: true,
113-
};
118+
if (this.panel) {
119+
this.panel.webview.options = {
120+
enableScripts: true,
121+
};
122+
}
114123

115124
// eslint-disable-next-line promise/prefer-await-to-then -- can't use async in constructor
116-
this.loadProvider().catch(err => {
117-
this.html = err.toString();
118-
});
125+
119126
}
120127

121128
validUrlFromSchema(pathOrUrl: string) {
@@ -169,7 +176,6 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
169176
}
170177
}
171178
const endpointNames = Object.keys(endpoints);
172-
173179
if (endpointNames.length === 0) {
174180
this.reportError(
175181
'Error: endpoint data missing from graphql config endpoints extension',
@@ -247,12 +253,12 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
247253
}
248254

249255
async loadConfig() {
256+
250257
const { rootDir, literal } = this;
251258
if (!rootDir) {
252259
this.reportError('Error: this file is outside the workspace.');
253260
return;
254261
}
255-
256262
const config = await loadConfig({
257263
rootDir: rootDir.uri.fsPath,
258264
throwOnEmpty: false,

0 commit comments

Comments
 (0)