feat(editor): resolve table hover across schemas in current database
This commit is contained in:
1 parent
90ad4e62e6
commit
254b9baaff
21 files changed
+675
-29
No files matched your search
@@ -103,6 +103,7 @@ import {
|
||||
type ClickTableNavigationTarget,
|
||||
type EditorSettings,
|
||||
type SqlCompletionTriggerMode,
|
||||
type TableHoverLookupMode,
|
||||
SIDEBAR_INDENT_MIN,
|
||||
SIDEBAR_INDENT_MAX,
|
||||
SIDEBAR_FONT_SIZE_MIN,
|
||||
@@ -657,6 +658,15 @@ const editGenerateSqlIncludeDatabaseName = ref(settingsStore.editorSettings.gene
|
||||
const editGenerateSqlQuoteIdentifiers = ref(settingsStore.editorSettings.generateSqlQuoteIdentifiers);
|
||||
const editFormatSqlOnSqlFileSave = ref(settingsStore.editorSettings.formatSqlOnSqlFileSave);
|
||||
const editShowTableDdlHoverPreview = ref(settingsStore.editorSettings.showTableDdlHoverPreview);
|
||||
const editTableHoverLookupMode = ref<TableHoverLookupMode>(settingsStore.editorSettings.tableHoverLookupMode);
|
||||
const tableHoverLookupModeDescription = computed(() => {
|
||||
const key = {
|
||||
current: "settings.tableHoverLookupModeCurrentDescription",
|
||||
fallback: "settings.tableHoverLookupModeFallbackDescription",
|
||||
always: "settings.tableHoverLookupModeAlwaysDescription",
|
||||
}[editTableHoverLookupMode.value];
|
||||
return t(key);
|
||||
});
|
||||
const editClickTableNavigationTarget = ref<ClickTableNavigationTarget>(settingsStore.editorSettings.clickTableNavigationTarget);
|
||||
const editUpdateNotificationsEnabled = ref(settingsStore.editorSettings.updateNotificationsEnabled);
|
||||
const editAutoDownloadUpdates = ref(settingsStore.editorSettings.autoDownloadUpdates);
|
||||
@@ -816,6 +826,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
|
||||
generateSqlQuoteIdentifiers: editGenerateSqlQuoteIdentifiers.value,
|
||||
formatSqlOnSqlFileSave: editFormatSqlOnSqlFileSave.value,
|
||||
showTableDdlHoverPreview: editShowTableDdlHoverPreview.value,
|
||||
tableHoverLookupMode: editTableHoverLookupMode.value,
|
||||
updateNotificationsEnabled: editUpdateNotificationsEnabled.value,
|
||||
autoDownloadUpdates: editAutoDownloadUpdates.value,
|
||||
sidebarObjectInfoMode: editSidebarObjectInfoMode.value,
|
||||
@@ -1341,6 +1352,7 @@ function syncEditorSettingsDraftFromStore() {
|
||||
editGenerateSqlQuoteIdentifiers.value = settingsStore.editorSettings.generateSqlQuoteIdentifiers;
|
||||
editFormatSqlOnSqlFileSave.value = settingsStore.editorSettings.formatSqlOnSqlFileSave;
|
||||
editShowTableDdlHoverPreview.value = settingsStore.editorSettings.showTableDdlHoverPreview;
|
||||
editTableHoverLookupMode.value = settingsStore.editorSettings.tableHoverLookupMode;
|
||||
editClickTableNavigationTarget.value = settingsStore.editorSettings.clickTableNavigationTarget;
|
||||
editUpdateNotificationsEnabled.value = settingsStore.editorSettings.updateNotificationsEnabled;
|
||||
editAutoDownloadUpdates.value = settingsStore.editorSettings.autoDownloadUpdates;
|
||||
@@ -1458,6 +1470,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
|
||||
generateSqlQuoteIdentifiers: editGenerateSqlQuoteIdentifiers,
|
||||
formatSqlOnSqlFileSave: editFormatSqlOnSqlFileSave,
|
||||
showTableDdlHoverPreview: editShowTableDdlHoverPreview,
|
||||
tableHoverLookupMode: editTableHoverLookupMode,
|
||||
updateNotificationsEnabled: editUpdateNotificationsEnabled,
|
||||
autoDownloadUpdates: editAutoDownloadUpdates,
|
||||
sidebarObjectInfoMode: editSidebarObjectInfoMode,
|
||||
@@ -1842,6 +1855,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
|
||||
editAppCloseUnsavedTabsMode.value = DEFAULT_EDITOR_SETTINGS.appCloseUnsavedTabsMode;
|
||||
editSavedSqlOpenTargetMode.value = DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode;
|
||||
editShowTableDdlHoverPreview.value = DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview;
|
||||
editTableHoverLookupMode.value = DEFAULT_EDITOR_SETTINGS.tableHoverLookupMode;
|
||||
editExternalSqlEditorMaxMb.value = DEFAULT_EDITOR_SETTINGS.externalSqlEditorMaxMb;
|
||||
editClickTableNavigationTarget.value = DEFAULT_EDITOR_SETTINGS.clickTableNavigationTarget;
|
||||
editSqlVariableSubstitutionEnabled.value = DEFAULT_EDITOR_SETTINGS.sqlVariableSubstitutionEnabled;
|
||||
@@ -2034,6 +2048,7 @@ function resetAllDefaults() {
|
||||
editGenerateSqlQuoteIdentifiers.value = DEFAULT_EDITOR_SETTINGS.generateSqlQuoteIdentifiers;
|
||||
editFormatSqlOnSqlFileSave.value = DEFAULT_EDITOR_SETTINGS.formatSqlOnSqlFileSave;
|
||||
editShowTableDdlHoverPreview.value = DEFAULT_EDITOR_SETTINGS.showTableDdlHoverPreview;
|
||||
editTableHoverLookupMode.value = DEFAULT_EDITOR_SETTINGS.tableHoverLookupMode;
|
||||
editUpdateNotificationsEnabled.value = DEFAULT_EDITOR_SETTINGS.updateNotificationsEnabled;
|
||||
editAutoDownloadUpdates.value = DEFAULT_EDITOR_SETTINGS.autoDownloadUpdates;
|
||||
editSidebarObjectInfoMode.value = DEFAULT_EDITOR_SETTINGS.sidebarObjectInfoMode;
|
||||
@@ -2197,6 +2212,12 @@ function onCompletionTriggerModeChange(v: any) {
|
||||
}
|
||||
}
|
||||
|
||||
function onTableHoverLookupModeChange(v: any) {
|
||||
if (v === "current" || v === "fallback" || v === "always") {
|
||||
editTableHoverLookupMode.value = v;
|
||||
}
|
||||
}
|
||||
|
||||
function onSqlSemanticDiagnosticsEnabledChange(value: boolean) {
|
||||
editSqlSemanticDiagnosticsEnabled.value = value;
|
||||
editSqlSemanticDiagnosticsMode.value = value ? "enabled" : "disabled";
|
||||
@@ -5605,6 +5626,25 @@ onUnmounted(() => {
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2 md:col-span-2" data-editor-table-hover-lookup-mode>
|
||||
<div class="min-w-0 space-y-1">
|
||||
<Label>{{ t("settings.tableHoverLookupMode") }}</Label>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
{{ tableHoverLookupModeDescription }}
|
||||
</p>
|
||||
</div>
|
||||
<Select :model-value="editTableHoverLookupMode" @update:model-value="onTableHoverLookupModeChange">
|
||||
<SelectTrigger class="h-8 w-48 shrink-0">
|
||||
<SelectValue :placeholder="t('settings.tableHoverLookupMode')" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="current">{{ t("settings.tableHoverLookupModeCurrent") }}</SelectItem>
|
||||
<SelectItem value="fallback">{{ t("settings.tableHoverLookupModeFallback") }}</SelectItem>
|
||||
<SelectItem value="always">{{ t("settings.tableHoverLookupModeAlways") }}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="settings-item flex items-center justify-between gap-4 rounded-md border bg-muted/20 px-3 py-2 md:col-span-2" data-editor-default-transaction-mode>
|
||||
<div class="min-w-0 space-y-1">
|
||||
<Label for="editor-default-transaction-mode">{{ t("settings.defaultTransactionMode") }}</Label>
|
||||
|
||||
@@ -128,6 +128,7 @@ import {
|
||||
type SqlObjectNavigationTarget,
|
||||
} from "@/lib/sql/sqlNavigation";
|
||||
import { buildHoverTableSql, ddlForHoverPreview, hoverTableMatchesScope, normalizeAlignedSqlWhitespace, quoteIdentifier, quoteQualifiedName, reformatHoverDdl, scopeHoverTables, type HoverTableScope } from "@/lib/editor/hoverTableSql";
|
||||
import { matchHoverTableCandidates, resolveHoverTableLookupTarget } from "@/lib/editor/hoverTableLookup";
|
||||
import { constrainSqlHoverLayout } from "@/lib/editor/sqlHoverLayout";
|
||||
import { createHoverSearch, type HoverSearchController } from "@/lib/editor/sqlHoverSearch";
|
||||
import { lineColumnToOffset, sqlErrorDecorationRange as resolveSqlErrorDecorationRange, sqlErrorSqlMatchesEditor } from "@/lib/sql/sqlDiagnostics";
|
||||
@@ -3176,11 +3177,24 @@ async function resolveSqlHoverTooltip(currentView: EditorViewType, pos: number)
|
||||
const tableLookupName = semanticTarget && !semanticQualifierIsRowSource ? semanticTarget.name : name;
|
||||
const qualifiedTableLookup = semanticTarget?.schema ? `${semanticTarget.schema}.${semanticTarget.name}` : identifier;
|
||||
|
||||
const hoverTarget = completionMetadataTarget({
|
||||
name: tableLookupName,
|
||||
const lookup = resolveHoverTableLookupTarget({
|
||||
database: props.database,
|
||||
schema: props.schema,
|
||||
catalog: props.catalog,
|
||||
database: semanticTarget?.database,
|
||||
schema: semanticTarget?.schema,
|
||||
databaseType: props.databaseType,
|
||||
tableName: tableLookupName,
|
||||
// Alias.column must not be treated as schema.table; keep the bare column/table token.
|
||||
identifierParts: semanticQualifierIsRowSource ? [tableLookupName] : parts,
|
||||
semanticDatabase: semanticQualifierIsRowSource ? undefined : semanticTarget?.database,
|
||||
semanticSchema: semanticQualifierIsRowSource ? undefined : semanticTarget?.schema,
|
||||
mode: settingsStore.editorSettings.tableHoverLookupMode,
|
||||
});
|
||||
|
||||
const hoverTarget = completionMetadataTarget({
|
||||
name: lookup.tableName,
|
||||
catalog: props.catalog,
|
||||
database: lookup.database !== props.database ? lookup.database : undefined,
|
||||
schema: lookup.schema,
|
||||
});
|
||||
if (!hoverTarget) return null;
|
||||
const hoverScope: HoverTableScope = {
|
||||
@@ -3188,27 +3202,73 @@ async function resolveSqlHoverTooltip(currentView: EditorViewType, pos: number)
|
||||
database: hoverTarget.database,
|
||||
schema: hoverTarget.schema,
|
||||
};
|
||||
const preferredSchema = props.schema;
|
||||
const matchHoverTable = (tables: typeof cachedTables) =>
|
||||
matchHoverTableCandidates(tables, {
|
||||
lookups: [qualifiedTableLookup, identifier],
|
||||
tableName: tableLookupName,
|
||||
preferredSchema,
|
||||
});
|
||||
|
||||
try {
|
||||
let hoverTables = cachedTables.filter((table) => hoverTableMatchesScope(table, hoverScope));
|
||||
let table = matchTable(qualifiedTableLookup, hoverTables) ?? matchTable(tableLookupName, hoverTables) ?? matchTable(identifier, hoverTables) ?? matchTable(name, hoverTables);
|
||||
if (!table) {
|
||||
const localTables = connectionStore.lookupLocalCompletionTables(props.connectionId, hoverScope.database, tableLookupName, MAX_COMPLETION_TABLES, hoverScope.schema, hoverScope.catalog);
|
||||
const localHoverTables = scopeHoverTables(localTables, hoverScope);
|
||||
const searchLocal = (schema?: string) => {
|
||||
const scopeForFilter: HoverTableScope = { ...hoverScope, schema };
|
||||
const localTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, hoverScope.database, tableLookupName, MAX_COMPLETION_TABLES, schema, hoverScope.catalog);
|
||||
const localHoverTables = schema ? scopeHoverTables(localTables, scopeForFilter) : scopeHoverTables(localTables, { ...hoverScope, schema: undefined });
|
||||
return localHoverTables;
|
||||
};
|
||||
const searchRemote = async (schema: string | undefined, globalSearch: boolean) => {
|
||||
const loadedTables = await connectionStore.listCompletionTables(props.connectionId!, hoverScope.database, tableLookupName, MAX_COMPLETION_TABLES, schema, globalSearch, preferredSchema, hoverScope.catalog);
|
||||
return schema ? scopeHoverTables(loadedTables, { ...hoverScope, schema }) : loadedTables;
|
||||
};
|
||||
|
||||
let hoverTables = cachedTables.filter((table) => (lookup.preferGlobalFirst || !hoverScope.schema ? hoverTableMatchesScope(table, { ...hoverScope, schema: undefined }) : hoverTableMatchesScope(table, hoverScope)));
|
||||
let table = matchHoverTable(hoverTables);
|
||||
|
||||
if (!table && !lookup.preferGlobalFirst) {
|
||||
const localHoverTables = searchLocal(hoverScope.schema);
|
||||
hoverTables = mergeCompletionTables(localHoverTables, hoverTables);
|
||||
cachedTables = mergeCompletionTables(localHoverTables, cachedTables);
|
||||
table = matchTable(qualifiedTableLookup, hoverTables) ?? matchTable(tableLookupName, hoverTables) ?? matchTable(identifier, hoverTables) ?? matchTable(name, hoverTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
}
|
||||
if (!table && !usesLocalOnlyCompletionMetadata()) {
|
||||
const loadedTables = await connectionStore.listCompletionTables(props.connectionId, hoverScope.database, tableLookupName, MAX_COMPLETION_TABLES, hoverScope.schema, false, hoverScope.schema, hoverScope.catalog);
|
||||
const remoteHoverTables = scopeHoverTables(loadedTables, hoverScope);
|
||||
|
||||
if (!table && lookup.preferGlobalFirst) {
|
||||
const localHoverTables = searchLocal(undefined);
|
||||
hoverTables = mergeCompletionTables(localHoverTables, hoverTables);
|
||||
cachedTables = mergeCompletionTables(localHoverTables, cachedTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
}
|
||||
|
||||
if (!table && !usesLocalOnlyCompletionMetadata() && !lookup.preferGlobalFirst) {
|
||||
const remoteHoverTables = await searchRemote(hoverScope.schema, false);
|
||||
hoverTables = mergeCompletionTables(hoverTables, remoteHoverTables);
|
||||
cachedTables = mergeCompletionTables(cachedTables, remoteHoverTables);
|
||||
table = matchTable(qualifiedTableLookup, hoverTables) ?? matchTable(tableLookupName, hoverTables) ?? matchTable(identifier, hoverTables) ?? matchTable(name, hoverTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
}
|
||||
|
||||
if (!table && !usesLocalOnlyCompletionMetadata() && lookup.preferGlobalFirst) {
|
||||
const remoteHoverTables = await searchRemote(undefined, true);
|
||||
hoverTables = mergeCompletionTables(hoverTables, remoteHoverTables);
|
||||
cachedTables = mergeCompletionTables(cachedTables, remoteHoverTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
}
|
||||
|
||||
if (!table && lookup.allowGlobalFallback && !lookup.preferGlobalFirst) {
|
||||
const localHoverTables = searchLocal(undefined);
|
||||
hoverTables = mergeCompletionTables(localHoverTables, hoverTables);
|
||||
cachedTables = mergeCompletionTables(localHoverTables, cachedTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
if (!table && !usesLocalOnlyCompletionMetadata()) {
|
||||
const remoteHoverTables = await searchRemote(undefined, true);
|
||||
hoverTables = mergeCompletionTables(hoverTables, remoteHoverTables);
|
||||
cachedTables = mergeCompletionTables(cachedTables, remoteHoverTables);
|
||||
table = matchHoverTable(hoverTables);
|
||||
}
|
||||
}
|
||||
|
||||
if (table && settingsStore.editorSettings.showTableDdlHoverPreview && !semanticQualifierIsRowSource && (!qualifier || table.schema?.toLowerCase() === qualifier.toLowerCase() || table.name === name)) {
|
||||
const hoverDatabase = hoverScope.database;
|
||||
const hoverSchema = hoverScope.schema ?? table.schema ?? "";
|
||||
const hoverDatabase = table.database ?? hoverScope.database;
|
||||
const hoverSchema = table.schema ?? hoverScope.schema ?? "";
|
||||
const hoverQualifiedName = [hoverScope.catalog, hoverDatabase, hoverSchema, table.name].filter(Boolean).join(".");
|
||||
const objectMetadataRequest = {
|
||||
connectionId: props.connectionId,
|
||||
@@ -6851,6 +6911,22 @@ onMounted(async () => {
|
||||
const objectSchemaHint = identity.parts.length >= 3 ? identity.schema : identity.parts.length === 1 ? props.schema : undefined;
|
||||
const isRoutineCall = identity.role === "routine_call";
|
||||
const isRelationColumnList = identity.role === "relation_column_list";
|
||||
const tableLookup = resolveHoverTableLookupTarget({
|
||||
database: props.database!,
|
||||
schema: props.schema,
|
||||
catalog: props.catalog,
|
||||
databaseType: props.databaseType,
|
||||
tableName: tableLookupFilter,
|
||||
identifierParts,
|
||||
mode: settingsStore.editorSettings.tableHoverLookupMode,
|
||||
});
|
||||
const tableLookupSchema = tableLookup.preferGlobalFirst ? undefined : (tableLookup.schema ?? props.schema);
|
||||
const matchNavigationTable = (tables: typeof cachedTables) =>
|
||||
matchHoverTableCandidates(tables, {
|
||||
lookups: [identifier],
|
||||
tableName: tableLookupFilter,
|
||||
preferredSchema: props.schema,
|
||||
});
|
||||
const relationNavigationTarget = (target: SqlObjectNavigationTarget) =>
|
||||
queryTableNavigationTargetAtSqlPosition(
|
||||
{
|
||||
@@ -6864,17 +6940,26 @@ onMounted(async () => {
|
||||
target,
|
||||
);
|
||||
|
||||
// 1. Local table cache (sync). Relation column lists always prefer tables over routines.
|
||||
if (cachedTables.length === 0) {
|
||||
cachedTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, props.database!, tableLookupFilter, MAX_COMPLETION_TABLES, props.schema, props.catalog);
|
||||
}
|
||||
|
||||
let matchedTable = matchTable(identifier, cachedTables);
|
||||
// 1. Local table lookup with the resolved scope (do not trust stale editor cache alone —
|
||||
// completion/hover may have filled cachedTables with the current schema only).
|
||||
const localScopedTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, MAX_COMPLETION_TABLES, tableLookupSchema, props.catalog);
|
||||
cachedTables = mergeCompletionTables(localScopedTables, cachedTables);
|
||||
let matchedTable = matchNavigationTable(localScopedTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!matchedTable && tableLookup.allowGlobalFallback && !tableLookup.preferGlobalFirst) {
|
||||
const localGlobalTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, MAX_COMPLETION_TABLES, undefined, props.catalog);
|
||||
cachedTables = mergeCompletionTables(cachedTables, localGlobalTables);
|
||||
matchedTable = matchNavigationTable(localGlobalTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const preserveOracleStoreCase = (value?: string) => !!value && value !== value.toUpperCase();
|
||||
const openMatchedObject = (matchedObject: { name: string; schema?: string; type: string; signature?: string; parentName?: string; parentSchema?: string }) => {
|
||||
const navigationType = sqlObjectNavigationTypeFromCompletionObjectType(matchedObject.type);
|
||||
@@ -6929,21 +7014,39 @@ onMounted(async () => {
|
||||
// Routine-call sites may still hit this when a table and procedure share a name and
|
||||
// local caches were empty; table wins only if listed as a relation.
|
||||
if (!usesLocalOnlyCompletionMetadata() && (!isRoutineCall || isRelationColumnList || identity.role === "unknown")) {
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId!, props.database!, tableLookupFilter, MAX_COMPLETION_TABLES, props.schema, false, props.schema, props.catalog);
|
||||
matchedTable = matchTable(identifier, cachedTables);
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, MAX_COMPLETION_TABLES, tableLookupSchema, tableLookup.preferGlobalFirst, props.schema, props.catalog);
|
||||
matchedTable = matchNavigationTable(cachedTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
if (tableLookup.allowGlobalFallback && !tableLookup.preferGlobalFirst) {
|
||||
const globalTables = await connectionStore.listCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, MAX_COMPLETION_TABLES, undefined, true, props.schema, props.catalog);
|
||||
cachedTables = mergeCompletionTables(cachedTables, globalTables);
|
||||
matchedTable = matchNavigationTable(cachedTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (!usesLocalOnlyCompletionMetadata() && isRoutineCall) {
|
||||
// Lightweight table check so INSERT INTO ORDERS(…) is not the only guarded path —
|
||||
// still avoid global scans: only session-scoped prefix lookup.
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId!, props.database!, tableLookupFilter, 20, props.schema, false, props.schema, props.catalog);
|
||||
matchedTable = matchTable(identifier, cachedTables);
|
||||
// still avoid unbounded scans: session-scoped first, optional name-filtered global fallback.
|
||||
cachedTables = await connectionStore.listCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, 20, tableLookupSchema, tableLookup.preferGlobalFirst, props.schema, props.catalog);
|
||||
matchedTable = matchNavigationTable(cachedTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
if (tableLookup.allowGlobalFallback && !tableLookup.preferGlobalFirst) {
|
||||
const globalTables = await connectionStore.listCompletionTables(props.connectionId!, tableLookup.database, tableLookupFilter, 20, undefined, true, props.schema, props.catalog);
|
||||
cachedTables = mergeCompletionTables(cachedTables, globalTables);
|
||||
matchedTable = matchNavigationTable(cachedTables);
|
||||
if (matchedTable) {
|
||||
emit("clickTable", relationNavigationTarget(matchedTable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1d. Optimistic routine open only for confirmed call sites after metadata + table checks.
|
||||
|
||||
Reference in new issue
Block a user