@@ -17,9 +17,13 @@ import { languageServerApiManager } from "../languageServerApi/languageServerApi
17
17
import { Settings } from "../settings" ;
18
18
import { IUriData , Trie , TrieNode } from "../views/nodeCache/Trie" ;
19
19
import { IClasspathResult } from "./GenerateJarExecutor" ;
20
+ import { IExportJarStepExecutor } from "./IExportJarStepExecutor" ;
20
21
import { IClasspath , IStepMetadata } from "./IStepMetadata" ;
21
22
import { IMainClassInfo } from "./ResolveMainClassExecutor" ;
22
- import { ExportJarConstants , ExportJarStep , failMessage , getExtensionApi , stepMap , successMessage , toPosixPath , toWinPath } from "./utility" ;
23
+ import {
24
+ ExportJarConstants , ExportJarMessages , ExportJarStep , failMessage , getExtensionApi ,
25
+ resetStepMetadata , stepMap , successMessage , toPosixPath , toWinPath ,
26
+ } from "./utility" ;
23
27
24
28
interface IExportJarTaskDefinition extends TaskDefinition {
25
29
label ?: string ;
@@ -38,23 +42,33 @@ export async function executeExportJarTask(node?: INodeData): Promise<void> {
38
42
const stepMetadata : IStepMetadata = {
39
43
entry : node ,
40
44
steps : [ ] ,
45
+ projectList : [ ] ,
46
+ elements : [ ] ,
47
+ classpaths : [ ] ,
41
48
} ;
42
49
try {
43
- await stepMap . get ( ExportJarStep . ResolveJavaProject ) . execute ( stepMetadata ) ;
50
+ const resolveJavaProjectExecutor : IExportJarStepExecutor | undefined = stepMap . get ( ExportJarStep . ResolveJavaProject ) ;
51
+ if ( ! resolveJavaProjectExecutor ) {
52
+ throw new Error ( ExportJarMessages . stepErrorMessage ( ExportJarMessages . StepAction . FINDEXECUTOR , ExportJarStep . ResolveJavaProject ) ) ;
53
+ }
54
+ await resolveJavaProjectExecutor . execute ( stepMetadata ) ;
55
+ tasks . executeTask ( ExportJarTaskProvider . getTask ( stepMetadata ) ) ;
44
56
} catch ( err ) {
45
57
if ( err ) {
46
58
failMessage ( `${ err } ` ) ;
47
59
}
48
60
isExportingJar = false ;
49
61
return ;
50
62
}
51
- tasks . executeTask ( ExportJarTaskProvider . getTask ( stepMetadata ) ) ;
52
63
}
53
64
export class ExportJarTaskProvider implements TaskProvider {
54
65
55
66
public static exportJarType : string = "java" ;
56
67
57
68
public static getTask ( stepMetadata : IStepMetadata ) : Task {
69
+ if ( ! stepMetadata . workspaceFolder ) {
70
+ throw new Error ( ExportJarMessages . fieldUndefinedMessage ( ExportJarMessages . Field . WORKSPACEFOLDER , ExportJarStep . ResolveTask ) ) ;
71
+ }
58
72
const defaultDefinition : IExportJarTaskDefinition = {
59
73
type : ExportJarTaskProvider . exportJarType ,
60
74
label : `${ ExportJarTaskProvider . exportJarType } : exportjar:default` ,
@@ -82,19 +96,25 @@ export class ExportJarTaskProvider implements TaskProvider {
82
96
workspaceFolder : folder ,
83
97
projectList : await Jdtls . getProjects ( folder . uri . toString ( ) ) ,
84
98
steps : [ ] ,
99
+ elements : [ ] ,
100
+ classpaths : [ ] ,
85
101
} ;
86
102
return new ExportJarTaskTerminal ( resolvedDefinition , stepMetadata ) ;
87
103
} ) ) ;
88
104
resolvedTask . presentationOptions . reveal = TaskRevealKind . Never ;
89
105
return resolvedTask ;
90
106
}
91
107
92
- public async provideTasks ( ) : Promise < Task [ ] > {
108
+ public async provideTasks ( ) : Promise < Task [ ] | undefined > {
109
+ const folders : readonly WorkspaceFolder [ ] = workspace . workspaceFolders || [ ] ;
110
+ if ( _ . isEmpty ( folders ) ) {
111
+ return undefined ;
112
+ }
93
113
if ( ! _ . isEmpty ( this . tasks ) ) {
94
114
return this . tasks ;
95
115
}
96
116
this . tasks = [ ] ;
97
- for ( const folder of workspace . workspaceFolders ) {
117
+ for ( const folder of folders ) {
98
118
const projectList : INodeData [ ] = await Jdtls . getProjects ( folder . uri . toString ( ) ) ;
99
119
const elementList : string [ ] = [ ] ;
100
120
if ( _ . isEmpty ( projectList ) ) {
@@ -123,6 +143,8 @@ export class ExportJarTaskProvider implements TaskProvider {
123
143
workspaceFolder : folder ,
124
144
projectList : await Jdtls . getProjects ( folder . uri . toString ( ) ) ,
125
145
steps : [ ] ,
146
+ elements : [ ] ,
147
+ classpaths : [ ] ,
126
148
} ;
127
149
return new ExportJarTaskTerminal ( resolvedDefinition , stepMetadata ) ;
128
150
} ) , undefined ) ;
@@ -147,11 +169,15 @@ class ExportJarTaskTerminal implements Pseudoterminal {
147
169
this . stepMetadata = stepMetadata ;
148
170
this . stepMetadata . mainClass = exportJarTaskDefinition . mainClass ;
149
171
this . stepMetadata . outputPath = exportJarTaskDefinition . targetPath ;
150
- this . stepMetadata . elements = exportJarTaskDefinition . elements ;
172
+ this . stepMetadata . elements = exportJarTaskDefinition . elements || [ ] ;
151
173
}
152
174
153
175
public async open ( _initialDimensions : TerminalDimensions | undefined ) : Promise < void > {
176
+ let exportResult : boolean | undefined ;
154
177
try {
178
+ if ( ! this . stepMetadata . workspaceFolder ) {
179
+ throw new Error ( ExportJarMessages . fieldUndefinedMessage ( ExportJarMessages . Field . WORKSPACEFOLDER , ExportJarStep . ResolveTask ) ) ;
180
+ }
155
181
if ( this . stepMetadata . outputPath === undefined ) {
156
182
// TODO: get resolved path from setting configuration.java.project.exportJar.targetPath.
157
183
// For the tasks whose targetPath is undefined, the user will select the output location manually.
@@ -170,12 +196,20 @@ class ExportJarTaskTerminal implements Pseudoterminal {
170
196
this . stepMetadata . classpaths = await this . resolveClasspaths ( outputFolderMap ,
171
197
artifactMap , testOutputFolderMap , testArtifactMap ) ;
172
198
}
173
- await this . createJarFile ( this . stepMetadata ) ;
199
+ exportResult = await this . createJarFile ( this . stepMetadata ) ;
174
200
} catch ( err ) {
175
201
if ( err ) {
176
202
failMessage ( `${ err } ` ) ;
177
203
}
178
204
} finally {
205
+ isExportingJar = false ;
206
+ if ( exportResult === true ) {
207
+ successMessage ( this . stepMetadata . outputPath ) ;
208
+ } else if ( exportResult === false ) {
209
+ // We call `executeExportJarTask()` with the same entry here
210
+ // to help the user reselect the Java project.
211
+ executeExportJarTask ( this . stepMetadata . entry ) ;
212
+ }
179
213
this . closeEmitter . fire ( ) ;
180
214
}
181
215
}
@@ -184,28 +218,46 @@ class ExportJarTaskTerminal implements Pseudoterminal {
184
218
185
219
}
186
220
187
- private async createJarFile ( stepMetadata : IStepMetadata ) : Promise < void > {
221
+ private async createJarFile ( stepMetadata : IStepMetadata ) : Promise < boolean > {
188
222
let step : ExportJarStep = ExportJarStep . ResolveJavaProject ;
223
+ let previousStep : ExportJarStep | undefined ;
224
+ let executor : IExportJarStepExecutor | undefined ;
189
225
while ( step !== ExportJarStep . Finish ) {
190
- try {
191
- step = await stepMap . get ( step ) . execute ( stepMetadata ) ;
192
- if ( step === ExportJarStep . ResolveJavaProject ) {
193
- // If the user comes back to the step resolving Java project, we need to finish
194
- // the current task and start a new task related to the new Java project.
195
- isExportingJar = false ;
196
- executeExportJarTask ( stepMetadata . entry ) ;
197
- return ;
226
+ executor = stepMap . get ( step ) ;
227
+ if ( ! executor ) {
228
+ throw new Error ( ExportJarMessages . stepErrorMessage ( ExportJarMessages . StepAction . FINDEXECUTOR , step ) ) ;
229
+ }
230
+ if ( ! await executor . execute ( stepMetadata ) ) {
231
+ // Go back
232
+ previousStep = stepMetadata . steps . pop ( ) ;
233
+ if ( ! previousStep ) {
234
+ throw new Error ( ExportJarMessages . stepErrorMessage ( ExportJarMessages . StepAction . GOBACK , step ) ) ;
198
235
}
199
- } catch ( err ) {
200
- if ( err ) {
201
- failMessage ( `${ err } ` ) ;
236
+ resetStepMetadata ( previousStep , stepMetadata ) ;
237
+ step = previousStep ;
238
+ } else {
239
+ // Go ahead
240
+ switch ( step ) {
241
+ case ExportJarStep . ResolveJavaProject :
242
+ step = ExportJarStep . ResolveMainClass ;
243
+ break ;
244
+ case ExportJarStep . ResolveMainClass :
245
+ step = ExportJarStep . GenerateJar ;
246
+ break ;
247
+ case ExportJarStep . GenerateJar :
248
+ step = ExportJarStep . Finish ;
249
+ break ;
250
+ default :
251
+ throw new Error ( ExportJarMessages . stepErrorMessage ( ExportJarMessages . StepAction . GOAHEAD , step ) ) ;
202
252
}
203
- isExportingJar = false ;
204
- return ;
253
+ }
254
+ if ( step === ExportJarStep . ResolveJavaProject ) {
255
+ // It's possible for a user who comes back to the step selecting the Java project to change the workspace.
256
+ // Since a specific task corresponds to a specific workspace, we return "false" as a mark.
257
+ return false ;
205
258
}
206
259
}
207
- isExportingJar = false ;
208
- successMessage ( stepMetadata . outputPath ) ;
260
+ return true ;
209
261
}
210
262
211
263
private async setClasspathMap ( project : INodeData , classpathScope : string ,
@@ -236,7 +288,10 @@ class ExportJarTaskTerminal implements Pseudoterminal {
236
288
if ( element . length === 0 ) {
237
289
continue ;
238
290
}
239
- const matchResult = element . match ( regExp ) ;
291
+ const matchResult : RegExpMatchArray | null = element . match ( regExp ) ;
292
+ if ( matchResult === null ) {
293
+ continue ;
294
+ }
240
295
if ( _ . isEmpty ( matchResult ) || matchResult . length <= 2 ) {
241
296
if ( extname ( element ) === ".jar" ) {
242
297
artifacts . push ( this . toAbsolutePosixPath ( element ) ) ;
@@ -245,7 +300,7 @@ class ExportJarTaskTerminal implements Pseudoterminal {
245
300
}
246
301
continue ;
247
302
}
248
- const projectName : string = ( matchResult [ 2 ] === undefined ) ? undefined : matchResult [ 2 ] . substring ( 1 ) ;
303
+ const projectName : string | undefined = matchResult [ 2 ] ? .substring ( 1 ) ;
249
304
switch ( matchResult [ 1 ] ) {
250
305
case ExportJarConstants . DEPENDENCIES :
251
306
artifacts = artifacts . concat ( this . getJarElementsFromClasspathMapping ( matchResult , artifactMap , projectName ) ) ;
@@ -278,8 +333,9 @@ class ExportJarTaskTerminal implements Pseudoterminal {
278
333
}
279
334
const sources : IClasspath [ ] = [ ] ;
280
335
for ( const glob of await globby ( globPatterns ) ) {
281
- const tireNode : TrieNode < IUriData > = trie . find ( Uri . file ( platform ( ) === "win32" ? toWinPath ( glob ) : glob ) . fsPath , /* returnEarly = */ true ) ;
282
- if ( tireNode === undefined ) {
336
+ const tireNode : TrieNode < IUriData > | undefined = trie . find (
337
+ Uri . file ( platform ( ) === "win32" ? toWinPath ( glob ) : glob ) . fsPath , /* returnEarly = */ true ) ;
338
+ if ( ! tireNode ?. value ?. uri ) {
283
339
continue ;
284
340
}
285
341
let fsPath = Uri . parse ( tireNode . value . uri ) . fsPath ;
@@ -309,8 +365,15 @@ class ExportJarTaskTerminal implements Pseudoterminal {
309
365
private getJarElementsFromClasspathMapping ( matchResult : RegExpMatchArray , rawClasspathEntries : Map < string , string [ ] > ,
310
366
projectName : string | undefined ) : string [ ] {
311
367
const result : string [ ] = [ ] ;
368
+ if ( ! matchResult . input ) {
369
+ return result ;
370
+ }
312
371
if ( projectName !== undefined ) {
313
- for ( const classpath of rawClasspathEntries . get ( projectName ) ) {
372
+ const entries : string [ ] = rawClasspathEntries . get ( projectName ) || [ ] ;
373
+ if ( _ . isEmpty ( entries ) ) {
374
+ return result ;
375
+ }
376
+ for ( const classpath of entries ) {
314
377
result . push ( this . toAbsolutePosixPath ( matchResult . input . replace ( matchResult [ 0 ] , classpath ) ) ) ;
315
378
}
316
379
} else {
@@ -324,6 +387,9 @@ class ExportJarTaskTerminal implements Pseudoterminal {
324
387
}
325
388
326
389
private toAbsolutePosixPath ( path : string ) : string {
390
+ if ( ! this . stepMetadata . workspaceFolder ) {
391
+ throw new Error ( ExportJarMessages . fieldUndefinedMessage ( ExportJarMessages . Field . WORKSPACEFOLDER , ExportJarStep . ResolveTask ) ) ;
392
+ }
327
393
const negative : boolean = ( path [ 0 ] === "!" ) ;
328
394
let positivePath : string = negative ? path . substring ( 1 ) : path ;
329
395
if ( ! isAbsolute ( positivePath ) ) {
0 commit comments