fix(editor): keep select column suggestions active
This commit is contained in:
parent
df94a7c1bf
commit
9c116e8f7e
|
|
@ -1547,6 +1547,9 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
|
|||
const prioritizeSelectAliases = isInOrderOrGroupByContext(beforeCursor);
|
||||
const inCallRoutineContext = isCallRoutineContext(beforeCursor);
|
||||
const inPotentialPackageMemberContext = !!qualifier && !exclusiveTableSuggestions && !insertInfo && !oracleTableFunctionContext;
|
||||
const suggestColumns = !!qualifier || !!updateInfo?.inSetClause || (inColumnContext && referencedTables.length > 0);
|
||||
const preferColumnsOverGlobalRoutines = suggestColumns && referencedTables.length > 0 && !qualifier;
|
||||
const suggestRoutines = inCallRoutineContext || oracleTableFunctionContext || inPotentialPackageMemberContext || (!preferColumnsOverGlobalRoutines && !exclusiveTableSuggestions && !exclusiveColumnSuggestions && !insertInfo && prefix.length >= 2);
|
||||
|
||||
const statementKind = detectStatementKind(beforeCursor || fullStatement);
|
||||
const preferredKeywords = preferredKeywordsForCompletion(updateInfo, deleteInfo);
|
||||
|
|
@ -1556,9 +1559,9 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
|
|||
qualifier: insertInfo ? undefined : qualifier,
|
||||
qualifierParts: insertInfo ? undefined : qualifierParts,
|
||||
suggestTables: insertInfo ? false : afterTableTrigger,
|
||||
suggestColumns: !!qualifier || !!updateInfo?.inSetClause || (inColumnContext && referencedTables.length > 0),
|
||||
suggestColumns,
|
||||
suggestKeywords: !exclusiveTableSuggestions && !exclusiveColumnSuggestions && !insertInfo && !inCallRoutineContext,
|
||||
suggestRoutines: inCallRoutineContext || oracleTableFunctionContext || inPotentialPackageMemberContext || (!exclusiveTableSuggestions && !exclusiveColumnSuggestions && !insertInfo && prefix.length >= 2),
|
||||
suggestRoutines,
|
||||
suggestJoinConditions: insertInfo ? false : inJoinConditionContext && referencedTables.length >= 2,
|
||||
exclusiveTableSuggestions: insertInfo ? false : exclusiveTableSuggestions,
|
||||
exclusiveColumnSuggestions: exclusiveColumnSuggestions || !!insertInfo || !!updateInfo?.inSetClause,
|
||||
|
|
@ -1667,6 +1670,8 @@ function parseTrailingIdentifierPart(input: string, endExclusive: number): { sta
|
|||
function isInColumnContext(beforeCursor: string): boolean {
|
||||
if (!beforeCursor) return false;
|
||||
|
||||
if (isInSelectListContext(beforeCursor)) return true;
|
||||
|
||||
// Strip string literals
|
||||
const cleaned = beforeCursor.replace(/'[^']*'/g, "''").replace(/"[^"]*"/g, "''");
|
||||
|
||||
|
|
@ -1692,6 +1697,77 @@ function isInColumnContext(beforeCursor: string): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
function isInSelectListContext(beforeCursor: string): boolean {
|
||||
let depth = 0;
|
||||
let inSingleQuote = false;
|
||||
let inDoubleQuote = false;
|
||||
let inBacktick = false;
|
||||
const selectOpenByDepth = new Map<number, boolean>();
|
||||
|
||||
for (let i = 0; i < beforeCursor.length; i++) {
|
||||
const ch = beforeCursor[i] ?? "";
|
||||
const next = beforeCursor[i + 1] ?? "";
|
||||
|
||||
if (inSingleQuote) {
|
||||
if (ch === "\\" && next) {
|
||||
i++;
|
||||
} else if (ch === "'" && next === "'") {
|
||||
i++;
|
||||
} else if (ch === "'") {
|
||||
inSingleQuote = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inDoubleQuote) {
|
||||
if (ch === '"' && next === '"') {
|
||||
i++;
|
||||
} else if (ch === '"') {
|
||||
inDoubleQuote = false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (inBacktick) {
|
||||
if (ch === "`") inBacktick = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "'") {
|
||||
inSingleQuote = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === '"') {
|
||||
inDoubleQuote = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === "`") {
|
||||
inBacktick = true;
|
||||
continue;
|
||||
}
|
||||
if (ch === "(") {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
if (ch === ")") {
|
||||
selectOpenByDepth.delete(depth);
|
||||
depth = Math.max(0, depth - 1);
|
||||
continue;
|
||||
}
|
||||
if (!/[A-Za-z_]/.test(ch)) continue;
|
||||
|
||||
let end = i + 1;
|
||||
while (end < beforeCursor.length && /[A-Za-z0-9_$]/.test(beforeCursor[end] ?? "")) end++;
|
||||
const word = beforeCursor.slice(i, end).toLowerCase();
|
||||
if (word === "select") {
|
||||
selectOpenByDepth.set(depth, true);
|
||||
} else if (word === "from") {
|
||||
selectOpenByDepth.set(depth, false);
|
||||
}
|
||||
i = end - 1;
|
||||
}
|
||||
|
||||
return selectOpenByDepth.get(depth) === true;
|
||||
}
|
||||
|
||||
function isInJoinConditionContext(beforeCursor: string): boolean {
|
||||
const cleaned = beforeCursor
|
||||
.replace(/'[^']*'/g, "''")
|
||||
|
|
|
|||
|
|
@ -1841,6 +1841,28 @@ test("prefix matches still rank above fuzzy matches", () => {
|
|||
assert.equal(items[0]?.label, "name");
|
||||
});
|
||||
|
||||
test("suggests columns after multiple select-list expressions", () => {
|
||||
const sql = "select project_name, review_accountant, doc from ypmng_archive LIMIT 100";
|
||||
const cursor = "select project_name, review_accountant, doc".length;
|
||||
const items = buildSqlCompletionItems(sql, cursor, {
|
||||
tables: [{ name: "ypmng_archive", type: "table" }],
|
||||
objects: [{ name: "proc_get_ypfmm_pd_score_list_with_template_doc_id", schema: "y_jnpf", type: "procedure" }],
|
||||
columnsByTable: new Map([
|
||||
[
|
||||
"ypmng_archive",
|
||||
[
|
||||
{ name: "doc_id", table: "ypmng_archive", dataType: "bigint" },
|
||||
{ name: "project_name", table: "ypmng_archive", dataType: "varchar" },
|
||||
{ name: "review_accountant", table: "ypmng_archive", dataType: "varchar" },
|
||||
],
|
||||
],
|
||||
]),
|
||||
});
|
||||
|
||||
assert.ok(items.some((item) => item.label === "doc_id" && item.type === "column"));
|
||||
assert.ok(!items.some((item) => item.type === "function" && item.label.startsWith("proc_")));
|
||||
});
|
||||
|
||||
// --- Type-aware comparison hints ---
|
||||
|
||||
test("suggests NULL and IS NULL after comparison operator", () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue