@@ -24,6 +24,7 @@ import {
2424 exportedUris ,
2525 getWsFolder ,
2626 handleError ,
27+ isClass ,
2728 isClassDeployed ,
2829 isClassOrRtn ,
2930 isCompilable ,
@@ -37,6 +38,7 @@ import {
3738import { StudioActions } from "./studio" ;
3839import { NodeBase , PackageNode , RootNode } from "../explorer/nodes" ;
3940import { getUrisForDocument , updateIndex } from "../utils/documentIndex" ;
41+ import { Document } from "../api/atelier" ;
4042
4143/**
4244 * For files being locally edited, get and return its mtime timestamp from workspace-state cache if present there,
@@ -80,8 +82,10 @@ export async function checkChangedOnServer(file: CurrentTextFile | CurrentBinary
8082 return mtime ;
8183}
8284
85+ // Synchronize the client version and the server version of the same file
8386export async function importFile (
8487 file : CurrentTextFile | CurrentBinaryFile ,
88+ willCompile : boolean ,
8589 ignoreConflict ?: boolean ,
8690 skipDeplCheck = false
8791) : Promise < any > {
@@ -114,78 +118,88 @@ export async function importFile(
114118 mtime < 0 ||
115119 ( notIsfs ( file . uri ) &&
116120 vscode . workspace . getConfiguration ( "objectscript" , file . uri ) . get < boolean > ( "overwriteServerChanges" ) ) ;
117- return api
118- . putDoc (
121+ try {
122+ const data = await api . putDoc (
119123 file . name ,
120124 {
121125 content,
122126 enc,
123127 mtime,
124128 } ,
125129 ignoreConflict
126- )
127- . then ( ( data ) => {
128- // Update cache entry
129- workspaceState . update ( `${ file . uniqueId } :mtime` , Number ( new Date ( data . result . ts + "Z" ) ) ) ;
130+ ) ;
131+ // Update cache entry
132+ workspaceState . update ( `${ file . uniqueId } :mtime` , Number ( new Date ( data . result . ts + "Z" ) ) ) ;
130133
131- // In case another extension has used an 'objectscript://' uri to load a document read-only from the server,
132- // make it reload with what we just imported to the server.
133- const serverUri = DocumentContentProvider . getUri (
134- file . name ,
135- file . workspaceFolder ,
136- undefined ,
137- false ,
138- undefined ,
139- true
140- ) ;
141- if ( serverUri ) documentContentProvider . update ( serverUri ) ;
142- } )
143- . catch ( ( error ) => {
144- if ( error ?. statusCode == 409 ) {
145- const choices : string [ ] = [ ] ;
146- if ( ! enc ) {
147- choices . push ( "Compare" ) ;
148- }
149- choices . push ( "Overwrite on Server" , "Pull Server Changes" , "Cancel" ) ;
150- return vscode . window
151- . showErrorMessage (
152- `Failed to import '${ file . name } ': The version of the file on the server is newer.
134+ if ( ! willCompile && isClass ( file . name ) && data . result . content . length ) {
135+ // In this case, the file must be a CLS and data.result.content must be the new Storage definitions
136+ // (with the rest of the class if flags === 0)
137+ const oldContent = new TextDecoder ( ) . decode ( await vscode . workspace . fs . readFile ( file . uri ) ) ;
138+ const oldContentArray = oldContent . split ( / \r ? \n / ) ;
139+ const storage = Buffer . isBuffer ( data . result . content )
140+ ? new TextDecoder ( ) . decode ( data . result . content ) . split ( / \r ? \n / )
141+ : data . result . content ;
142+ const newContentArray = updateStorage ( oldContentArray , storage ) ;
143+ if ( oldContentArray . some ( ( oldLine , index ) => oldLine !== newContentArray [ index ] ) ) {
144+ const EOL = ( ( < CurrentTextFile > file ) ?. eol ?? vscode . EndOfLine . LF ) == vscode . EndOfLine . CRLF ? "\r\n" : "\n" ;
145+ const newContent = newContentArray . join ( EOL ) ;
146+ await vscode . workspace . fs . writeFile ( file . uri , new TextEncoder ( ) . encode ( newContent ) ) ;
147+ }
148+ }
149+ // In case another extension has used an 'objectscript://' uri to load a document read-only from the server,
150+ // make it reload with what we just imported to the server.
151+ const serverUri = DocumentContentProvider . getUri (
152+ file . name ,
153+ file . workspaceFolder ,
154+ undefined ,
155+ false ,
156+ undefined ,
157+ true
158+ ) ;
159+ if ( serverUri ) documentContentProvider . update ( serverUri ) ;
160+ } catch ( error ) {
161+ if ( error ?. statusCode == 409 ) {
162+ const choices : string [ ] = [ ] ;
163+ if ( ! enc ) {
164+ choices . push ( "Compare" ) ;
165+ }
166+ choices . push ( "Overwrite on Server" , "Pull Server Changes" , "Cancel" ) ;
167+ const action = await vscode . window . showErrorMessage (
168+ `Failed to import '${ file . name } ': The version of the file on the server is newer.
153169What do you want to do?` ,
154- ...choices
155- )
156- . then ( ( action ) => {
157- switch ( action ) {
158- case "Compare" :
159- return vscode . commands
160- . executeCommand (
161- "vscode.diff" ,
162- vscode . Uri . file ( file . name ) . with ( {
163- scheme : OBJECTSCRIPT_FILE_SCHEMA ,
164- authority : file . workspaceFolder ,
165- query : file . name . includes ( "/" ) ? "csp" : "" ,
166- } ) ,
167- file . uri ,
168- `Server • ${ file . name } ↔ Local • ${ file . fileName } `
169- )
170- . then ( ( ) => Promise . reject ( ) ) ;
171- case "Overwrite on Server" :
172- // Clear cache entry
173- workspaceState . update ( `${ file . uniqueId } :mtime` , undefined ) ;
174- // Overwrite
175- return importFile ( file , true , true ) ;
176- case "Pull Server Changes" :
177- loadChanges ( [ file ] ) ;
178- return Promise . reject ( ) ;
179- case "Cancel" :
180- return Promise . reject ( ) ;
181- }
182- return Promise . reject ( ) ;
183- } ) ;
184- } else {
185- handleError ( error , `Failed to save file '${ file . name } ' on the server.` ) ;
186- return Promise . reject ( ) ;
170+ ...choices
171+ ) ;
172+ switch ( action ) {
173+ case "Compare" :
174+ return vscode . commands
175+ . executeCommand (
176+ "vscode.diff" ,
177+ vscode . Uri . file ( file . name ) . with ( {
178+ scheme : OBJECTSCRIPT_FILE_SCHEMA ,
179+ authority : file . workspaceFolder ,
180+ query : file . name . includes ( "/" ) ? "csp" : "" ,
181+ } ) ,
182+ file . uri ,
183+ `Server • ${ file . name } ↔ Local • ${ file . fileName } `
184+ )
185+ . then ( ( ) => Promise . reject ( ) ) ;
186+ case "Overwrite on Server" :
187+ // Clear cache entry
188+ workspaceState . update ( `${ file . uniqueId } :mtime` , undefined ) ;
189+ // Overwrite
190+ return importFile ( file , willCompile , true , true ) ;
191+ case "Pull Server Changes" :
192+ loadChanges ( [ file ] ) ;
193+ return Promise . reject ( ) ;
194+ case "Cancel" :
195+ return Promise . reject ( ) ;
187196 }
188- } ) ;
197+ return Promise . reject ( ) ;
198+ } else {
199+ handleError ( error , `Failed to save file '${ file . name } ' on the server.` ) ;
200+ return Promise . reject ( ) ;
201+ }
202+ }
189203}
190204
191205function updateOthers ( others : string [ ] , baseUri : vscode . Uri ) {
@@ -218,7 +232,21 @@ export async function loadChanges(files: (CurrentTextFile | CurrentBinaryFile)[]
218232 const mtime = Number ( new Date ( doc . ts + "Z" ) ) ;
219233 workspaceState . update ( `${ file . uniqueId } :mtime` , mtime > 0 ? mtime : undefined ) ;
220234 if ( notIsfs ( file . uri ) ) {
221- const content = await api . getDoc ( file . name , file . uri ) . then ( ( data ) => data . result . content ) ;
235+ let content : Document [ "content" ] ;
236+ if (
237+ ! (
238+ isClass ( file . uri . path ) &&
239+ ! vscode . workspace . getConfiguration ( "objectscript" , file . uri ) . get ( "refreshClassesOnSync" )
240+ )
241+ ) {
242+ content = ( await api . getDoc ( file . name , file . uri ) ) . result . content ;
243+ } else {
244+ // Insert/update the storage part of class definition.
245+ content = new TextDecoder ( ) . decode ( await vscode . workspace . fs . readFile ( file . uri ) ) . split ( / \r ? \n / ) ;
246+ let storage = ( await api . getDoc ( file . name , file . uri , undefined , true ) ) . result . content ;
247+ storage = Buffer . isBuffer ( storage ) ? new TextDecoder ( ) . decode ( storage ) . split ( / \r ? \n / ) : storage ;
248+ content = updateStorage ( content , storage ) ;
249+ }
222250 exportedUris . add ( file . uri . toString ( ) ) ; // Set optimistically
223251 await vscode . workspace . fs
224252 . writeFile (
@@ -250,6 +278,52 @@ export async function loadChanges(files: (CurrentTextFile | CurrentBinaryFile)[]
250278 ) ;
251279}
252280
281+ function updateStorage ( content : string [ ] , storage : string [ ] ) : string [ ] {
282+ const storageMap = storageToMap ( storage ) ;
283+ let contentString = content . join ( "\n" ) ;
284+ contentString = contentString
285+ // update existing Storages
286+ . replaceAll ( / \n ( \s * s t o r a g e \s + ( \w + ) \s * { \s * ) ( [ ^ } ] * ?) ( \s * } ) / gim, ( _match , beforeXML , name , _oldXML , afterXML ) => {
287+ const newXML = storageMap . get ( name ) ;
288+ if ( newXML === undefined ) {
289+ return "" ;
290+ }
291+ storageMap . delete ( name ) ;
292+ return "\n" + beforeXML + newXML + afterXML ;
293+ } ) ;
294+ contentString = contentString
295+ // insert remaining Storages
296+ . replace ( / } \s * $ / , ( m ) => {
297+ for ( const [ name , content ] of storageMap . entries ( ) ) {
298+ m = `Storage ${ name } \n{\n${ content } \n}\n\n${ m } ` ;
299+ }
300+ return m ;
301+ } ) ;
302+ return contentString . split ( "\n" ) ;
303+ }
304+
305+ function storageToMap ( storage : string [ ] ) : Map < string , string > {
306+ const map : Map < string , string > = new Map ( ) ;
307+ let k : string ;
308+ let v = [ ] ;
309+ for ( const line of storage ) {
310+ if ( line . startsWith ( "Storage " ) ) {
311+ k = line . slice ( "Storage " . length , line . length ) ;
312+ v = [ ] ;
313+ } else if ( k !== undefined ) {
314+ if ( line === "{" ) {
315+ continue ;
316+ } else if ( line === "}" ) {
317+ map . set ( k , v . join ( "\n" ) ) ;
318+ k = undefined ;
319+ } else {
320+ v . push ( line ) ;
321+ }
322+ }
323+ }
324+ return map ;
325+ }
326+
253327export async function compile ( docs : ( CurrentTextFile | CurrentBinaryFile ) [ ] , askFlags = false ) : Promise < any > {
254328 docs = docs . filter ( notNull ) ;
255329 if ( ! docs . length ) return ;
@@ -318,14 +392,16 @@ export async function importAndCompile(document?: vscode.TextDocument, askFlags
318392 return ;
319393 }
320394
321- return (
322- importFile ( file )
323- . then ( ( ) => {
324- if ( isCompilable ( file . name ) ) compile ( [ file ] , askFlags ) ;
325- } )
326- // importFile handles any server errors
327- . catch ( ( ) => { } )
328- ) ;
395+ try {
396+ if ( isCompilable ( file . name ) ) {
397+ await importFile ( file , true ) ;
398+ compile ( [ file ] , askFlags ) ;
399+ } else {
400+ await importFile ( file , false ) ;
401+ }
402+ } catch {
403+ // importFile handles any server errors
404+ }
329405}
330406
331407export async function compileOnly ( document ?: vscode . TextDocument , askFlags = false ) : Promise < any > {
@@ -395,28 +471,26 @@ async function importFiles(files: vscode.Uri[], noCompile = false) {
395471 await Promise . allSettled < void > (
396472 files . map ( ( uri ) =>
397473 rateLimiter . call ( async ( ) => {
398- return vscode . workspace . fs
399- . readFile ( uri )
400- . then ( ( contentBytes ) =>
401- currentFileFromContent (
402- uri ,
403- isText ( uri . path . split ( "/" ) . pop ( ) , Buffer . from ( contentBytes ) )
404- ? new TextDecoder ( ) . decode ( contentBytes )
405- : Buffer . from ( contentBytes )
406- )
407- )
408- . then ( ( curFile ) => {
409- if ( curFile ) {
410- if ( typeof curFile . content == "string" && isCompilable ( curFile . name ) ) toCompile . push ( curFile ) ;
411- return importFile ( curFile ) . then ( ( ) => outputChannel . appendLine ( "Imported file: " + curFile . fileName ) ) ;
412- }
413- } ) ;
474+ const contentBytes = await vscode . workspace . fs . readFile ( uri ) ;
475+ const curFile = currentFileFromContent (
476+ uri ,
477+ isText ( uri . path . split ( "/" ) . pop ( ) , Buffer . from ( contentBytes ) )
478+ ? new TextDecoder ( ) . decode ( contentBytes )
479+ : Buffer . from ( contentBytes )
480+ ) ;
481+ if ( curFile ) {
482+ if ( typeof curFile . content == "string" && isCompilable ( curFile . name ) ) {
483+ toCompile . push ( curFile ) ;
484+ }
485+ await importFile ( curFile , ! noCompile ) ;
486+ outputChannel . appendLine ( "Imported file: " + curFile . fileName ) ;
487+ }
414488 } )
415489 )
416490 ) ;
417491
418492 if ( ! noCompile && toCompile . length > 0 ) {
419- return compile ( toCompile ) ;
493+ await compile ( toCompile ) ;
420494 }
421495 return ;
422496}
0 commit comments