Skip to content

Commit 186c75a

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

File tree

4 files changed

+89
-35
lines changed

4 files changed

+89
-35
lines changed

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

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,30 +42,31 @@ 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+
6162
// }
6263

6364
let commandContentProvider: GraphQLContentProvider;
6465

6566
const registerContentProvider = () => {
66-
return commands.registerCommand(
67+
const provider = commands.registerCommand(
6768
'vscode-graphql-execution.contentProvider',
68-
(literal: ExtractedTemplateLiteral) => {
69+
async (literal: ExtractedTemplateLiteral) => {
6970
const uri = Uri.parse('graphql://authority/graphql');
7071

7172
const panel = window.createWebviewPanel(
@@ -81,6 +82,7 @@ export function activate(context: ExtensionContext) {
8182
literal,
8283
panel,
8384
);
85+
await commandContentProvider.loadProvider();
8486
const registration = workspace.registerTextDocumentContentProvider(
8587
'graphql',
8688
commandContentProvider,
@@ -89,23 +91,33 @@ export function activate(context: ExtensionContext) {
8991
panel.webview.html = commandContentProvider.getCurrentHtml();
9092
},
9193
);
94+
context.subscriptions.push(provider);
95+
return provider;
9296
};
9397

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

97100
// workspace.onDidChangeConfiguration(async () => {
98101
// // const newSettings = workspace.getConfiguration("vscode-graphql-execution")
99102
// // if (newSettings.showExecCodeLens !== false) {
100-
// commandContentProvider.dispose()
103+
// provider.dispose();
101104
// // }
102105
// });
106+
103107
workspace.onDidSaveTextDocument(async e => {
104108
if (
105109
e.fileName.includes('graphql.config') ||
106110
e.fileName.includes('graphqlrc')
107111
) {
108-
await commandContentProvider.loadConfig();
112+
if (contentProvider) {
113+
await contentProvider.dispose();
114+
registerContentProvider();
115+
}
116+
117+
if (codeLensProvider) {
118+
await codeLensProvider.dispose();
119+
registerCodeLens();
120+
}
109121
}
110122
});
111123
}

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

Lines changed: 26 additions & 0 deletions
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');
201+
// https://regex101.com/r/FvG9qc/1
178202

179203
let result: RegExpExecArray | null;
204+
180205
while ((result = regExpGQL.exec(text)) !== null) {
181206
const contents = result[1];
182207

@@ -199,6 +224,7 @@ export class SourceHelper {
199224
const operations = ast.definitions.filter(
200225
def => def.kind === 'OperationDefinition',
201226
);
227+
202228
for (const operation of operations) {
203229
const op = operation as any;
204230
const filteredAst = {

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

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,41 @@ 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 (
39+
!this.contentProvider.hasConfig ||
40+
!(await this.contentProvider.loadEndpoint())
41+
) {
42+
return [];
43+
}
3044
const literals: ExtractedTemplateLiteral[] =
3145
this.sourceHelper.extractAllTemplateLiterals(document, [
3246
'gql',
@@ -47,6 +61,6 @@ export class GraphQLCodeLensProvider implements CodeLensProvider {
4761
);
4862
});
4963

50-
return results as ProviderResult<[]>;
64+
return results;
5165
}
5266
}

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

Lines changed: 15 additions & 13 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,14 @@ 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-
};
114-
115-
// 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-
});
118+
if (this.panel) {
119+
this.panel.webview.options = {
120+
enableScripts: true,
121+
};
122+
}
119123
}
120124

121125
validUrlFromSchema(pathOrUrl: string) {
@@ -169,7 +173,6 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
169173
}
170174
}
171175
const endpointNames = Object.keys(endpoints);
172-
173176
if (endpointNames.length === 0) {
174177
this.reportError(
175178
'Error: endpoint data missing from graphql config endpoints extension',
@@ -252,7 +255,6 @@ export class GraphQLContentProvider implements TextDocumentContentProvider {
252255
this.reportError('Error: this file is outside the workspace.');
253256
return;
254257
}
255-
256258
const config = await loadConfig({
257259
rootDir: rootDir.uri.fsPath,
258260
throwOnEmpty: false,

0 commit comments

Comments
 (0)