@@ -15,7 +15,7 @@ declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...a
15
15
export type Entry = File | Directory ;
16
16
17
17
export class FileSystemProvider implements vscode . FileSystemProvider {
18
- public root = new Directory ( "" , "" ) ;
18
+ private superRoot = new Directory ( "" , "" ) ;
19
19
20
20
public readonly onDidChangeFile : vscode . Event < vscode . FileChangeEvent [ ] > ;
21
21
@@ -86,9 +86,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
86
86
)
87
87
. map ( ( item : StudioOpenDialog ) => {
88
88
const name = item . Name ;
89
- const fullName = folder === "" ? name : csp ? folder + name : folder + "/" + name ;
90
89
if ( item . Type === "10" || item . Type === "9" ) {
91
90
if ( ! parent . entries . has ( name ) ) {
91
+ const fullName = folder === "" ? name : csp ? folder + name : folder + "/" + name ;
92
92
parent . entries . set ( name , new Directory ( name , fullName ) ) ;
93
93
}
94
94
return [ name , vscode . FileType . Directory ] ;
@@ -99,7 +99,15 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
99
99
if ( ! csp ) {
100
100
return results ;
101
101
}
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 ;
103
111
} )
104
112
. catch ( ( error ) => {
105
113
if ( error ) {
@@ -143,24 +151,42 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
143
151
} ) ;
144
152
}
145
153
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" ) ;
147
156
const fileExt = fileName . split ( "." ) . pop ( ) . toLowerCase ( ) ;
148
157
if ( fileExt === "cls" ) {
149
158
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
+ }
150
175
return {
151
- content : [ `Class ${ className } {}` ] ,
176
+ content,
152
177
enc : false ,
153
178
} ;
154
179
} else if ( [ "int" , "inc" , "mac" ] . includes ( fileExt ) ) {
180
+ sourceLines . shift ( ) ;
155
181
const routineName = fileName . split ( "." ) . slice ( 0 , - 1 ) . join ( "." ) ;
156
182
const routineType = `[ type = ${ fileExt } ]` ;
157
183
return {
158
- content : [ `ROUTINE ${ routineName } ${ routineType } ` ] ,
184
+ content : [ `ROUTINE ${ routineName } ${ routineType } ` , ... sourceLines ] ,
159
185
enc : false ,
160
186
} ;
161
187
}
162
188
return {
163
- content : [ content . toString ( "base64" ) ] ,
189
+ content : [ sourceContent . toString ( "base64" ) ] ,
164
190
enc : true ,
165
191
} ;
166
192
}
@@ -265,27 +291,42 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
265
291
if ( fileName . startsWith ( "." ) ) {
266
292
return ;
267
293
}
294
+ if ( ! fileName . includes ( "." ) ) {
295
+ throw new Error ( `${ csp ? "Folder" : "Package" } deletion is not supported on server` ) ;
296
+ }
268
297
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 ;
272
324
}
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
+ ) ;
280
326
}
281
327
282
328
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" ) ;
289
330
}
290
331
291
332
public watch ( uri : vscode . Uri ) : vscode . Disposable {
@@ -296,17 +337,23 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
296
337
297
338
// Fetch entry (a file or directory) from cache, else from server
298
339
private async _lookup ( uri : vscode . Uri ) : Promise < Entry > {
340
+ const api = new AtelierAPI ( uri ) ;
299
341
if ( uri . path === "/" ) {
300
- const api = new AtelierAPI ( uri ) ;
301
342
await api
302
343
. serverInfo ( )
303
344
. then ( )
304
345
. catch ( ( ) => {
305
346
throw vscode . FileSystemError . Unavailable ( `${ uri . toString ( ) } is unavailable` ) ;
306
347
} ) ;
307
348
}
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
+ }
308
356
const parts = uri . path . split ( "/" ) ;
309
- let entry : Entry = this . root ;
310
357
for ( let i = 0 ; i < parts . length ; i ++ ) {
311
358
const part = parts [ i ] ;
312
359
if ( ! part ) {
@@ -317,10 +364,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
317
364
child = entry . entries . get ( part ) ;
318
365
// If the last element of path is dotted and is one we haven't already cached as a directory
319
366
// 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 ) ;
324
369
}
325
370
}
326
371
if ( ! child ) {
0 commit comments