|
3 | 3 | using System.Diagnostics; |
4 | 4 | using System.IO; |
5 | 5 | using System.Linq; |
6 | | -using System.Threading; |
7 | 6 | using System.Xml.Linq; |
8 | 7 | using Microsoft.VisualStudio.Shell.Interop; |
9 | | -using Newtonsoft.Json; |
10 | 8 | using Newtonsoft.Json.Linq; |
11 | 9 |
|
12 | 10 | namespace SmallSharp.Build |
@@ -70,42 +68,104 @@ void UpdateStartupFile(string? path) |
70 | 68 | { |
71 | 69 | activeFile = path; |
72 | 70 |
|
| 71 | +#if DEBUG && !CI |
| 72 | + //Debugger.Launch(); |
| 73 | +#endif |
| 74 | + |
73 | 75 | if (!string.IsNullOrEmpty(path) && |
74 | 76 | path!.IndexOfAny(Path.GetInvalidPathChars()) == -1 && |
75 | 77 | startupFiles.TryGetValue(Path.GetFileName(path), out var startupFile)) |
76 | 78 | { |
| 79 | + // NOTE: we could skip writing the profiles altogether, since the |
| 80 | + // targets already JsonPoke these entries. This causes issues, however, |
| 81 | + // when the entries there don't match *exactly* what the compile files |
| 82 | + // are (i.e. WSL). In this scenario, the project system remains in an |
| 83 | + // in-memory "dirty" state where it doesn't refresh the active debug |
| 84 | + // profile anymore because it keeps its own WSL selection around. |
77 | 85 | var settings = new JObject( |
78 | 86 | new JProperty("profiles", new JObject( |
79 | | - new JProperty(startupFile, new JObject( |
| 87 | + startupFiles.Select(file => new JProperty(file.Key, new JObject( |
80 | 88 | new JProperty("commandName", "Project") |
81 | | - )) |
| 89 | + ))) |
82 | 90 | )) |
83 | 91 | ); |
84 | 92 |
|
85 | | - var json = settings.ToString(Formatting.Indented); |
| 93 | + var json = settings.ToString(Newtonsoft.Json.Formatting.Indented); |
86 | 94 |
|
87 | 95 | // Only write if different content. |
88 | 96 | if (File.Exists(launchProfilesPath) && |
89 | | - File.ReadAllText(launchProfilesPath) == json) |
90 | | - return; |
91 | | - |
92 | | - Directory.CreateDirectory(Path.GetDirectoryName(launchProfilesPath)); |
93 | | - File.WriteAllText(launchProfilesPath, json); |
| 97 | + File.ReadAllText(launchProfilesPath) != json) |
| 98 | + { |
| 99 | + Directory.CreateDirectory(Path.GetDirectoryName(launchProfilesPath)); |
| 100 | + File.WriteAllText(launchProfilesPath, json); |
| 101 | + } |
94 | 102 |
|
95 | 103 | try |
96 | 104 | { |
97 | 105 | // Get the value as it was exists in the original dictionary, |
98 | 106 | // since it has to match what the source generator created in the |
99 | 107 | // launch profiles. |
100 | 108 | var xdoc = XDocument.Load(userFile); |
101 | | - var active = xdoc |
| 109 | + var save = false; |
| 110 | + |
| 111 | + // The additional ActiveCompile is a prerequisite for supporting non-file |
| 112 | + // debug profiles, such as WSL. At this point, it's not working, but it's |
| 113 | + // will be based on this extra property eventually, so we keep it here. |
| 114 | + |
| 115 | + var activeCompile = xdoc |
| 116 | + .Descendants("{http://schemas.microsoft.com/developer/msbuild/2003}ActiveCompile") |
| 117 | + .FirstOrDefault(); |
| 118 | + |
| 119 | + if (activeCompile == null) |
| 120 | + { |
| 121 | + var props = xdoc.Root.Elements("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup").LastOrDefault(); |
| 122 | + if (props == null) |
| 123 | + { |
| 124 | + props = new XElement("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup"); |
| 125 | + xdoc.Root.Add(props); |
| 126 | + } |
| 127 | + activeCompile = new XElement("{http://schemas.microsoft.com/developer/msbuild/2003}ActiveCompile", startupFile); |
| 128 | + props.Add(activeCompile); |
| 129 | + save = true; |
| 130 | + } |
| 131 | + |
| 132 | + if (!startupFile.Equals(activeCompile.Value, StringComparison.OrdinalIgnoreCase)) |
| 133 | + { |
| 134 | + activeCompile.Value = startupFile; |
| 135 | + save = true; |
| 136 | + } |
| 137 | + |
| 138 | + var activeDebug = xdoc |
102 | 139 | .Descendants("{http://schemas.microsoft.com/developer/msbuild/2003}ActiveDebugProfile") |
103 | 140 | .FirstOrDefault(); |
104 | 141 |
|
105 | | - if (active != null && !startupFile.Equals(active.Value, StringComparison.OrdinalIgnoreCase)) |
| 142 | + if (activeDebug == null) |
| 143 | + { |
| 144 | + var props = xdoc.Root.Elements("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup").LastOrDefault(); |
| 145 | + if (props == null) |
| 146 | + { |
| 147 | + props = new XElement("{http://schemas.microsoft.com/developer/msbuild/2003}PropertyGroup"); |
| 148 | + xdoc.Root.Add(props); |
| 149 | + } |
| 150 | + activeDebug = new XElement("{http://schemas.microsoft.com/developer/msbuild/2003}ActiveDebugProfile", startupFile); |
| 151 | + props.Add(activeDebug); |
| 152 | + save = true; |
| 153 | + } |
| 154 | + |
| 155 | + if (activeDebug.Value != null && |
| 156 | + activeDebug.Value.IndexOfAny(Path.GetInvalidPathChars()) == -1 && |
| 157 | + // TODO: Don't mess with debug profile unless it's a file-like name, so we can support WSL |
| 158 | + // Path.HasExtension(activeDebug.Value) && |
| 159 | + !startupFile.Equals(activeDebug.Value, StringComparison.OrdinalIgnoreCase)) |
| 160 | + { |
| 161 | + activeDebug.Value = startupFile; |
| 162 | + save = true; |
| 163 | + } |
| 164 | + |
| 165 | + if (save) |
106 | 166 | { |
107 | | - active.Value = startupFile; |
108 | 167 | xdoc.Save(userFile); |
| 168 | + File.SetLastWriteTime(launchProfilesPath, DateTime.Now); |
109 | 169 | } |
110 | 170 | } |
111 | 171 | catch (Exception e) |
@@ -186,13 +246,13 @@ int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved) |
186 | 246 | int IVsRunningDocTableEvents.OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) => 0; |
187 | 247 | int IVsSelectionEvents.OnElementValueChanged(uint elementid, object varValueOld, object varValueNew) => 0; |
188 | 248 | int IVsSelectionEvents.OnCmdUIContextChanged(uint dwCmdUICookie, int fActive) => 0; |
189 | | - int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) => throw new NotImplementedException(); |
190 | | - int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) => throw new NotImplementedException(); |
191 | | - int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) => throw new NotImplementedException(); |
192 | | - int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) => throw new NotImplementedException(); |
193 | | - int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) => throw new NotImplementedException(); |
194 | | - int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) => throw new NotImplementedException(); |
195 | | - int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) => throw new NotImplementedException(); |
196 | | - int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) => throw new NotImplementedException(); |
| 249 | + int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) => 0; |
| 250 | + int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) => 0; |
| 251 | + int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) => 0; |
| 252 | + int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) => 0; |
| 253 | + int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) => 0; |
| 254 | + int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) => 0; |
| 255 | + int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) => 0; |
| 256 | + int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) => 0; |
197 | 257 | } |
198 | 258 | } |
0 commit comments