1111using System . Text . RegularExpressions ;
1212using UnityEngine ;
1313using IOPath = System . IO . Path ;
14- using System . Runtime . InteropServices ;
15- using System . Text ;
14+ using Newtonsoft . Json ;
15+ using Newtonsoft . Json . Linq ;
1616
1717namespace Microsoft . Unity . VisualStudio . Editor
1818{
@@ -44,7 +44,7 @@ private string GetExtensionPath()
4444 return null ;
4545
4646 return Directory
47- . EnumerateDirectories ( extensionsPath , "visualstudiotoolsforunity.vstuc *") // publisherid.extensionid
47+ . EnumerateDirectories ( extensionsPath , $ " { MicrosoftUnityExtensionId } *") // publisherid.extensionid
4848 . OrderByDescending ( n => n )
4949 . FirstOrDefault ( ) ;
5050 }
@@ -193,7 +193,6 @@ public override void CreateExtraFiles(string projectDirectory)
193193 {
194194 try
195195 {
196- // see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
197196 var vscodeDirectory = IOPath . Combine ( projectDirectory . NormalizePathSeparators ( ) , ".vscode" ) ;
198197 Directory . CreateDirectory ( vscodeDirectory ) ;
199198
@@ -206,31 +205,90 @@ public override void CreateExtraFiles(string projectDirectory)
206205 }
207206 }
208207
209- private static void CreateLaunchFile ( string vscodeDirectory )
210- {
211- var launchFile = IOPath . Combine ( vscodeDirectory , "launch.json" ) ;
212- if ( File . Exists ( launchFile ) )
213- return ;
214-
215- const string content = @"{
208+ private const string DefaultLaunchFileContent = @"{
216209 ""version"": ""0.2.0"",
217210 ""configurations"": [
218211 {
219212 ""name"": ""Attach to Unity"",
220213 ""type"": ""vstuc"",
221- ""request"": ""attach"",
214+ ""request"": ""attach""
222215 }
223216 ]
224217}" ;
225218
226- File . WriteAllText ( launchFile , content ) ;
219+ private static void CreateLaunchFile ( string vscodeDirectory )
220+ {
221+ var launchFile = IOPath . Combine ( vscodeDirectory , "launch.json" ) ;
222+ if ( File . Exists ( launchFile ) )
223+ {
224+ PatchLaunchFile ( launchFile ) ;
225+ return ;
226+ }
227+
228+ File . WriteAllText ( launchFile , DefaultLaunchFileContent ) ;
229+ }
230+
231+ private static void PatchLaunchFile ( string launchFile )
232+ {
233+ try
234+ {
235+ const string configurationsKey = "configurations" ;
236+ const string typeKey = "type" ;
237+
238+ var content = File . ReadAllText ( launchFile ) ;
239+ var launch = JObject . Parse ( content ) ;
240+
241+ var configurations = ( JArray ) launch [ configurationsKey ] ;
242+ if ( configurations == null )
243+ {
244+ configurations = new JArray ( ) ;
245+ launch . Add ( configurationsKey , configurations ) ;
246+ }
247+
248+ var containsVstucEntry = false ;
249+ var patched = false ;
250+
251+ foreach ( var entry in configurations . ToArray ( ) )
252+ {
253+ var type = entry [ typeKey ] . Value < string > ( ) ;
254+
255+ switch ( type )
256+ {
257+ case "unity" :
258+ entry . Remove ( ) ;
259+ patched = true ;
260+ break ;
261+
262+ case "vstuc" :
263+ containsVstucEntry = true ;
264+ break ;
265+ }
266+ }
267+
268+ if ( ! containsVstucEntry )
269+ {
270+ var defaultContent = JObject . Parse ( DefaultLaunchFileContent ) ;
271+ configurations . Add ( defaultContent [ configurationsKey ] . First ( ) ) ;
272+ patched = true ;
273+ }
274+
275+ if ( patched )
276+ WriteAllTextFromJObject ( launchFile , launch ) ;
277+ }
278+ catch ( Exception )
279+ {
280+ // do not fail if we cannot patch the launch.json file
281+ }
227282 }
228283
229284 private void CreateSettingsFile ( string vscodeDirectory )
230285 {
231286 var settingsFile = IOPath . Combine ( vscodeDirectory , "settings.json" ) ;
232287 if ( File . Exists ( settingsFile ) )
288+ {
289+ PatchSettingsFile ( settingsFile ) ;
233290 return ;
291+ }
234292
235293 const string excludes = @" ""files.exclude"":
236294 {
@@ -299,19 +357,125 @@ private void CreateSettingsFile(string vscodeDirectory)
299357 File . WriteAllText ( settingsFile , content ) ;
300358 }
301359
302- private static void CreateRecommendedExtensionsFile ( string vscodeDirectory )
360+ private void PatchSettingsFile ( string settingsFile )
303361 {
304- var extensionFile = IOPath . Combine ( vscodeDirectory , "extensions.json" ) ;
305- if ( File . Exists ( extensionFile ) )
306- return ;
362+ try
363+ {
364+ const string excludesKey = "files.exclude" ;
365+ const string solutionKey = "dotnet.defaultSolution" ;
366+
367+ var content = File . ReadAllText ( settingsFile ) ;
368+ var settings = JObject . Parse ( content ) ;
369+
370+ var excludes = ( JObject ) settings [ excludesKey ] ;
371+ if ( excludes == null )
372+ return ;
307373
308- const string content = @"{
374+ var patchList = new List < string > ( ) ;
375+ var patched = false ;
376+
377+ // Remove files.exclude for solution files in the project root
378+ foreach ( var exclude in excludes )
379+ {
380+ if ( ! exclude . Value . Value < bool > ( ) )
381+ continue ;
382+
383+ if ( Regex . IsMatch ( exclude . Key , "^(\\ *\\ *[\\ \\ \\ /])?\\ *\\ .sln$" ) )
384+ {
385+ patchList . Add ( exclude . Key ) ;
386+ patched = true ;
387+ }
388+ }
389+
390+ // Check default solution
391+ var defaultSolution = settings [ solutionKey ] ;
392+ var solutionFile = IOPath . GetFileName ( ProjectGenerator . SolutionFile ( ) ) ;
393+ if ( defaultSolution == null || defaultSolution . Value < string > ( ) != solutionFile )
394+ {
395+ settings [ solutionKey ] = solutionFile ;
396+ patched = true ;
397+ }
398+
399+ if ( patched )
400+ {
401+ foreach ( var patch in patchList )
402+ excludes . Remove ( patch ) ;
403+
404+ WriteAllTextFromJObject ( settingsFile , settings ) ;
405+ }
406+ }
407+ catch ( Exception )
408+ {
409+ // do not fail if we cannot patch the settings.json file
410+ }
411+ }
412+
413+ private const string MicrosoftUnityExtensionId = "visualstudiotoolsforunity.vstuc" ;
414+ private const string DefaultRecommendedExtensionsContent = @"{
309415 ""recommendations"": [
310- ""visualstudiotoolsforunity.vstuc ""
416+ """ + MicrosoftUnityExtensionId + @" ""
311417 ]
312418}
313419" ;
314- File . WriteAllText ( extensionFile , content ) ;
420+
421+ private static void CreateRecommendedExtensionsFile ( string vscodeDirectory )
422+ {
423+ // see https://tattoocoder.com/recommending-vscode-extensions-within-your-open-source-projects/
424+ var extensionFile = IOPath . Combine ( vscodeDirectory , "extensions.json" ) ;
425+ if ( File . Exists ( extensionFile ) )
426+ {
427+ PatchRecommendedExtensionsFile ( extensionFile ) ;
428+ return ;
429+ }
430+
431+ File . WriteAllText ( extensionFile , DefaultRecommendedExtensionsContent ) ;
432+ }
433+
434+ private static void PatchRecommendedExtensionsFile ( string extensionFile )
435+ {
436+ try
437+ {
438+ const string recommendationsKey = "recommendations" ;
439+
440+ var content = File . ReadAllText ( extensionFile ) ;
441+ var extensions = JObject . Parse ( content ) ;
442+
443+ var recommendations = ( JArray ) extensions [ recommendationsKey ] ;
444+ if ( recommendations == null )
445+ {
446+ recommendations = new JArray ( ) ;
447+ extensions . Add ( recommendationsKey , recommendations ) ;
448+ }
449+
450+ foreach ( var entry in recommendations )
451+ {
452+ if ( entry . Value < string > ( ) == MicrosoftUnityExtensionId )
453+ return ;
454+ }
455+
456+ recommendations . Add ( MicrosoftUnityExtensionId ) ;
457+ WriteAllTextFromJObject ( extensionFile , extensions ) ;
458+ }
459+ catch ( Exception )
460+ {
461+ // do not fail if we cannot patch the extensions.json file
462+ }
463+ }
464+
465+ private static void WriteAllTextFromJObject ( string file , JObject jobject )
466+ {
467+ using ( var fs = File . Open ( file , FileMode . Create ) )
468+ using ( var sw = new StreamWriter ( fs ) )
469+ using ( var jw = new JsonTextWriter ( sw ) )
470+ {
471+ // Keep formatting/indent in sync with default contents
472+ jw . Formatting = Formatting . Indented ;
473+ jw . IndentChar = ' ' ;
474+ jw . Indentation = 4 ;
475+
476+ var serializer = new JsonSerializer ( ) ;
477+ serializer . Serialize ( jw , jobject ) ;
478+ }
315479 }
316480
317481 public override bool Open ( string path , int line , int column , string solution )
0 commit comments