feat(editor): keep semicolon-gap execution on previous statement

This commit is contained in:
t8y2 2026-06-24 22:28:21 +08:00
parent 15546387db
commit 0e2d0b0413
3 changed files with 30 additions and 1 deletions

View File

@ -108,10 +108,24 @@ describe("statementRangeAtCursor", () => {
expect(range?.sql.trim()).toBe("SELECT 2");
});
it("returns the next same-line statement when the cursor is in whitespace before it", () => {
it("returns the previous statement when the cursor is in same-line whitespace after its semicolon", () => {
const sql = "SELECT 1; SELECT 2;";
const gapPos = sql.indexOf(";") + 2;
const range = statementRangeAtCursor(sql, gapPos);
expect(range?.sql.trim()).toBe("SELECT 1");
});
it("returns the previous statement when the cursor is just after its semicolon before a later statement", () => {
const sql = "SELECT *\nFROM system_dept;\n\nSELECT *\nFROM sys;";
const gapPos = sql.indexOf(";") + 1;
const range = statementRangeAtCursor(sql, gapPos);
expect(range?.sql.trim()).toBe("SELECT *\nFROM system_dept");
});
it("returns the next same-line statement when the cursor is inside it", () => {
const sql = "SELECT 1; SELECT 2;";
const pos = indexOf(sql, "SELECT 2") + 1;
const range = statementRangeAtCursor(sql, pos);
expect(range?.sql.trim()).toBe("SELECT 2");
});

View File

@ -308,6 +308,11 @@ export function statementRangeAtCursor(sql: string, cursorPos: number, databaseT
// the statement should still target that statement, while the returned
// execution range remains tight around the SQL text itself.
if (pos >= statement.hitFrom && pos < statement.from && sql.slice(pos, statement.from).trim() === "") {
const previous = statements[index - 1];
if (previous && isCursorInSameLineDelimiterGap(sql, previous.to, pos)) {
const previousSoftRanges = splitStatementRangeAtSoftStarts(sql, previous, databaseType);
return rangeForCursorInSoftRanges(sql, previousSoftRanges, pos) ?? rangeFor(previous, sql);
}
return rangeForCursorInSoftRanges(sql, softRanges, pos) ?? rangeFor(statement, sql);
}
@ -320,6 +325,15 @@ export function statementRangeAtCursor(sql: string, cursorPos: number, databaseT
return null;
}
function isCursorInSameLineDelimiterGap(sql: string, previousStatementEnd: number, cursorPos: number): boolean {
if (cursorPos <= previousStatementEnd) return false;
const between = sql.slice(previousStatementEnd, cursorPos);
const delimiterIndex = between.lastIndexOf(";");
if (delimiterIndex === -1) return false;
const afterDelimiter = between.slice(delimiterIndex + 1);
return !afterDelimiter.includes("\n") && between.slice(0, delimiterIndex).trim() === "" && afterDelimiter.trim() === "";
}
function rangeForCursorInSoftRanges(sql: string, ranges: RawStatement[], pos: number): SqlTextRange | null {
for (let index = 0; index < ranges.length; index += 1) {
const range = ranges[index];

View File

@ -986,6 +986,7 @@ WHERE u.id = picked.id;
database_type: Some(DatabaseType::Postgres),
pagination: QueryPagination { limit: 100, offset: 0, session_id: None },
use_agent_cursor: false,
first_page_uses_actual_sql: false,
});
assert_eq!(plan.sql_to_execute, sql);