From 9285168c1773cc8ed48f387799eda9ac883fc674 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Fri, 10 May 2024 11:47:03 -0500 Subject: [PATCH 1/2] Fall back to VS looking for port. Add way to disable csharpier server. references #1249 --- Src/CSharpier.Cli/Server/ServerFormatter.cs | 9 ++- .../source.extension.vsixmanifest | 2 +- .../source.extension.vsixmanifest | 2 +- .../CSharpierOptions.cs | 11 +++ .../CSharpierProcessProvider.cs | 15 +++- .../CSharpierProcessServer.cs | 74 ++++++++++++++++--- Src/CSharpier.VisualStudio/ChangeLog.md | 6 +- 7 files changed, 104 insertions(+), 15 deletions(-) diff --git a/Src/CSharpier.Cli/Server/ServerFormatter.cs b/Src/CSharpier.Cli/Server/ServerFormatter.cs index 707452641..862af9c84 100644 --- a/Src/CSharpier.Cli/Server/ServerFormatter.cs +++ b/Src/CSharpier.Cli/Server/ServerFormatter.cs @@ -49,9 +49,14 @@ public static int FindFreePort() var ipEndPoint = ipGlobalProperties.GetActiveTcpListeners(); var usedPorts = ipEndPoint + .Where(o => o.Port >= startPort) .Select(o => o.Port) - .Concat(tcpConnInfoArray.Select(o => o.LocalEndPoint.Port)) - .ToList(); + .Concat( + tcpConnInfoArray + .Where(o => o.LocalEndPoint.Port >= startPort) + .Select(o => o.LocalEndPoint.Port) + ) + .ToHashSet(); for (var i = startPort; i < endPort; i++) { diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudio/source.extension.vsixmanifest b/Src/CSharpier.VisualStudio/CSharpier.VisualStudio/source.extension.vsixmanifest index b87ae4f94..69b7eb991 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudio/source.extension.vsixmanifest +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudio/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + CSharpier CSharpier is an opinionated code formatter for c#. It uses Roslyn to parse your code and re-prints it using its own rules. https://github.com/belav/csharpier diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudio2019/source.extension.vsixmanifest b/Src/CSharpier.VisualStudio/CSharpier.VisualStudio2019/source.extension.vsixmanifest index 77bb4a250..4be956029 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudio2019/source.extension.vsixmanifest +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudio2019/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + CSharpier 2019 CSharpier is an opinionated code formatter for c#. It uses Roslyn to parse your code and re-prints it using its own rules. https://github.com/belav/csharpier diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierOptions.cs b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierOptions.cs index e8f6b778d..ac603689e 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierOptions.cs +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierOptions.cs @@ -39,12 +39,20 @@ public class CSharpierOptions )] public string? CustomPath { get; set; } + [Category("CSharpier - Developer")] + [DisplayName("Disable CSharpier Server")] + [Description( + "Disable CSharpier Server - Use the legacy version of piping stdin to csharpier for formatting files." + )] + public bool DisableCSharpierServer { get; set; } + protected void LoadFrom(CSharpierOptions newInstance) { this.SolutionRunOnSave = newInstance.SolutionRunOnSave; this.GlobalRunOnSave = newInstance.GlobalRunOnSave; this.GlobalLogDebugMessages = newInstance.GlobalLogDebugMessages; this.CustomPath = newInstance.CustomPath; + this.DisableCSharpierServer = newInstance.DisableCSharpierServer; } private static readonly AsyncLazy liveModel = @@ -118,6 +126,7 @@ await LoadOptionsFromFile( newInstance.GlobalRunOnSave = o.RunOnSave; newInstance.GlobalLogDebugMessages = o.LogDebugMessages; newInstance.CustomPath = o.CustomPath; + newInstance.DisableCSharpierServer = o.DisableCSharpierServer; } ); @@ -172,6 +181,7 @@ await SaveOptions( RunOnSave = this.GlobalRunOnSave, LogDebugMessages = this.GlobalLogDebugMessages, CustomPath = this.CustomPath, + DisableCSharpierServer = this.DisableCSharpierServer, } ); } @@ -206,6 +216,7 @@ private class OptionsDto public bool? RunOnSave { get; set; } public bool LogDebugMessages { get; set; } public string? CustomPath { get; set; } + public bool DisableCSharpierServer { get; set; } } } } diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessProvider.cs b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessProvider.cs index bbed930f3..726cec613 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessProvider.cs +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessProvider.cs @@ -220,12 +220,25 @@ private ICSharpierProcess SetupCSharpierProcess(string directory, string version var serverVersion = new Version("0.28.0"); ICSharpierProcess cSharpierProcess; - if (installedVersion.CompareTo(serverVersion) >= 0) + if ( + installedVersion.CompareTo(serverVersion) >= 0 + && !CSharpierOptions.Instance.DisableCSharpierServer + ) { cSharpierProcess = new CSharpierProcessServer(customPath, version, this.logger); } else if (installedVersion.CompareTo(pipeFilesVersion) >= 0) { + if ( + installedVersion.CompareTo(serverVersion) >= 0 + && CSharpierOptions.Instance.DisableCSharpierServer + ) + { + this.logger.Debug( + "CSharpier server is disabled, falling back to piping via stdin" + ); + } + cSharpierProcess = new CSharpierProcessPipeMultipleFiles( customPath, version, diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs index 6c816c215..c45a6bdcf 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs @@ -1,7 +1,10 @@ using System; +using System.CodeDom; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; +using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; using CSharpier.VisualStudio; @@ -32,10 +35,66 @@ public CSharpierProcessServer(string csharpierPath, string version, Logger logge } private void StartProcess() + { + if (this.ActuallyStartProcess()) + { + return; + } + + var portToUse = this.FindFreePort(); + if (!this.ActuallyStartProcess(portToUse)) + { + this.ProcessFailedToStart = true; + } + } + + private int FindFreePort() + { + this.logger.Debug("Trying to find free port in extension"); + const int startPort = 49152; + const int endPort = 65535; + var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections(); + var ipEndPoint = ipGlobalProperties.GetActiveTcpListeners(); + + var usedPorts = ipEndPoint + .Where(o => o.Port >= startPort) + .Select(o => o.Port) + .Concat( + tcpConnInfoArray + .Where(o => o.LocalEndPoint.Port >= startPort) + .Select(o => o.LocalEndPoint.Port) + ) + .ToHashSet(); + + this.logger.Debug($"Found {usedPorts.Count} used ports that could conflict"); + + for (var i = startPort; i < endPort; i++) + { + if (!usedPorts.Contains(i)) + { + return i; + } + } + + throw new InvalidOperationException( + $"Could not find any free TCP port between ports {startPort}-{endPort}" + ); + } + + private bool ActuallyStartProcess(int portToUse = -1) { try { - var processStartInfo = new ProcessStartInfo(this.csharpierPath, "--server") + var arguments = "--server"; + if (portToUse > 0) + { + arguments += " --server-port " + portToUse; + } + + this.logger.Debug("Running " + this.csharpierPath + " " + arguments); + + var processStartInfo = new ProcessStartInfo(this.csharpierPath, arguments) { RedirectStandardOutput = true, RedirectStandardError = true, @@ -55,13 +114,10 @@ private void StartProcess() if (!task.Wait(TimeSpan.FromSeconds(2))) { this.logger.Warn( - "Spawning the csharpier server timed out. Formatting cannot occur." - + Environment.NewLine - + output + "Spawning the csharpier server timed out." + Environment.NewLine + output ); this.process!.Kill(); - this.ProcessFailedToStart = true; - return; + return false; } if (this.process!.HasExited) @@ -70,19 +126,19 @@ private void StartProcess() "Spawning the csharpier server failed because it exited. " + this.process!.StandardError.ReadToEnd() ); - this.ProcessFailedToStart = true; - return; + return false; } var portString = output.Replace("Started on ", ""); this.port = int.Parse(portString); this.logger.Debug("Connecting via port " + portString); + return true; } catch (Exception e) { this.logger.Warn("Failed to spawn the needed csharpier server." + e); - this.ProcessFailedToStart = true; + return false; } } diff --git a/Src/CSharpier.VisualStudio/ChangeLog.md b/Src/CSharpier.VisualStudio/ChangeLog.md index 87f343416..aeb8ca154 100644 --- a/Src/CSharpier.VisualStudio/ChangeLog.md +++ b/Src/CSharpier.VisualStudio/ChangeLog.md @@ -1,4 +1,8 @@ -## [1.7.2] +## [1.7.3] +- If CSharpier doesn't respond when trying to find a port, then try to find a port in extension +- Add option to bypass csharpier server. + +## [1.7.2] - Fix bad code path when csharpier server failed to start ## [1.7.1] From 8834ed4061785a39063c6ed84a6e12608ae381fc Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Fri, 10 May 2024 11:55:26 -0500 Subject: [PATCH 2/2] code review changes --- .../CSharpierProcessServer.cs | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs index c45a6bdcf..108f94ade 100644 --- a/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs +++ b/Src/CSharpier.VisualStudio/CSharpier.VisualStudioShared/CSharpierProcessServer.cs @@ -48,40 +48,6 @@ private void StartProcess() } } - private int FindFreePort() - { - this.logger.Debug("Trying to find free port in extension"); - const int startPort = 49152; - const int endPort = 65535; - var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); - var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections(); - var ipEndPoint = ipGlobalProperties.GetActiveTcpListeners(); - - var usedPorts = ipEndPoint - .Where(o => o.Port >= startPort) - .Select(o => o.Port) - .Concat( - tcpConnInfoArray - .Where(o => o.LocalEndPoint.Port >= startPort) - .Select(o => o.LocalEndPoint.Port) - ) - .ToHashSet(); - - this.logger.Debug($"Found {usedPorts.Count} used ports that could conflict"); - - for (var i = startPort; i < endPort; i++) - { - if (!usedPorts.Contains(i)) - { - return i; - } - } - - throw new InvalidOperationException( - $"Could not find any free TCP port between ports {startPort}-{endPort}" - ); - } - private bool ActuallyStartProcess(int portToUse = -1) { try @@ -113,10 +79,12 @@ private bool ActuallyStartProcess(int portToUse = -1) if (!task.Wait(TimeSpan.FromSeconds(2))) { + this.process!.Kill(); this.logger.Warn( - "Spawning the csharpier server timed out." + Environment.NewLine + output + "Spawning the csharpier server timed out." + + Environment.NewLine + + this.process!.StandardError.ReadToEnd() ); - this.process!.Kill(); return false; } @@ -142,6 +110,40 @@ private bool ActuallyStartProcess(int portToUse = -1) } } + private int FindFreePort() + { + this.logger.Debug("Trying to find free port in extension"); + const int startPort = 49152; + const int endPort = 65535; + var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections(); + var ipEndPoint = ipGlobalProperties.GetActiveTcpListeners(); + + var usedPorts = ipEndPoint + .Where(o => o.Port >= startPort) + .Select(o => o.Port) + .Concat( + tcpConnInfoArray + .Where(o => o.LocalEndPoint.Port >= startPort) + .Select(o => o.LocalEndPoint.Port) + ) + .ToHashSet(); + + this.logger.Debug($"Found {usedPorts.Count} used ports that could conflict"); + + for (var i = startPort; i < endPort; i++) + { + if (!usedPorts.Contains(i)) + { + return i; + } + } + + throw new InvalidOperationException( + $"Could not find any free TCP port between ports {startPort}-{endPort}" + ); + } + public string FormatFile(string content, string filePath) { var parameter = new FormatFileParameter { fileName = filePath, fileContents = content };