LCOV - code coverage report
Current view: top level - dashql-app/src/session/editor - editor_focus.ts Coverage Total Hit
Test: DashQL 915828b Lines: 86.4 % 44 38
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : 
       3              : import { EditorView } from '@codemirror/view';
       4              : 
       5              : import { DashQLCompletionAbortEffect, DashQLProcessorPlugin } from './dashql_processor.js';
       6              : 
       7              : /// The writable DashQL editor of a DOM element, null for read-only editors and other elements
       8            1 : function dashqlEditorOf(target: EventTarget | null): EditorView | null {
       9            1 :     if (!(target instanceof Element)) return null;
      10            1 :     const dom = target.closest('.cm-editor');
      11            1 :     if (!(dom instanceof HTMLElement)) return null;
      12            1 :     const view = EditorView.findFromDOM(dom);
      13            1 :     if (view == null || view.state.readOnly || view.state.field(DashQLProcessorPlugin, false) == null) return null;
      14            1 :     return view;
      15            1 : }
      16              : 
      17              : /// The last writable DashQL editor of the document that had the focus, a script editor or the prompt of the composer.
      18              : /// The editor keeps its selection when it loses the focus, a mention can be inserted at its cursor afterwards.
      19            1 : export function useLastFocusedEditor(): () => EditorView | null {
      20            1 :     const last = React.useRef<EditorView | null>(null);
      21            1 :     React.useEffect(() => {
      22            1 :         const onFocusIn = (event: FocusEvent) => {
      23            1 :             const view = dashqlEditorOf(event.target);
      24            1 :             if (view != null) last.current = view;
      25            1 :         };
      26            1 :         document.addEventListener('focusin', onFocusIn);
      27            1 :         return () => document.removeEventListener('focusin', onFocusIn);
      28            1 :     }, []);
      29            1 :     return React.useCallback(() => {
      30            0 :         const view = last.current;
      31              :         // A destroyed view leaves the document
      32            0 :         if (view == null || !view.dom.isConnected) {
      33            0 :             last.current = null;
      34            0 :             return null;
      35            0 :         }
      36            0 :         return view;
      37            1 :     }, []);
      38            1 : }
      39              : 
      40              : /// Insert a mention at the cursor of an editor, separated by spaces from the text around it, and focus the editor
      41            1 : export function insertMentionAtCursor(view: EditorView, mention: string): void {
      42            1 :     const text = view.state.doc;
      43            1 :     const cursor = view.state.selection.main;
      44            1 :     const charBefore = cursor.from > 0 ? text.sliceString(cursor.from - 1, cursor.from) : '';
      45            1 :     const charAfter = cursor.to < text.length ? text.sliceString(cursor.to, cursor.to + 1) : '';
      46            1 :     const before = charBefore !== '' && !/\s|\(/.test(charBefore) ? ' ' : '';
      47            1 :     const after = charAfter !== '' && /\s|\)|;|,/.test(charAfter) ? '' : ' ';
      48            1 :     const insert = `${before}${mention}${after}`;
      49            1 :     view.dispatch({
      50            1 :         changes: { from: cursor.from, to: cursor.to, insert },
      51            1 :         selection: { anchor: cursor.from + insert.length },
      52            1 :         effects: DashQLCompletionAbortEffect.of(null),
      53            1 :         userEvent: 'input',
      54            1 :         scrollIntoView: true,
      55            1 :     });
      56            1 :     view.focus();
      57            1 : }
        

Generated by: LCOV version 2.0-1