1
+ import type { ClientReadableStream } from '@grpc/grpc-js' ;
2
+ import { ApplicationError } from '@theia/core/lib/common/application-error' ;
3
+ import type { CancellationToken } from '@theia/core/lib/common/cancellation' ;
4
+ import { CommandService } from '@theia/core/lib/common/command' ;
5
+ import {
6
+ Disposable ,
7
+ DisposableCollection ,
8
+ } from '@theia/core/lib/common/disposable' ;
9
+ import { nls } from '@theia/core/lib/common/nls' ;
10
+ import type { Mutable } from '@theia/core/lib/common/types' ;
1
11
import { FileUri } from '@theia/core/lib/node/file-uri' ;
2
12
import { inject , injectable } from '@theia/core/shared/inversify' ;
3
- import { relative } from 'node:path' ;
4
13
import * as jspb from 'google-protobuf' ;
5
14
import { BoolValue } from 'google-protobuf/google/protobuf/wrappers_pb' ;
6
- import type { ClientReadableStream } from '@grpc/grpc-js ' ;
15
+ import path from 'node:path ' ;
7
16
import {
17
+ UploadResponse as ApiUploadResponse ,
18
+ OutputMessage ,
19
+ Port ,
20
+ PortIdentifier ,
21
+ resolveDetectedPort ,
22
+ } from '../common/protocol' ;
23
+ import {
24
+ CompileSummary ,
8
25
CompilerWarnings ,
9
- CoreService ,
10
26
CoreError ,
11
- CompileSummary ,
27
+ CoreService ,
12
28
isCompileSummary ,
13
29
isUploadResponse ,
14
30
} from '../common/protocol/core-service' ;
31
+ import { ResponseService } from '../common/protocol/response-service' ;
32
+ import { firstToUpperCase , notEmpty } from '../common/utils' ;
33
+ import { BoardDiscovery , createApiPort } from './board-discovery' ;
34
+ import { tryParseError } from './cli-error-parser' ;
35
+ import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb' ;
36
+ import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb' ;
15
37
import {
16
38
CompileRequest ,
17
39
CompileResponse ,
18
40
} from './cli-protocol/cc/arduino/cli/commands/v1/compile_pb' ;
19
- import { CoreClientAware } from './core-client-provider ' ;
41
+ import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb ' ;
20
42
import {
21
43
BurnBootloaderRequest ,
22
44
BurnBootloaderResponse ,
@@ -25,26 +47,13 @@ import {
25
47
UploadUsingProgrammerRequest ,
26
48
UploadUsingProgrammerResponse ,
27
49
} from './cli-protocol/cc/arduino/cli/commands/v1/upload_pb' ;
28
- import { ResponseService } from '../common/protocol/response-service' ;
29
- import {
30
- resolveDetectedPort ,
31
- OutputMessage ,
32
- PortIdentifier ,
33
- Port ,
34
- UploadResponse as ApiUploadResponse ,
35
- } from '../common/protocol' ;
36
- import { ArduinoCoreServiceClient } from './cli-protocol/cc/arduino/cli/commands/v1/commands_grpc_pb' ;
37
- import { Port as RpcPort } from './cli-protocol/cc/arduino/cli/commands/v1/port_pb' ;
38
- import { ApplicationError , CommandService , Disposable , nls } from '@theia/core' ;
50
+ import { CoreClientAware } from './core-client-provider' ;
51
+ import { ExecuteWithProgress , ProgressResponse } from './grpc-progressible' ;
39
52
import { MonitorManager } from './monitor-manager' ;
40
- import { AutoFlushingBuffer } from './utils/buffers' ;
41
- import { tryParseError } from './cli-error-parser' ;
42
- import { Instance } from './cli-protocol/cc/arduino/cli/commands/v1/common_pb' ;
43
- import { firstToUpperCase , notEmpty } from '../common/utils' ;
44
53
import { ServiceError } from './service-error' ;
45
- import { ExecuteWithProgress , ProgressResponse } from './grpc-progressible ' ;
46
- import type { Mutable } from '@theia/core/lib/ common/types ' ;
47
- import { BoardDiscovery , createApiPort } from './board-discovery ' ;
54
+ import { AutoFlushingBuffer } from './utils/buffers ' ;
55
+ import { userAbort } from '../ common/nls ' ;
56
+ import { UserAbortApplicationError } from '../common/protocol/progressible ' ;
48
57
49
58
namespace Uploadable {
50
59
export type Request = UploadRequest | UploadUsingProgrammerRequest ;
@@ -64,9 +73,13 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
64
73
@inject ( BoardDiscovery )
65
74
private readonly boardDiscovery : BoardDiscovery ;
66
75
67
- async compile ( options : CoreService . Options . Compile ) : Promise < void > {
76
+ async compile (
77
+ options : CoreService . Options . Compile ,
78
+ cancellationToken ?: CancellationToken
79
+ ) : Promise < void > {
68
80
const coreClient = await this . coreClient ;
69
81
const { client, instance } = coreClient ;
82
+ const request = this . compileRequest ( options , instance ) ;
70
83
const compileSummary = < CompileSummaryFragment > { } ;
71
84
const progressHandler = this . createProgressHandler ( options ) ;
72
85
const compileSummaryHandler = ( response : CompileResponse ) =>
@@ -75,10 +88,15 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
75
88
progressHandler ,
76
89
compileSummaryHandler
77
90
) ;
78
- const request = this . compileRequest ( options , instance ) ;
91
+ const toDisposeOnFinally = new DisposableCollection ( handler ) ;
79
92
return new Promise < void > ( ( resolve , reject ) => {
80
- client
81
- . compile ( request )
93
+ const call = client . compile ( request ) ;
94
+ if ( cancellationToken ) {
95
+ toDisposeOnFinally . push (
96
+ cancellationToken . onCancellationRequested ( ( ) => call . cancel ( ) )
97
+ ) ;
98
+ }
99
+ call
82
100
. on ( 'data' , handler . onData )
83
101
. on ( 'error' , ( error ) => {
84
102
if ( ! ServiceError . is ( error ) ) {
@@ -87,30 +105,39 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
87
105
error
88
106
) ;
89
107
reject ( error ) ;
90
- } else {
91
- const compilerErrors = tryParseError ( {
92
- content : handler . content ,
93
- sketch : options . sketch ,
94
- } ) ;
95
- const message = nls . localize (
96
- 'arduino/compile/error' ,
97
- 'Compilation error: {0}' ,
98
- compilerErrors
99
- . map ( ( { message } ) => message )
100
- . filter ( notEmpty )
101
- . shift ( ) ?? error . details
102
- ) ;
103
- this . sendResponse (
104
- error . details + '\n\n' + message ,
105
- OutputMessage . Severity . Error
106
- ) ;
107
- reject ( CoreError . VerifyFailed ( message , compilerErrors ) ) ;
108
+ return ;
108
109
}
110
+ if ( ServiceError . isCancel ( error ) ) {
111
+ console . log ( userAbort ) ;
112
+ reject ( UserAbortApplicationError ( ) ) ;
113
+ return ;
114
+ }
115
+ const compilerErrors = tryParseError ( {
116
+ content : handler . content ,
117
+ sketch : options . sketch ,
118
+ } ) ;
119
+ const message = nls . localize (
120
+ 'arduino/compile/error' ,
121
+ 'Compilation error: {0}' ,
122
+ compilerErrors
123
+ . map ( ( { message } ) => message )
124
+ . filter ( notEmpty )
125
+ . shift ( ) ?? error . details
126
+ ) ;
127
+ this . sendResponse (
128
+ error . details + '\n\n' + message ,
129
+ OutputMessage . Severity . Error
130
+ ) ;
131
+ reject ( CoreError . VerifyFailed ( message , compilerErrors ) ) ;
109
132
} )
110
133
. on ( 'end' , resolve ) ;
111
134
} ) . finally ( ( ) => {
112
- handler . dispose ( ) ;
135
+ toDisposeOnFinally . dispose ( ) ;
113
136
if ( ! isCompileSummary ( compileSummary ) ) {
137
+ if ( cancellationToken && cancellationToken . isCancellationRequested ) {
138
+ // NOOP
139
+ return ;
140
+ }
114
141
console . error (
115
142
`Have not received the full compile summary from the CLI while running the compilation. ${ JSON . stringify (
116
143
compileSummary
@@ -176,7 +203,10 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
176
203
return request ;
177
204
}
178
205
179
- upload ( options : CoreService . Options . Upload ) : Promise < ApiUploadResponse > {
206
+ upload (
207
+ options : CoreService . Options . Upload ,
208
+ cancellationToken ?: CancellationToken
209
+ ) : Promise < ApiUploadResponse > {
180
210
const { usingProgrammer } = options ;
181
211
return this . doUpload (
182
212
options ,
@@ -190,7 +220,8 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
190
220
usingProgrammer
191
221
? CoreError . UploadUsingProgrammerFailed
192
222
: CoreError . UploadFailed ,
193
- `upload${ usingProgrammer ? ' using programmer' : '' } `
223
+ `upload${ usingProgrammer ? ' using programmer' : '' } ` ,
224
+ cancellationToken
194
225
) ;
195
226
}
196
227
@@ -204,7 +235,8 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
204
235
client : ArduinoCoreServiceClient
205
236
) => ( request : REQ ) => ClientReadableStream < RESP > ,
206
237
errorCtor : ApplicationError . Constructor < number , CoreError . ErrorLocation [ ] > ,
207
- task : string
238
+ task : string ,
239
+ cancellationToken ?: CancellationToken
208
240
) : Promise < ApiUploadResponse > {
209
241
const portBeforeUpload = options . port ;
210
242
const uploadResponseFragment : Mutable < Partial < ApiUploadResponse > > = {
@@ -241,33 +273,47 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
241
273
progressHandler ,
242
274
updateUploadResponseFragmentHandler
243
275
) ;
276
+ const toDisposeOnFinally = new DisposableCollection ( handler ) ;
244
277
const grpcCall = responseFactory ( client ) ;
245
278
return this . notifyUploadWillStart ( options ) . then ( ( ) =>
246
279
new Promise < ApiUploadResponse > ( ( resolve , reject ) => {
247
- grpcCall ( this . initUploadRequest ( request , options , instance ) )
280
+ const call = grpcCall (
281
+ this . initUploadRequest ( request , options , instance )
282
+ ) ;
283
+ if ( cancellationToken ) {
284
+ toDisposeOnFinally . push (
285
+ cancellationToken . onCancellationRequested ( ( ) => call . cancel ( ) )
286
+ ) ;
287
+ }
288
+ call
248
289
. on ( 'data' , handler . onData )
249
290
. on ( 'error' , ( error ) => {
250
291
if ( ! ServiceError . is ( error ) ) {
251
292
console . error ( `Unexpected error occurred while ${ task } .` , error ) ;
252
293
reject ( error ) ;
253
- } else {
254
- const message = nls . localize (
255
- 'arduino/upload/error' ,
256
- '{0} error: {1}' ,
257
- firstToUpperCase ( task ) ,
258
- error . details
259
- ) ;
260
- this . sendResponse ( error . details , OutputMessage . Severity . Error ) ;
261
- reject (
262
- errorCtor (
263
- message ,
264
- tryParseError ( {
265
- content : handler . content ,
266
- sketch : options . sketch ,
267
- } )
268
- )
269
- ) ;
294
+ return ;
295
+ }
296
+ if ( ServiceError . isCancel ( error ) ) {
297
+ console . log ( userAbort ) ;
298
+ reject ( UserAbortApplicationError ( ) ) ;
299
+ return ;
270
300
}
301
+ const message = nls . localize (
302
+ 'arduino/upload/error' ,
303
+ '{0} error: {1}' ,
304
+ firstToUpperCase ( task ) ,
305
+ error . details
306
+ ) ;
307
+ this . sendResponse ( error . details , OutputMessage . Severity . Error ) ;
308
+ reject (
309
+ errorCtor (
310
+ message ,
311
+ tryParseError ( {
312
+ content : handler . content ,
313
+ sketch : options . sketch ,
314
+ } )
315
+ )
316
+ ) ;
271
317
} )
272
318
. on ( 'end' , ( ) => {
273
319
if ( isUploadResponse ( uploadResponseFragment ) ) {
@@ -285,7 +331,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
285
331
}
286
332
} ) ;
287
333
} ) . finally ( async ( ) => {
288
- handler . dispose ( ) ;
334
+ toDisposeOnFinally . dispose ( ) ;
289
335
await this . notifyUploadDidFinish (
290
336
Object . assign ( options , {
291
337
afterPort : uploadResponseFragment . portAfterUpload ,
@@ -320,16 +366,25 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
320
366
return request ;
321
367
}
322
368
323
- async burnBootloader ( options : CoreService . Options . Bootloader ) : Promise < void > {
369
+ async burnBootloader (
370
+ options : CoreService . Options . Bootloader ,
371
+ cancellationToken ?: CancellationToken
372
+ ) : Promise < void > {
324
373
const coreClient = await this . coreClient ;
325
374
const { client, instance } = coreClient ;
326
375
const progressHandler = this . createProgressHandler ( options ) ;
327
376
const handler = this . createOnDataHandler ( progressHandler ) ;
328
377
const request = this . burnBootloaderRequest ( options , instance ) ;
378
+ const toDisposeOnFinally = new DisposableCollection ( handler ) ;
329
379
return this . notifyUploadWillStart ( options ) . then ( ( ) =>
330
380
new Promise < void > ( ( resolve , reject ) => {
331
- client
332
- . burnBootloader ( request )
381
+ const call = client . burnBootloader ( request ) ;
382
+ if ( cancellationToken ) {
383
+ toDisposeOnFinally . push (
384
+ cancellationToken . onCancellationRequested ( ( ) => call . cancel ( ) )
385
+ ) ;
386
+ }
387
+ call
333
388
. on ( 'data' , handler . onData )
334
389
. on ( 'error' , ( error ) => {
335
390
if ( ! ServiceError . is ( error ) ) {
@@ -338,23 +393,28 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
338
393
error
339
394
) ;
340
395
reject ( error ) ;
341
- } else {
342
- this . sendResponse ( error . details , OutputMessage . Severity . Error ) ;
343
- reject (
344
- CoreError . BurnBootloaderFailed (
345
- nls . localize (
346
- 'arduino/burnBootloader/error' ,
347
- 'Error while burning the bootloader: {0}' ,
348
- error . details
349
- ) ,
350
- tryParseError ( { content : handler . content } )
351
- )
352
- ) ;
396
+ return ;
353
397
}
398
+ if ( ServiceError . isCancel ( error ) ) {
399
+ console . log ( userAbort ) ;
400
+ reject ( UserAbortApplicationError ( ) ) ;
401
+ return ;
402
+ }
403
+ this . sendResponse ( error . details , OutputMessage . Severity . Error ) ;
404
+ reject (
405
+ CoreError . BurnBootloaderFailed (
406
+ nls . localize (
407
+ 'arduino/burnBootloader/error' ,
408
+ 'Error while burning the bootloader: {0}' ,
409
+ error . details
410
+ ) ,
411
+ tryParseError ( { content : handler . content } )
412
+ )
413
+ ) ;
354
414
} )
355
415
. on ( 'end' , resolve ) ;
356
416
} ) . finally ( async ( ) => {
357
- handler . dispose ( ) ;
417
+ toDisposeOnFinally . dispose ( ) ;
358
418
await this . notifyUploadDidFinish (
359
419
Object . assign ( options , { afterPort : options . port } )
360
420
) ;
@@ -463,7 +523,7 @@ export class CoreServiceImpl extends CoreClientAware implements CoreService {
463
523
for ( const uri of Object . keys ( options . sourceOverride ) ) {
464
524
const content = options . sourceOverride [ uri ] ;
465
525
if ( content ) {
466
- const relativePath = relative ( sketchPath , FileUri . fsPath ( uri ) ) ;
526
+ const relativePath = path . relative ( sketchPath , FileUri . fsPath ( uri ) ) ;
467
527
req . getSourceOverrideMap ( ) . set ( relativePath , content ) ;
468
528
}
469
529
}
0 commit comments