@@ -14,9 +14,11 @@ import {
14
14
import { DocumentContentProvider } from "../providers/DocumentContentProvider" ;
15
15
import {
16
16
cspAppsForUri ,
17
+ CurrentBinaryFile ,
17
18
currentFile ,
18
19
CurrentFile ,
19
20
currentFileFromContent ,
21
+ CurrentTextFile ,
20
22
isClassDeployed ,
21
23
notNull ,
22
24
outputChannel ,
@@ -25,6 +27,7 @@ import {
25
27
import { PackageNode } from "../explorer/models/packageNode" ;
26
28
import { NodeBase } from "../explorer/models/nodeBase" ;
27
29
import { RootNode } from "../explorer/models/rootNode" ;
30
+ import { isText } from "istextorbinary" ;
28
31
29
32
async function compileFlags ( ) : Promise < string > {
30
33
const defaultFlags = config ( ) . compileFlags ;
@@ -46,7 +49,7 @@ async function compileFlags(): Promise<string> {
46
49
* @param force If passed true, use server mtime.
47
50
* @return mtime timestamp or -1.
48
51
*/
49
- export async function checkChangedOnServer ( file : CurrentFile , force = false ) : Promise < number > {
52
+ export async function checkChangedOnServer ( file : CurrentTextFile | CurrentBinaryFile , force = false ) : Promise < number > {
50
53
if ( ! file || ! file . uri ) {
51
54
return - 1 ;
52
55
}
@@ -57,11 +60,16 @@ export async function checkChangedOnServer(file: CurrentFile, force = false): Pr
57
60
. getDoc ( file . name )
58
61
. then ( ( data ) => data . result )
59
62
. then ( async ( { ts, content } ) => {
60
- const fileContent = file . content . split ( / \r ? \n / ) ;
61
63
const serverTime = Number ( new Date ( ts + "Z" ) ) ;
62
- const sameContent = force
63
- ? false
64
- : content . every ( ( line , index ) => line . trim ( ) == ( fileContent [ index ] || "" ) . trim ( ) ) ;
64
+ let sameContent : boolean ;
65
+ if ( typeof file . content === "string" ) {
66
+ const fileContent = file . content . split ( / \r ? \n / ) ;
67
+ sameContent = force
68
+ ? false
69
+ : ( content as string [ ] ) . every ( ( line , index ) => line . trim ( ) == ( fileContent [ index ] || "" ) . trim ( ) ) ;
70
+ } else {
71
+ sameContent = force ? false : Buffer . compare ( content as Buffer , file . content ) === 0 ;
72
+ }
65
73
const mtime =
66
74
force || sameContent ? serverTime : Math . max ( ( await vscode . workspace . fs . stat ( file . uri ) ) . mtime , serverTime ) ;
67
75
return mtime ;
@@ -71,23 +79,43 @@ export async function checkChangedOnServer(file: CurrentFile, force = false): Pr
71
79
return mtime ;
72
80
}
73
81
74
- async function importFile ( file : CurrentFile , ignoreConflict ?: boolean , skipDeplCheck = false ) : Promise < any > {
82
+ async function importFile (
83
+ file : CurrentTextFile | CurrentBinaryFile ,
84
+ ignoreConflict ?: boolean ,
85
+ skipDeplCheck = false
86
+ ) : Promise < any > {
75
87
const api = new AtelierAPI ( file . uri ) ;
76
88
if ( file . name . split ( "." ) . pop ( ) . toLowerCase ( ) === "cls" && ! skipDeplCheck ) {
77
89
if ( await isClassDeployed ( file . name , api ) ) {
78
90
vscode . window . showErrorMessage ( `Cannot import ${ file . name } because it is deployed on the server.` , "Dismiss" ) ;
79
91
return Promise . reject ( ) ;
80
92
}
81
93
}
82
- const content = file . content . split ( / \r ? \n / ) ;
94
+ let enc : boolean ;
95
+ let content : string [ ] ;
96
+ if ( typeof file . content === "string" ) {
97
+ enc = false ;
98
+ content = file . content . split ( / \r ? \n / ) ;
99
+ } else {
100
+ // Base64 encoding must be in chunk size multiple of 3 and within the server's potential 32K string limit
101
+ // Output is 4 chars for each 3 input, so 24573/3*4 = 32764
102
+ const chunkSize = 24573 ;
103
+ let start = 0 ;
104
+ content = [ ] ;
105
+ enc = true ;
106
+ while ( start < file . content . byteLength ) {
107
+ content . push ( file . content . toString ( "base64" , start , start + chunkSize ) ) ;
108
+ start += chunkSize ;
109
+ }
110
+ }
83
111
const mtime = await checkChangedOnServer ( file ) ;
84
112
ignoreConflict = ignoreConflict || mtime < 0 || ( file . uri . scheme === "file" && config ( "overwriteServerChanges" ) ) ;
85
113
return api
86
114
. putDoc (
87
115
file . name ,
88
116
{
89
117
content,
90
- enc : false ,
118
+ enc,
91
119
mtime,
92
120
} ,
93
121
ignoreConflict
@@ -112,14 +140,16 @@ async function importFile(file: CurrentFile, ignoreConflict?: boolean, skipDeplC
112
140
} )
113
141
. catch ( ( error ) => {
114
142
if ( error ?. statusCode == 409 ) {
143
+ const choices : string [ ] = [ ] ;
144
+ if ( ! enc ) {
145
+ choices . push ( "Compare" ) ;
146
+ }
147
+ choices . push ( "Overwrite on Server" , "Pull Server Changes" , "Cancel" ) ;
115
148
return vscode . window
116
149
. showErrorMessage (
117
150
`Failed to import '${ file . name } ': The version of the file on the server is newer.
118
151
What do you want to do?` ,
119
- "Compare" ,
120
- "Overwrite on Server" ,
121
- "Pull Server Changes" ,
122
- "Cancel"
152
+ ...choices
123
153
)
124
154
. then ( ( action ) => {
125
155
switch ( action ) {
@@ -200,7 +230,7 @@ function updateOthers(others: string[], baseUri: vscode.Uri) {
200
230
} ) ;
201
231
}
202
232
203
- export async function loadChanges ( files : CurrentFile [ ] ) : Promise < any > {
233
+ export async function loadChanges ( files : ( CurrentTextFile | CurrentBinaryFile ) [ ] ) : Promise < any > {
204
234
if ( ! files . length ) {
205
235
return ;
206
236
}
@@ -210,11 +240,19 @@ export async function loadChanges(files: CurrentFile[]): Promise<any> {
210
240
api
211
241
. getDoc ( file . name )
212
242
. then ( async ( data ) => {
213
- const content = ( data . result . content || [ ] ) . join ( file . eol === vscode . EndOfLine . LF ? "\n" : "\r\n" ) ;
214
243
const mtime = Number ( new Date ( data . result . ts + "Z" ) ) ;
215
244
workspaceState . update ( `${ file . uniqueId } :mtime` , mtime > 0 ? mtime : undefined ) ;
216
245
if ( file . uri . scheme === "file" ) {
217
- await vscode . workspace . fs . writeFile ( file . uri , new TextEncoder ( ) . encode ( content ) ) ;
246
+ if ( Buffer . isBuffer ( data . result . content ) ) {
247
+ // This is a binary file
248
+ await vscode . workspace . fs . writeFile ( file . uri , data . result . content ) ;
249
+ } else {
250
+ // This is a text file
251
+ const content = ( data . result . content || [ ] ) . join (
252
+ ( file as CurrentTextFile ) . eol === vscode . EndOfLine . LF ? "\n" : "\r\n"
253
+ ) ;
254
+ await vscode . workspace . fs . writeFile ( file . uri , new TextEncoder ( ) . encode ( content ) ) ;
255
+ }
218
256
} else if ( file . uri . scheme === FILESYSTEM_SCHEMA || file . uri . scheme === FILESYSTEM_READONLY_SCHEMA ) {
219
257
fileSystemProvider . fireFileChanged ( file . uri ) ;
220
258
}
@@ -410,23 +448,37 @@ export async function namespaceCompile(askFlags = false): Promise<any> {
410
448
) ;
411
449
}
412
450
413
- function importFiles ( files : string [ ] , noCompile = false ) {
414
- return Promise . all < CurrentFile > (
451
+ async function importFiles ( files : string [ ] , noCompile = false ) {
452
+ const toCompile : CurrentFile [ ] = [ ] ;
453
+ await Promise . all < void > (
415
454
files . map (
416
- throttleRequests ( ( file : string ) =>
417
- vscode . workspace . fs
418
- . readFile ( vscode . Uri . file ( file ) )
419
- . then ( ( contentBytes ) => new TextDecoder ( ) . decode ( contentBytes ) )
420
- . then ( ( content ) => currentFileFromContent ( vscode . Uri . file ( file ) , content ) )
455
+ throttleRequests ( ( file : string ) => {
456
+ const uri = vscode . Uri . file ( file ) ;
457
+ return vscode . workspace . fs
458
+ . readFile ( uri )
459
+ . then ( ( contentBytes ) => {
460
+ if ( isText ( file , Buffer . from ( contentBytes ) ) ) {
461
+ const textFile = currentFileFromContent ( uri , new TextDecoder ( ) . decode ( contentBytes ) ) ;
462
+ toCompile . push ( textFile ) ;
463
+ return textFile ;
464
+ } else {
465
+ return currentFileFromContent ( uri , Buffer . from ( contentBytes ) ) ;
466
+ }
467
+ } )
421
468
. then ( ( curFile ) =>
422
469
importFile ( curFile ) . then ( ( data ) => {
423
470
outputChannel . appendLine ( "Imported file: " + curFile . fileName ) ;
424
- return curFile ;
471
+ return ;
425
472
} )
426
- )
427
- )
473
+ ) ;
474
+ } )
428
475
)
429
- ) . then ( noCompile ? Promise . resolve : compile ) ;
476
+ ) ;
477
+
478
+ if ( ! noCompile && toCompile . length > 0 ) {
479
+ return compile ( toCompile ) ;
480
+ }
481
+ return ;
430
482
}
431
483
432
484
export async function importFolder ( uri : vscode . Uri , noCompile = false ) : Promise < any > {
@@ -437,7 +489,7 @@ export async function importFolder(uri: vscode.Uri, noCompile = false): Promise<
437
489
let globpattern = "*.{cls,inc,int,mac}" ;
438
490
if ( cspAppsForUri ( uri ) . findIndex ( ( cspApp ) => uri . path . includes ( cspApp + "/" ) || uri . path . endsWith ( cspApp ) ) != - 1 ) {
439
491
// This folder is a CSP application, so import all files
440
- // We need to include eveything becuase CSP applications can
492
+ // We need to include eveything because CSP applications can
441
493
// include non-InterSystems files
442
494
globpattern = "*" ;
443
495
}
0 commit comments