24
24
*/
25
25
26
26
import * as vscode from 'vscode' ;
27
+ import * as path from 'path' ;
28
+ import * as fs from 'fs' ;
29
+ import * as logUtils from './logUtils' ;
27
30
28
31
29
32
const NBLS_GET_SOURCE_ROOTS_COMMAND = 'nbls.java.get.project.source.roots' ;
@@ -37,9 +40,10 @@ const JDT_EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand';
37
40
38
41
const MICRONAUT_TOOLS_SELECTED_SUBPROJECT_COMMAND = 'extension.micronaut-tools.navigation.getSelectedSubproject' ;
39
42
40
- // TODO: integrate with Micronaut Tools to only track sources of the selected GCN application module
41
43
export async function getSourceRoots ( workspaceFolder ?: vscode . WorkspaceFolder ) : Promise < string [ ] | undefined > {
44
+ logUtils . logInfo ( `[projectUtils] Computing source roots${ workspaceFolder ? ' for folder ' + workspaceFolder . uri . fsPath : '' } ...` ) ;
42
45
if ( ! vscode . workspace . workspaceFolders ?. length ) { // No folder opened
46
+ logUtils . logInfo ( '[projectUtils] No folder opened' ) ;
43
47
return [ ] ;
44
48
}
45
49
@@ -56,6 +60,7 @@ export async function getSourceRoots(workspaceFolder?: vscode.WorkspaceFolder):
56
60
const hasNblsProjectSourceRootsCommand = commands . includes ( NBLS_GET_SOURCE_ROOTS_COMMAND ) ;
57
61
const jdtApi = vscode . extensions . getExtension ( JDT_EXTENSION_ID ) ?. exports ;
58
62
if ( ! hasNblsProjectSourceRootsCommand && ! jdtApi ?. getProjectSettings ) {
63
+ logUtils . logWarning ( '[projectUtils] No Java support to compute source roots' ) ;
59
64
// TODO: wait for NBLS/JDT if installed
60
65
return undefined ; // No Java support available
61
66
}
@@ -65,16 +70,45 @@ export async function getSourceRoots(workspaceFolder?: vscode.WorkspaceFolder):
65
70
66
71
const sourceRoots : string [ ] = [ ] ;
67
72
const getUriSourceRoots = hasNblsProjectSourceRootsCommand ? getUriSourceRootsNbls : getUriSourceRootsJdt ;
73
+ logUtils . logInfo ( `[projectUtils] Using ${ hasNblsProjectSourceRootsCommand ? 'NBLS' : 'JDT' } to compute source roots` ) ;
68
74
for ( const folder of workspaceFolders ) {
69
- await getUriSourceRoots ( sourceRoots , folder , folder . uri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand , jdtApi ) ;
75
+ const unrecognizedProjectFolders : vscode . Uri [ ] = [ ] ;
76
+ try {
77
+ const foundSourceRoots = await getUriSourceRoots ( sourceRoots , folder , folder . uri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand , jdtApi ) ;
78
+ if ( ! foundSourceRoots && ! isSupportedProjectUri ( folder . uri ) ) {
79
+ unrecognizedProjectFolders . push ( folder . uri ) ;
80
+ }
81
+ } catch ( err ) {
82
+ logUtils . logError ( `[projectUtils] Error computing source roots: ${ err } ` ) ;
83
+ }
84
+
85
+ // Try to find a nested supported project folder
86
+ while ( unrecognizedProjectFolders . length ) {
87
+ const unrecognizedProjectFolder = unrecognizedProjectFolders . shift ( ) ;
88
+ if ( unrecognizedProjectFolder ) {
89
+ const subfolders = fs . readdirSync ( unrecognizedProjectFolder . fsPath ) ;
90
+ for ( const subfolder of subfolders ) {
91
+ const subfolderUri = vscode . Uri . joinPath ( unrecognizedProjectFolder , subfolder ) ;
92
+ if ( fs . lstatSync ( subfolderUri . fsPath ) ?. isDirectory ( ) ) {
93
+ const foundSourceRoots = await getUriSourceRoots ( sourceRoots , folder , subfolderUri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand , jdtApi ) ;
94
+ if ( ! foundSourceRoots && ! isSupportedProjectUri ( folder . uri ) ) {
95
+ unrecognizedProjectFolders . push ( subfolderUri ) ;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
70
101
}
102
+ logUtils . logInfo ( `[projectUtils] Found ${ sourceRoots . length } source root(s)` ) ;
71
103
return sourceRoots ;
72
104
}
73
105
74
- async function getUriSourceRootsNbls ( sourceRoots : string [ ] , folder : vscode . WorkspaceFolder , uri : string , hasNblsProjectInfoCommand : boolean , hasMicronautToolsSubprojectCommand : boolean ) {
106
+ async function getUriSourceRootsNbls ( sourceRoots : string [ ] , folder : vscode . WorkspaceFolder , uri : string , hasNblsProjectInfoCommand : boolean , hasMicronautToolsSubprojectCommand : boolean ) : Promise < boolean > {
107
+ let foundSourceRoots = false ;
75
108
const uriSourceRoots : string [ ] | undefined = await vscode . commands . executeCommand ( NBLS_GET_SOURCE_ROOTS_COMMAND , uri ) ;
76
109
if ( uriSourceRoots ) {
77
110
if ( uriSourceRoots . length ) { // found project source roots
111
+ foundSourceRoots = true ;
78
112
for ( const uriSourceRoot of uriSourceRoots ) {
79
113
const sourceRoot = vscode . Uri . parse ( uriSourceRoot ) . fsPath ;
80
114
if ( ! sourceRoots . includes ( sourceRoot ) ) {
@@ -101,44 +135,52 @@ async function getUriSourceRootsNbls(sourceRoots: string[], folder: vscode.Works
101
135
}
102
136
}
103
137
for ( const subproject of subprojects ) {
104
- await getUriSourceRootsNbls ( sourceRoots , folder , subproject , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
138
+ foundSourceRoots = foundSourceRoots || await getUriSourceRootsNbls ( sourceRoots , folder , subproject , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
105
139
}
106
140
}
107
141
}
108
142
}
109
143
}
144
+ return foundSourceRoots ;
110
145
}
111
146
112
- // TODO: add support for modules/subprojects
113
- async function getUriSourceRootsJdt ( sourceRoots : string [ ] , _folder : vscode . WorkspaceFolder , uri : string , _hasNblsProjectInfoCommand : boolean , _hasMicronautToolsSubprojectCommand : boolean , api : any ) {
147
+ // TODO: add support for modules/subprojects?
148
+ // NOTE: modules/subprojects are defined by the Micronaut Tools ext., which has NBLS as a required dependency -> getUriSourceRootsNbls will be executed instead of getUriSourceRootsJdt
149
+ async function getUriSourceRootsJdt ( sourceRoots : string [ ] , _folder : vscode . WorkspaceFolder , uri : string , _hasNblsProjectInfoCommand : boolean , _hasMicronautToolsSubprojectCommand : boolean , api : any ) : Promise < boolean > {
150
+ let foundSourceRoots = false ;
114
151
try {
115
152
const settings = await api . getProjectSettings ( uri , [ JDT_SETTINGS_SOURCE_PATHS ] ) ;
116
153
if ( settings ) {
117
154
const uriSourceRoots = settings [ JDT_SETTINGS_SOURCE_PATHS ] ;
118
- if ( uriSourceRoots ) {
155
+ if ( uriSourceRoots ?. length ) {
156
+ foundSourceRoots = true ;
119
157
for ( const uriSourceRoot of uriSourceRoots ) {
120
158
if ( ! sourceRoots . includes ( uriSourceRoot ) ) {
121
159
sourceRoots . push ( uriSourceRoot ) ;
122
160
}
123
161
}
124
162
}
125
163
}
164
+ return foundSourceRoots ;
126
165
} catch ( err ) {
127
- // <project_folder>-parent does not exist
166
+ // Error: Given URI does not belong to any Java project.
167
+ return false ;
128
168
}
129
169
}
130
170
131
- // TODO: integrate with Micronaut Tools to only track packages of the selected GCN application module
132
171
export async function getPackages ( workspaceFolder ?: vscode . WorkspaceFolder ) : Promise < string [ ] | undefined > {
172
+ logUtils . logInfo ( `[projectUtils] Computing packages${ workspaceFolder ? ' for folder ' + workspaceFolder . uri . fsPath : '' } ...` ) ;
133
173
const workspaceFolders = workspaceFolder ? [ workspaceFolder ] : vscode . workspace . workspaceFolders ;
134
174
if ( ! workspaceFolders ?. length ) { // No folder opened
175
+ logUtils . logInfo ( '[projectUtils] No folder opened' ) ;
135
176
return [ ] ;
136
177
}
137
178
138
179
const commands = await vscode . commands . getCommands ( ) ;
139
180
const hasNblsProjectPackagesCommand = commands . includes ( NBLS_GET_PACKAGES_COMMAND ) ;
140
181
const hasJdtWorkspaceCommand = commands . includes ( JDT_EXECUTE_WORKSPACE_COMMAND ) ;
141
182
if ( ! hasNblsProjectPackagesCommand && ! hasJdtWorkspaceCommand ) {
183
+ logUtils . logWarning ( '[projectUtils] No Java support to compute packages' ) ;
142
184
// TODO: wait for NBLS/JDT if installed
143
185
return undefined ; // No Java support available
144
186
}
@@ -148,16 +190,45 @@ export async function getPackages(workspaceFolder?: vscode.WorkspaceFolder): Pro
148
190
149
191
const packages : string [ ] = [ ] ;
150
192
const getUriPackages = hasNblsProjectPackagesCommand ? getUriPackagesNbls : getUriPackagesJdt ;
193
+ logUtils . logInfo ( `[projectUtils] Using ${ hasNblsProjectPackagesCommand ? 'NBLS' : 'JDT' } to compute packages` ) ;
151
194
for ( const folder of workspaceFolders ) {
152
- await getUriPackages ( packages , folder , folder . uri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand ) ;
195
+ const unrecognizedProjectFolders : vscode . Uri [ ] = [ ] ;
196
+ try {
197
+ const foundPackages = await getUriPackages ( packages , folder , folder . uri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand ) ;
198
+ if ( ! foundPackages && ! isSupportedProjectUri ( folder . uri ) ) {
199
+ unrecognizedProjectFolders . push ( folder . uri ) ;
200
+ }
201
+ } catch ( err ) {
202
+ logUtils . logError ( `[projectUtils] Error computing packages: ${ err } ` ) ;
203
+ }
204
+
205
+ // Try to find a nested supported project folder
206
+ while ( unrecognizedProjectFolders . length ) {
207
+ const unrecognizedProjectFolder = unrecognizedProjectFolders . shift ( ) ;
208
+ if ( unrecognizedProjectFolder ) {
209
+ const subfolders = fs . readdirSync ( unrecognizedProjectFolder . fsPath ) ;
210
+ for ( const subfolder of subfolders ) {
211
+ const subfolderUri = vscode . Uri . joinPath ( unrecognizedProjectFolder , subfolder ) ;
212
+ if ( fs . lstatSync ( subfolderUri . fsPath ) ?. isDirectory ( ) ) {
213
+ const foundPackages = await getUriPackages ( packages , folder , subfolderUri . toString ( ) , hasNblsProjectInfoCommand , hasMicronautToolsSubprojectCommand ) ;
214
+ if ( ! foundPackages && ! isSupportedProjectUri ( folder . uri ) ) {
215
+ unrecognizedProjectFolders . push ( subfolderUri ) ;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ }
153
221
}
222
+ logUtils . logInfo ( `[projectUtils] Found ${ packages . length } package(s)` ) ;
154
223
return packages ;
155
224
}
156
225
157
- async function getUriPackagesNbls ( packages : string [ ] , folder : vscode . WorkspaceFolder , uri : string , hasNblsProjectInfoCommand : boolean , hasMicronautToolsSubprojectCommand : boolean ) {
226
+ async function getUriPackagesNbls ( packages : string [ ] , folder : vscode . WorkspaceFolder , uri : string , hasNblsProjectInfoCommand : boolean , hasMicronautToolsSubprojectCommand : boolean ) : Promise < boolean > {
227
+ let foundPackages = false ;
158
228
const uriPackages : string [ ] | undefined = await vscode . commands . executeCommand ( NBLS_GET_PACKAGES_COMMAND , uri , true ) ;
159
229
if ( uriPackages ) {
160
230
if ( uriPackages . length ) { // found project packages
231
+ foundPackages = true ;
161
232
for ( const uriPackage of uriPackages ) {
162
233
const wildcardPackage = uriPackage + '.*' ;
163
234
if ( ! packages . includes ( wildcardPackage ) ) {
@@ -168,41 +239,68 @@ async function getUriPackagesNbls(packages: string[], folder: vscode.WorkspaceFo
168
239
if ( hasMicronautToolsSubprojectCommand ) { // include only packages of the module selected in Micronaut Tools
169
240
const subprojectUri = await vscode . commands . executeCommand ( MICRONAUT_TOOLS_SELECTED_SUBPROJECT_COMMAND , folder ) ;
170
241
if ( subprojectUri instanceof vscode . Uri ) { // folder tracked by Micronaut Tools and module selected
171
- await getUriPackagesNbls ( packages , folder , subprojectUri . toString ( ) , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
242
+ return await getUriPackagesNbls ( packages , folder , subprojectUri . toString ( ) , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
172
243
// TODO: include dependency subprojects (oci -> lib)?
173
- return ;
174
244
}
175
245
}
176
246
if ( hasNblsProjectInfoCommand ) { // include packages from all found modules
177
247
const infos : any [ ] = await vscode . commands . executeCommand ( NBLS_PROJECT_INFO_COMMAND , uri , { projectStructure : true } ) ;
178
248
if ( infos ?. length && infos [ 0 ] ) {
179
249
for ( const subproject of infos [ 0 ] . subprojects ) { // multimodule - most likely GCN
180
- await getUriPackagesNbls ( packages , folder , subproject , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
250
+ foundPackages = foundPackages || await getUriPackagesNbls ( packages , folder , subproject , false , false ) ; // false prevents deep search (OK for GCN, may need to be enabled for other projects)
181
251
}
182
252
}
183
253
}
184
254
}
185
255
}
256
+ return foundPackages ;
186
257
}
187
258
188
- // TODO: add support for modules/subprojects
189
- async function getUriPackagesJdt ( packages : string [ ] , _folder : vscode . WorkspaceFolder , uri : string ) {
190
- const projectEntries = await getPackageDataJdt ( { kind : 2 , projectUri : uri } ) ;
191
- for ( const projectEntry of projectEntries ) {
192
- if ( projectEntry . entryKind === 1 ) { // source root
193
- const packageRoots = await getPackageDataJdt ( { kind : 3 , projectUri : uri , rootPath : projectEntry . path , isHierarchicalView : false } ) ;
194
- for ( const packageRoot of packageRoots ) {
195
- if ( packageRoot . kind === 4 ) { // package root
196
- const wildcardPackage = packageRoot . name + '.*' ;
197
- if ( ! packages . includes ( wildcardPackage ) ) {
198
- packages . push ( wildcardPackage ) ;
259
+ // TODO: add support for modules/subprojects?
260
+ // NOTE: modules/subprojects are defined by the Micronaut Tools ext., which has NBLS as a required dependency -> getUriPackagesNbls will be executed instead of getUriPackagesJdt
261
+ async function getUriPackagesJdt ( packages : string [ ] , _folder : vscode . WorkspaceFolder , uri : string ) : Promise < boolean > {
262
+ let foundPackages = false ;
263
+ try {
264
+ const projectEntries = await getPackageDataJdt ( { kind : 2 , projectUri : uri } ) ;
265
+ for ( const projectEntry of projectEntries ) {
266
+ if ( projectEntry . entryKind === 1 ) { // source root
267
+ const packageRoots = await getPackageDataJdt ( { kind : 3 , projectUri : uri , rootPath : projectEntry . path , isHierarchicalView : false } ) ;
268
+ for ( const packageRoot of packageRoots ) {
269
+ if ( packageRoot . kind === 4 ) { // package root
270
+ foundPackages = true ;
271
+ const wildcardPackage = packageRoot . name + '.*' ;
272
+ if ( ! packages . includes ( wildcardPackage ) ) {
273
+ packages . push ( wildcardPackage ) ;
274
+ }
199
275
}
200
276
}
201
277
}
202
278
}
279
+ return foundPackages ;
280
+ } catch ( err ) {
281
+ // Error: Did not find container for URI <uri>
282
+ return false ;
203
283
}
204
284
}
205
285
206
286
async function getPackageDataJdt ( params : { [ key : string ] : any } ) : Promise < any [ ] > {
207
287
return await vscode . commands . executeCommand ( JDT_EXECUTE_WORKSPACE_COMMAND , JDT_GET_PACKAGE_DATA , params ) || [ ] ;
208
288
}
289
+
290
+ // Currently supports recognizing Maven Java project root (pom.xml) and Gradle Java project root (settings.gradle or build.gradle)
291
+ // Ideally a NBLS / JDT API should be used to identify a project folder supported by the particular LS
292
+ function isSupportedProjectUri ( uri : vscode . Uri ) : boolean {
293
+ const mavenPomFile = path . join ( uri . fsPath , 'pom.xml' ) ;
294
+ if ( fs . existsSync ( mavenPomFile ) && fs . lstatSync ( mavenPomFile ) ?. isFile ( ) ) {
295
+ return true ;
296
+ }
297
+ const gradleSettingsFile = path . join ( uri . fsPath , 'settings.gradle' ) ;
298
+ if ( fs . existsSync ( gradleSettingsFile ) && fs . lstatSync ( gradleSettingsFile ) ?. isFile ( ) ) {
299
+ return true ;
300
+ }
301
+ const gradleBuildFile = path . join ( uri . fsPath , 'build.gradle' ) ;
302
+ if ( fs . existsSync ( gradleBuildFile ) && fs . lstatSync ( gradleBuildFile ) ?. isFile ( ) ) {
303
+ return true ;
304
+ }
305
+ return false ;
306
+ }
0 commit comments