LCOV - code coverage report
Current view: top level - dashql-app/src/session/editor - readonly_sql_view.tsx Coverage Total Hit
Test: DashQL 915828b Lines: 98.7 % 75 74
Test Date: 2026-11-06 19:30:32

            Line data    Source code
       1              : import * as React from 'react';
       2              : import * as dashql from '@ankoh/dashql-core';
       3              : 
       4              : import { EditorView } from '@codemirror/view';
       5              : 
       6              : import { CodeMirror, createReadonlyCodeMirrorExtensions } from './codemirror.js';
       7              : import { DashQLUpdateEffect } from './dashql_processor.js';
       8              : import { useDashQLCoreSetup } from '../../runtime/core_provider.js';
       9              : import { formatSqlText } from '../scripts/script_format.js';
      10              : 
      11              : /// Analyze a SQL text in a throwaway script session, the result highlights the text in a read-only view
      12            1 : export function projectSqlText(
      13            1 :     core: dashql.DashQL,
      14            1 :     catalog: dashql.DashQLCatalog,
      15            1 :     text: string,
      16            1 : ): dashql.buffers.editor.EditorUpdateT {
      17            1 :     const session = core.createScriptSession(catalog);
      18            1 :     try {
      19            1 :         session.replaceText(0n, text);
      20            1 :         return session.analyze();
      21            1 :     } finally {
      22            1 :         session.destroy();
      23            1 :     }
      24            1 : }
      25              : 
      26              : /// A SQL text with the analysis that highlights it
      27              : export interface ProjectedSqlText {
      28              :     /// The text, formatted if a formatting was requested and the formatter can format all of it
      29              :     text: string;
      30              :     /// The analysis of the text, null until the core is set up
      31              :     editorUpdate: dashql.buffers.editor.EditorUpdateT | null;
      32              : }
      33              : 
      34              : /// Format and analyze a SQL text against an empty catalog once the core is set up
      35            1 : export function useProjectedSqlText(
      36            1 :     text: string,
      37            1 :     context: string,
      38            1 :     format: dashql.buffers.formatting.FormattingConfigT | null = null,
      39            1 : ): ProjectedSqlText {
      40            1 :     const coreSetup = useDashQLCoreSetup();
      41            1 :     const [core, setCore] = React.useState<dashql.DashQL | null>(null);
      42            1 :     React.useEffect(() => {
      43            1 :         if (coreSetup == null) return;
      44            1 :         let cancelled = false;
      45            1 :         coreSetup(context).then(c => {
      46            1 :             if (!cancelled) setCore(c);
      47            1 :         });
      48            1 :         return () => { cancelled = true; };
      49            1 :     }, [coreSetup, context]);
      50            1 :     return React.useMemo(() => {
      51            1 :         if (core == null) return { text, editorUpdate: null };
      52            1 :         const formatted = format == null ? text : formatSqlText(core, text, format) ?? text;
      53            1 :         const catalog = core.createCatalog();
      54            1 :         try {
      55            1 :             return { text: formatted, editorUpdate: projectSqlText(core, catalog, formatted) };
      56            1 :         } catch {
      57            0 :             return { text: formatted, editorUpdate: null };
      58            1 :         } finally {
      59            1 :             catalog.ptr.destroy();
      60            1 :         }
      61            1 :     }, [core, text, format]);
      62            1 : }
      63              : 
      64              : export interface ReadonlySqlViewProps {
      65              :     /// The SQL text
      66              :     text: string;
      67              :     /// The analysis of the text that highlights it, null for plain text
      68              :     editorUpdate: dashql.buffers.editor.EditorUpdateT | null;
      69              :     /// Wrap long lines instead of scrolling horizontally
      70              :     lineWrapping?: boolean;
      71              :     /// Grow with the text instead of filling the container
      72              :     autoHeight?: boolean;
      73              :     /// Called with the view once it is created and whenever its size or font metrics change, e.g. to fit a text to its width
      74              :     onResize?: (view: EditorView) => void;
      75              : }
      76              : 
      77              : /// A read-only CodeMirror view of a SQL text, highlighted by DashQL
      78            1 : export function ReadonlySqlView(props: ReadonlySqlViewProps) {
      79            1 :     const [view, setView] = React.useState<EditorView | null>(null);
      80            1 :     const onResize = React.useRef(props.onResize);
      81            1 :     React.useLayoutEffect(() => { onResize.current = props.onResize; });
      82            1 :     const extensions = React.useMemo(() => {
      83            1 :         const base = createReadonlyCodeMirrorExtensions();
      84              :         // CodeMirror reports changes of the size and of the character width, e.g. once the font loaded
      85            1 :         const geometry = EditorView.updateListener.of(update => {
      86            1 :             if (update.geometryChanged) onResize.current?.(update.view);
      87            1 :         });
      88            1 :         return props.lineWrapping ? [...base, EditorView.lineWrapping, geometry] : [...base, geometry];
      89            1 :     }, [props.lineWrapping]);
      90              : 
      91            1 :     React.useEffect(() => {
      92            1 :         if (view != null) onResize.current?.(view);
      93            1 :     }, [view]);
      94              : 
      95            1 :     React.useEffect(() => {
      96            1 :         if (view == null) return;
      97            1 :         view.dispatch({
      98            1 :             changes: { from: 0, to: view.state.doc.length, insert: props.text },
      99            1 :             effects: props.editorUpdate == null ? [] : [
     100            1 :                 DashQLUpdateEffect.of({
     101            1 :                     scriptKey: 0,
     102            1 :                     scriptSession: null,
     103            1 :                     editorUpdate: props.editorUpdate,
     104            1 :                     scriptBuffers: null,
     105            1 :                     scriptCompletion: null,
     106            1 :                     scriptPendingDiff: null,
     107            1 :                     derivedFocus: null,
     108            1 :                     onUpdate: () => { },
     109            1 :                 }),
     110            1 :             ],
     111            1 :         });
     112            1 :     }, [view, props.text, props.editorUpdate]);
     113              : 
     114            1 :     return <CodeMirror ref={setView} extensions={extensions} style={props.autoHeight ? { height: 'auto' } : undefined} />;
     115            1 : }
        

Generated by: LCOV version 2.0-1