From 88383e77642c2153ed9028a772bd1805161afd03 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 18 Aug 2024 13:59:55 -0500 Subject: [PATCH] Using dotnet tool list for rider extension (#1325) --- Src/CSharpier.Rider/CHANGELOG.md | 3 + Src/CSharpier.Rider/gradle.properties | 2 +- .../csharpier/CSharpierProcessProvider.java | 122 ++++++++---------- .../intellij/csharpier/FunctionRunner.java | 15 +++ .../intellij/csharpier/InstallerService.java | 3 +- 5 files changed, 74 insertions(+), 71 deletions(-) create mode 100644 Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java diff --git a/Src/CSharpier.Rider/CHANGELOG.md b/Src/CSharpier.Rider/CHANGELOG.md index cdf64d05e..f8f8a10f2 100644 --- a/Src/CSharpier.Rider/CHANGELOG.md +++ b/Src/CSharpier.Rider/CHANGELOG.md @@ -2,6 +2,9 @@ # csharpier-rider Changelog +## [1.8.0] +- Use dotnet tool list to find both local and global installs of csharpier. + ## [1.7.4] - Only use CSharpier Server for 0.29.0+ - Add option to bypass csharpier server. diff --git a/Src/CSharpier.Rider/gradle.properties b/Src/CSharpier.Rider/gradle.properties index 764d2c6c6..2aebc438d 100644 --- a/Src/CSharpier.Rider/gradle.properties +++ b/Src/CSharpier.Rider/gradle.properties @@ -3,7 +3,7 @@ pluginGroup = com.intellij.csharpier pluginName = csharpier -pluginVersion = 1.7.4 +pluginVersion = 1.8.0 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessProvider.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessProvider.java index 54453ced0..db01a7bd1 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessProvider.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessProvider.java @@ -22,10 +22,12 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Instant; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class CSharpierProcessProvider implements DocumentListener, Disposable, IProcessKiller { private final CustomPathInstaller customPathInstaller; @@ -54,10 +56,7 @@ public void documentChanged(@NotNull DocumentEvent event) { var document = event.getDocument(); var file = FileDocumentManager.getInstance().getFile(document); - if (file == null - || file.getExtension() == null - || !file.getExtension().equalsIgnoreCase("cs") - ) { + if (file == null || file.getExtension() == null || !file.getExtension().equalsIgnoreCase("cs")) { return; } var filePath = file.getPath(); @@ -87,10 +86,7 @@ private void findAndWarmProcess(String filePath) { } if (!this.csharpierProcessesByVersion.containsKey(version)) { - this.csharpierProcessesByVersion.put(version, this.setupCSharpierProcess( - directory, - version - )); + this.csharpierProcessesByVersion.put(version, this.setupCSharpierProcess(directory, version)); } } @@ -116,36 +112,53 @@ public ICSharpierProcess getProcessFor(String filePath) { } private String getCSharpierVersion(String directoryThatContainsFile) { + var csharpierVersion = FunctionRunner.runUntilNonNull( + () -> this.findVersionInCsProjOfParentsDirectories(directoryThatContainsFile), + () -> this.findCSharpierVersionInToolOutput(directoryThatContainsFile, false), + () -> this.findCSharpierVersionInToolOutput(directoryThatContainsFile, true) + ); + + if (csharpierVersion == null) { + return ""; + } + + var versionWithoutHash = csharpierVersion.split(Pattern.quote("+"))[0]; + this.logger.debug("Using " + versionWithoutHash + " as the version number."); + return versionWithoutHash; + } + + private String findCSharpierVersionInToolOutput(String directoryThatContainsFile, boolean isGlobal) { + var command = List.of("tool", "list", (isGlobal ? "-g" : "")); + var output = DotNetProvider.getInstance(this.project).execDotNet(command, new File(directoryThatContainsFile)); + + this.logger.debug("Running 'dotnet tool list" + (isGlobal ? "-g" : "") + "' to look for version"); + this.logger.debug("Output was: \n " + output); + + var lines = Arrays.stream(output.split("\n")).map(String::trim).filter(line -> !line.isEmpty()).collect(Collectors.toList()); + + // The first two lines are headers, so we start at index 2 + for (int i = 2; i < lines.size(); i++) { + String[] columns = lines.get(i).split("\\s{2,}"); // Split by 2 or more spaces + if (columns.length >= 2) { + if (columns[0].equalsIgnoreCase("csharpier")) { + return columns[1]; + } + } + } + + return null; + } + + private String findVersionInCsProjOfParentsDirectories(String directoryThatContainsFile) { + this.logger.debug("Looking for csproj in or above " + directoryThatContainsFile + " that references CSharpier.MsBuild"); var currentDirectory = Path.of(directoryThatContainsFile); try { while (true) { - var csProjVersion = this.FindVersionInCsProj(currentDirectory); + var csProjVersion = this.findVersionInCsProj(currentDirectory); if (csProjVersion != null) { return csProjVersion; } - var configPath = Path.of(currentDirectory.toString(), ".config/dotnet-tools.json"); - var dotnetToolsPath = configPath.toString(); - var file = new File(dotnetToolsPath); - this.logger.debug("Looking for " + dotnetToolsPath); - if (file.exists()) { - var data = new String(Files.readAllBytes(configPath)); - var configData = new Gson().fromJson(data, JsonObject.class); - var tools = configData.getAsJsonObject("tools"); - if (tools != null) { - var csharpier = tools.getAsJsonObject("csharpier"); - if (csharpier != null) { - var version = csharpier.get("version").getAsString(); - if (version != null) { - this.logger.debug("Found version " + version + " in " + dotnetToolsPath); - var versionWithoutHash = version.split(Pattern.quote("+"))[0]; - this.logger.debug("Using " + versionWithoutHash + " as the version number."); - return versionWithoutHash; - } - } - } - } - if (currentDirectory.getParent() == null) { break; } @@ -155,31 +168,15 @@ private String getCSharpierVersion(String directoryThatContainsFile) { this.logger.error(ex); } - this.logger.debug( - "Unable to find dotnet-tools.json, falling back to running dotnet csharpier --version" - ); - - var command = List.of("csharpier", "--version"); - var version = DotNetProvider.getInstance(this.project).execDotNet(command, new File(directoryThatContainsFile)); - - if (version == null) { - version = ""; - } - this.logger.debug("dotnet csharpier --version output: " + version); - var versionWithoutHash = version.split(Pattern.quote("+"))[0]; - this.logger.debug("Using " + versionWithoutHash + " as the version number."); - - return versionWithoutHash; + return null; } - private String FindVersionInCsProj(Path currentDirectory) { + private String findVersionInCsProj(Path currentDirectory) { + this.logger.debug("Looking for " + currentDirectory + "/*.csproj"); for (var pathToCsProj : currentDirectory.toFile().listFiles((dir, name) -> name.toLowerCase().endsWith(".csproj"))) { try { - var xmlDocument = DocumentBuilderFactory - .newInstance() - .newDocumentBuilder() - .parse(pathToCsProj); + var xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(pathToCsProj); var selector = XPathFactory.newInstance().newXPath(); var node = (Node) selector.compile("//PackageReference[@Include='CSharpier.MsBuild']").evaluate(xmlDocument, XPathConstants.NODE); @@ -222,28 +219,20 @@ private ICSharpierProcess setupCSharpierProcess(String directory, String version var serverVersion = 29; ICSharpierProcess csharpierProcess; - if (versionWeCareAbout >= serverVersion - && !CSharpierSettings.getInstance(this.project).getDisableCSharpierServer() - ) { + if (versionWeCareAbout >= serverVersion && !CSharpierSettings.getInstance(this.project).getDisableCSharpierServer()) { csharpierProcess = new CSharpierProcessServer(customPath, version, this.project); } else if (versionWeCareAbout >= 12) { var useUtf8 = versionWeCareAbout >= 14; - if (versionWeCareAbout >= serverVersion - && CSharpierSettings.getInstance(this.project).getDisableCSharpierServer() - ) { - this.logger.debug( - "CSharpier server is disabled, falling back to piping via stdin" - ); + if (versionWeCareAbout >= serverVersion && CSharpierSettings.getInstance(this.project).getDisableCSharpierServer()) { + this.logger.debug("CSharpier server is disabled, falling back to piping via stdin"); } csharpierProcess = new CSharpierProcessPipeMultipleFiles(customPath, useUtf8, version, this.project); } else { if (!this.warnedForOldVersion) { var content = "Please upgrade to CSharpier >= 0.12.0 for bug fixes and improved formatting speed."; - NotificationGroupManager.getInstance().getNotificationGroup("CSharpier") - .createNotification(content, NotificationType.INFORMATION) - .notify(this.project); + NotificationGroupManager.getInstance().getNotificationGroup("CSharpier").createNotification(content, NotificationType.INFORMATION).notify(this.project); this.warnedForOldVersion = true; } @@ -267,8 +256,7 @@ private ICSharpierProcess setupCSharpierProcess(String directory, String version private void displayFailureMessage() { var title = "CSharpier unable to format files"; var message = "CSharpier could not be set up properly so formatting is not currently supported. See log file for more details."; - var notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier") - .createNotification(title, message, NotificationType.WARNING); + var notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier").createNotification(title, message, NotificationType.WARNING); notification.addAction(new OpenUrlAction("Read More", "https://csharpier.com/docs/EditorsTroubleshooting")); notification.notify(this.project); } @@ -280,9 +268,7 @@ public void dispose() { public void killRunningProcesses() { for (var key : this.csharpierProcessesByVersion.keySet()) { - this.logger.debug( - "disposing of process for version " + (key == "" ? "null" : key) - ); + this.logger.debug("disposing of process for version " + (key == "" ? "null" : key)); this.csharpierProcessesByVersion.get(key).dispose(); } this.lastWarmedByDirectory.clear(); diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java new file mode 100644 index 000000000..f78109e11 --- /dev/null +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java @@ -0,0 +1,15 @@ +package com.intellij.csharpier; + +import java.util.function.Supplier; + +public class FunctionRunner { + public static String runUntilNonNull(Supplier... functions) { + for (Supplier function : functions) { + String result = function.get(); + if (result != null) { + return result; + } + } + return null; + } +} diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallerService.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallerService.java index fd6a842f4..d4ee3885d 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallerService.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallerService.java @@ -52,7 +52,6 @@ private boolean ignoreDirectory(String directoryThatContainsFile) { var normalizedPath = directoryThatContainsFile.replace('\\', '/'); return StringUtils.containsIgnoreCase(normalizedPath, "resharper-host/DecompilerCache") || StringUtils.containsIgnoreCase(normalizedPath, "resharper-host/SourcesCache") - || directoryThatContainsFile.equals("/") - || directoryThatContainsFile.equals("\\"); + || directoryThatContainsFile.equals("/"); } }