fix(editor): skip duplicate table aliases

This commit is contained in:
vrustx authored and GitHub committed 2026-07-12 12:21:12 +08:00
1 parent 0650428d1c
commit 02e1b943e8
2 files changed
+102 -2

No files matched your search

+49 -2
View File
@@ -1158,6 +1158,7 @@ export interface SqlCompletionContext {
deleteTarget?: { table: string; schema?: string };
oracleTableFunctionContext?: boolean;
autoAliasTableCompletions: boolean;
tableAliasAfterCursor?: boolean;
contextKind: SqlCompletionContextKind;
}
@@ -1286,7 +1287,7 @@ class SqlCompletionProvider {
}
const emptyTableNameCompletion = !context.prefix && (context.suggestTables || context.exclusiveTableSuggestions);
if (!pendingJoinKeyword && !emptyTableNameCompletion && context.referencedTables.length > 0 && !context.suggestColumns && !context.insertTable) {
if (!pendingJoinKeyword && !emptyTableNameCompletion && !context.tableAliasAfterCursor && context.referencedTables.length > 0 && !context.suggestColumns && !context.insertTable) {
this.items.push(...buildAliasItems(context, this.databaseType));
}
@@ -1617,6 +1618,50 @@ function isCallRoutineContext(beforeToken: string): boolean {
return /\bcall\s+(?:[A-Za-z_][\w$]*\.)?$/i.test(beforeToken) || /\bcall\s+(?:[A-Za-z_][\w$]*\.)?[A-Za-z_][\w$]*$/i.test(beforeToken);
}
const SQL_IDENTIFIER_CONTINUE_CHAR = /[$_\u200c\u200d\p{ID_Continue}]/u;
function hasTableAliasAfterCursor(sql: string, cursor: number): boolean {
if (hasAliasMarkerAt(sql, cursor, false)) return true;
let pos = cursor;
while (pos < sql.length) {
const codePoint = sql.codePointAt(pos);
if (codePoint === undefined) break;
const char = String.fromCodePoint(codePoint);
if (char !== "." && !SQL_IDENTIFIER_CONTINUE_CHAR.test(char)) break;
// Advance by the full code point so supplementary Unicode identifiers
// do not leave the scan between UTF-16 surrogate halves.
pos += char.length;
}
if (sql[pos] === '"' || sql[pos] === "`" || sql[pos] === "]") pos++;
return hasAliasMarkerAt(sql, pos, true);
}
function hasAliasMarkerAt(sql: string, pos: number, allowImplicitAlias: boolean): boolean {
const following = sql.slice(skipSqlWhitespaceAndComments(sql, pos));
if (/^as\b/i.test(following)) return true;
if (/^(?:"[^"]+"|`[^`]+`|\[[^\]]+\])/.test(following)) return true;
if (!allowImplicitAlias) return false;
const implicitAlias = /^([A-Za-z_][\w$]*)/.exec(following)?.[1]?.toLowerCase();
return !!implicitAlias && !isUnsafeSqlAlias(implicitAlias);
}
function skipSqlWhitespaceAndComments(sql: string, pos: number): number {
for (;;) {
while (pos < sql.length && /\s/.test(sql[pos])) pos++;
if (sql.startsWith("--", pos)) {
const newline = sql.indexOf("\n", pos + 2);
if (newline === -1) return sql.length;
pos = newline + 1;
} else if (sql.startsWith("/*", pos)) {
const end = sql.indexOf("*/", pos + 2);
if (end === -1) return sql.length;
pos = end + 2;
} else {
return pos;
}
}
}
export function getSqlCompletionContext(sql: string, cursor: number): SqlCompletionContext {
// Extract the full statement at cursor position for referenced tables
const fullStatement = extractStatementAt(sql, cursor);
@@ -1664,7 +1709,8 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
const afterTableTrigger = TABLE_TRIGGER_KEYWORDS.has(lastWord) || (JOIN_MODIFIERS.has(lastWord) && isFollowedByJoin(beforeToken)) || isInTableListContext(beforeToken);
const exclusiveTableSuggestions = EXCLUSIVE_TABLE_TRIGGER_KEYWORDS.has(lastWord) || (JOIN_MODIFIERS.has(lastWord) && isFollowedByJoin(beforeToken)) || isInTableListContext(beforeToken);
const autoAliasTableCompletions = lastWord === "from" || lastWord === "join" || (JOIN_MODIFIERS.has(lastWord) && isFollowedByJoin(beforeToken)) || isInTableListContext(beforeToken);
const tableAliasAfterCursor = hasTableAliasAfterCursor(sql, cursor);
const autoAliasTableCompletions = (lastWord === "from" || lastWord === "join" || (JOIN_MODIFIERS.has(lastWord) && isFollowedByJoin(beforeToken)) || isInTableListContext(beforeToken)) && !tableAliasAfterCursor;
const exclusiveColumnSuggestions = !!qualifier && !exclusiveTableSuggestions && !insertInfo;
const activePrefixIsCte = cteDefs.some((cte) => normalizeIdentifierPart(cte.name) === normalizeIdentifierPart(prefix));
if (exclusiveTableSuggestions && prefix && !activePrefixIsCte && referencedTables.length > 1) {
@@ -1728,6 +1774,7 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
deleteTarget: deleteInfo?.target,
oracleTableFunctionContext,
autoAliasTableCompletions,
tableAliasAfterCursor,
contextKind,
};
}