Skip to content

Commit e8d3198

Browse files
committed
fix(web-console): update query extracting logic from cursor
1 parent 53788d2 commit e8d3198

5 files changed

Lines changed: 153 additions & 26 deletions

File tree

packages/browser-tests/cypress/commands.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ Cypress.Commands.add("getGridRow", (n) =>
103103
cy.get(".qg-r").filter(":visible").eq(n)
104104
);
105105

106+
Cypress.Commands.add("getColumnName", (n) => {
107+
cy.get(".qg-header-name").filter(":visible").eq(n);
108+
})
109+
106110
Cypress.Commands.add("getGridCol", (n) =>
107111
cy.get(".qg-c").filter(":visible").eq(n)
108112
);
@@ -132,6 +136,12 @@ Cypress.Commands.add("runLineWithResponse", (response) => {
132136
cy.wait("@exec");
133137
});
134138

139+
Cypress.Commands.add("clickLine", (n) => {
140+
cy.get(".monaco-editor .view-line")
141+
.eq(n - 1)
142+
.click();
143+
});
144+
135145
Cypress.Commands.add("clickRun", () => {
136146
cy.intercept("/exec*").as("exec");
137147
return cy.get("button").contains("Run").click().wait("@exec");

packages/browser-tests/cypress/integration/console/editor.spec.js

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,71 @@ const baseUrl = `http://localhost:9999${contextPath}`;
66
const getTabDragHandleByTitle = (title) =>
77
`.chrome-tab[data-tab-title="${title}"] .chrome-tab-drag-handle`;
88

9+
describe("run query", () => {
10+
beforeEach(() => {
11+
cy.loadConsoleWithAuth();
12+
cy.getEditorContent().should("be.visible");
13+
cy.clearEditor();
14+
});
15+
16+
it("should correctly run query in the first line", () => {
17+
cy.typeQuery("select 1;\n\nselect 2;");
18+
cy.clickLine(1);
19+
cy.clickRun();
20+
cy.getGridRow(0).should("contain", "1");
21+
});
22+
23+
it("should run the correct query when there are multiple queries in the same line", () => {
24+
cy.typeQuery(
25+
"with longseq as (\nselect * from long_sequence(100)\n-- comment"
26+
);
27+
cy.typeQuery(" select count(*) from longseq;select 1;");
28+
29+
// go to the end of second query
30+
cy.clickLine(4);
31+
cy.clickRun();
32+
cy.getGridCol(0).should("contain", "1");
33+
cy.getGridRow(0).should("contain", "1");
34+
35+
// go inside the second query
36+
cy.clickLine(4);
37+
cy.realPress("ArrowLeft");
38+
cy.realPress("ArrowLeft");
39+
cy.clickRun();
40+
cy.getColumnName(0).should("contain", "1");
41+
cy.getGridRow(0).should("contain", "1");
42+
43+
// go to the end of first query
44+
cy.clickLine(4);
45+
for (let i = 0; i < 9; i++) {
46+
cy.realPress("ArrowLeft");
47+
}
48+
cy.clickRun();
49+
cy.getColumnName(0).should("contain", "count");
50+
cy.getGridRow(0).should("contain", "100");
51+
52+
// go inside the first query
53+
cy.clickLine(4);
54+
for (let i = 0; i < 11; i++) {
55+
cy.realPress("ArrowLeft");
56+
}
57+
cy.clickRun();
58+
cy.getColumnName(0).should("contain", "count");
59+
cy.getGridRow(0).should("contain", "100");
60+
});
61+
62+
it("should not suggest any query for running if the cursor is in an empty line between queries", () => {
63+
cy.typeQuery("select 1;\n\nselect 2;");
64+
cy.getCursorQueryGlyph().should("be.visible");
65+
66+
cy.realPress("ArrowUp");
67+
cy.getCursorQueryGlyph().should("not.exist");
68+
69+
cy.realPress("ArrowUp");
70+
cy.getCursorQueryGlyph().should("be.visible");
71+
});
72+
});
73+
974
describe("appendQuery", () => {
1075
const consoleConfiguration = {
1176
savedQueries: [
@@ -158,6 +223,7 @@ describe("&query URL param", () => {
158223
it("should not append query if it already exists in editor", () => {
159224
const query = "select x\nfrom long_sequence(1);\n\n-- a\n-- b\n-- c";
160225
cy.typeQuery(query);
226+
cy.clickLine(1);
161227
cy.clickRun();
162228
cy.visit(`${baseUrl}?query=${encodeURIComponent(query)}&executeQuery=true`);
163229
cy.getEditorContent().should("be.visible");
@@ -167,7 +233,6 @@ describe("&query URL param", () => {
167233
it("should append query and scroll to it", () => {
168234
cy.typeQuery("select x from long_sequence(1);");
169235
cy.typeQuery("\n".repeat(20));
170-
cy.clickRun(); // take space so that query is not visible later, save by running
171236

172237
const appendedQuery = "-- hello world";
173238
cy.visit(`${baseUrl}?query=${encodeURIComponent(appendedQuery)}`);
@@ -396,7 +461,7 @@ describe("running query with F9", () => {
396461
"select * from long_sequence(1); -- comment\nselect * from long_sequence(2);{upArrow}{rightArrow}{rightArrow}"
397462
);
398463
cy.F9();
399-
cy.getGridRows().should("have.length", 2);
464+
cy.getGridRows().should("have.length", 1);
400465
cy.getCursorQueryDecoration().should("have.length", 1);
401466
});
402467
});
@@ -487,7 +552,8 @@ describe("editor tabs", () => {
487552
});
488553
});
489554

490-
describe("editor tabs history", () => {
555+
// TODO: This test is flaky because of the IndexedDB calls. Investigate the slow response time in test environment.
556+
describe.skip("editor tabs history", () => {
491557
before(() => {
492558
cy.loadConsoleWithAuth();
493559
cy.getEditorContent().should("be.visible");

packages/browser-tests/questdb

Submodule questdb updated 45 files

packages/web-console/src/scenes/Editor/Monaco/index.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ const MonacoEditor = () => {
185185
editor: editor.IStandaloneCodeEditor,
186186
) => {
187187
const queryAtCursor = getQueryFromCursor(editor)
188+
189+
if (!queryAtCursor) {
190+
decorationsRef.current?.clear()
191+
return
192+
}
193+
188194
const model = editor.getModel()
189195
if (queryAtCursor && model !== null) {
190196
const activeBufferId = activeBufferRef.current.id as number

packages/web-console/src/scenes/Editor/Monaco/utils.ts

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,15 @@ export const getQueryFromCursor = (
7070
let startRow = 0
7171
let startCol = 0
7272
let startPos = -1
73-
let sql = null
73+
let nextSql = null
7474
let inQuote = false
7575

7676
if (!position) return
7777

78-
for (let i = 0; i < text.length; i++) {
79-
if (sql !== null) {
78+
let i = 0;
79+
80+
for (; i < text.length; i++) {
81+
if (nextSql !== null) {
8082
break
8183
}
8284

@@ -97,6 +99,8 @@ export const getQueryFromCursor = (
9799
row: startRow,
98100
col: startCol,
99101
position: startPos,
102+
endRow: row,
103+
endCol: column,
100104
limit: i,
101105
})
102106
startRow = row
@@ -105,7 +109,14 @@ export const getQueryFromCursor = (
105109
column++
106110
} else {
107111
// empty queries, aka ;; , make sql.length === 0
108-
sql = text.substring(startPos === -1 ? 0 : startPos, i)
112+
nextSql = {
113+
row: startRow,
114+
col: startCol,
115+
position: startPos,
116+
endRow: row,
117+
endCol: column,
118+
limit: i,
119+
}
109120
}
110121
break
111122
}
@@ -130,7 +141,6 @@ export const getQueryFromCursor = (
130141
startRow = row
131142
startCol = column
132143
startPos = i + 1
133-
column++
134144
}
135145
break
136146
}
@@ -148,28 +158,63 @@ export const getQueryFromCursor = (
148158
}
149159
}
150160

151-
if (sql === null) {
152-
sql = startPos === -1 ? text : text.substring(startPos)
161+
// lastStackItem is the last query that is completed before the current cursor position.
162+
// nextSql is the next query that is not completed before the current cursor position, or started after the current cursor position.
163+
const normalizedCurrentRow = position.lineNumber - 1
164+
const lastStackItem = sqlTextStack.length > 0 ? sqlTextStack[sqlTextStack.length - 1] : null
165+
166+
if (!nextSql) {
167+
const sqlText = startPos === - 1 ? text : text.substring(startPos)
168+
if (sqlText.length > 0) {
169+
nextSql = {
170+
row: startRow,
171+
col: startCol,
172+
position: startPos === -1 ? 0 : startPos,
173+
endRow: row,
174+
endCol: column,
175+
limit: i,
176+
}
177+
}
153178
}
154179

155-
if (sql.length === 0) {
156-
const prev = sqlTextStack.pop()
157-
158-
if (prev) {
180+
const lastStackItemRowRange = lastStackItem ? {
181+
start: lastStackItem.row,
182+
end: lastStackItem.endRow,
183+
} : null
184+
const nextSqlRowRange = nextSql ? {
185+
start: nextSql.row,
186+
end: nextSql.endRow,
187+
} : null
188+
const isInLastStackItemRowRange = lastStackItemRowRange && normalizedCurrentRow >= lastStackItemRowRange.start && normalizedCurrentRow <= lastStackItemRowRange.end
189+
const isInNextSqlRowRange = nextSqlRowRange && normalizedCurrentRow >= nextSqlRowRange.start && normalizedCurrentRow <= nextSqlRowRange.end
190+
191+
if (isInLastStackItemRowRange && !isInNextSqlRowRange) {
192+
return {
193+
query: text.substring(lastStackItem!.position, lastStackItem!.limit),
194+
row: lastStackItem!.row,
195+
column: lastStackItem!.col,
196+
}
197+
} else if (isInNextSqlRowRange && !isInLastStackItemRowRange) {
198+
return {
199+
query: text.substring(nextSql!.position, nextSql!.limit),
200+
row: nextSql!.row,
201+
column: nextSql!.col,
202+
}
203+
} else if (isInLastStackItemRowRange && isInNextSqlRowRange) {
204+
const lastStackItemEndCol = lastStackItem!.endCol
205+
const normalizedCurrentCol = position.column - 1
206+
if (normalizedCurrentCol > lastStackItemEndCol + 1) {
159207
return {
160-
column: prev.col,
161-
query: text.substring(prev.position, prev.limit),
162-
row: prev.row,
208+
query: text.substring(nextSql!.position, nextSql!.limit),
209+
row: nextSql!.row,
210+
column: nextSql!.col,
163211
}
164212
}
165-
166-
return
167-
}
168-
169-
return {
170-
column: startCol,
171-
query: sql,
172-
row: startRow,
213+
return {
214+
query: text.substring(lastStackItem!.position, lastStackItem!.limit),
215+
row: lastStackItem!.row,
216+
column: lastStackItem!.col,
217+
}
173218
}
174219
}
175220

0 commit comments

Comments
 (0)