1+ /*---------------------------------------------------------------------------------------------
2+ * Copyright (c) Microsoft Corporation. All rights reserved.
3+ * Licensed under the MIT License. See License.txt in the project root for license information.
4+ *--------------------------------------------------------------------------------------------*/
5+
6+ 'use strict' ;
7+
8+ import * as fs from 'fs-extra-promise' ;
9+ import * as path from 'path' ;
10+ import * as vscode from 'vscode' ;
11+ import * as tasks from 'vscode-tasks' ;
12+ import { OmnisharpServer } from './omnisharpServer' ;
13+ import * as serverUtils from './omnisharpUtils' ;
14+ import * as protocol from './protocol.ts'
15+
16+ interface DebugConfiguration {
17+ name : string ,
18+ type : string ,
19+ request : string ,
20+ }
21+
22+ interface ConsoleLaunchConfiguration extends DebugConfiguration {
23+ preLaunchTask : string ,
24+ program : string ,
25+ args : string [ ] ,
26+ cwd : string ,
27+ stopAtEntry : boolean
28+ }
29+
30+ interface CommandLine {
31+ command : string ,
32+ args ?: string
33+ }
34+
35+ interface LaunchBrowserConfiguration {
36+ enabled : boolean ,
37+ args : string ,
38+ windows ?: CommandLine ,
39+ osx : CommandLine ,
40+ linux : CommandLine
41+ }
42+
43+ interface WebLaunchConfiguration extends ConsoleLaunchConfiguration {
44+ launchBrowser : LaunchBrowserConfiguration
45+ }
46+
47+ interface AttachConfiguration extends DebugConfiguration {
48+ processName : string
49+ }
50+
51+ interface Paths {
52+ vscodeFolder : string ;
53+ tasksJsonPath : string ;
54+ launchJsonPath : string ;
55+ }
56+
57+ function getPaths ( ) : Paths {
58+ const vscodeFolder = path . join ( vscode . workspace . rootPath , '.vscode' ) ;
59+
60+ return {
61+ vscodeFolder : vscodeFolder ,
62+ tasksJsonPath : path . join ( vscodeFolder , 'tasks.json' ) ,
63+ launchJsonPath : path . join ( vscodeFolder , 'launch.json' )
64+ }
65+ }
66+
67+ interface Operations {
68+ addTasksJson ?: boolean ,
69+ updateTasksJson ?: boolean ,
70+ addLaunchJson ?: boolean
71+ }
72+
73+ function hasOperations ( operations : Operations ) {
74+ return operations . addLaunchJson ||
75+ operations . updateTasksJson ||
76+ operations . addLaunchJson ;
77+ }
78+
79+ function getOperations ( ) {
80+ const paths = getPaths ( ) ;
81+
82+ return getBuildOperations ( paths . tasksJsonPath ) . then ( operations =>
83+ getLaunchOperations ( paths . launchJsonPath , operations ) ) ;
84+ }
85+
86+ function getBuildOperations ( tasksJsonPath : string ) {
87+ return new Promise < Operations > ( ( resolve , reject ) => {
88+ return fs . existsAsync ( tasksJsonPath ) . then ( exists => {
89+ if ( exists ) {
90+ fs . readFileAsync ( tasksJsonPath ) . then ( buffer => {
91+ const text = buffer . toString ( ) ;
92+ const tasksJson : tasks . TaskConfiguration = JSON . parse ( text ) ;
93+ const buildTask = tasksJson . tasks . find ( td => td . taskName === 'build' ) ;
94+
95+ resolve ( { updateTasksJson : ( buildTask === undefined ) } ) ;
96+ } ) ;
97+ }
98+ else {
99+ resolve ( { addTasksJson : true } ) ;
100+ }
101+ } ) ;
102+ } ) ;
103+ }
104+
105+ function getLaunchOperations ( launchJsonPath : string , operations : Operations ) {
106+ return new Promise < Operations > ( ( resolve , reject ) => {
107+ return fs . existsAsync ( launchJsonPath ) . then ( exists => {
108+ if ( exists ) {
109+ resolve ( operations ) ;
110+ }
111+ else {
112+ operations . addLaunchJson = true ;
113+ resolve ( operations ) ;
114+ }
115+ } ) ;
116+ } ) ;
117+ }
118+
119+ function promptToAddAssets ( ) {
120+ return new Promise < boolean > ( ( resolve , reject ) => {
121+ const item = { title : 'Yes' }
122+
123+ vscode . window . showInformationMessage ( 'Required assets to build and debug are missing from your project. Add them?' , item ) . then ( selection => {
124+ return selection
125+ ? resolve ( true )
126+ : resolve ( false ) ;
127+ } ) ;
128+ } ) ;
129+ }
130+
131+ function createLaunchConfiguration ( targetFramework : string , executableName : string ) : ConsoleLaunchConfiguration {
132+ return {
133+ name : '.NET Core Launch (console)' ,
134+ type : 'coreclr' ,
135+ request : 'launch' ,
136+ preLaunchTask : 'build' ,
137+ program : '${workspaceRoot}/bin/Debug/' + targetFramework + '/' + executableName ,
138+ args : [ ] ,
139+ cwd : '${workspaceRoot}' ,
140+ stopAtEntry : false
141+ }
142+ }
143+
144+ function createWebLaunchConfiguration ( targetFramework : string , executableName : string ) : WebLaunchConfiguration {
145+ return {
146+ name : '.NET Core Launch (web)' ,
147+ type : 'coreclr' ,
148+ request : 'launch' ,
149+ preLaunchTask : 'build' ,
150+ program : '${workspaceRoot}/bin/Debug/' + targetFramework + '/' + executableName ,
151+ args : [ ] ,
152+ cwd : '${workspaceRoot}' ,
153+ stopAtEntry : false ,
154+ launchBrowser : {
155+ enabled : true ,
156+ args : '${auto-detect-url}' ,
157+ windows : {
158+ command : 'cmd.exe' ,
159+ args : '/C start ${auto-detect-url}'
160+ } ,
161+ osx : {
162+ command : 'open'
163+ } ,
164+ linux : {
165+ command : 'xdg-open'
166+ }
167+ }
168+ }
169+ }
170+
171+ function createAttachConfiguration ( ) : AttachConfiguration {
172+ return {
173+ name : '.NET Core Attach' ,
174+ type : 'coreclr' ,
175+ request : 'attach' ,
176+ processName : '<example>'
177+ }
178+ }
179+
180+ function createLaunchJson ( targetFramework : string , executableName : string ) : any {
181+ return {
182+ version : '0.2.0' ,
183+ configurations : [
184+ createLaunchConfiguration ( targetFramework , executableName ) ,
185+ createWebLaunchConfiguration ( targetFramework , executableName ) ,
186+ createAttachConfiguration ( )
187+ ]
188+ }
189+ }
190+
191+ function createBuildTaskDescription ( ) : tasks . TaskDescription {
192+ return {
193+ taskName : 'build' ,
194+ args : [ ] ,
195+ isBuildCommand : true ,
196+ problemMatcher : '$msCompile'
197+ } ;
198+ }
199+
200+ function createTasksConfiguration ( ) : tasks . TaskConfiguration {
201+ return {
202+ version : '0.1.0' ,
203+ command : 'dotnet' ,
204+ isShellCommand : true ,
205+ args : [ ] ,
206+ tasks : [ createBuildTaskDescription ( ) ]
207+ } ;
208+ }
209+
210+ function addTasksJsonIfNecessary ( info : protocol . DotNetWorkspaceInformation , paths : Paths , operations : Operations ) {
211+ return new Promise < void > ( ( resolve , reject ) => {
212+ if ( ! operations . addTasksJson ) {
213+ return resolve ( ) ;
214+ }
215+
216+ const tasksJson = createTasksConfiguration ( ) ;
217+ const tasksJsonText = JSON . stringify ( tasksJson , null , ' ' ) ;
218+
219+ return fs . writeFileAsync ( paths . tasksJsonPath , tasksJsonText ) ;
220+ } ) ;
221+ }
222+
223+ function addLaunchJsonIfNecessary ( info : protocol . DotNetWorkspaceInformation , paths : Paths , operations : Operations ) {
224+ return new Promise < void > ( ( resolve , reject ) => {
225+ if ( ! operations . addLaunchJson ) {
226+ return resolve ( ) ;
227+ }
228+
229+ let targetFramework = '<target-framework>' ;
230+ let executableName = '<project-name.dll>' ;
231+
232+ let projectWithEntryPoint = info . Projects . find ( project => project . EmitEntryPoint === true ) ;
233+
234+ if ( projectWithEntryPoint ) {
235+ targetFramework = projectWithEntryPoint . TargetFramework . ShortName ;
236+ executableName = path . basename ( projectWithEntryPoint . CompilationOutputAssemblyFile ) ;
237+ }
238+
239+ const launchJson = createLaunchJson ( targetFramework , executableName ) ;
240+ const launchJsonText = JSON . stringify ( launchJson , null , ' ' ) ;
241+
242+ return fs . writeFileAsync ( paths . launchJsonPath , launchJsonText ) ;
243+ } ) ;
244+ }
245+
246+ export function addAssetsIfNecessary ( server : OmnisharpServer ) {
247+ if ( ! vscode . workspace . rootPath ) {
248+ return ;
249+ }
250+
251+ // If there is no project.json, we won't bother to prompt the user for tasks.json.
252+ const projectJsonPath = path . join ( vscode . workspace . rootPath , 'project.json' ) ;
253+ if ( ! fs . existsSync ( projectJsonPath ) ) {
254+ return ;
255+ }
256+
257+ return serverUtils . requestWorkspaceInformation ( server ) . then ( info => {
258+ // If there are no .NET Core projects, we won't bother offering to add assets.
259+ if ( 'DotNet' in info && info . DotNet . Projects . length > 0 ) {
260+ return getOperations ( ) . then ( operations => {
261+ if ( ! hasOperations ( operations ) ) {
262+ return ;
263+ }
264+
265+ promptToAddAssets ( ) . then ( addAssets => {
266+ if ( ! addAssets ) {
267+ return ;
268+ }
269+
270+ const paths = getPaths ( ) ;
271+
272+ return fs . ensureDirAsync ( paths . vscodeFolder ) . then ( ( ) => {
273+ return Promise . all ( [
274+ addTasksJsonIfNecessary ( info . DotNet , paths , operations ) ,
275+ addLaunchJsonIfNecessary ( info . DotNet , paths , operations )
276+ ] ) ;
277+ } ) ;
278+ } ) ;
279+ } ) ;
280+ }
281+ } ) ;
282+ }
0 commit comments