diff --git a/src/Stratis.DevEx.Base/Runtime.cs b/src/Stratis.DevEx.Base/Runtime.cs index 33c7084..80861ae 100644 --- a/src/Stratis.DevEx.Base/Runtime.cs +++ b/src/Stratis.DevEx.Base/Runtime.cs @@ -289,6 +289,129 @@ private static void AppDomain_UnhandledException(object sender, UnhandledExcepti } } + public static Dictionary RunCmd(string filename, string arguments, string workingdirectory) + { + ProcessStartInfo info = new ProcessStartInfo(); + info.FileName = filename; + info.Arguments = arguments; + info.WorkingDirectory = workingdirectory; + info.RedirectStandardOutput = true; + info.RedirectStandardError = true; + info.UseShellExecute = false; + info.CreateNoWindow = true; + var output = new Dictionary(); + using (var process = new Process()) + { + process.StartInfo = info; + try + { + if (!process.Start()) + { + output["error"] = ("Could not start {file} {args} in {dir}.", info.FileName, info.Arguments, info.WorkingDirectory); + return output; + } + var stdout = process.StandardOutput.ReadToEnd(); + var stderr = process.StandardError.ReadToEnd(); + if (stdout != null && stdout.Length > 0) + { + output["stdout"] = stdout; + } + if (stderr != null && stderr.Length > 0) + { + output["stderr"] = stderr; + } + return output; + } + catch (Exception ex) + { + output["exception"] = ex; + return output; + } + } + } + + public static async Task> RunCmdAsync(string filename, string arguments, string workingdirectory) + { + ProcessStartInfo info = new ProcessStartInfo(); + info.FileName = filename; + info.Arguments = arguments; + info.WorkingDirectory = workingdirectory; + info.RedirectStandardOutput = true; + info.RedirectStandardError = true; + info.UseShellExecute = false; + info.CreateNoWindow = true; + var output = new Dictionary(); + using (var process = new Process()) + { + process.StartInfo = info; + try + { + if (!process.Start()) + { + output["error"] = ("Could not start {file} {args} in {dir}.", info.FileName, info.Arguments, info.WorkingDirectory); + return output; + } + var stdout = await process.StandardOutput.ReadToEndAsync(); + var stderr = await process.StandardError.ReadToEndAsync(); + if (stdout != null && stdout.Length > 0) + { + output["stdout"] = stdout; + } + if (stderr != null && stderr.Length > 0) + { + output["stderr"] = stderr; + } + return output; + } + catch (Exception ex) + { + output["exception"] = ex; + return output; + } + } + } + + public static bool CheckRunCmdOutput(Dictionary output, string checktext) + { + if (output.ContainsKey("error") || output.ContainsKey("exception")) + { + if (output.ContainsKey("error")) + { + Error((string)output["error"]); + } + if (output.ContainsKey("exception")) + { + Error((Exception) output["exception"], "Exception thrown during process execution."); + } + return false; + } + else + { + if (output.ContainsKey("stderr")) + { + var stderr = (string)output["stderr"]; + Info(stderr); + } + if (output.ContainsKey("stdout")) + { + var stdout = (string)output["stdout"]; + Info(stdout); + if (stdout.Contains(checktext)) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + } + public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive = false) { using var op = Begin("Copying {0} to {1}", sourceDir, destinationDir); diff --git a/src/Stratis.VS.StratisEVM/SolidityLanguageClient.cs b/src/Stratis.VS.StratisEVM/SolidityLanguageClient.cs index 92ec70d..ff719e7 100644 --- a/src/Stratis.VS.StratisEVM/SolidityLanguageClient.cs +++ b/src/Stratis.VS.StratisEVM/SolidityLanguageClient.cs @@ -24,6 +24,7 @@ using Microsoft.VisualStudio.Shell.Interop; using System.Windows.Ink; using Stratis.VS.StratisEVM; +using System.Text; namespace Stratis.VS { @@ -45,8 +46,8 @@ public SolidityLanguageClient() #endregion #region Properties - public static string SolutionOpenFolder {get; set;} - + public static string SolutionOpenFolder { get; set; } + public string Name => "Solidity Language Extension"; public IEnumerable ConfigurationSections @@ -77,8 +78,6 @@ internal static SolidityLanguageClient Instance set; } - internal System.Diagnostics.Process ServerProcess { get; set; } - internal JsonRpc Rpc { get; @@ -86,8 +85,10 @@ internal JsonRpc Rpc } #endregion + #region Events public event AsyncEventHandler StartAsync; public event AsyncEventHandler StopAsync; + #endregion #region Methods protected bool StartLanguageServerProcess() @@ -95,8 +96,9 @@ protected bool StartLanguageServerProcess() ProcessStartInfo info = new ProcessStartInfo(); var programPath = "cmd.exe"; info.FileName = programPath; - info.Arguments = "/c " + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "npm", "nomicfoundation-solidity-language-server.cmd") + " --stdio"; - //info.Arguments = "/c " + "node " + "c:\\Projects\\vscode-solidity\\dist\\cli\\server.js" + " --stdio"; + //info.Arguments = "/c " + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "npm", "nomicfoundation-solidity-language-server.cmd") + " --stdio"; + info.Arguments = "/c " + "node \"" + Path.Combine(Runtime.AssemblyLocation, "node_modules", "solidity", "dist", "cli", "server.js") + "\" --stdio"; + info.WorkingDirectory = AssemblyLocation; info.RedirectStandardInput = true; info.RedirectStandardOutput = true; info.UseShellExecute = false; @@ -108,15 +110,51 @@ protected bool StartLanguageServerProcess() { Info("Language server proceess exited."); }; - ServerProcess = process; - return process.Start(); + serverProcess = process; + try + { + return process.Start(); + } + catch (Exception ex) + { + Error(ex, "Exception thrown starting language server process."); + return false; + } + + } + + public static Dictionary InstallVSCodeSolidityLanguageServer() + { + return RunCmd("cmd.exe", "/c npm install solidity-0.0.165.tgz --force --quiet --no-progress", AssemblyLocation); + } + + public static async Task> InstallVSCodeSolidityLanguageServerAsync() + { + return await RunCmdAsync("cmd.exe", "/c npm install solidity-0.0.165.tgz --force --quiet --no-progress", AssemblyLocation); } #region ILanguageClient, ILanguageClientCustomMessage2 implementation public async Task ActivateAsync(CancellationToken token) { await Task.Yield(); - var solution = (IVsSolution) await ServiceProvider.GetGlobalServiceAsync(typeof(SVsSolution)); + if (Directory.Exists(Path.Combine(Runtime.AssemblyLocation, "node_modules")) && File.Exists(Path.Combine(Runtime.AssemblyLocation, "node_modules", "solidity", "dist", "cli", "server.js"))) + { + Info("VSCode Solidity language server present."); + } + else + { + var output = await InstallVSCodeSolidityLanguageServerAsync(); + if (CheckRunCmdOutput(output, "Run `npm audit` for details.")) + { + Info("VSCode Solidity language server installed."); + } + else + { + Error("Could not install VSCode Solidity language server."); + return null; + } + } + var solution = (IVsSolution)await ServiceProvider.GetGlobalServiceAsync(typeof(SVsSolution)); solution.GetSolutionInfo(out var dir, out var f, out var d); Info("Solution dir is {d}", dir); this.InitializationOptions = JObject.FromObject(new @@ -134,12 +172,12 @@ public async Task ActivateAsync(CancellationToken token) if (StartLanguageServerProcess()) { - Info("Started language server process {proc} {p}.", ServerProcess.StartInfo.FileName, ServerProcess.StartInfo.Arguments); - return new Connection(ServerProcess.StandardOutput.BaseStream, ServerProcess.StandardInput.BaseStream); + Info("Started language server process {proc} {p}.", serverProcess.StartInfo.FileName, serverProcess.StartInfo.Arguments); + return new Connection(serverProcess.StandardOutput.BaseStream, serverProcess.StandardInput.BaseStream); } else { - Info("Could not start language server process {proc}", ServerProcess.StartInfo.FileName); + Error("Could not start language server process: {proc} {args}. Exit code: {code}.", serverProcess.StartInfo.FileName, serverProcess.StartInfo.Arguments, serverProcess.ExitCode); return null; } } @@ -189,6 +227,8 @@ public Task OnServerInitializeFailedAsync(ILanguag #endregion #endregion + + #region Types internal class SolidityLanguageClientMiddleLayer : ILanguageClientMiddleLayer { public bool CanHandle(string methodName) @@ -198,25 +238,25 @@ public bool CanHandle(string methodName) public async Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification) { - Info("Notification {req} {param}.", methodName, methodParam.ToString()); - await sendNotification(methodParam); - /* + //Info("Notification {req} {param}.", methodName, methodParam.ToString()); + //await sendNotification(methodParam); + if (methodName == "textDocument/didChange") { - methodParam.Root["contentChanges"] = JArray.FromObject(new[] {new + methodParam.Root["contentChanges"] = JArray.FromObject(new[] {new { text = methodParam.Root["contentChanges"][0]["text"].Value(), } }); Info("didchange Notification {req} {param}.", methodName, methodParam.ToString()); await sendNotification(methodParam); - + } else { Info("Notification {req} {param}.", methodName, methodParam.ToString()); await sendNotification(methodParam); } - */ + } public async Task HandleRequestAsync(string methodName, JToken methodParam, Func> sendRequest) @@ -226,5 +266,14 @@ public async Task HandleRequestAsync(string methodName, JToken methodPar return resp; } } + #endregion + + #region Fields + internal System.Diagnostics.Process serverProcess; + #endregion } + + + + } diff --git a/src/Stratis.VS.StratisEVM/SolidityLanguageSettings.json b/src/Stratis.VS.StratisEVM/SolidityLanguageSettings.json index 7134b68..5a66e0e 100644 --- a/src/Stratis.VS.StratisEVM/SolidityLanguageSettings.json +++ b/src/Stratis.VS.StratisEVM/SolidityLanguageSettings.json @@ -4,5 +4,5 @@ "solidity.maxNumberOfProblems": 1, "solidity.enabledAsYouTypeCompilationErrorCheck": true, "solidity.defaultCompiler": "localFile", - "solidity.compileUsingLocalVersion": "C:\\Users\\Allister\\Downloads\\soljson.js" + "solidity.compileUsingLocalVersion": "C:\\Users\\Allister\\Downloads\\soljson-v0.8.23+commit.f704f362.js" } \ No newline at end of file diff --git a/src/Stratis.VS.StratisEVM/Stratis.VS.StratisEVM.csproj b/src/Stratis.VS.StratisEVM/Stratis.VS.StratisEVM.csproj index ad660a3..e633e26 100644 --- a/src/Stratis.VS.StratisEVM/Stratis.VS.StratisEVM.csproj +++ b/src/Stratis.VS.StratisEVM/Stratis.VS.StratisEVM.csproj @@ -64,6 +64,12 @@ true + + true + + + true + Designer diff --git a/src/Stratis.VS.StratisEVM/StratisEVMPackage.cs b/src/Stratis.VS.StratisEVM/StratisEVMPackage.cs index fd556e4..b086d7d 100644 --- a/src/Stratis.VS.StratisEVM/StratisEVMPackage.cs +++ b/src/Stratis.VS.StratisEVM/StratisEVMPackage.cs @@ -8,6 +8,9 @@ using Microsoft.VisualStudio.Shell.Interop; using Stratis.DevEx; using Microsoft.VisualStudio.TaskRunnerExplorer; +using Microsoft.IO; +using System.Windows.Threading; +using System.Collections.Generic; namespace Stratis.VS.StratisEVM { @@ -91,17 +94,53 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); if (VSUtil.InitializeVSServices(ServiceProvider.GlobalProvider)) { - Runtime.Info("StratisEVM package initialized."); + VSUtil.LogInfo("Stratis EVM", $"Extension assembly directory is {Runtime.AssemblyLocation}. StratisEVM package initialized."); } else { Runtime.Error("Could not initialize StratisEVM package VS services."); + return; } + + /* + if (Directory.Exists(Path.Combine(Runtime.AssemblyLocation, "node_modules")) && File.Exists(Path.Combine(Runtime.AssemblyLocation, "node_modules"))) + { + VSUtil.LogInfo("Stratis EVM", "VSCode Solidity language server present."); + } + else + { + VSUtil.LogInfo("Stratis EVM", "VSCode Solidity language server not present, installing..."); + + /* + ThreadPool.QueueUserWorkItem((o) => + { + + var output = SolidityLanguageClient.InstallVSCodeSolidityLanguageServer(); + ThreadHelper.JoinableTaskContext.MainThread. + + }); + + var output = await SolidityLanguageClient.InstallVSCodeSolidityLanguageServerAsync(); + if (VSUtil.CheckRunCmdOutput(output, "Stratis EVM", "Run `npm audit` for details.")) + { + VSUtil.LogInfo("Stratis EVM", "VSCode Solidity language server installed."); + } + else + { + VSUtil.LogError("Stratis EVM", "Could not install VSCode Solidity language server."); + } + + } + */ // When initialized asynchronously, the current thread may be a background thread at this point. // Do any initialization that requires the UI thread after switching to the UI thread. } #endregion #endregion + + #region Fields + public static Dispatcher _dispatcher; + #endregion } } diff --git a/src/Stratis.VS.StratisEVM/VSUtil.cs b/src/Stratis.VS.StratisEVM/VSUtil.cs index 5613acd..29d6b25 100644 --- a/src/Stratis.VS.StratisEVM/VSUtil.cs +++ b/src/Stratis.VS.StratisEVM/VSUtil.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; @@ -147,6 +149,47 @@ public static void ShowLogOutputWindowPane(IServiceProvider provider, string pan Error("Could not get a reference to the VsUIShell."); } } + + public static bool CheckRunCmdOutput(Dictionary output, string logname, string checktext) + { + if (output.ContainsKey("error") || output.ContainsKey("exception")) + { + if (output.ContainsKey("error")) + { + LogError(logname, (string)output["error"]); + } + if (output.ContainsKey("exception")) + { + LogError(logname, (Exception) output["exception"]); + } + return false; + } + else + { + if (output.ContainsKey("stderr")) + { + var stderr = (string)output["stderr"]; + LogInfo(logname, stderr); + } + if (output.ContainsKey("stdout")) + { + var stdout = (string)output["stdout"]; + LogInfo(logname, stdout); + if (stdout.Contains(checktext)) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + } + } public static bool VSServicesInitialized = false; } } diff --git a/src/Stratis.VS.StratisEVM/package.json b/src/Stratis.VS.StratisEVM/package.json new file mode 100644 index 0000000..186bc43 --- /dev/null +++ b/src/Stratis.VS.StratisEVM/package.json @@ -0,0 +1,11 @@ +{ + "name": "stratisevm-vs", + "version": "0.1.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/src/Stratis.VS.StratisEVM/solidity-0.0.165.tgz b/src/Stratis.VS.StratisEVM/solidity-0.0.165.tgz new file mode 100644 index 0000000..5c1ddaf Binary files /dev/null and b/src/Stratis.VS.StratisEVM/solidity-0.0.165.tgz differ diff --git a/tests/solidity/test2/BasicContract.sol b/tests/solidity/test2/BasicContract.sol index 9c084ea..e81f094 100644 --- a/tests/solidity/test2/BasicContract.sol +++ b/tests/solidity/test2/BasicContract.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.23; + // Uncomment this line to use console.log // import "hardhat/console.sol"; @@ -15,11 +16,7 @@ contract BasicContract { emit BasicEvent(funcArg); } - function foo(uint s) private pure { - s = 4; - } + - function foo2(string s) { - - } + }