1+ import { useCallback , useEffect , useMemo , useState } from 'react'
2+ import CodeMirror from '@uiw/react-codemirror'
3+ import { python } from '@codemirror/lang-python'
4+ import { StreamLanguage } from '@codemirror/language'
5+
6+ import { yaml } from '@codemirror/legacy-modes/mode/yaml'
7+ import { type Extension } from '@codemirror/state'
8+ import { type Model } from '~/api/client'
9+ import { useStoreFileTree } from '~/context/fileTree'
10+ import { type ModelFile } from '~/models'
11+ import {
12+ events ,
13+ SqlMeshModel ,
14+ HoverTooltip ,
15+ useSqlMeshExtension ,
16+ } from './extensions'
17+ import { sqlglotWorker } from '~/library/components/editor/workers'
18+ import { dracula , tomorrow } from 'thememirror'
19+ import { useColorScheme , EnumColorScheme } from '~/context/theme'
20+
21+ export default function CodeEditor ( {
22+ file,
23+ models,
24+ dialect,
25+ dialects,
26+ onChange,
27+ } : {
28+ file : ModelFile
29+ models : Map < string , Model >
30+ dialect ?: string
31+ dialects ?: string [ ]
32+ onChange : ( value : string ) => void
33+ } ) : JSX . Element {
34+ const { mode } = useColorScheme ( )
35+ const theme = mode === EnumColorScheme . Dark ? dracula : tomorrow
36+
37+ const files = useStoreFileTree ( s => s . files )
38+ const selectFile = useStoreFileTree ( s => s . selectFile )
39+
40+ const [ sqlDialectOptions , setSqlDialectOptions ] = useState ( )
41+
42+ const [ SqlMeshDialectExtension , SqlMeshDialectCleanUp ] =
43+ useSqlMeshExtension ( dialects )
44+
45+ const extensions = useMemo ( ( ) => {
46+ const showSqlSqlMeshDialect =
47+ file . extension === '.sql' && models != null && sqlDialectOptions != null
48+ return [
49+ models != null && HoverTooltip ( models ) ,
50+ models != null && events ( models , files , selectFile ) ,
51+ models != null && SqlMeshModel ( models ) ,
52+ showSqlSqlMeshDialect &&
53+ SqlMeshDialectExtension ( models , file , sqlDialectOptions ) ,
54+ file . extension === '.py' && python ( ) ,
55+ file . extension === '.yaml' && StreamLanguage . define ( yaml ) ,
56+ theme ,
57+ ] . filter ( Boolean ) as Extension [ ]
58+ } , [ file , models , sqlDialectOptions ] )
59+
60+ const handleSqlGlotWorkerMessage = useCallback ( ( e : MessageEvent ) : void => {
61+ if ( e . data . topic === 'dialect' ) {
62+ setSqlDialectOptions ( e . data . payload )
63+ }
64+ } , [ ] )
65+
66+ useEffect ( ( ) => {
67+ sqlglotWorker . addEventListener ( 'message' , handleSqlGlotWorkerMessage )
68+
69+ return ( ) => {
70+ sqlglotWorker . removeEventListener ( 'message' , handleSqlGlotWorkerMessage )
71+ SqlMeshDialectCleanUp ( )
72+ }
73+ } , [ ] )
74+
75+ useEffect ( ( ) => {
76+ sqlglotWorker . postMessage ( {
77+ topic : 'parse' ,
78+ payload : file . content ,
79+ } )
80+ } , [ file . content , sqlglotWorker ] )
81+
82+ useEffect ( ( ) => {
83+ sqlglotWorker . postMessage ( {
84+ topic : 'dialect' ,
85+ payload : dialect ,
86+ } )
87+ } , [ dialect , sqlglotWorker ] )
88+
89+ return (
90+ < CodeMirror
91+ value = { file . content }
92+ height = "100%"
93+ width = "100%"
94+ className = "w-full h-full overflow-auto text-sm"
95+ extensions = { extensions }
96+ onChange = { onChange }
97+ />
98+ )
99+ }
0 commit comments