fix(editor): distinguish SELECT wildcards from multiplication
This commit is contained in:
1 parent
24179cb45d
commit
ca8bc9bf12
5 files changed
+185
-14
No files matched your search
@@ -1352,6 +1352,7 @@ export interface SqlCompletionItem {
|
||||
boost: number;
|
||||
exactMatch?: boolean;
|
||||
dedupeKey?: string;
|
||||
replaceSelectWildcard?: true;
|
||||
/** Enables the query editor's checkbox-based batch insertion for this column. */
|
||||
batchSelectionMode?: "select" | "insert";
|
||||
/** Qualifier to prepend to every batch-selected column after the first one. */
|
||||
@@ -1428,6 +1429,7 @@ export interface SqlCompletionContext {
|
||||
comparisonLeftColumn?: string;
|
||||
onStar: boolean;
|
||||
selectListColumnContext: boolean;
|
||||
selectListWildcardAfterCursor?: boolean;
|
||||
preferredKeywords: string[];
|
||||
updateTarget?: { table: string; schema?: string };
|
||||
deleteTarget?: { table: string; schema?: string };
|
||||
@@ -1448,22 +1450,47 @@ const SQL_COMPLETION_CLOSING_QUOTES: Readonly<Record<string, SqlCompletionClosin
|
||||
"[": "]",
|
||||
};
|
||||
|
||||
export function prepareSqlCompletionReplacement(sql: string, cursor: number, context: Pick<SqlCompletionContext, "prefix" | "qualifier" | "replacementRange">, items: SqlCompletionItem[]): { from: number; items: SqlCompletionItem[] } {
|
||||
const SELECT_WILDCARD_FOLLOWING_CLAUSES = new Set(["except", "fetch", "for", "from", "group", "having", "intersect", "into", "limit", "offset", "order", "qualify", "union", "where", "window"]);
|
||||
const SELECT_PROJECTION_MODIFIERS = new Set(["all", "distinct", "distinctrow"]);
|
||||
|
||||
function isStandaloneSelectWildcard(sql: string, cursor: number, selectListColumnContext: boolean, statementKind: SqlStatementKind, dialectId?: string): boolean {
|
||||
if (!selectListColumnContext || statementKind !== "select" || sql[cursor] !== "*") return false;
|
||||
|
||||
const tokens = tokenizeSqlSemantic(sql, dialectId).filter((token) => token.kind !== "comment");
|
||||
const starIndex = tokens.findIndex((token) => token.span.start === cursor && token.text === "*");
|
||||
if (starIndex < 0) return false;
|
||||
|
||||
const star = tokens[starIndex]!;
|
||||
let previousIndex = starIndex - 1;
|
||||
while (previousIndex >= 0 && tokens[previousIndex]!.depth === star.depth && tokens[previousIndex]!.kind === "word" && SELECT_PROJECTION_MODIFIERS.has(tokens[previousIndex]!.normalized)) previousIndex--;
|
||||
const previous = tokens[previousIndex];
|
||||
const startsProjection = previous?.depth === star.depth && (previous.normalized === "select" || previous.text === ",");
|
||||
if (!startsProjection) return false;
|
||||
|
||||
const next = tokens.slice(starIndex + 1).find((token) => token.depth <= star.depth);
|
||||
if (!next) return true;
|
||||
if (next.depth !== star.depth) return false;
|
||||
return next.text === "," || next.text === ";" || (next.kind === "word" && SELECT_WILDCARD_FOLLOWING_CLAUSES.has(next.normalized));
|
||||
}
|
||||
|
||||
export function prepareSqlCompletionReplacement(sql: string, cursor: number, context: Pick<SqlCompletionContext, "prefix" | "qualifier" | "replacementRange" | "selectListWildcardAfterCursor">, items: SqlCompletionItem[]): { from: number; items: SqlCompletionItem[] } {
|
||||
const range = context.replacementRange;
|
||||
const from = range && range.start >= 0 && range.start <= cursor && range.end === cursor ? range.start : cursor - context.prefix.length;
|
||||
const closingQuote = from < cursor ? SQL_COMPLETION_CLOSING_QUOTES[sql[from] ?? ""] : undefined;
|
||||
if (!closingQuote) return { from, items };
|
||||
const replaceClosingQuote = sql[cursor] === closingQuote ? closingQuote : undefined;
|
||||
const replaceSelectWildcard = from === cursor && context.selectListWildcardAfterCursor === true;
|
||||
if (!closingQuote && !replaceSelectWildcard) return { from, items };
|
||||
return {
|
||||
from,
|
||||
items: items.map((item) => {
|
||||
let prepared = item;
|
||||
const apply = item.apply ?? item.label;
|
||||
if (item.type === "column" && !(apply.startsWith(sql[from] ?? "") && apply.endsWith(closingQuote)) && (context.qualifier || !apply.includes("."))) {
|
||||
if (closingQuote && item.type === "column" && !(apply.startsWith(sql[from] ?? "") && apply.endsWith(closingQuote)) && (context.qualifier || !apply.includes("."))) {
|
||||
const escaped = apply.replaceAll(closingQuote, closingQuote + closingQuote);
|
||||
prepared = { ...prepared, apply: `${sql[from]}${escaped}${closingQuote}` };
|
||||
}
|
||||
return replaceClosingQuote && !prepared.replaceClosingQuote ? { ...prepared, replaceClosingQuote } : prepared;
|
||||
if (replaceClosingQuote && !prepared.replaceClosingQuote) prepared = { ...prepared, replaceClosingQuote };
|
||||
return replaceSelectWildcard && !prepared.replaceSelectWildcard ? { ...prepared, replaceSelectWildcard: true } : prepared;
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -2435,6 +2462,7 @@ export function getSqlCompletionContext(sql: string, cursor: number, options: Sq
|
||||
const suggestRoutines = inCallRoutineContext || oracleTableFunctionContext || inPotentialPackageMemberContext || (!exclusiveTableSuggestions && !exclusiveColumnSuggestions && !insertInfo && !updateInfo?.inSetClause && prefix.length >= 2);
|
||||
|
||||
const statementKind = detectStatementKind(beforeCursor || fullStatement);
|
||||
const selectListWildcardAfterCursor = isStandaloneSelectWildcard(rawStatement, cursor - statementSpan.start, selectListColumnContext, statementKind, resolveSqlDialectId(options));
|
||||
const dataTypeContext = isCreateTableColumnTypeContext(beforeToken, options.databaseType);
|
||||
const preferredValueKeywords = sqlServerDatepartCompletionValues(beforeCursor, options.databaseType);
|
||||
const preferredKeywords = qualifier ? [] : preferredKeywordsForCompletion(beforeCursor, beforeToken, selectListColumnContext, exclusiveTableSuggestions, updateInfo, deleteInfo, options.databaseType);
|
||||
@@ -2480,6 +2508,7 @@ export function getSqlCompletionContext(sql: string, cursor: number, options: Sq
|
||||
comparisonLeftColumn: detectComparisonLeftColumn(beforeCursor),
|
||||
onStar: detectOnStar(beforeCursor),
|
||||
selectListColumnContext,
|
||||
selectListWildcardAfterCursor,
|
||||
preferredKeywords,
|
||||
updateTarget: updateInfo?.target,
|
||||
deleteTarget: deleteInfo?.target,
|
||||
|
||||
Reference in new issue
Block a user