44import * as fse from "fs-extra" ;
55import * as _ from "lodash" ;
66import * as path from "path" ;
7- import { commands , Disposable , ExtensionContext , QuickPickItem , Uri , window , workspace } from "vscode" ;
8- import { instrumentOperationAsVsCodeCommand , sendInfo } from "vscode-extension-telemetry-wrapper" ;
7+ import { commands , Disposable , Extension , ExtensionContext , extensions , QuickPickItem , Uri , window , workspace } from "vscode" ;
8+ import { instrumentOperationAsVsCodeCommand } from "vscode-extension-telemetry-wrapper" ;
99import { Commands } from "../commands" ;
10- import { Context } from "../constants" ;
11- import { contextManager } from "../contextManager" ;
12- import { getExpService } from "../ExperimentationService" ;
1310import { Utility } from "../utility" ;
1411
1512export class ProjectController implements Disposable {
@@ -27,97 +24,173 @@ export class ProjectController implements Disposable {
2724 }
2825
2926 public async createJavaProject ( ) {
30- const projectKinds : QuickPickItem [ ] = [ {
31- label : BuildTool . None ,
32- detail : "A project without any build tools" ,
33- } ] ;
34- if ( contextManager . getContextValue ( Context . MAVEN_ENABLED ) ) {
35- const isMavenDefault : boolean = await getExpService ( ) ?. isCachedFlightEnabled ( "defaultMaven" ) || false ;
36- const mavenItem : QuickPickItem = {
37- label : BuildTool . Maven ,
38- detail : "Use Maven to manage your project" ,
27+ const items : IProjectTypeQuickPick [ ] = projectTypes . map ( ( type : IProjectType ) => {
28+ return {
29+ label : type . displayName ,
30+ description : type . description ,
31+ detail : type . metadata . extensionName ? `Provided by $(extensions) ${ type . metadata . extensionName } ` : type . detail ,
32+ metadata : type . metadata ,
3933 } ;
40- if ( isMavenDefault ) {
41- projectKinds . unshift ( mavenItem ) ;
42- } else {
43- projectKinds . push ( mavenItem ) ;
44- }
45- }
46- const choice : QuickPickItem | undefined = projectKinds . length === 1 ? projectKinds [ 0 ] :
47- await window . showQuickPick ( projectKinds , {
48- ignoreFocusOut : true ,
49- placeHolder : "Select the project build tool" ,
50- } ,
51- ) ;
52-
53- if ( ! choice ) {
34+ } ) ;
35+ const choice = await window . showQuickPick ( items , {
36+ ignoreFocusOut : true ,
37+ placeHolder : "Select the project type" ,
38+ } ) ;
39+ if ( ! choice || ! await ensureExtension ( choice . label , choice . metadata ) ) {
5440 return ;
5541 }
5642
57- if ( projectKinds . length > 1 ) {
58- const chooseDefault : boolean = choice . label === projectKinds [ 0 ] . label ;
59- sendInfo ( "" , { "project.create.chooseDefault" : `${ chooseDefault } ` } ) ;
60- }
61-
62- switch ( choice . label ) {
63- case BuildTool . Maven :
64- await commands . executeCommand ( Commands . JAVA_MAVEN_CREATE_PROJECT ) ;
65- break ;
66- case BuildTool . None :
67- await this . scaffoldSimpleProject ( ) ;
68- break ;
69- default :
70- break ;
43+ if ( choice . metadata . type === ProjectType . NoBuildTool ) {
44+ await scaffoldSimpleProject ( ) ;
45+ } else if ( choice . metadata . createCommandId ) {
46+ await commands . executeCommand ( choice . metadata . createCommandId ) ;
7147 }
7248 }
49+ }
7350
74- private async scaffoldSimpleProject ( ) : Promise < void > {
75- const workspaceFolder = Utility . getDefaultWorkspaceFolder ( ) ;
76- const location : Uri [ ] | undefined = await window . showOpenDialog ( {
77- defaultUri : workspaceFolder && workspaceFolder . uri ,
78- canSelectFiles : false ,
79- canSelectFolders : true ,
80- openLabel : "Select the location" ,
81- } ) ;
82- if ( ! location || ! location . length ) {
83- return ;
84- }
51+ interface IProjectType {
52+ displayName : string ;
53+ description ?: string ;
54+ detail ?: string ;
55+ metadata : IProjectTypeMetadata ;
56+ }
8557
86- const basePath : string = location [ 0 ] . fsPath ;
87- const projectName : string | undefined = await window . showInputBox ( {
88- prompt : "Input a java project name" ,
89- ignoreFocusOut : true ,
90- validateInput : async ( name : string ) : Promise < string > => {
91- if ( name && ! name . match ( / ^ [ ^ * ~ / \\ ] + $ / ) ) {
92- return "Please input a valid project name" ;
93- }
94- if ( name && await fse . pathExists ( path . join ( basePath , name ) ) ) {
95- return "A project with this name already exists." ;
96- }
97- return "" ;
98- } ,
99- } ) ;
58+ interface IProjectTypeMetadata {
59+ type : ProjectType ;
60+ extensionId : string ;
61+ extensionName : string ;
62+ createCommandId : string ;
63+ }
10064
101- if ( ! projectName ) {
102- return ;
103- }
65+ interface IProjectTypeQuickPick extends QuickPickItem {
66+ metadata : IProjectTypeMetadata ;
67+ }
10468
105- const projectRoot : string = path . join ( basePath , projectName ) ;
106- const templateRoot : string = path . join ( this . context . extensionPath , "templates" , "invisible-project" ) ;
107- try {
108- await fse . ensureDir ( projectRoot ) ;
109- await fse . copy ( templateRoot , projectRoot ) ;
110- await fse . ensureDir ( path . join ( projectRoot , "lib" ) ) ;
111- } catch ( error ) {
112- window . showErrorMessage ( error . message ) ;
113- return ;
114- }
115- const openInNewWindow = workspace && ! _ . isEmpty ( workspace . workspaceFolders ) ;
116- await commands . executeCommand ( Commands . VSCODE_OPEN_FOLDER , Uri . file ( path . join ( basePath , projectName ) ) , openInNewWindow ) ;
69+ enum ProjectType {
70+ NoBuildTool = "NoBuildTool" ,
71+ Maven = "Maven" ,
72+ SpringBoot = "SpringBoot" ,
73+ Quarkus = "Quarkus" ,
74+ MicroProfile = "MicroProfile" ,
75+ }
76+
77+ async function ensureExtension ( typeName : string , metaData : IProjectTypeMetadata ) : Promise < boolean > {
78+ if ( ! metaData . extensionId ) {
79+ return true ;
80+ }
81+
82+ const extension : Extension < any > | undefined = extensions . getExtension ( metaData . extensionId ) ;
83+ if ( extension === undefined ) {
84+ await promptInstallExtension ( typeName , metaData ) ;
85+ return false ;
11786 }
87+
88+ await extension . activate ( ) ;
89+ return true ;
11890}
11991
120- enum BuildTool {
121- Maven = "Maven" ,
122- None = "No build tools" ,
92+ async function promptInstallExtension ( projectType : string , metaData : IProjectTypeMetadata ) : Promise < void > {
93+ const choice : string | undefined = await window . showInformationMessage ( `${ metaData . extensionName } is required to create ${ projectType } projects. Please re-run the command 'Java: Create Java Project...' after the extension is installed.` , "Install" ) ;
94+ if ( choice === "Install" ) {
95+ commands . executeCommand ( "workbench.extensions.installExtension" , metaData . extensionId ) ;
96+ // So far there is no API to query the extension's state, so we open the extension's homepage
97+ // here, where users can check the state: installing, disabled, installed, etc...
98+ // See: https://github.com/microsoft/vscode/issues/14444
99+ commands . executeCommand ( "extension.open" , metaData . extensionId ) ;
100+ }
101+ }
102+
103+ async function scaffoldSimpleProject ( ) : Promise < void > {
104+ const workspaceFolder = Utility . getDefaultWorkspaceFolder ( ) ;
105+ const location : Uri [ ] | undefined = await window . showOpenDialog ( {
106+ defaultUri : workspaceFolder && workspaceFolder . uri ,
107+ canSelectFiles : false ,
108+ canSelectFolders : true ,
109+ openLabel : "Select the project location" ,
110+ } ) ;
111+ if ( ! location || ! location . length ) {
112+ return ;
113+ }
114+
115+ const basePath : string = location [ 0 ] . fsPath ;
116+ const projectName : string | undefined = await window . showInputBox ( {
117+ prompt : "Input a Java project name" ,
118+ ignoreFocusOut : true ,
119+ validateInput : async ( name : string ) : Promise < string > => {
120+ if ( name && ! name . match ( / ^ [ ^ * ~ / \\ ] + $ / ) ) {
121+ return "Please input a valid project name" ;
122+ }
123+ if ( name && await fse . pathExists ( path . join ( basePath , name ) ) ) {
124+ return "A project with this name already exists" ;
125+ }
126+ return "" ;
127+ } ,
128+ } ) ;
129+
130+ if ( ! projectName ) {
131+ return ;
132+ }
133+
134+ const projectRoot : string = path . join ( basePath , projectName ) ;
135+ const templateRoot : string = path . join ( this . context . extensionPath , "templates" , "invisible-project" ) ;
136+ try {
137+ await fse . ensureDir ( projectRoot ) ;
138+ await fse . copy ( templateRoot , projectRoot ) ;
139+ await fse . ensureDir ( path . join ( projectRoot , "lib" ) ) ;
140+ } catch ( error ) {
141+ window . showErrorMessage ( error . message ) ;
142+ return ;
143+ }
144+ const openInNewWindow = workspace && ! _ . isEmpty ( workspace . workspaceFolders ) ;
145+ await commands . executeCommand ( Commands . VSCODE_OPEN_FOLDER , Uri . file ( path . join ( basePath , projectName ) ) , openInNewWindow ) ;
123146}
147+
148+ const projectTypes : IProjectType [ ] = [
149+ {
150+ displayName : "No build tools" ,
151+ detail : "Create a project without any build tools" ,
152+ metadata : {
153+ type : ProjectType . NoBuildTool ,
154+ extensionId : "" ,
155+ extensionName : "" ,
156+ createCommandId : "" ,
157+ } ,
158+ } ,
159+ {
160+ displayName : "Maven" ,
161+ description : "create from archetype" ,
162+ metadata : {
163+ type : ProjectType . Maven ,
164+ extensionId : "vscjava.vscode-maven" ,
165+ extensionName : "Maven for Java" ,
166+ createCommandId : "maven.archetype.generate" ,
167+ } ,
168+ } ,
169+ {
170+ displayName : "Spring Boot" ,
171+ metadata : {
172+ type : ProjectType . SpringBoot ,
173+ extensionId : "vscjava.vscode-spring-initializr" ,
174+ extensionName : "Spring Initializr Java Support" ,
175+ createCommandId : "spring.initializr.createProject" ,
176+ } ,
177+ } ,
178+ {
179+ displayName : "Quarkus" ,
180+ metadata : {
181+ type : ProjectType . Quarkus ,
182+ extensionId : "redhat.vscode-quarkus" ,
183+ extensionName : "Quarkus" ,
184+ createCommandId : "quarkusTools.createProject" ,
185+ } ,
186+ } ,
187+ {
188+ displayName : "MicroProfile" ,
189+ metadata : {
190+ type : ProjectType . MicroProfile ,
191+ extensionId : "microprofile-community.mp-starter-vscode-ext" ,
192+ extensionName : "MicroProfile Starter" ,
193+ createCommandId : "extension.microProfileStarter" ,
194+ } ,
195+ } ,
196+ ] ;
0 commit comments