@@ -15,7 +15,7 @@ declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...a
1515export type Entry = File | Directory ;
1616
1717export class FileSystemProvider implements vscode . FileSystemProvider {
18- public root = new Directory ( "" , "" ) ;
18+ private superRoot = new Directory ( "" , "" ) ;
1919
2020 public readonly onDidChangeFile : vscode . Event < vscode . FileChangeEvent [ ] > ;
2121
@@ -86,9 +86,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
8686 )
8787 . map ( ( item : StudioOpenDialog ) => {
8888 const name = item . Name ;
89- const fullName = folder === "" ? name : csp ? folder + name : folder + "/" + name ;
9089 if ( item . Type === "10" || item . Type === "9" ) {
9190 if ( ! parent . entries . has ( name ) ) {
91+ const fullName = folder === "" ? name : csp ? folder + name : folder + "/" + name ;
9292 parent . entries . set ( name , new Directory ( name , fullName ) ) ;
9393 }
9494 return [ name , vscode . FileType . Directory ] ;
@@ -99,7 +99,15 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
9999 if ( ! csp ) {
100100 return results ;
101101 }
102- return results . concat ( cspSubfolders ) ;
102+ cspSubfolders . forEach ( ( value ) => {
103+ const name = value [ 0 ] ;
104+ if ( ! parent . entries . has ( name ) ) {
105+ const fullName = folder + name ;
106+ parent . entries . set ( name , new Directory ( name , fullName ) ) ;
107+ }
108+ results . push ( value ) ;
109+ } ) ;
110+ return results ;
103111 } )
104112 . catch ( ( error ) => {
105113 if ( error ) {
@@ -143,24 +151,42 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
143151 } ) ;
144152 }
145153
146- private generateFileContent ( fileName : string , content : Buffer ) : { content : string [ ] ; enc : boolean } {
154+ private generateFileContent ( fileName : string , sourceContent : Buffer ) : { content : string [ ] ; enc : boolean } {
155+ const sourceLines = sourceContent . toString ( ) . split ( "\n" ) ;
147156 const fileExt = fileName . split ( "." ) . pop ( ) . toLowerCase ( ) ;
148157 if ( fileExt === "cls" ) {
149158 const className = fileName . split ( "." ) . slice ( 0 , - 1 ) . join ( "." ) ;
159+ const content : string [ ] = [ ] ;
160+ const preamble : string [ ] = [ ] ;
161+
162+ // If content was provided (e.g. when copying a file), use all lines except for
163+ // the Class x.y one. Replace that with one to match fileName.
164+ while ( sourceLines . length > 0 ) {
165+ const nextLine = sourceLines . shift ( ) ;
166+ if ( nextLine . startsWith ( "Class " ) ) {
167+ content . push ( ...preamble , `Class ${ className } ` , ...sourceLines ) ;
168+ break ;
169+ }
170+ preamble . push ( nextLine ) ;
171+ }
172+ if ( content . length === 0 ) {
173+ content . push ( `Class ${ className } ` , "{" , "}" ) ;
174+ }
150175 return {
151- content : [ `Class ${ className } {}` ] ,
176+ content,
152177 enc : false ,
153178 } ;
154179 } else if ( [ "int" , "inc" , "mac" ] . includes ( fileExt ) ) {
180+ sourceLines . shift ( ) ;
155181 const routineName = fileName . split ( "." ) . slice ( 0 , - 1 ) . join ( "." ) ;
156182 const routineType = `[ type = ${ fileExt } ]` ;
157183 return {
158- content : [ `ROUTINE ${ routineName } ${ routineType } ` ] ,
184+ content : [ `ROUTINE ${ routineName } ${ routineType } ` , ... sourceLines ] ,
159185 enc : false ,
160186 } ;
161187 }
162188 return {
163- content : [ content . toString ( "base64" ) ] ,
189+ content : [ sourceContent . toString ( "base64" ) ] ,
164190 enc : true ,
165191 } ;
166192 }
@@ -265,27 +291,42 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
265291 if ( fileName . startsWith ( "." ) ) {
266292 return ;
267293 }
294+ if ( ! fileName . includes ( "." ) ) {
295+ throw new Error ( `${ csp ? "Folder" : "Package" } deletion is not supported on server` ) ;
296+ }
268297 const api = new AtelierAPI ( uri ) ;
269- return api . deleteDoc ( fileName ) . then ( ( response ) => {
270- if ( response . result . ext ) {
271- fireOtherStudioAction ( OtherStudioAction . DeletedDocument , uri , response . result . ext ) ;
298+ return api . deleteDoc ( fileName ) . then (
299+ async ( response ) => {
300+ if ( response . result . ext ) {
301+ fireOtherStudioAction ( OtherStudioAction . DeletedDocument , uri , response . result . ext ) ;
302+ }
303+ // Remove entry from our cache, plus any now-empty ancestor entries
304+ let thisUri = vscode . Uri . parse ( uri . toString ( ) , true ) ;
305+ const events : vscode . FileChangeEvent [ ] = [ ] ;
306+ while ( thisUri . path !== "/" ) {
307+ events . push ( { type : vscode . FileChangeType . Deleted , uri : thisUri } ) ;
308+ const parentDir = await this . _lookupParentDirectory ( thisUri ) ;
309+ const name = path . basename ( thisUri . path ) ;
310+ parentDir . entries . delete ( name ) ;
311+ if ( ! csp && parentDir . entries . size === 0 ) {
312+ thisUri = thisUri . with ( { path : path . posix . dirname ( thisUri . path ) } ) ;
313+ } else {
314+ break ;
315+ }
316+ }
317+ this . _fireSoon ( ...events ) ;
318+ } ,
319+ ( error ) => {
320+ if ( error . errorText !== "" ) {
321+ error . message = error . errorText ;
322+ }
323+ throw error ;
272324 }
273- // Remove entry from our cache
274- this . _lookupParentDirectory ( uri ) . then ( ( parent ) => {
275- const name = path . basename ( uri . path ) ;
276- parent . entries . delete ( name ) ;
277- } ) ;
278- this . _fireSoon ( { type : vscode . FileChangeType . Deleted , uri } ) ;
279- } ) ;
325+ ) ;
280326 }
281327
282328 public rename ( oldUri : vscode . Uri , newUri : vscode . Uri , options : { overwrite : boolean } ) : void | Thenable < void > {
283- throw new Error ( "Not implemented" ) ;
284- return ;
285- }
286- public copy ?( source : vscode . Uri , destination : vscode . Uri , options : { overwrite : boolean } ) : void | Thenable < void > {
287- throw new Error ( "Not implemented" ) ;
288- return ;
329+ throw new Error ( "Move / rename is not supported on server" ) ;
289330 }
290331
291332 public watch ( uri : vscode . Uri ) : vscode . Disposable {
@@ -296,17 +337,23 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
296337
297338 // Fetch entry (a file or directory) from cache, else from server
298339 private async _lookup ( uri : vscode . Uri ) : Promise < Entry > {
340+ const api = new AtelierAPI ( uri ) ;
299341 if ( uri . path === "/" ) {
300- const api = new AtelierAPI ( uri ) ;
301342 await api
302343 . serverInfo ( )
303344 . then ( )
304345 . catch ( ( ) => {
305346 throw vscode . FileSystemError . Unavailable ( `${ uri . toString ( ) } is unavailable` ) ;
306347 } ) ;
307348 }
349+ const config = api . config ;
350+ const rootName = `${ config . username } @${ config . host } :${ config . port } ${ config . pathPrefix } /${ config . ns . toUpperCase ( ) } ` ;
351+ let entry : Entry = this . superRoot . entries . get ( rootName ) ;
352+ if ( ! entry ) {
353+ entry = new Directory ( rootName , "" ) ;
354+ this . superRoot . entries . set ( rootName , entry ) ;
355+ }
308356 const parts = uri . path . split ( "/" ) ;
309- let entry : Entry = this . root ;
310357 for ( let i = 0 ; i < parts . length ; i ++ ) {
311358 const part = parts [ i ] ;
312359 if ( ! part ) {
@@ -317,10 +364,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
317364 child = entry . entries . get ( part ) ;
318365 // If the last element of path is dotted and is one we haven't already cached as a directory
319366 // then it is assumed to be a file.
320- if ( ( ! part . includes ( "." ) || i + 1 < parts . length ) && ! child ) {
321- const fullName = entry . name === "" ? part : entry . fullName + "/" + part ;
322- child = new Directory ( part , fullName ) ;
323- entry . entries . set ( part , child ) ;
367+ if ( ! child && ( ! part . includes ( "." ) || i + 1 < parts . length ) ) {
368+ throw vscode . FileSystemError . FileNotFound ( uri ) ;
324369 }
325370 }
326371 if ( ! child ) {
0 commit comments