@@ -56,21 +56,13 @@ import CodeEditor from './CodeEditor'
5656import { type PropsComponent } from '~/main'
5757import { sqlglotWorker } from './workers'
5858
59- export const EnumEditorFileStatus = {
60- Edit : 'edit' ,
61- Editing : 'editing' ,
62- Saving : 'saving' ,
63- Saved : 'saved' ,
64- } as const
65-
6659export const EnumEditorTabs = {
6760 QueryPreview : 'queryPreview' ,
6861 Table : 'table' ,
6962 Terminal : 'terminal' ,
7063} as const
7164
7265export type EditorTabs = KeyOf < typeof EnumEditorTabs >
73- export type EditorFileStatus = KeyOf < typeof EnumEditorFileStatus >
7466
7567interface PropsEditor extends React . HTMLAttributes < HTMLElement > {
7668 environment : ModelEnvironment
@@ -110,9 +102,6 @@ export default function Editor({
110102 const tabTableContent = useStoreEditor ( s => s . tabTableContent )
111103 const tabTerminalContent = useStoreEditor ( s => s . tabTerminalContent )
112104
113- const [ fileStatus , setEditorFileStatus ] = useState < EditorFileStatus > (
114- EnumEditorFileStatus . Edit ,
115- )
116105 const [ isSaved , setIsSaved ] = useState ( true )
117106 const [ formEvaluate , setFormEvaluate ] = useState < FormEvaluate > ( {
118107 start : toDateFormat ( toDate ( Date . now ( ) - DAY ) ) ,
@@ -134,37 +123,29 @@ export default function Editor({
134123 const { data : fileData , refetch : getFileContent } = useApiFileByPath (
135124 activeFile . path ,
136125 )
126+ const debouncedPlanRun = useCallback ( debounceAsync ( planRun , 1000 , true ) , [
127+ planRun ,
128+ ] )
129+
137130 const mutationSaveFile = useMutationApiSaveFile ( client , {
138131 onSuccess ( file : File ) {
139132 setIsSaved ( true )
140- setEditorFileStatus ( EnumEditorFileStatus . Edit )
141133
142134 if ( file == null ) return
143135
144- activeFile . content = file . content ?? ''
136+ activeFile . updateContent ( file . content )
145137
146138 setOpenedFiles ( openedFiles )
139+
140+ void debouncedPlanRun ( )
147141 } ,
148142 onMutate ( ) {
149143 setIsSaved ( false )
150- setEditorFileStatus ( EnumEditorFileStatus . Saving )
151144 } ,
152145 } )
153- const debouncedPlanRun = useCallback ( debounceAsync ( planRun , 5000 ) , [
154- planRun ,
155- ] )
156- const debouncedChange = useMemo (
157- ( ) =>
158- debounce (
159- onChange ,
160- ( ) => {
161- setEditorFileStatus ( EnumEditorFileStatus . Editing )
162- } ,
163- ( ) => {
164- setEditorFileStatus ( EnumEditorFileStatus . Edit )
165- } ,
166- 1000 ,
167- ) ,
146+
147+ const debouncedSaveChange = useMemo (
148+ ( ) => debounce ( saveChange , 1000 , true ) ,
168149 [ activeFile ] ,
169150 )
170151
@@ -197,25 +178,26 @@ export default function Editor({
197178 } , [ handleSqlGlotWorkerMessage ] )
198179
199180 useEffect ( ( ) => {
200- if ( activeFile . isSQLMeshModel || activeFile . isSQLMeshSeed ) {
201- void debouncedPlanRun ( )
202- }
203-
204181 return ( ) => {
182+ debouncedPlanRun . cancel ( )
183+
205184 apiCancelPlanRun ( client )
206185 }
207- } , [ activeFile . content ] )
186+ } , [ ] )
208187
209188 useEffect ( ( ) => {
210189 if ( fileData == null ) return
211190
212- activeFile . content = fileData . content ?? ''
191+ activeFile . updateContent ( fileData . content )
213192
214193 setOpenedFiles ( openedFiles )
215194 } , [ fileData ] )
216195
217196 useEffect ( ( ) => {
218- if ( isFalse ( isStringEmptyOrNil ( activeFile . path ) ) ) {
197+ if (
198+ isFalse ( isStringEmptyOrNil ( activeFile . path ) ) &&
199+ isStringEmptyOrNil ( activeFile . content )
200+ ) {
219201 void getFileContent ( )
220202 }
221203
@@ -257,19 +239,19 @@ export default function Editor({
257239 selectFile ( new ModelFile ( ) )
258240 }
259241
260- function onChange ( value : string ) : void {
242+ function updateFileContent ( value : string ) : void {
261243 if ( activeFile . content === value ) return
262244
263245 activeFile . content = value
264246
265- if ( activeFile . isLocal ) {
266- setOpenedFiles ( openedFiles )
267- } else {
268- mutationSaveFile . mutate ( {
269- path : activeFile . path ,
270- body : { content : value } ,
271- } )
272- }
247+ setOpenedFiles ( openedFiles )
248+ }
249+
250+ function saveChange ( ) : void {
251+ mutationSaveFile . mutate ( {
252+ path : activeFile . path ,
253+ body : { content : activeFile . content } ,
254+ } )
273255 }
274256
275257 function sendQuery ( ) : void {
@@ -314,7 +296,10 @@ export default function Editor({
314296 model : formEvaluate . model ,
315297 } )
316298 . then ( updateTabs )
317- . catch ( console . log )
299+ . catch ( error => {
300+ bucket . set ( EnumEditorTabs . Terminal , error . message )
301+ setTabTerminalContent ( bucket . get ( EnumEditorTabs . Terminal ) )
302+ } )
318303 }
319304 }
320305
@@ -345,10 +330,6 @@ export default function Editor({
345330 }
346331 }
347332
348- function cleanUp ( ) : void {
349- setEditorFileStatus ( EnumEditorFileStatus . Edit )
350- }
351-
352333 // TODO: remove once we have a better way to determine if a file is a model
353334 const hasContentActiveFile = isFalse ( isStringEmptyOrNil ( activeFile . content ) )
354335 const shouldEvaluate =
@@ -363,7 +344,7 @@ export default function Editor({
363344 : [ 100 , 0 ]
364345 const sizesMain =
365346 activeFile . isSQLMeshModel &&
366- [ tabTableContent , tabTerminalContent ] . some ( isTrue )
347+ [ tabTableContent , tabTerminalContent ] . some ( Boolean )
367348 ? [ 75 , 25 ]
368349 : [ 100 , 0 ]
369350
@@ -413,13 +394,15 @@ export default function Editor({
413394 < small className = "text-xs" >
414395 { file . isUntitled ? `SQL-${ idx + 1 } ` : file . name }
415396 </ small >
397+ { file . isChanged && (
398+ < small className = "group-hover:hidden text-xs inline-block mx-2 w-2 h-2 bg-warning-500 rounded-full" > </ small >
399+ ) }
416400 { openedFiles . size > 1 && (
417401 < XCircleIcon
418- className = "inline-block opacity-0 group-hover:opacity-100 text-neutral-600 dark:text-neutral-100 w-4 h-4 ml-2 cursor-pointer"
402+ className = "hidden group-hover:inline-block text-neutral-600 dark:text-neutral-100 w-4 h-4 ml-2 cursor-pointer"
419403 onClick = { ( e : MouseEvent ) => {
420404 e . stopPropagation ( )
421405
422- cleanUp ( )
423406 closeEditorTab ( file )
424407 } }
425408 />
@@ -442,7 +425,8 @@ export default function Editor({
442425 < div className = "flex flex-col h-full" >
443426 { models != null && (
444427 < CodeEditor
445- onChange = { debouncedChange }
428+ onChange = { updateFileContent }
429+ saveChange = { debouncedSaveChange }
446430 models = { models }
447431 file = { activeFile }
448432 dialect = { dialect }
@@ -641,7 +625,6 @@ export default function Editor({
641625 < EditorFooter
642626 activeFile = { activeFile }
643627 isSaved = { isSaved }
644- fileStatus = { fileStatus }
645628 dialects = { dialects }
646629 dialect = { dialect }
647630 setDialect = { setDialect }
@@ -690,12 +673,10 @@ function EditorFooter({
690673 activeFile,
691674 isSaved,
692675 isValid,
693- fileStatus,
694676} : {
695677 activeFile : ModelFile
696678 isSaved : boolean
697679 isValid : boolean
698- fileStatus : string
699680 dialects : Array < { dialect_title : string ; dialect_name : string } >
700681 dialect ?: string
701682 setDialect : ( dialect ?: string ) => void
@@ -711,14 +692,9 @@ function EditorFooter({
711692 < Indicator
712693 className = "mr-2"
713694 text = "Saved"
714- ok = { isSaved }
695+ ok = { isFalse ( activeFile . isChanged ) }
715696 />
716697 ) }
717- < Indicator
718- className = "mr-2"
719- text = "Status"
720- value = { fileStatus }
721- />
722698 < Indicator
723699 className = "mr-2"
724700 text = "Language"
0 commit comments