1+ /*---------------------------------------------------------
2+ * Copyright (C) Microsoft Corporation. All rights reserved.
3+ *--------------------------------------------------------*/
4+
5+ import vscode = require( 'vscode' ) ;
6+ import { IFeature } from '../feature' ;
7+ import { LanguageClient , RequestType , NotificationType } from 'vscode-languageclient' ;
8+
9+ export class NewFileOrProjectFeature implements IFeature {
10+
11+ private readonly loadIcon = " $(sync) " ;
12+ private command : vscode . Disposable ;
13+ private languageClient : LanguageClient ;
14+ private waitingForClientToken : vscode . CancellationTokenSource ;
15+
16+ constructor ( ) {
17+ this . command =
18+ vscode . commands . registerCommand ( 'PowerShell.NewProjectFromTemplate' , ( ) => {
19+
20+ if ( ! this . languageClient && ! this . waitingForClientToken ) {
21+
22+ // If PowerShell isn't finished loading yet, show a loading message
23+ // until the LanguageClient is passed on to us
24+ this . waitingForClientToken = new vscode . CancellationTokenSource ( ) ;
25+ vscode . window
26+ . showQuickPick (
27+ [ "Cancel" ] ,
28+ { placeHolder : "New Project: Please wait, starting PowerShell..." } ,
29+ this . waitingForClientToken . token )
30+ . then ( response => { if ( response === "Cancel" ) { this . clearWaitingToken ( ) ; } } ) ;
31+
32+ // Cancel the loading prompt after 60 seconds
33+ setTimeout ( ( ) => {
34+ if ( this . waitingForClientToken ) {
35+ this . clearWaitingToken ( ) ;
36+
37+ vscode . window . showErrorMessage (
38+ "New Project: PowerShell session took too long to start." ) ;
39+ }
40+ } , 60000 ) ;
41+ }
42+ else {
43+ this . showProjectTemplates ( ) ;
44+ }
45+ } ) ;
46+ }
47+
48+ public setLanguageClient ( languageClient : LanguageClient ) {
49+ this . languageClient = languageClient ;
50+
51+ if ( this . waitingForClientToken ) {
52+ this . clearWaitingToken ( ) ;
53+ this . showProjectTemplates ( ) ;
54+ }
55+ }
56+
57+ public dispose ( ) {
58+ this . command . dispose ( ) ;
59+ }
60+
61+ private showProjectTemplates ( includeInstalledModules : boolean = false ) : void {
62+ vscode . window
63+ . showQuickPick (
64+ this . getProjectTemplates ( includeInstalledModules ) ,
65+ { placeHolder : "Choose a template to create a new project" ,
66+ ignoreFocusOut : true } )
67+ . then ( template => {
68+ if ( template . label . startsWith ( this . loadIcon ) ) {
69+ this . showProjectTemplates ( true ) ;
70+ }
71+ else {
72+ this . createProjectFromTemplate ( template . template ) ;
73+ }
74+ } ) ;
75+ }
76+
77+ private getProjectTemplates ( includeInstalledModules : boolean ) : Thenable < TemplateQuickPickItem [ ] > {
78+ return this . languageClient
79+ . sendRequest (
80+ GetProjectTemplatesRequest . type ,
81+ { includeInstalledModules : includeInstalledModules } )
82+ . then ( response => {
83+ if ( response . needsModuleInstall ) {
84+ // TODO: Offer to install Plaster
85+ vscode . window . showErrorMessage ( "Plaster is not installed!" ) ;
86+ return Promise . reject < TemplateQuickPickItem [ ] > ( "Plaster needs to be installed" ) ;
87+ }
88+ else {
89+ var templates = response . templates . map < TemplateQuickPickItem > (
90+ template => {
91+ return {
92+ label : template . title ,
93+ description : `v${ template . version } by ${ template . author } , tags: ${ template . tags } ` ,
94+ detail : template . description ,
95+ template : template
96+ }
97+ } ) ;
98+
99+ if ( ! includeInstalledModules ) {
100+ templates =
101+ [ { label : this . loadIcon , description : "Load additional templates from installed modules" , template : undefined } ]
102+ . concat ( templates )
103+ }
104+ else {
105+ templates =
106+ [ { label : this . loadIcon , description : "Refresh template list" , template : undefined } ]
107+ . concat ( templates )
108+ }
109+
110+ return templates ;
111+ }
112+ } ) ;
113+ }
114+
115+ private createProjectFromTemplate ( template : TemplateDetails ) : void {
116+ vscode . window
117+ . showInputBox (
118+ { placeHolder : "Enter an absolute path to the folder where the project should be created" ,
119+ ignoreFocusOut : true } )
120+ . then ( destinationPath => {
121+
122+ if ( destinationPath ) {
123+ // Show the PowerShell session output in case an error occurred
124+ vscode . commands . executeCommand ( "PowerShell.ShowSessionOutput" ) ;
125+
126+ this . languageClient
127+ . sendRequest (
128+ NewProjectFromTemplateRequest . type ,
129+ { templatePath : template . templatePath , destinationPath : destinationPath } )
130+ . then ( result => {
131+ if ( result . creationSuccessful ) {
132+ this . openWorkspacePath ( destinationPath ) ;
133+ }
134+ else {
135+ vscode . window . showErrorMessage (
136+ "Project creation failed, read the Output window for more details." ) ;
137+ }
138+ } ) ;
139+ }
140+ else {
141+ vscode . window
142+ . showErrorMessage (
143+ "New Project: You must enter an absolute folder path to continue. Try again?" ,
144+ "Yes" , "No" )
145+ . then (
146+ response => {
147+ if ( response === "Yes" ) {
148+ this . createProjectFromTemplate ( template ) ;
149+ }
150+ } ) ;
151+ }
152+ } ) ;
153+ }
154+
155+ private openWorkspacePath ( workspacePath : string ) {
156+ // Open the created project in a new window
157+ vscode . commands . executeCommand (
158+ "vscode.openFolder" ,
159+ vscode . Uri . file ( workspacePath ) ,
160+ true ) ;
161+ }
162+
163+ private clearWaitingToken ( ) {
164+ if ( this . waitingForClientToken ) {
165+ this . waitingForClientToken . dispose ( ) ;
166+ this . waitingForClientToken = undefined ;
167+ }
168+ }
169+ }
170+
171+ interface TemplateQuickPickItem extends vscode . QuickPickItem {
172+ template : TemplateDetails
173+ }
174+
175+ interface TemplateDetails {
176+ title : string ;
177+ version : string ;
178+ author : string ;
179+ description : string ;
180+ tags : string ;
181+ templatePath : string ;
182+ }
183+
184+ namespace GetProjectTemplatesRequest {
185+ export const type : RequestType < GetProjectTemplatesRequestArgs , GetProjectTemplatesResponseBody , string > =
186+ { get method ( ) { return 'powerShell/getProjectTemplates' ; } } ;
187+ }
188+
189+ interface GetProjectTemplatesRequestArgs {
190+ includeInstalledModules : boolean ;
191+ }
192+
193+ interface GetProjectTemplatesResponseBody {
194+ needsModuleInstall : boolean ;
195+ templates : TemplateDetails [ ] ;
196+ }
197+
198+ namespace NewProjectFromTemplateRequest {
199+ export const type : RequestType < any , NewProjectFromTemplateResponseBody , string > =
200+ { get method ( ) { return 'powerShell/newProjectFromTemplate' ; } } ;
201+ }
202+
203+ interface NewProjectFromTemplateRequestArgs {
204+ destinationPath : string ;
205+ templatePath : string ;
206+ }
207+
208+ interface NewProjectFromTemplateResponseBody {
209+ creationSuccessful : boolean ;
210+ }
0 commit comments