fix(completion): preserve longer keyword prefixes
This commit is contained in:
2 files changed
+42
No files matched your search
@@ -35,6 +35,34 @@ function semanticCompletion(markedSql: string, input: Partial<SqlCompletionProvi
|
||||
}
|
||||
|
||||
describe("semantic SQL completion candidates", () => {
|
||||
it.each([
|
||||
["MySQL ORDER BY", "SELECT * FROM t LIMIT 100 or|", "mysql", "mysql", "ORDER BY"],
|
||||
["PostgreSQL ON CONFLICT", "INSERT INTO t VALUES (1) on|", "postgres", "postgres", "ON CONFLICT"],
|
||||
["Oracle EXECUTE IMMEDIATE", "exec|", "oracle", undefined, "EXECUTE IMMEDIATE"],
|
||||
] as const)("keeps the longer %s keyword available before the current token is committed", (_label, sql, databaseType, dialect, expectedKeyword) => {
|
||||
const { context, items } = semanticCompletion(sql, {}, { databaseType, dialect });
|
||||
|
||||
expect(context.suggestKeywords).toBe(false);
|
||||
expect(items).toEqual(expect.arrayContaining([expect.objectContaining({ label: expectedKeyword, type: "keyword" })]));
|
||||
});
|
||||
|
||||
it("does not offer keyword continuations for qualified column prefixes", () => {
|
||||
const columnsByTable = new Map<string, SqlCompletionColumn[]>([["t", [{ name: "order_number", table: "t" }]]]);
|
||||
const { context, items } = semanticCompletion("SELECT * FROM t WHERE t.or|", { columnsByTable }, { databaseType: "mysql", dialect: "mysql" });
|
||||
|
||||
expect(context.qualifier).toBe("t");
|
||||
expect(items.some((item) => item.label === "ORDER BY")).toBe(false);
|
||||
});
|
||||
|
||||
it("stops offering ORDER BY as a prefix continuation after OR is committed with whitespace", () => {
|
||||
const columnsByTable = new Map<string, SqlCompletionColumn[]>([["t", [{ name: "id", table: "t" }]]]);
|
||||
const { context, items } = semanticCompletion("SELECT * FROM t WHERE id = 1 OR |", { columnsByTable }, { databaseType: "mysql", dialect: "mysql" });
|
||||
|
||||
expect(context.prefix).toBe("");
|
||||
expect(items.some((item) => item.label === "ORDER BY")).toBe(false);
|
||||
expect(items.some((item) => item.label === "id" && item.type === "column")).toBe(true);
|
||||
});
|
||||
|
||||
it("keeps matching functions available in column expressions", () => {
|
||||
const columnsByTable = new Map<string, SqlCompletionColumn[]>([
|
||||
[
|
||||
|
||||
@@ -1393,6 +1393,8 @@ class SqlCompletionProvider {
|
||||
if (context.suggestKeywords && !context.exclusiveRoutineSuggestions && !pendingJoinKeyword) {
|
||||
this.items.push(...buildJoinModifierKeywordItems(context.prefix, this.input.keywordCase));
|
||||
this.items.push(...buildKeywordItems(context.prefix, context, this.databaseType, this.input.keywordCase));
|
||||
} else if (shouldOfferKeywordPrefixContinuations(context, pendingJoinKeyword)) {
|
||||
this.items.push(...buildKeywordPrefixContinuationItems(context.prefix, context, this.databaseType, this.input.keywordCase));
|
||||
}
|
||||
|
||||
if (!context.exclusiveTableSuggestions && context.suggestColumns) {
|
||||
@@ -4147,6 +4149,18 @@ function buildKeywordItems(prefix: string, context: SqlCompletionContext, databa
|
||||
});
|
||||
}
|
||||
|
||||
function shouldOfferKeywordPrefixContinuations(context: SqlCompletionContext, pendingJoinKeyword: boolean): boolean {
|
||||
return !!context.prefix && !pendingJoinKeyword && !context.qualifier && !context.exclusiveTableSuggestions && !context.exclusiveColumnSuggestions;
|
||||
}
|
||||
|
||||
function buildKeywordPrefixContinuationItems(prefix: string, context: SqlCompletionContext, databaseType?: DatabaseType, keywordCase?: SqlKeywordCase): SqlCompletionItem[] {
|
||||
const normalizedPrefix = prefix.toLowerCase();
|
||||
return buildKeywordItems(prefix, context, databaseType, keywordCase).filter((item) => {
|
||||
const normalizedLabel = item.label.toLowerCase();
|
||||
return normalizedLabel.length > normalizedPrefix.length && normalizedLabel.startsWith(normalizedPrefix);
|
||||
});
|
||||
}
|
||||
|
||||
function matchesPrefix(candidate: string, prefix: string): boolean {
|
||||
if (!prefix) return true;
|
||||
return computeMatchScore(candidate, prefix) >= 0;
|
||||
|
||||
Reference in new issue
Block a user