LCOV - code coverage report
Current view: top level - dashql-app/src/session/scripts - focus.ts Coverage Total Hit
Test: DashQL 915828b Lines: 86.7 % 150 130
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as dashql from '@ankoh/dashql-core';
       2              : 
       3              : import { ScriptData, ScriptKey } from './session_scripts.js';
       4              : import { VariantKind } from '../../utils/variant.js';
       5              : import { QUALIFIED_DATABASE_ID, QUALIFIED_SCHEMA_ID, QUALIFIED_TABLE_COLUMN_ID, QUALIFIED_TABLE_ID, QualifiedCatalogObjectID } from './catalog_object_id.js';
       6              : 
       7              : export interface FocusedExpression {
       8              :     /// The expression id
       9              :     expression: dashql.ExternalObjectID.Value;
      10              : }
      11              : export interface FocusedTableRef {
      12              :     /// The table ref
      13              :     tableReference: dashql.ExternalObjectID.Value;
      14              : }
      15              : 
      16              : export interface FocusedCompletion {
      17              :     /// The completion
      18              :     completion: dashql.FlatBufferPtr<dashql.buffers.completion.Completion>;
      19              :     /// The index of the selected completion candidate
      20              :     completionCandidateIndex: number;
      21              : }
      22              : 
      23            1 : export const FOCUSED_TABLE_REF_ID = Symbol('FOCUSED_TABLE_REF_ID');
      24            1 : export const FOCUSED_EXPRESSION_ID = Symbol('FOCUSED_EXPRESSION_ID');
      25            1 : export const FOCUSED_COMPLETION = Symbol('FOCUSED_COMPLETION');
      26              : 
      27              : export type FocusTarget =
      28              :     QualifiedCatalogObjectID
      29              :     | VariantKind<typeof FOCUSED_TABLE_REF_ID, FocusedTableRef>
      30              :     | VariantKind<typeof FOCUSED_EXPRESSION_ID, FocusedExpression>
      31              :     | VariantKind<typeof FOCUSED_COMPLETION, FocusedCompletion>
      32              :     ;
      33              : 
      34            1 : export enum FocusType {
      35            1 :     COMPLETION_CANDIDATE,
      36            1 :     CATALOG_ENTRY,
      37            1 :     COLUMN_REF,
      38            1 :     COLUMN_REF_UNDER_CURSOR,
      39            1 :     COLUMN_REF_OF_TARGET_TABLE,
      40            1 :     COLUMN_REF_OF_TARGET_COLUMN,
      41            1 :     COLUMN_REF_OF_PEER_COLUMN,
      42            1 :     TABLE_REF,
      43            1 :     TABLE_REF_UNDER_CURSOR,
      44            1 :     TABLE_REF_OF_TARGET_TABLE,
      45            1 :     TABLE_REF_OF_TARGET_COLUMN,
      46            1 : }
      47              : 
      48              : export interface SemanticUserFocus {
      49              :     /// The input focus target
      50              :     focusTarget: FocusTarget;
      51              :     /// The focused catalog object
      52              :     catalogObject: (QualifiedCatalogObjectID & { focus: FocusType }) | null;
      53              :     /// The column references in the script, referencing the catalog object
      54              :     scriptColumnRefs: Map<dashql.ExternalObjectID.Value, FocusType>;
      55              :     /// The table references in the script, referencing the catalog object
      56              :     scriptTableRefs: Map<dashql.ExternalObjectID.Value, FocusType>;
      57              : }
      58              : 
      59            1 : export function deriveFocusFromEditorUpdate(
      60            1 :     scriptKey: ScriptKey,
      61            1 :     update: dashql.buffers.editor.EditorUpdateT | null | undefined,
      62            1 : ): SemanticUserFocus | null {
      63            1 :     const context = update?.primaryCursorContext;
      64            1 :     if (context == null || context.kind === dashql.buffers.editor.EditorCursorSemanticKind.NONE) {
      65            1 :         return null;
      66              :     }
      67            1 :     const isTable = context.kind === dashql.buffers.editor.EditorCursorSemanticKind.TABLE_REFERENCE;
      68            1 :     const focusTarget: FocusTarget = isTable
      69            1 :         ? {
      70            1 :             type: FOCUSED_TABLE_REF_ID,
      71            1 :             value: { tableReference: dashql.ExternalObjectID.create(scriptKey, context.referenceId) },
      72            1 :         }
      73            1 :         : {
      74            1 :             type: FOCUSED_EXPRESSION_ID,
      75            1 :             value: { expression: dashql.ExternalObjectID.create(scriptKey, context.referenceId) },
      76            1 :         };
      77            1 :     const focus: SemanticUserFocus = {
      78            1 :         focusTarget,
      79            1 :         catalogObject: null,
      80            1 :         scriptTableRefs: new Map(),
      81            1 :         scriptColumnRefs: new Map(),
      82            1 :     };
      83            1 :     if (context.resolved) {
      84            1 :         focus.catalogObject = context.kind === dashql.buffers.editor.EditorCursorSemanticKind.COLUMN_REFERENCE
      85            1 :             ? {
      86            1 :                 type: QUALIFIED_TABLE_COLUMN_ID,
      87            1 :                 value: {
      88            1 :                     database: context.catalogDatabaseId,
      89            1 :                     schema: context.catalogSchemaId,
      90            1 :                     table: context.catalogTableId,
      91            1 :                     column: context.catalogColumnId,
      92            1 :                     referencedCatalogVersion: context.referencedCatalogVersion,
      93            1 :                 },
      94            1 :                 focus: FocusType.COLUMN_REF,
      95            1 :             }
      96            1 :             : {
      97            1 :                 type: QUALIFIED_TABLE_ID,
      98            1 :                 value: {
      99            1 :                     database: context.catalogDatabaseId,
     100            1 :                     schema: context.catalogSchemaId,
     101            1 :                     table: context.catalogTableId,
     102            1 :                     referencedCatalogVersion: context.referencedCatalogVersion,
     103            1 :                 },
     104            1 :                 focus: FocusType.TABLE_REF,
     105            1 :             };
     106              :     }
     107            1 :     for (const referenceId of context.relatedTableReferenceIds) {
     108            1 :         focus.scriptTableRefs.set(
     109            1 :             dashql.ExternalObjectID.create(scriptKey, referenceId),
     110            1 :             isTable && referenceId === context.referenceId
     111            1 :                 ? FocusType.TABLE_REF_UNDER_CURSOR
     112            1 :                 : isTable ? FocusType.TABLE_REF_OF_TARGET_TABLE : FocusType.TABLE_REF_OF_TARGET_COLUMN,
     113            1 :         );
     114              :     }
     115            1 :     for (const referenceId of context.relatedColumnReferenceIds) {
     116            1 :         let focusType = isTable ? FocusType.COLUMN_REF_OF_TARGET_TABLE : FocusType.COLUMN_REF_OF_TARGET_COLUMN;
     117            1 :         if (!isTable && referenceId === context.referenceId) focusType = FocusType.COLUMN_REF_UNDER_CURSOR;
     118            1 :         focus.scriptColumnRefs.set(dashql.ExternalObjectID.create(scriptKey, referenceId), focusType);
     119            1 :     }
     120            1 :     return focus;
     121            1 : }
     122              : 
     123              : /// Derive focus from script completion
     124            1 : export function deriveFocusFromCompletionCandidates(
     125            1 :     _scriptKey: ScriptKey,
     126            1 :     scriptData: ScriptData,
     127            1 : ): SemanticUserFocus | null {
     128            1 :     if (scriptData.completion == null) {
     129            1 :         return null;
     130              :     }
     131            1 :     const completion = scriptData.completion.buffer.read();
     132            1 :     if (completion.candidatesLength() == 0 || scriptData.completion.candidateId >= completion.candidatesLength()) {
     133            1 :         return null;
     134              :     }
     135              : 
     136            1 :     const focusedCandidateId = scriptData.completion.candidateId ?? 0;
     137              :     const focusTarget: FocusTarget = {
     138            1 :         type: FOCUSED_COMPLETION,
     139            1 :         value: {
     140            1 :             completion: scriptData.completion.buffer,
     141            1 :             completionCandidateIndex: focusedCandidateId
     142            1 :         }
     143              :     };
     144            1 :     const focus: SemanticUserFocus = {
     145            1 :         focusTarget,
     146            1 :         catalogObject: null,
     147            1 :         scriptTableRefs: new Map(),
     148            1 :         scriptColumnRefs: new Map(),
     149            1 :     };
     150              : 
     151              :     // Are we focusing a valid catalog object?
     152            1 :     const candidate = completion.candidates(focusedCandidateId)!;
     153            1 :     if (scriptData.completion.catalogObjectId >= candidate.catalogObjectsLength()) {
     154            1 :         return focus;
     155              :     }
     156            1 :     const candidateObject = candidate.catalogObjects(scriptData.completion.catalogObjectId)!;
     157              : 
     158              :     // Inspect the catalog object and derive a focus target
     159            1 :     switch (candidateObject.objectType()) {
     160            0 :         case dashql.buffers.completion.CompletionCandidateObjectType.DATABASE:
     161            0 :             focus.catalogObject = {
     162            0 :                 type: QUALIFIED_DATABASE_ID,
     163            0 :                 value: {
     164            0 :                     database: candidateObject.catalogDatabaseId()
     165            0 :                 },
     166            0 :                 focus: FocusType.COMPLETION_CANDIDATE
     167            0 :             };
     168            0 :             break;
     169            0 :         case dashql.buffers.completion.CompletionCandidateObjectType.SCHEMA:
     170            0 :             focus.catalogObject = {
     171            0 :                 type: QUALIFIED_SCHEMA_ID,
     172            0 :                 value: {
     173            0 :                     database: candidateObject.catalogDatabaseId(),
     174            0 :                     schema: candidateObject.catalogSchemaId()
     175            0 :                 },
     176            0 :                 focus: FocusType.COMPLETION_CANDIDATE
     177            0 :             };
     178            0 :             break;
     179            1 :         case dashql.buffers.completion.CompletionCandidateObjectType.TABLE:
     180            1 :             focus.catalogObject = {
     181            1 :                 type: QUALIFIED_TABLE_ID,
     182            1 :                 value: {
     183            1 :                     database: candidateObject.catalogDatabaseId(),
     184            1 :                     schema: candidateObject.catalogSchemaId(),
     185            1 :                     table: candidateObject.catalogTableId(),
     186            1 :                     referencedCatalogVersion: candidateObject.referencedCatalogVersion(),
     187            1 :                 },
     188            1 :                 focus: FocusType.COMPLETION_CANDIDATE
     189            1 :             };
     190            1 :             break;
     191            1 :         case dashql.buffers.completion.CompletionCandidateObjectType.COLUMN:
     192            1 :             focus.catalogObject = {
     193            1 :                 type: QUALIFIED_TABLE_COLUMN_ID,
     194            1 :                 value: {
     195            1 :                     database: candidateObject.catalogDatabaseId(),
     196            1 :                     schema: candidateObject.catalogSchemaId(),
     197            1 :                     table: candidateObject.catalogTableId(),
     198            1 :                     column: candidateObject.tableColumnId(),
     199            1 :                     referencedCatalogVersion: candidateObject.referencedCatalogVersion(),
     200            1 :                 },
     201            1 :                 focus: FocusType.COMPLETION_CANDIDATE
     202            1 :             };
     203            1 :             break;
     204            0 :         case dashql.buffers.completion.CompletionCandidateObjectType.FUNCTION:
     205              :             break;
     206            1 :     }
     207            1 :     return focus;
     208            1 : }
        

Generated by: LCOV version 2.0-1