feat(query-editor): 增强 SQL 编辑器执行与历史体验

- 支持仅执行选中 SQL、光标所在语句和增量新增语句

- 持久化查询草稿,避免重启后丢失历史 SQL

- 在表字段提示中展示注释信息

- 修复清空默认 SQL 后被自动回填的问题

Refs #483
This commit is contained in:
Syngnat
2026-05-23 17:07:47 +08:00
parent 09af56b1c2
commit b9c743d67e
20 changed files with 1431 additions and 119 deletions

View File

@@ -4,6 +4,13 @@ export interface SqlStatementRange {
text: string;
}
export type SqlExecutionSelectionSource = 'selection' | 'statement' | 'line';
export interface SqlExecutionSelection {
sql: string;
source: SqlExecutionSelectionSource;
}
const isWhitespace = (ch: string): boolean => (
ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r'
);
@@ -157,3 +164,38 @@ export const resolveCurrentSqlStatementRange = (sql: string, cursorOffset: numbe
return ranges[ranges.length - 1];
};
export const resolveExecutableSql = (
sql: string,
cursorOffset: number,
selectedSql = '',
): SqlExecutionSelection | null => {
const selected = String(selectedSql || '').trim();
if (selected) {
return { sql: selectedSql, source: 'selection' };
}
const text = String(sql || '').replace(/\r\n/g, '\n');
const offset = Math.max(0, Math.min(text.length, Number.isFinite(cursorOffset) ? cursorOffset : 0));
const ranges = findSqlStatementRanges(text);
const statement = ranges.find((range) => offset >= range.start && offset <= range.end);
if (statement?.text.trim()) {
return { sql: statement.text, source: 'statement' };
}
const lineStart = text.lastIndexOf('\n', Math.max(0, offset - 1)) + 1;
const nextLineBreak = text.indexOf('\n', offset);
const lineEnd = nextLineBreak === -1 ? text.length : nextLineBreak;
const line = text.slice(lineStart, lineEnd).trim();
if (line) {
const lineStatement = [...ranges].reverse().find((range) => range.start < lineEnd && range.end >= lineStart);
if (lineStatement?.text.trim()) {
return { sql: lineStatement.text, source: 'statement' };
}
}
if (line) {
return { sql: line, source: 'line' };
}
return null;
};