Skip to content

Commit 4107972

Browse files
committed
Fix rendering of generate query code lens
1 parent 64d4926 commit 4107972

File tree

2 files changed

+122
-75
lines changed

2 files changed

+122
-75
lines changed

firebase-vscode/src/data-connect/code-lens-provider.ts

Lines changed: 83 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -72,84 +72,88 @@ export class OperationCodeLensProvider extends ComputedCodeLensProvider {
7272
): vscode.CodeLens[] {
7373
// Wait for configs to be loaded and emulator to be running
7474
const fdcConfigs = this.watch(dataConnectConfigs)?.tryReadValue;
75-
const projectId = this.watch(firebaseRC)?.tryReadValue?.projects.default;
7675
if (!fdcConfigs) {
7776
return [];
7877
}
79-
80-
const codeLenses: vscode.CodeLens[] = [];
78+
const projectId = this.watch(firebaseRC)?.tryReadValue?.projects.default;
8179

8280
const documentText = document.getText();
83-
// TODO: replace w/ online-parser to work with malformed documents
8481
const documentNode = parse(documentText);
82+
const operations = [];
83+
for (const def of documentNode.definitions) {
84+
switch (def.kind) {
85+
case Kind.OPERATION_DEFINITION:
86+
operations.push(def);
87+
break;
88+
case Kind.FRAGMENT_DEFINITION:
89+
break;
90+
default:
91+
// No code lenses for schema files
92+
return [];
93+
}
94+
}
8595

86-
for (let i = 0; i < documentNode.definitions.length; i++) {
87-
const x = documentNode.definitions[i];
88-
if (x.kind === Kind.OPERATION_DEFINITION && x.loc) {
89-
// startToken.line is 1-indexed, range is 0-indexed
90-
const line = x.loc.startToken.line - 1;
91-
const range = new vscode.Range(line, 0, line, 0);
92-
const position = new vscode.Position(line, 0);
93-
const service = fdcConfigs.findEnclosingServiceForPath(document.fileName);
94-
if (service) {
95-
{
96-
const arg: ExecutionInput = {
97-
operationAst: x,
98-
document: documentText,
99-
documentPath: document.fileName,
100-
position: position,
101-
instance: InstanceType.LOCAL,
102-
};
103-
codeLenses.push(
104-
new vscode.CodeLens(range, {
105-
title: `$(play) Run (local)`,
106-
command: "firebase.dataConnect.executeOperation",
107-
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
108-
arguments: [arg],
109-
}),
110-
);
111-
}
96+
const codeLenses: vscode.CodeLens[] = [];
97+
for (let i = 0; i < operations.length; i++) {
98+
const x = operations[i];
99+
// startToken.line is 1-indexed, range is 0-indexed
100+
const line = x.loc!.startToken.line - 1;
101+
const range = new vscode.Range(line, 0, line, 0);
102+
const position = new vscode.Position(line, 0);
103+
const service = fdcConfigs.findEnclosingServiceForPath(document.fileName);
104+
if (service) {
105+
{
106+
const arg: ExecutionInput = {
107+
operationAst: x,
108+
document: documentText,
109+
documentPath: document.fileName,
110+
position: position,
111+
instance: InstanceType.LOCAL,
112+
};
113+
codeLenses.push(
114+
new vscode.CodeLens(range, {
115+
title: `$(play) Run (local)`,
116+
command: "firebase.dataConnect.executeOperation",
117+
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
118+
arguments: [arg],
119+
}),
120+
);
121+
}
112122

113-
if (projectId) {
114-
const arg: ExecutionInput = {
115-
operationAst: x,
116-
document: documentText,
117-
documentPath: document.fileName,
118-
position: position,
119-
instance: InstanceType.PRODUCTION,
120-
};
121-
codeLenses.push(
122-
new vscode.CodeLens(range, {
123-
title: `$(play) Run (Production – Project: ${projectId})`,
124-
command: "firebase.dataConnect.executeOperation",
125-
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
126-
arguments: [arg],
127-
}),
128-
);
129-
}
123+
if (projectId) {
124+
const arg: ExecutionInput = {
125+
operationAst: x,
126+
document: documentText,
127+
documentPath: document.fileName,
128+
position: position,
129+
instance: InstanceType.PRODUCTION,
130+
};
131+
codeLenses.push(
132+
new vscode.CodeLens(range, {
133+
title: `$(play) Run (Production – Project: ${projectId})`,
134+
command: "firebase.dataConnect.executeOperation",
135+
tooltip: "Execute the operation (⌘+enter or Ctrl+Enter)",
136+
arguments: [arg],
137+
}),
138+
);
130139
}
131140
}
132141
}
133142

134-
const comments = findCommentsBlocks(documentText);
143+
const comments = findCommentsBlocks(documentText, operations);
135144
for (let i = 0; i < comments.length; i++) {
136145
const c = comments[i];
137146
const range = new vscode.Range(c.startLine, 0, c.startLine, 0);
138-
const queryDoc = documentNode.definitions.find((d) =>
139-
d.kind === Kind.OPERATION_DEFINITION &&
140-
// startToken.line is 1-indexed, endLine is 0-indexed
141-
d.loc?.startToken.line === c.endLine + 2
142-
);
143147
const arg: GenerateOperationInput = {
144148
projectId,
145149
document: document,
146150
description: c.text,
147151
insertPosition: c.endIndex + 1,
148-
existingQuery: queryDoc?.loc ? documentText.substring(c.endIndex + 1, queryDoc.loc.endToken.end) : '',
152+
existingQuery: c.queryDoc?.loc ? documentText.substring(c.endIndex + 1, c.queryDoc.loc.endToken.end) : '',
149153
};
150154
codeLenses.push(
151155
new vscode.CodeLens(range, {
152-
title: queryDoc ? `$(sparkle) Refine Operation` : `$(sparkle) Generate Operation`,
156+
title: c.queryDoc ? `$(sparkle) Refine Operation` : `$(sparkle) Generate Operation`,
153157
command: "firebase.dataConnect.generateOperation",
154158
tooltip: "Generate the operation (⌘+enter or Ctrl+Enter)",
155159
arguments: [arg],
@@ -195,29 +199,37 @@ export class SchemaCodeLensProvider extends ComputedCodeLensProvider {
195199
// );
196200
// }
197201

198-
codeLenses.push(
199-
new vscode.CodeLens(range, {
200-
title: `$(database) Add data`,
201-
command: "firebase.dataConnect.schemaAddData",
202-
tooltip: "Generate a mutation to add data of this type",
203-
arguments: [x, documentPath],
204-
}),
205-
);
202+
const isTable = x.directives?.some((d) => d.name.value === "table");
203+
const isView = x.directives?.some((d) => d.name.value === "view");
206204

207-
codeLenses.push(
208-
new vscode.CodeLens(range, {
209-
title: `$(database) Read data`,
210-
command: "firebase.dataConnect.schemaReadData",
211-
tooltip: "Generate a query to read data of this type",
212-
arguments: [documentNode, x, documentPath],
213-
}),
214-
);
205+
if (isTable) {
206+
codeLenses.push(
207+
new vscode.CodeLens(range, {
208+
title: `$(database) Add data`,
209+
command: "firebase.dataConnect.schemaAddData",
210+
tooltip: "Generate a mutation to add data of this type",
211+
arguments: [x, documentPath],
212+
}),
213+
);
214+
}
215+
216+
if (isTable || isView) {
217+
codeLenses.push(
218+
new vscode.CodeLens(range, {
219+
title: `$(database) Read data`,
220+
command: "firebase.dataConnect.schemaReadData",
221+
tooltip: "Generate a query to read data of this type",
222+
arguments: [documentNode, x, documentPath],
223+
}),
224+
);
225+
}
215226
}
216227
}
217228

218229
return codeLenses;
219230
}
220231
}
232+
221233
/**
222234
* CodeLensProvider for Configure SDK in Connector.yaml
223235
*/

firebase-vscode/src/utils/find_comments.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
1+
import { OperationDefinitionNode } from "graphql";
2+
13
interface Comment {
24
text: string;
35
startLine: number;
46
endLine: number;
57
endIndex: number;
8+
queryDoc?: OperationDefinitionNode;
69
}
710

8-
export function findCommentsBlocks(text: string): Comment[] {
11+
export function findCommentsBlocks(text: string, operations: OperationDefinitionNode[]): Comment[] {
12+
// Find all line endings
913
const lineEnds: number[] = [];
1014
let searchIndex: number = -1;
1115
while ((searchIndex = text.indexOf('\n', searchIndex + 1)) !== -1) {
1216
lineEnds.push(searchIndex);
1317
}
1418
lineEnds.push(text.length);
19+
20+
// Find all lines that start with comments.
1521
const comments: Comment[] = [];
1622
for (let i = 0; i < lineEnds.length; i++) {
1723
const lineStart = i === 0 ? 0 : lineEnds[i - 1] + 1;
@@ -20,17 +26,46 @@ export function findCommentsBlocks(text: string): Comment[] {
2026
comments.push({ startLine: i, endLine: i, text: lineText.substring(1).trim(), endIndex: lineEnds[i] });
2127
}
2228
}
29+
30+
// Filter out comments that are inside operations
31+
const commentsOutsideOperations: Comment[] = [];
32+
let j = 0;
33+
for (let i = 0; i < operations.length; i++) {
34+
const loc = operations[i].loc!;
35+
const opStartLine = loc.startToken.line - 1;
36+
const opEndLine = loc.endToken.line - 1;
37+
for (; j < comments.length; j++) {
38+
const c = comments[j];
39+
if (c.endLine > opEndLine) {
40+
break;
41+
}
42+
if (c.endLine < opStartLine) {
43+
commentsOutsideOperations.push(c);
44+
if (c.endLine + 1 === opStartLine) {
45+
c.queryDoc = operations[i];
46+
}
47+
} else {
48+
// Ignore comments inside operation
49+
}
50+
}
51+
}
52+
for (; j < comments.length; j++) {
53+
commentsOutsideOperations.push(comments[j]);
54+
}
55+
56+
// Combine consecutive comment lines into multi-line blocks.
2357
const commentBlocks: Comment[] = [];
24-
for (let i = 0; i < comments.length; i++) {
25-
const current = comments[i];
26-
if (i === 0 || current.startLine > comments[i - 1].endLine + 1) {
58+
for (let i = 0; i < commentsOutsideOperations.length; i++) {
59+
const current = commentsOutsideOperations[i];
60+
if (i === 0 || current.startLine > commentsOutsideOperations[i - 1].endLine + 1) {
2761
commentBlocks.push({ ...current });
2862
} else {
2963
// Continuation of the previous block
3064
const lastBlock = commentBlocks[commentBlocks.length - 1];
3165
lastBlock.endLine = current.endLine;
3266
lastBlock.endIndex = current.endIndex;
3367
lastBlock.text += '\n' + current.text;
68+
lastBlock.queryDoc = current.queryDoc;
3469
}
3570
}
3671
return commentBlocks;

0 commit comments

Comments
 (0)