feat(completion): improve SQL function suggestions
This commit is contained in:
12 files changed
+412
-40
No files matched your search
@@ -41,7 +41,7 @@ import { buildSqlSemanticModel } from "@/lib/sql/semantic/model";
|
||||
import { mergeSqlSemanticReferenceAnalysis, resolveSqlSemanticNavigationTarget } from "@/lib/sql/semantic/references";
|
||||
import { buildElasticsearchCompletionItemsFromContext, getElasticsearchCompletionContext, getElasticsearchCompletionResultValidFor, shouldAutoOpenElasticsearchCompletion, type ElasticsearchCompletionItem } from "@/lib/elasticsearch/elasticsearchCompletion";
|
||||
import { buildMongoCompletionItemsFromContext, getMongoCompletionContext, getMongoCompletionResultValidFor, mongoCompletionNeedsCollections, mongoCompletionNeedsFields, shouldAutoOpenMongoCompletion, type MongoCompletionItem } from "@/lib/mongo/mongoCompletion";
|
||||
import { resolveSqlCompletionTableLookupTarget } from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import { resolveSqlCompletionRoutineLookupTarget, resolveSqlCompletionTableLookupTarget } from "@/lib/sql/sqlCompletionLookupTarget";
|
||||
import { extractIdentifierDetailsAt, isSqlKeyword, matchTable, mergeSqlObjectNavigationType, splitQualifiedIdentifier, sqlObjectHoverDetail, sqlObjectNavigationTarget, type SqlObjectNavigationTarget } from "@/lib/sql/sqlNavigation";
|
||||
import { lineColumnToOffset, parseSqlErrorLocation } from "@/lib/sql/sqlDiagnostics";
|
||||
import {
|
||||
@@ -74,7 +74,7 @@ import { areSqlSemanticDiagnosticsEqual, buildSqlParserErrorDiagnostic, buildSql
|
||||
import { buildRedisSyntaxDiagnostics, shouldRunRedisDiagnostics } from "@/lib/redis/redisSyntaxDiagnostics";
|
||||
import { buildRedisCompletionItemsFromContext, getRedisCompletionContext, getRedisCompletionResultValidFor, shouldAutoOpenRedisCompletion, takesKeyArgument, type RedisCompletionItem } from "@/lib/redis/redisCompletion";
|
||||
import type { SqlCompletionColumn, SqlCompletionForeignKey, SqlCompletionItem, SqlCompletionObject, SqlCompletionReferencedTable, SqlCompletionTable } from "@/lib/sql/sqlCompletion";
|
||||
import type { DatabaseType, SqlReferenceAnalysis, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
import type { CompletionAssistantObjectKind, DatabaseType, SqlReferenceAnalysis, SqlTableReference, SqlTextSpan } from "@/types/database";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
@@ -2278,12 +2278,12 @@ async function provideSqlCompletions(context: CompletionContext) {
|
||||
const shouldResolveAsyncCompletion = tableNameCompletion || shouldResolveColumnCompletion;
|
||||
const localResult = buildLocalSqlCompletionResult(completionContext, fullDoc, position);
|
||||
if (localResult) {
|
||||
scheduleCompletionMetadataRefresh(completionContext);
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position);
|
||||
const hasLocalColumnResult = localResult.options.some((option) => option.type === "column");
|
||||
if ((!explicit || typedActivation) && (!shouldResolveColumnCompletion || hasLocalColumnResult)) return localResult;
|
||||
}
|
||||
if ((!explicit || typedActivation) && !shouldResolveAsyncCompletion) {
|
||||
scheduleCompletionMetadataRefresh(completionContext);
|
||||
scheduleCompletionMetadataRefresh(completionContext, fullDoc, position);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2465,7 +2465,7 @@ function buildLocalSqlCompletionResult(completionContext: ReturnType<typeof getS
|
||||
return buildCompletionResult(items, position - completionContext.prefix.length, getSqlCompletionResultValidFor(fullDoc, position));
|
||||
}
|
||||
|
||||
function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof getSqlCompletionContext>) {
|
||||
function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
if (!props.connectionId || props.database == null) return;
|
||||
const localOnlyMetadata = usesLocalOnlyCompletionMetadata();
|
||||
const onDemandOnlyColumns = usesOnDemandOnlyCompletionColumns();
|
||||
@@ -2494,7 +2494,10 @@ function scheduleCompletionMetadataRefresh(completionContext: ReturnType<typeof
|
||||
if (!localOnlyMetadata && shouldLoadCompletionObjects(completionContext)) {
|
||||
void listCompletionObjectsForContext(completionContext)
|
||||
.then((objects) => {
|
||||
cachedCompletionObjects = mergeCompletionObjects(cachedCompletionObjects, objects);
|
||||
const merged = mergeCompletionObjects(cachedCompletionObjects, objects);
|
||||
const changed = completionObjectsDiffer(cachedCompletionObjects, merged);
|
||||
cachedCompletionObjects = merged;
|
||||
if (changed) refreshActiveSqlCompletion(fullDoc, position, completionContext);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@@ -2609,18 +2612,29 @@ function lookupLocalCompletionObjectsForContext(completionContext: ReturnType<ty
|
||||
if (props.databaseType === "oracle") {
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, props.database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
}
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, props.database, completionContext.qualifier || completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier && !completionContext.exclusiveColumnSuggestions ? completionContext.qualifier : props.schema);
|
||||
const target = resolveSqlCompletionRoutineLookupTarget({ currentSchema: props.schema, completionContext });
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, props.database, target.mask, MAX_COMPLETION_TABLES, target.schema);
|
||||
}
|
||||
|
||||
async function listCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>): Promise<SqlCompletionObject[]> {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
const objectKinds = completionObjectKindsForContext(completionContext);
|
||||
if (props.databaseType !== "oracle") {
|
||||
return connectionStore.listCompletionObjects(props.connectionId, props.database, completionContext.qualifier || completionContext.prefix, MAX_COMPLETION_TABLES, props.schema);
|
||||
const target = resolveSqlCompletionRoutineLookupTarget({ currentSchema: props.schema, completionContext });
|
||||
return connectionStore.listCompletionObjects(props.connectionId, props.database, target.mask, MAX_COMPLETION_TABLES, target.schema, undefined, false, props.schema, objectKinds);
|
||||
}
|
||||
const groups = await Promise.all(oracleRoutineCompletionTargets(completionContext).map((target) => connectionStore.listCompletionObjects(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, target.schema, target.parentName, target.globalSearch, props.schema)));
|
||||
const groups = await Promise.all(
|
||||
oracleRoutineCompletionTargets(completionContext).map((target) => connectionStore.listCompletionObjects(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, target.schema, target.parentName, target.globalSearch, props.schema, objectKinds)),
|
||||
);
|
||||
return groups.reduce((objects, group) => mergeCompletionObjects(objects, group), [] as SqlCompletionObject[]);
|
||||
}
|
||||
|
||||
function completionObjectKindsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>): CompletionAssistantObjectKind[] {
|
||||
if (completionContext.contextKind === "exec") return ["procedure"];
|
||||
if (completionContext.suggestColumns && completionContext.referencedTables.length > 0 && !completionContext.qualifier) return ["function"];
|
||||
return ["routine"];
|
||||
}
|
||||
|
||||
async function performAsyncCompletionWithResult(epoch: number, completionContext: ReturnType<typeof getSqlCompletionContext>, fullDoc: string, position: number) {
|
||||
const localOnlyMetadata = usesLocalOnlyCompletionMetadata();
|
||||
const onDemandOnlyColumns = usesOnDemandOnlyCompletionColumns();
|
||||
@@ -2876,6 +2890,35 @@ function mergeCompletionObjects(existing: SqlCompletionObject[], incoming: SqlCo
|
||||
return merged;
|
||||
}
|
||||
|
||||
function completionObjectsDiffer(existing: SqlCompletionObject[], incoming: SqlCompletionObject[]): boolean {
|
||||
if (existing.length !== incoming.length) return true;
|
||||
return existing.some((object, index) => {
|
||||
const other = incoming[index];
|
||||
return (
|
||||
!other ||
|
||||
object.name !== other.name ||
|
||||
object.schema !== other.schema ||
|
||||
object.type !== other.type ||
|
||||
object.parentSchema !== other.parentSchema ||
|
||||
object.parentName !== other.parentName ||
|
||||
object.dataType !== other.dataType ||
|
||||
object.signature !== other.signature ||
|
||||
object.comment !== other.comment ||
|
||||
object.applyName !== other.applyName ||
|
||||
object.boost !== other.boost
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function refreshActiveSqlCompletion(fullDoc: string, position: number, completionContext: ReturnType<typeof getSqlCompletionContext>) {
|
||||
const currentView = view.value;
|
||||
if (!currentView || codeMirrorCompletionStatus?.(currentView.state) !== "active") return;
|
||||
if (currentView.state.doc.toString() !== fullDoc || currentView.state.selection.main.head !== position) return;
|
||||
const currentContext = getSqlCompletionContext(fullDoc, position);
|
||||
if (currentContext.prefix !== completionContext.prefix || currentContext.contextKind !== completionContext.contextKind) return;
|
||||
scheduleSqlCompletionStart(currentView);
|
||||
}
|
||||
|
||||
function refreshCompletionCache() {
|
||||
cachedTables = [];
|
||||
cachedCompletionObjects = [];
|
||||
|
||||
Reference in new issue
Block a user