VSCode.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304
  1. /*
  2. * Unity VSCode Support
  3. *
  4. * Seamless support for Microsoft Visual Studio Code in Unity
  5. *
  6. * Version:
  7. * 2.7
  8. *
  9. * Authors:
  10. * Matthew Davey <matthew.davey@dotbunny.com>
  11. */
  12. namespace dotBunny.Unity
  13. {
  14. using System;
  15. using System.IO;
  16. using System.Text.RegularExpressions;
  17. using UnityEditor;
  18. using UnityEngine;
  19. [InitializeOnLoad]
  20. public static class VSCode
  21. {
  22. /// <summary>
  23. /// Current Version Number
  24. /// </summary>
  25. public const float Version = 2.7f;
  26. /// <summary>
  27. /// Current Version Code
  28. /// </summary>
  29. public const string VersionCode = "-RELEASE";
  30. /// <summary>
  31. /// Download URL for Unity Debbuger
  32. /// </summary>
  33. public const string UnityDebuggerURL = "https://raw.githubusercontent.com/dotBunny/VSCode-Test/master/Downloads/unity-debug-101.vsix";
  34. #region Properties
  35. /// <summary>
  36. /// Path to VSCode executable
  37. public static string CodePath
  38. {
  39. get
  40. {
  41. string current = EditorPrefs.GetString("VSCode_CodePath", "");
  42. if(current == "" || !VSCodeExists(current))
  43. {
  44. //Value not set, set to "" or current path is invalid, try to autodetect it
  45. //If autodetect fails, a error will be printed and the default value set
  46. EditorPrefs.SetString("VSCode_CodePath", AutodetectCodePath());
  47. //If its not installed or the install folder isn't a "normal" one,
  48. //AutodetectCodePath will print a error message to the Unity Console
  49. }
  50. return EditorPrefs.GetString("VSCode_CodePath", current);
  51. }
  52. set
  53. {
  54. EditorPrefs.SetString("VSCode_CodePath", value);
  55. }
  56. }
  57. /// <summary>
  58. /// Get Program Files Path
  59. /// </summary>
  60. /// <returns>The platforms "Program Files" path.</returns>
  61. static string ProgramFilesx86()
  62. {
  63. if( 8 == IntPtr.Size
  64. || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"))))
  65. {
  66. return Environment.GetEnvironmentVariable("ProgramFiles(x86)");
  67. }
  68. return Environment.GetEnvironmentVariable("ProgramFiles");
  69. }
  70. /// <summary>
  71. /// Should debug information be displayed in the Unity terminal?
  72. /// </summary>
  73. public static bool Debug
  74. {
  75. get
  76. {
  77. return EditorPrefs.GetBool("VSCode_Debug", false);
  78. }
  79. set
  80. {
  81. EditorPrefs.SetBool("VSCode_Debug", value);
  82. }
  83. }
  84. /// <summary>
  85. /// Is the Visual Studio Code Integration Enabled?
  86. /// </summary>
  87. /// <remarks>
  88. /// We do not want to automatically turn it on, for in larger projects not everyone is using VSCode
  89. /// </remarks>
  90. public static bool Enabled
  91. {
  92. get
  93. {
  94. return EditorPrefs.GetBool("VSCode_Enabled", false);
  95. }
  96. set
  97. {
  98. // When turning the plugin on, we should remove all the previous project files
  99. if (!Enabled && value)
  100. {
  101. ClearProjectFiles();
  102. }
  103. EditorPrefs.SetBool("VSCode_Enabled", value);
  104. }
  105. }
  106. public static bool UseUnityDebugger
  107. {
  108. get
  109. {
  110. return EditorPrefs.GetBool("VSCode_UseUnityDebugger", false);
  111. }
  112. set
  113. {
  114. if ( value != UseUnityDebugger ) {
  115. // Set value
  116. EditorPrefs.SetBool("VSCode_UseUnityDebugger", value);
  117. // Do not write the launch JSON file because the debugger uses its own
  118. if ( value ) {
  119. WriteLaunchFile = false;
  120. }
  121. // Update launch file
  122. UpdateLaunchFile();
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Should the launch.json file be written?
  128. /// </summary>
  129. /// <remarks>
  130. /// Useful to disable if someone has their own custom one rigged up
  131. /// </remarks>
  132. public static bool WriteLaunchFile
  133. {
  134. get
  135. {
  136. return EditorPrefs.GetBool("VSCode_WriteLaunchFile", true);
  137. }
  138. set
  139. {
  140. EditorPrefs.SetBool("VSCode_WriteLaunchFile", value);
  141. }
  142. }
  143. /// <summary>
  144. /// Should the plugin automatically update itself.
  145. /// </summary>
  146. static bool AutomaticUpdates
  147. {
  148. get
  149. {
  150. return EditorPrefs.GetBool("VSCode_AutomaticUpdates", false);
  151. }
  152. set
  153. {
  154. EditorPrefs.SetBool("VSCode_AutomaticUpdates", value);
  155. }
  156. }
  157. static float GitHubVersion
  158. {
  159. get
  160. {
  161. return EditorPrefs.GetFloat("VSCode_GitHubVersion", Version);
  162. }
  163. set
  164. {
  165. EditorPrefs.SetFloat("VSCode_GitHubVersion", value);
  166. }
  167. }
  168. /// <summary>
  169. /// When was the last time that the plugin was updated?
  170. /// </summary>
  171. static DateTime LastUpdate
  172. {
  173. get
  174. {
  175. // Feature creation date.
  176. DateTime lastTime = new DateTime(2015, 10, 8);
  177. if (EditorPrefs.HasKey("VSCode_LastUpdate"))
  178. {
  179. DateTime.TryParse(EditorPrefs.GetString("VSCode_LastUpdate"), out lastTime);
  180. }
  181. return lastTime;
  182. }
  183. set
  184. {
  185. EditorPrefs.SetString("VSCode_LastUpdate", value.ToString());
  186. }
  187. }
  188. /// <summary>
  189. /// Quick reference to the VSCode launch settings file
  190. /// </summary>
  191. static string LaunchPath
  192. {
  193. get
  194. {
  195. return SettingsFolder + System.IO.Path.DirectorySeparatorChar + "launch.json";
  196. }
  197. }
  198. /// <summary>
  199. /// The full path to the project
  200. /// </summary>
  201. static string ProjectPath
  202. {
  203. get
  204. {
  205. return System.IO.Path.GetDirectoryName(UnityEngine.Application.dataPath);
  206. }
  207. }
  208. /// <summary>
  209. /// Should the script editor be reverted when quiting Unity.
  210. /// </summary>
  211. /// <remarks>
  212. /// Useful for environments where you do not use VSCode for everything.
  213. /// </remarks>
  214. static bool RevertExternalScriptEditorOnExit
  215. {
  216. get
  217. {
  218. return EditorPrefs.GetBool("VSCode_RevertScriptEditorOnExit", true);
  219. }
  220. set
  221. {
  222. EditorPrefs.SetBool("VSCode_RevertScriptEditorOnExit", value);
  223. }
  224. }
  225. /// <summary>
  226. /// Quick reference to the VSCode settings folder
  227. /// </summary>
  228. static string SettingsFolder
  229. {
  230. get
  231. {
  232. return ProjectPath + System.IO.Path.DirectorySeparatorChar + ".vscode";
  233. }
  234. }
  235. static string SettingsPath
  236. {
  237. get
  238. {
  239. return SettingsFolder + System.IO.Path.DirectorySeparatorChar + "settings.json";
  240. }
  241. }
  242. static int UpdateTime
  243. {
  244. get
  245. {
  246. return EditorPrefs.GetInt("VSCode_UpdateTime", 7);
  247. }
  248. set
  249. {
  250. EditorPrefs.SetInt("VSCode_UpdateTime", value);
  251. }
  252. }
  253. #endregion
  254. /// <summary>
  255. /// Integration Constructor
  256. /// </summary>
  257. static VSCode()
  258. {
  259. if (Enabled)
  260. {
  261. UpdateUnityPreferences(true);
  262. UpdateLaunchFile();
  263. // Add Update Check
  264. DateTime targetDate = LastUpdate.AddDays(UpdateTime);
  265. if (DateTime.Now >= targetDate && AutomaticUpdates)
  266. {
  267. CheckForUpdate();
  268. }
  269. }
  270. // Event for when script is reloaded
  271. System.AppDomain.CurrentDomain.DomainUnload += System_AppDomain_CurrentDomain_DomainUnload;
  272. }
  273. static void System_AppDomain_CurrentDomain_DomainUnload(object sender, System.EventArgs e)
  274. {
  275. if (Enabled && RevertExternalScriptEditorOnExit)
  276. {
  277. UpdateUnityPreferences(false);
  278. }
  279. }
  280. #region Public Members
  281. /// <summary>
  282. /// Force Unity To Write Project File
  283. /// </summary>
  284. /// <remarks>
  285. /// Reflection!
  286. /// </remarks>
  287. public static void SyncSolution()
  288. {
  289. System.Type T = System.Type.GetType("UnityEditor.SyncVS,UnityEditor");
  290. System.Reflection.MethodInfo SyncSolution = T.GetMethod("SyncSolution", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
  291. SyncSolution.Invoke(null, null);
  292. }
  293. /// <summary>
  294. /// Update the solution files so that they work with VS Code
  295. /// </summary>
  296. public static void UpdateSolution()
  297. {
  298. // No need to process if we are not enabled
  299. if (!VSCode.Enabled)
  300. {
  301. return;
  302. }
  303. if (VSCode.Debug)
  304. {
  305. UnityEngine.Debug.Log("[VSCode] Updating Solution & Project Files");
  306. }
  307. var currentDirectory = Directory.GetCurrentDirectory();
  308. var solutionFiles = Directory.GetFiles(currentDirectory, "*.sln");
  309. var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj");
  310. foreach (var filePath in solutionFiles)
  311. {
  312. string content = File.ReadAllText(filePath);
  313. content = ScrubSolutionContent(content);
  314. File.WriteAllText(filePath, content);
  315. ScrubFile(filePath);
  316. }
  317. foreach (var filePath in projectFiles)
  318. {
  319. string content = File.ReadAllText(filePath);
  320. content = ScrubProjectContent(content);
  321. File.WriteAllText(filePath, content);
  322. ScrubFile(filePath);
  323. }
  324. }
  325. #endregion
  326. #region Private Members
  327. /// <summary>
  328. /// Try to find automatically the installation of VSCode
  329. /// </summary>
  330. static string AutodetectCodePath()
  331. {
  332. string[] possiblePaths =
  333. #if UNITY_EDITOR_OSX
  334. {
  335. "/Applications/Visual Studio Code.app",
  336. "/Applications/Visual Studio Code - Insiders.app"
  337. };
  338. #elif UNITY_EDITOR_WIN
  339. {
  340. ProgramFilesx86() + Path.DirectorySeparatorChar + "Microsoft VS Code"
  341. + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "code.cmd",
  342. ProgramFilesx86() + Path.DirectorySeparatorChar + "Microsoft VS Code Insiders"
  343. + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "code-insiders.cmd"
  344. };
  345. #else
  346. {
  347. "/usr/bin/code",
  348. "/bin/code",
  349. "/usr/local/bin/code"
  350. };
  351. #endif
  352. for(int i = 0; i < possiblePaths.Length; i++)
  353. {
  354. if(VSCodeExists(possiblePaths[i]))
  355. {
  356. return possiblePaths[i];
  357. }
  358. }
  359. PrintNotFound(possiblePaths[0]);
  360. return possiblePaths[0]; //returns the default one, printing a warning message 'executable not found'
  361. }
  362. /// <summary>
  363. /// Call VSCode with arguments
  364. /// </summary>
  365. static void CallVSCode(string args)
  366. {
  367. System.Diagnostics.Process proc = new System.Diagnostics.Process();
  368. if(!VSCodeExists(CodePath))
  369. {
  370. PrintNotFound(CodePath);
  371. return;
  372. }
  373. #if UNITY_EDITOR_OSX
  374. proc.StartInfo.FileName = "open";
  375. // Check the path to see if there is "Insiders"
  376. if (CodePath.Contains("Insiders"))
  377. {
  378. proc.StartInfo.Arguments = " -n -b \"com.microsoft.VSCodeInsiders\" --args " + args.Replace(@"\", @"\\");
  379. }
  380. else
  381. {
  382. proc.StartInfo.Arguments = " -n -b \"com.microsoft.VSCode\" --args " + args.Replace(@"\", @"\\");
  383. }
  384. proc.StartInfo.UseShellExecute = false;
  385. #elif UNITY_EDITOR_WIN
  386. proc.StartInfo.FileName = CodePath;
  387. proc.StartInfo.Arguments = args;
  388. proc.StartInfo.UseShellExecute = false;
  389. #else
  390. proc.StartInfo.FileName = CodePath;
  391. proc.StartInfo.Arguments = args.Replace(@"\", @"\\");
  392. proc.StartInfo.UseShellExecute = false;
  393. #endif
  394. proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
  395. proc.StartInfo.CreateNoWindow = true;
  396. proc.StartInfo.RedirectStandardOutput = true;
  397. proc.Start();
  398. }
  399. /// <summary>
  400. /// Check for Updates with GitHub
  401. /// </summary>
  402. static void CheckForUpdate()
  403. {
  404. var fileContent = string.Empty;
  405. EditorUtility.DisplayProgressBar("VSCode", "Checking for updates ...", 0.5f);
  406. // Because were not a runtime framework, lets just use the simplest way of doing this
  407. try
  408. {
  409. using (var webClient = new System.Net.WebClient())
  410. {
  411. fileContent = webClient.DownloadString("https://raw.githubusercontent.com/dotBunny/VSCode/master/Plugins/Editor/VSCode.cs");
  412. }
  413. }
  414. catch (Exception e)
  415. {
  416. if (Debug)
  417. {
  418. UnityEngine.Debug.Log("[VSCode] " + e.Message);
  419. }
  420. // Don't go any further if there is an error
  421. return;
  422. }
  423. finally
  424. {
  425. EditorUtility.ClearProgressBar();
  426. }
  427. // Set the last update time
  428. LastUpdate = DateTime.Now;
  429. // Fix for oddity in downlo
  430. if (fileContent.Substring(0, 2) != "/*")
  431. {
  432. int startPosition = fileContent.IndexOf("/*", StringComparison.CurrentCultureIgnoreCase);
  433. // Jump over junk characters
  434. fileContent = fileContent.Substring(startPosition);
  435. }
  436. string[] fileExploded = fileContent.Split('\n');
  437. if (fileExploded.Length > 7)
  438. {
  439. float github = Version;
  440. if (float.TryParse(fileExploded[6].Replace("*", "").Trim(), out github))
  441. {
  442. GitHubVersion = github;
  443. }
  444. if (github > Version)
  445. {
  446. var GUIDs = AssetDatabase.FindAssets("t:Script VSCode");
  447. var path = Application.dataPath.Substring(0, Application.dataPath.Length - "/Assets".Length) + System.IO.Path.DirectorySeparatorChar +
  448. AssetDatabase.GUIDToAssetPath(GUIDs[0]).Replace('/', System.IO.Path.DirectorySeparatorChar);
  449. if (EditorUtility.DisplayDialog("VSCode Update", "A newer version of the VSCode plugin is available, would you like to update your version?", "Yes", "No"))
  450. {
  451. // Always make sure the file is writable
  452. System.IO.FileInfo fileInfo = new System.IO.FileInfo(path);
  453. fileInfo.IsReadOnly = false;
  454. // Write update file
  455. File.WriteAllText(path, fileContent);
  456. // Force update on text file
  457. AssetDatabase.ImportAsset(AssetDatabase.GUIDToAssetPath(GUIDs[0]), ImportAssetOptions.ForceUpdate);
  458. }
  459. }
  460. }
  461. }
  462. /// <summary>
  463. /// Clear out any existing project files and lingering stuff that might cause problems
  464. /// </summary>
  465. static void ClearProjectFiles()
  466. {
  467. var currentDirectory = Directory.GetCurrentDirectory();
  468. var solutionFiles = Directory.GetFiles(currentDirectory, "*.sln");
  469. var projectFiles = Directory.GetFiles(currentDirectory, "*.csproj");
  470. var unityProjectFiles = Directory.GetFiles(currentDirectory, "*.unityproj");
  471. foreach (string solutionFile in solutionFiles)
  472. {
  473. File.Delete(solutionFile);
  474. }
  475. foreach (string projectFile in projectFiles)
  476. {
  477. File.Delete(projectFile);
  478. }
  479. foreach (string unityProjectFile in unityProjectFiles)
  480. {
  481. File.Delete(unityProjectFile);
  482. }
  483. // Replace with our clean files (only in Unity 5)
  484. #if !UNITY_4_0 && !UNITY_4_1 && !UNITY_4_2 && !UNITY_4_3 && !UNITY_4_5 && !UNITY_4_6 && !UNITY_4_7
  485. SyncSolution();
  486. #endif
  487. }
  488. /// <summary>
  489. /// Force Unity Preferences Window To Read From Settings
  490. /// </summary>
  491. static void FixUnityPreferences()
  492. {
  493. // I want that window, please and thank you
  494. System.Type T = System.Type.GetType("UnityEditor.PreferencesWindow,UnityEditor");
  495. if (EditorWindow.focusedWindow == null)
  496. return;
  497. // Only run this when the editor window is visible (cause its what screwed us up)
  498. if (EditorWindow.focusedWindow.GetType() == T)
  499. {
  500. var window = EditorWindow.GetWindow(T, true, "Unity Preferences");
  501. if (window == null)
  502. {
  503. if (Debug)
  504. {
  505. UnityEngine.Debug.Log("[VSCode] No Preferences Window Found (really?)");
  506. }
  507. return;
  508. }
  509. var invokerType = window.GetType();
  510. var invokerMethod = invokerType.GetMethod("ReadPreferences",
  511. System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
  512. if (invokerMethod != null)
  513. {
  514. invokerMethod.Invoke(window, null);
  515. }
  516. else if (Debug)
  517. {
  518. UnityEngine.Debug.Log("[VSCode] No Reflection Method Found For Preferences");
  519. }
  520. }
  521. }
  522. /// <summary>
  523. /// Determine what port Unity is listening for on Windows
  524. /// </summary>
  525. static int GetDebugPort()
  526. {
  527. #if UNITY_EDITOR_WIN
  528. System.Diagnostics.Process process = new System.Diagnostics.Process();
  529. process.StartInfo.FileName = "netstat";
  530. process.StartInfo.Arguments = "-a -n -o -p TCP";
  531. process.StartInfo.UseShellExecute = false;
  532. process.StartInfo.RedirectStandardOutput = true;
  533. process.Start();
  534. string output = process.StandardOutput.ReadToEnd();
  535. string[] lines = output.Split('\n');
  536. process.WaitForExit();
  537. foreach (string line in lines)
  538. {
  539. string[] tokens = Regex.Split(line, "\\s+");
  540. if (tokens.Length > 4)
  541. {
  542. int test = -1;
  543. int.TryParse(tokens[5], out test);
  544. if (test > 1023)
  545. {
  546. try
  547. {
  548. var p = System.Diagnostics.Process.GetProcessById(test);
  549. if (p.ProcessName == "Unity")
  550. {
  551. return test;
  552. }
  553. }
  554. catch
  555. {
  556. }
  557. }
  558. }
  559. }
  560. #else
  561. System.Diagnostics.Process process = new System.Diagnostics.Process();
  562. process.StartInfo.FileName = "lsof";
  563. process.StartInfo.Arguments = "-c /^Unity$/ -i 4tcp -a";
  564. process.StartInfo.UseShellExecute = false;
  565. process.StartInfo.RedirectStandardOutput = true;
  566. process.Start();
  567. // Not thread safe (yet!)
  568. string output = process.StandardOutput.ReadToEnd();
  569. string[] lines = output.Split('\n');
  570. process.WaitForExit();
  571. foreach (string line in lines)
  572. {
  573. int port = -1;
  574. if (line.StartsWith("Unity"))
  575. {
  576. string[] portions = line.Split(new string[] { "TCP *:" }, System.StringSplitOptions.None);
  577. if (portions.Length >= 2)
  578. {
  579. Regex digitsOnly = new Regex(@"[^\d]");
  580. string cleanPort = digitsOnly.Replace(portions[1], "");
  581. if (int.TryParse(cleanPort, out port))
  582. {
  583. if (port > -1)
  584. {
  585. return port;
  586. }
  587. }
  588. }
  589. }
  590. }
  591. #endif
  592. return -1;
  593. }
  594. /// <summary>
  595. /// Manually install the original Unity Debuger
  596. /// </summary>
  597. /// <remarks>
  598. /// This should auto update to the latest.
  599. /// </remarks>
  600. static void InstallUnityDebugger()
  601. {
  602. EditorUtility.DisplayProgressBar("VSCode", "Downloading Unity Debugger ...", 0.1f);
  603. byte[] fileContent;
  604. try
  605. {
  606. using (var webClient = new System.Net.WebClient())
  607. {
  608. fileContent = webClient.DownloadData(UnityDebuggerURL);
  609. }
  610. }
  611. catch (Exception e)
  612. {
  613. if (Debug)
  614. {
  615. UnityEngine.Debug.Log("[VSCode] " + e.Message);
  616. }
  617. // Don't go any further if there is an error
  618. return;
  619. }
  620. finally
  621. {
  622. EditorUtility.ClearProgressBar();
  623. }
  624. // Do we have a file to install?
  625. if ( fileContent != null ) {
  626. string fileName = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".vsix";
  627. File.WriteAllBytes(fileName, fileContent);
  628. CallVSCode(fileName);
  629. }
  630. }
  631. // HACK: This is in until Unity can figure out why MD keeps opening even though a different program is selected.
  632. [MenuItem("Assets/Open C# Project In Code", false, 1000)]
  633. static void MenuOpenProject()
  634. {
  635. // Force the project files to be sync
  636. SyncSolution();
  637. // Load Project
  638. CallVSCode("\"" + ProjectPath + "\" -r");
  639. }
  640. /// <summary>
  641. /// Print a error message to the Unity Console about not finding the code executable
  642. /// </summary>
  643. static void PrintNotFound(string path)
  644. {
  645. UnityEngine.Debug.LogError("[VSCode] Code executable in '" + path + "' not found. Check your" +
  646. "Visual Studio Code installation and insert the correct path in the Preferences menu.");
  647. }
  648. [MenuItem("Assets/Open C# Project In Code", true, 1000)]
  649. static bool ValidateMenuOpenProject()
  650. {
  651. return Enabled;
  652. }
  653. /// <summary>
  654. /// VS Code Integration Preferences Item
  655. /// </summary>
  656. /// <remarks>
  657. /// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off
  658. /// </remarks>
  659. [PreferenceItem("VSCode")]
  660. static void VSCodePreferencesItem()
  661. {
  662. if (EditorApplication.isCompiling)
  663. {
  664. EditorGUILayout.HelpBox("Please wait for Unity to finish compiling. \nIf the window doesn't refresh, simply click on the window or move it around to cause a repaint to happen.", MessageType.Warning);
  665. return;
  666. }
  667. EditorGUILayout.BeginVertical();
  668. EditorGUILayout.HelpBox("Support development of this plugin, follow @reapazor and @dotbunny on Twitter.", MessageType.Info);
  669. EditorGUI.BeginChangeCheck();
  670. // Need the VS Code executable
  671. EditorGUILayout.BeginHorizontal();
  672. EditorGUILayout.LabelField("VS Code Path", GUILayout.Width(75));
  673. #if UNITY_5_3_OR_NEWER
  674. CodePath = EditorGUILayout.DelayedTextField(CodePath, GUILayout.ExpandWidth(true));
  675. #else
  676. CodePath = EditorGUILayout.TextField(CodePath, GUILayout.ExpandWidth(true));
  677. #endif
  678. GUI.SetNextControlName("PathSetButton");
  679. if(GUILayout.Button("...", GUILayout.Height(14), GUILayout.Width(20)))
  680. {
  681. GUI.FocusControl("PathSetButton");
  682. string path = EditorUtility.OpenFilePanel( "Visual Studio Code Executable", "", "" );
  683. if( path.Length != 0 && File.Exists(path) || Directory.Exists(path))
  684. {
  685. CodePath = path;
  686. }
  687. }
  688. EditorGUILayout.EndHorizontal();
  689. EditorGUILayout.Space();
  690. Enabled = EditorGUILayout.Toggle(new GUIContent("Enable Integration", "Should the integration work its magic for you?"), Enabled);
  691. UseUnityDebugger = EditorGUILayout.Toggle(new GUIContent("Use Unity Debugger", "Should the integration integrate with Unity's VSCode Extension (must be installed)."), UseUnityDebugger);
  692. EditorGUILayout.Space();
  693. RevertExternalScriptEditorOnExit = EditorGUILayout.Toggle(new GUIContent("Revert Script Editor On Unload", "Should the external script editor setting be reverted to its previous setting on project unload? This is useful if you do not use Code with all your projects."),RevertExternalScriptEditorOnExit);
  694. Debug = EditorGUILayout.Toggle(new GUIContent("Output Messages To Console", "Should informational messages be sent to Unity's Console?"), Debug);
  695. WriteLaunchFile = EditorGUILayout.Toggle(new GUIContent("Always Write Launch File", "Always write the launch.json settings when entering play mode?"), WriteLaunchFile);
  696. EditorGUILayout.Space();
  697. AutomaticUpdates = EditorGUILayout.Toggle(new GUIContent("Automatic Updates", "Should the plugin automatically update itself?"), AutomaticUpdates);
  698. UpdateTime = EditorGUILayout.IntSlider(new GUIContent("Update Timer (Days)", "After how many days should updates be checked for?"), UpdateTime, 1, 31);
  699. EditorGUILayout.Space();
  700. EditorGUILayout.Space();
  701. if (EditorGUI.EndChangeCheck())
  702. {
  703. UpdateUnityPreferences(Enabled);
  704. // TODO: Force Unity To Reload Preferences
  705. // This seems to be a hick up / issue
  706. if (VSCode.Debug)
  707. {
  708. if (Enabled)
  709. {
  710. UnityEngine.Debug.Log("[VSCode] Integration Enabled");
  711. }
  712. else
  713. {
  714. UnityEngine.Debug.Log("[VSCode] Integration Disabled");
  715. }
  716. }
  717. }
  718. if (GUILayout.Button(new GUIContent("Force Update", "Check for updates to the plugin, right NOW!")))
  719. {
  720. CheckForUpdate();
  721. EditorGUILayout.EndVertical();
  722. return;
  723. }
  724. if (GUILayout.Button(new GUIContent("Write Workspace Settings", "Output a default set of workspace settings for VSCode to use, ignoring many different types of files.")))
  725. {
  726. WriteWorkspaceSettings();
  727. EditorGUILayout.EndVertical();
  728. return;
  729. }
  730. EditorGUILayout.Space();
  731. if (UseUnityDebugger)
  732. {
  733. EditorGUILayout.HelpBox("In order for the \"Use Unity Debuggger\" option to function above, you need to have installed the Unity Debugger Extension for Visual Studio Code.", MessageType.Warning);
  734. if (GUILayout.Button(new GUIContent("Install Unity Debugger", "Install the Unity Debugger Extension into Code")))
  735. {
  736. InstallUnityDebugger();
  737. EditorGUILayout.EndVertical();
  738. return;
  739. }
  740. }
  741. GUILayout.FlexibleSpace();
  742. EditorGUILayout.BeginHorizontal();
  743. GUILayout.FlexibleSpace();
  744. GUILayout.Label(
  745. new GUIContent(
  746. string.Format("{0:0.00}", Version) + VersionCode,
  747. "GitHub's Version @ " + string.Format("{0:0.00}", GitHubVersion)));
  748. EditorGUILayout.EndHorizontal();
  749. EditorGUILayout.EndVertical();
  750. }
  751. /// <summary>
  752. /// Asset Open Callback (from Unity)
  753. /// </summary>
  754. /// <remarks>
  755. /// Called when Unity is about to open an asset.
  756. /// </remarks>
  757. [UnityEditor.Callbacks.OnOpenAssetAttribute()]
  758. static bool OnOpenedAsset(int instanceID, int line)
  759. {
  760. // bail out if we are not using VSCode
  761. if (!Enabled)
  762. {
  763. return false;
  764. }
  765. // current path without the asset folder
  766. string appPath = ProjectPath;
  767. // determine asset that has been double clicked in the project view
  768. UnityEngine.Object selected = EditorUtility.InstanceIDToObject(instanceID);
  769. if (selected.GetType().ToString() == "UnityEditor.MonoScript" ||
  770. selected.GetType().ToString() == "UnityEngine.Shader")
  771. {
  772. string completeFilepath = appPath + Path.DirectorySeparatorChar + AssetDatabase.GetAssetPath(selected);
  773. string args = null;
  774. if (line == -1)
  775. {
  776. args = "\"" + ProjectPath + "\" \"" + completeFilepath + "\" -r";
  777. }
  778. else
  779. {
  780. args = "\"" + ProjectPath + "\" -g \"" + completeFilepath + ":" + line.ToString() + "\" -r";
  781. }
  782. // call 'open'
  783. CallVSCode(args);
  784. return true;
  785. }
  786. // Didnt find a code file? let Unity figure it out
  787. return false;
  788. }
  789. /// <summary>
  790. /// Executed when the Editor's playmode changes allowing for capture of required data
  791. /// </summary>
  792. static void OnPlaymodeStateChanged()
  793. {
  794. if (UnityEngine.Application.isPlaying && EditorApplication.isPlayingOrWillChangePlaymode)
  795. {
  796. UpdateLaunchFile();
  797. }
  798. }
  799. /// <summary>
  800. /// Detect when scripts are reloaded and relink playmode detection
  801. /// </summary>
  802. [UnityEditor.Callbacks.DidReloadScripts()]
  803. static void OnScriptReload()
  804. {
  805. EditorApplication.playmodeStateChanged -= OnPlaymodeStateChanged;
  806. EditorApplication.playmodeStateChanged += OnPlaymodeStateChanged;
  807. }
  808. /// <summary>
  809. /// Remove extra/erroneous lines from a file.
  810. static void ScrubFile(string path)
  811. {
  812. string[] lines = File.ReadAllLines(path);
  813. System.Collections.Generic.List<string> newLines = new System.Collections.Generic.List<string>();
  814. for (int i = 0; i < lines.Length; i++)
  815. {
  816. // Check Empty
  817. if (string.IsNullOrEmpty(lines[i].Trim()) || lines[i].Trim() == "\t" || lines[i].Trim() == "\t\t")
  818. {
  819. }
  820. else
  821. {
  822. newLines.Add(lines[i]);
  823. }
  824. }
  825. File.WriteAllLines(path, newLines.ToArray());
  826. }
  827. /// <summary>
  828. /// Remove extra/erroneous data from project file (content).
  829. /// </summary>
  830. static string ScrubProjectContent(string content)
  831. {
  832. if (content.Length == 0)
  833. return "";
  834. #if !UNITY_EDITOR_WIN
  835. // Moved to 3.5, 2.0 is legacy.
  836. if (content.IndexOf("<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>") != -1)
  837. {
  838. content = Regex.Replace(content, "<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>", "<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>");
  839. }
  840. #endif
  841. string targetPath = "";// "<TargetPath>Temp" + Path.DirectorySeparatorChar + "bin" + Path.DirectorySeparatorChar + "Debug" + Path.DirectorySeparatorChar + "</TargetPath>"; //OutputPath
  842. string langVersion = "<LangVersion>default</LangVersion>";
  843. bool found = true;
  844. int location = 0;
  845. string addedOptions = "";
  846. int startLocation = -1;
  847. int endLocation = -1;
  848. int endLength = 0;
  849. while (found)
  850. {
  851. startLocation = -1;
  852. endLocation = -1;
  853. endLength = 0;
  854. addedOptions = "";
  855. startLocation = content.IndexOf("<PropertyGroup", location);
  856. if (startLocation != -1)
  857. {
  858. endLocation = content.IndexOf("</PropertyGroup>", startLocation);
  859. endLength = (endLocation - startLocation);
  860. if (endLocation == -1)
  861. {
  862. found = false;
  863. continue;
  864. }
  865. else
  866. {
  867. found = true;
  868. location = endLocation;
  869. }
  870. if (content.Substring(startLocation, endLength).IndexOf("<TargetPath>") == -1)
  871. {
  872. addedOptions += "\n\r\t" + targetPath + "\n\r";
  873. }
  874. if (content.Substring(startLocation, endLength).IndexOf("<LangVersion>") == -1)
  875. {
  876. addedOptions += "\n\r\t" + langVersion + "\n\r";
  877. }
  878. if (!string.IsNullOrEmpty(addedOptions))
  879. {
  880. content = content.Substring(0, endLocation) + addedOptions + content.Substring(endLocation);
  881. }
  882. }
  883. else
  884. {
  885. found = false;
  886. }
  887. }
  888. return content;
  889. }
  890. /// <summary>
  891. /// Remove extra/erroneous data from solution file (content).
  892. /// </summary>
  893. static string ScrubSolutionContent(string content)
  894. {
  895. // Replace Solution Version
  896. content = content.Replace(
  897. "Microsoft Visual Studio Solution File, Format Version 11.00\r\n# Visual Studio 2008\r\n",
  898. "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 2012");
  899. // Remove Solution Properties (Unity Junk)
  900. int startIndex = content.IndexOf("GlobalSection(SolutionProperties) = preSolution");
  901. if (startIndex != -1)
  902. {
  903. int endIndex = content.IndexOf("EndGlobalSection", startIndex);
  904. content = content.Substring(0, startIndex) + content.Substring(endIndex + 16);
  905. }
  906. return content;
  907. }
  908. /// <summary>
  909. /// Update Visual Studio Code Launch file
  910. /// </summary>
  911. static void UpdateLaunchFile()
  912. {
  913. if (!VSCode.Enabled)
  914. {
  915. return;
  916. }
  917. else if (VSCode.UseUnityDebugger)
  918. {
  919. if (!Directory.Exists(VSCode.SettingsFolder))
  920. System.IO.Directory.CreateDirectory(VSCode.SettingsFolder);
  921. // Write out proper formatted JSON (hence no more SimpleJSON here)
  922. string fileContent = "{\n\t\"version\": \"0.2.0\",\n\t\"configurations\": [\n\t\t{\n\t\t\t\"name\": \"Unity Editor\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Windows Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"OSX Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Linux Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"iOS Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"Android Player\",\n\t\t\t\"type\": \"unity\",\n\t\t\t\"request\": \"launch\"\n\n\t\t}\n\t]\n}";
  923. File.WriteAllText(VSCode.LaunchPath, fileContent);
  924. }
  925. else if (VSCode.WriteLaunchFile)
  926. {
  927. int port = GetDebugPort();
  928. if (port > -1)
  929. {
  930. if (!Directory.Exists(VSCode.SettingsFolder))
  931. System.IO.Directory.CreateDirectory(VSCode.SettingsFolder);
  932. // Write out proper formatted JSON (hence no more SimpleJSON here)
  933. string fileContent = "{\n\t\"version\":\"0.2.0\",\n\t\"configurations\":[ \n\t\t{\n\t\t\t\"name\":\"Unity\",\n\t\t\t\"type\":\"mono\",\n\t\t\t\"request\":\"attach\",\n\t\t\t\"address\":\"localhost\",\n\t\t\t\"port\":" + port + "\n\t\t}\n\t]\n}";
  934. File.WriteAllText(VSCode.LaunchPath, fileContent);
  935. if (VSCode.Debug)
  936. {
  937. UnityEngine.Debug.Log("[VSCode] Debug Port Found (" + port + ")");
  938. }
  939. }
  940. else
  941. {
  942. if (VSCode.Debug)
  943. {
  944. UnityEngine.Debug.LogWarning("[VSCode] Unable to determine debug port.");
  945. }
  946. }
  947. }
  948. }
  949. /// <summary>
  950. /// Update Unity Editor Preferences
  951. /// </summary>
  952. /// <param name="enabled">Should we turn on this party!</param>
  953. static void UpdateUnityPreferences(bool enabled)
  954. {
  955. if (enabled)
  956. {
  957. // App
  958. if (EditorPrefs.GetString("kScriptsDefaultApp") != CodePath)
  959. {
  960. EditorPrefs.SetString("VSCode_PreviousApp", EditorPrefs.GetString("kScriptsDefaultApp"));
  961. }
  962. EditorPrefs.SetString("kScriptsDefaultApp", CodePath);
  963. // Arguments
  964. if (EditorPrefs.GetString("kScriptEditorArgs") != "-r -g \"$(File):$(Line)\"")
  965. {
  966. EditorPrefs.SetString("VSCode_PreviousArgs", EditorPrefs.GetString("kScriptEditorArgs"));
  967. }
  968. EditorPrefs.SetString("kScriptEditorArgs", "-r -g \"$(File):$(Line)\"");
  969. EditorPrefs.SetString("kScriptEditorArgs" + CodePath, "-r -g \"$(File):$(Line)\"");
  970. // MonoDevelop Solution
  971. if (EditorPrefs.GetBool("kMonoDevelopSolutionProperties", false))
  972. {
  973. EditorPrefs.SetBool("VSCode_PreviousMD", true);
  974. }
  975. EditorPrefs.SetBool("kMonoDevelopSolutionProperties", false);
  976. // Support Unity Proj (JS)
  977. if (EditorPrefs.GetBool("kExternalEditorSupportsUnityProj", false))
  978. {
  979. EditorPrefs.SetBool("VSCode_PreviousUnityProj", true);
  980. }
  981. EditorPrefs.SetBool("kExternalEditorSupportsUnityProj", false);
  982. if (!EditorPrefs.GetBool("AllowAttachedDebuggingOfEditor", false))
  983. {
  984. EditorPrefs.SetBool("VSCode_PreviousAttach", false);
  985. }
  986. EditorPrefs.SetBool("AllowAttachedDebuggingOfEditor", true);
  987. }
  988. else
  989. {
  990. // Restore previous app
  991. if (!string.IsNullOrEmpty(EditorPrefs.GetString("VSCode_PreviousApp")))
  992. {
  993. EditorPrefs.SetString("kScriptsDefaultApp", EditorPrefs.GetString("VSCode_PreviousApp"));
  994. }
  995. // Restore previous args
  996. if (!string.IsNullOrEmpty(EditorPrefs.GetString("VSCode_PreviousArgs")))
  997. {
  998. EditorPrefs.SetString("kScriptEditorArgs", EditorPrefs.GetString("VSCode_PreviousArgs"));
  999. }
  1000. // Restore MD setting
  1001. if (EditorPrefs.GetBool("VSCode_PreviousMD", false))
  1002. {
  1003. EditorPrefs.SetBool("kMonoDevelopSolutionProperties", true);
  1004. }
  1005. // Restore MD setting
  1006. if (EditorPrefs.GetBool("VSCode_PreviousUnityProj", false))
  1007. {
  1008. EditorPrefs.SetBool("kExternalEditorSupportsUnityProj", true);
  1009. }
  1010. // Always leave editor attaching on, I know, it solves the problem of needing to restart for this
  1011. // to actually work
  1012. EditorPrefs.SetBool("AllowAttachedDebuggingOfEditor", true);
  1013. }
  1014. FixUnityPreferences();
  1015. }
  1016. /// <summary>
  1017. /// Determines if the current path to the code executable is valid or not (exists)
  1018. /// </summary>
  1019. static bool VSCodeExists(string curPath)
  1020. {
  1021. #if UNITY_EDITOR_OSX
  1022. return System.IO.Directory.Exists(curPath);
  1023. #else
  1024. System.IO.FileInfo code = new System.IO.FileInfo(curPath);
  1025. return code.Exists;
  1026. #endif
  1027. }
  1028. /// <summary>
  1029. /// Write Default Workspace Settings
  1030. /// </summary>
  1031. static void WriteWorkspaceSettings()
  1032. {
  1033. if (Debug)
  1034. {
  1035. UnityEngine.Debug.Log("[VSCode] Workspace Settings Written");
  1036. }
  1037. if (!Directory.Exists(VSCode.SettingsFolder))
  1038. {
  1039. System.IO.Directory.CreateDirectory(VSCode.SettingsFolder);
  1040. }
  1041. string exclusions =
  1042. "{\n" +
  1043. "\t\"files.exclude\":\n" +
  1044. "\t{\n" +
  1045. // Hidden Files
  1046. "\t\t\"**/.DS_Store\":true,\n" +
  1047. "\t\t\"**/.git\":true,\n" +
  1048. "\t\t\"**/.gitignore\":true,\n" +
  1049. "\t\t\"**/.gitattributes\":true,\n" +
  1050. "\t\t\"**/.gitmodules\":true,\n" +
  1051. "\t\t\"**/.svn\":true,\n" +
  1052. // Project Files
  1053. "\t\t\"**/*.booproj\":true,\n" +
  1054. "\t\t\"**/*.pidb\":true,\n" +
  1055. "\t\t\"**/*.suo\":true,\n" +
  1056. "\t\t\"**/*.user\":true,\n" +
  1057. "\t\t\"**/*.userprefs\":true,\n" +
  1058. "\t\t\"**/*.unityproj\":true,\n" +
  1059. "\t\t\"**/*.dll\":true,\n" +
  1060. "\t\t\"**/*.exe\":true,\n" +
  1061. // Media Files
  1062. "\t\t\"**/*.pdf\":true,\n" +
  1063. // Audio
  1064. "\t\t\"**/*.mid\":true,\n" +
  1065. "\t\t\"**/*.midi\":true,\n" +
  1066. "\t\t\"**/*.wav\":true,\n" +
  1067. // Textures
  1068. "\t\t\"**/*.gif\":true,\n" +
  1069. "\t\t\"**/*.ico\":true,\n" +
  1070. "\t\t\"**/*.jpg\":true,\n" +
  1071. "\t\t\"**/*.jpeg\":true,\n" +
  1072. "\t\t\"**/*.png\":true,\n" +
  1073. "\t\t\"**/*.psd\":true,\n" +
  1074. "\t\t\"**/*.tga\":true,\n" +
  1075. "\t\t\"**/*.tif\":true,\n" +
  1076. "\t\t\"**/*.tiff\":true,\n" +
  1077. // Models
  1078. "\t\t\"**/*.3ds\":true,\n" +
  1079. "\t\t\"**/*.3DS\":true,\n" +
  1080. "\t\t\"**/*.fbx\":true,\n" +
  1081. "\t\t\"**/*.FBX\":true,\n" +
  1082. "\t\t\"**/*.lxo\":true,\n" +
  1083. "\t\t\"**/*.LXO\":true,\n" +
  1084. "\t\t\"**/*.ma\":true,\n" +
  1085. "\t\t\"**/*.MA\":true,\n" +
  1086. "\t\t\"**/*.obj\":true,\n" +
  1087. "\t\t\"**/*.OBJ\":true,\n" +
  1088. // Unity File Types
  1089. "\t\t\"**/*.asset\":true,\n" +
  1090. "\t\t\"**/*.cubemap\":true,\n" +
  1091. "\t\t\"**/*.flare\":true,\n" +
  1092. "\t\t\"**/*.mat\":true,\n" +
  1093. "\t\t\"**/*.meta\":true,\n" +
  1094. "\t\t\"**/*.prefab\":true,\n" +
  1095. "\t\t\"**/*.unity\":true,\n" +
  1096. // Folders
  1097. "\t\t\"build/\":true,\n" +
  1098. "\t\t\"Build/\":true,\n" +
  1099. "\t\t\"Library/\":true,\n" +
  1100. "\t\t\"library/\":true,\n" +
  1101. "\t\t\"obj/\":true,\n" +
  1102. "\t\t\"Obj/\":true,\n" +
  1103. "\t\t\"ProjectSettings/\":true,\r" +
  1104. "\t\t\"temp/\":true,\n" +
  1105. "\t\t\"Temp/\":true\n" +
  1106. "\t}\n" +
  1107. "}";
  1108. // Dont like the replace but it fixes the issue with the JSON
  1109. File.WriteAllText(VSCode.SettingsPath, exclusions);
  1110. }
  1111. #endregion
  1112. }
  1113. /// <summary>
  1114. /// VSCode Asset AssetPostprocessor
  1115. /// <para>This will ensure any time that the project files are generated the VSCode versions will be made</para>
  1116. /// </summary>
  1117. /// <remarks>Undocumented Event</remarks>
  1118. public class VSCodeAssetPostprocessor : AssetPostprocessor
  1119. {
  1120. /// <summary>
  1121. /// On documented, project generation event callback
  1122. /// </summary>
  1123. private static void OnGeneratedCSProjectFiles()
  1124. {
  1125. // Force execution of VSCode update
  1126. VSCode.UpdateSolution();
  1127. }
  1128. }
  1129. }