From c529570bb54fa36b4fc9c594bd1ea57dfccfdde7 Mon Sep 17 00:00:00 2001 From: Bela VanderVoort Date: Sun, 18 Aug 2024 13:59:29 -0500 Subject: [PATCH] prettier all the java --- Src/CSharpier.Rider/.prettierrc.yaml | 2 + Src/CSharpier.Rider/package-lock.json | 126 ++++ Src/CSharpier.Rider/package.json | 5 + .../intellij/csharpier/CSharpierLogger.java | 12 +- .../CSharpierProcessPipeMultipleFiles.java | 271 +++++---- .../csharpier/CSharpierProcessProvider.java | 536 +++++++++++------- .../csharpier/CSharpierProcessServer.java | 271 ++++----- .../csharpier/CSharpierProcessSingleFile.java | 133 +++-- .../intellij/csharpier/CSharpierSettings.java | 72 +-- .../csharpier/CSharpierSettingsComponent.java | 193 ++++--- .../csharpier/CustomPathInstaller.java | 193 ++++--- .../intellij/csharpier/DotNetProvider.java | 138 +++-- .../intellij/csharpier/FormattingService.java | 194 ++++--- .../intellij/csharpier/FunctionRunner.java | 17 +- .../intellij/csharpier/ICSharpierProcess.java | 33 +- .../intellij/csharpier/IProcessKiller.java | 2 +- .../csharpier/InstallGlobalAction.java | 49 +- .../csharpier/InstallLocalAction.java | 67 ++- .../intellij/csharpier/InstallerService.java | 96 ++-- .../csharpier/NullCSharpierProcess.java | 33 +- .../com/intellij/csharpier/OpenUrlAction.java | 21 +- .../com/intellij/csharpier/ProcessHelper.java | 99 ++-- .../ReformatWithCSharpierAction.java | 103 ++-- .../ReformatWithCSharpierOnSaveListener.java | 49 +- 24 files changed, 1579 insertions(+), 1136 deletions(-) create mode 100644 Src/CSharpier.Rider/.prettierrc.yaml create mode 100644 Src/CSharpier.Rider/package-lock.json create mode 100644 Src/CSharpier.Rider/package.json diff --git a/Src/CSharpier.Rider/.prettierrc.yaml b/Src/CSharpier.Rider/.prettierrc.yaml new file mode 100644 index 000000000..869a6bcce --- /dev/null +++ b/Src/CSharpier.Rider/.prettierrc.yaml @@ -0,0 +1,2 @@ +plugins: + - prettier-plugin-java \ No newline at end of file diff --git a/Src/CSharpier.Rider/package-lock.json b/Src/CSharpier.Rider/package-lock.json new file mode 100644 index 000000000..d64c83cc2 --- /dev/null +++ b/Src/CSharpier.Rider/package-lock.json @@ -0,0 +1,126 @@ +{ + "name": "CSharpier.Rider", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "prettier-plugin-java": "^2.6.4" + } + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true + }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/java-parser": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-2.3.2.tgz", + "integrity": "sha512-/O42UbEHy3VVJw8W0ruHkQjW75oWvQx4QisoUDRIGir6q3/IZ4JslDMPMYEqp7LU56PYJkH5uXdQiBaCXt/Opw==", + "dev": true, + "dependencies": { + "chevrotain": "11.0.3", + "chevrotain-allstar": "0.3.1", + "lodash": "4.17.21" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-java": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-2.6.4.tgz", + "integrity": "sha512-57iGIFM4xSCqzHc4G6RLeC0DJk+i6Vd1JDj5xcIe7GsWZjRSl8WWkpL0f4BB0gZ+jDZ8R1uJaxtnMgnRtzjLDQ==", + "dev": true, + "dependencies": { + "java-parser": "2.3.2", + "lodash": "4.17.21", + "prettier": "3.2.5" + } + } + } +} diff --git a/Src/CSharpier.Rider/package.json b/Src/CSharpier.Rider/package.json new file mode 100644 index 000000000..65f6c163e --- /dev/null +++ b/Src/CSharpier.Rider/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "prettier-plugin-java": "^2.6.4" + } +} diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierLogger.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierLogger.java index f4020a8ee..845607bda 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierLogger.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierLogger.java @@ -3,10 +3,12 @@ import com.intellij.openapi.diagnostic.Logger; public class CSharpierLogger { - private final static Logger logger = Logger.getInstance(CSharpierLogger.class); - public static Logger getInstance() { - return logger; - } -} + private static final Logger logger = Logger.getInstance( + CSharpierLogger.class + ); + public static Logger getInstance() { + return logger; + } +} diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessPipeMultipleFiles.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessPipeMultipleFiles.java index 06b767ea7..032894df0 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessPipeMultipleFiles.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessPipeMultipleFiles.java @@ -3,147 +3,172 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; - import java.io.*; import java.nio.charset.Charset; -public class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess, Disposable { - private final boolean useUtf8; - private final String csharpierPath; - private final DotNetProvider dotNetProvider; - private final String version; - private Logger logger = CSharpierLogger.getInstance(); - - private Process process = null; - private OutputStreamWriter stdin; - private BufferedReader stdOut; - private boolean processFailedToStart; - - public CSharpierProcessPipeMultipleFiles(String csharpierPath, boolean useUtf8, String version, Project project) { - this.csharpierPath = csharpierPath; - this.useUtf8 = useUtf8; - this.dotNetProvider = DotNetProvider.getInstance(project); - this.version = version; - this.startProcess(); - - this.logger.debug("Warm CSharpier with initial format"); - // warm by formatting a file twice, the 3rd time is when it gets really fast - this.formatFile("public class ClassName { }", "Test.cs"); - this.formatFile("public class ClassName { }", "Test.cs"); +public class CSharpierProcessPipeMultipleFiles + implements ICSharpierProcess, Disposable { + + private final boolean useUtf8; + private final String csharpierPath; + private final DotNetProvider dotNetProvider; + private final String version; + private Logger logger = CSharpierLogger.getInstance(); + + private Process process = null; + private OutputStreamWriter stdin; + private BufferedReader stdOut; + private boolean processFailedToStart; + + public CSharpierProcessPipeMultipleFiles( + String csharpierPath, + boolean useUtf8, + String version, + Project project + ) { + this.csharpierPath = csharpierPath; + this.useUtf8 = useUtf8; + this.dotNetProvider = DotNetProvider.getInstance(project); + this.version = version; + this.startProcess(); + + this.logger.debug("Warm CSharpier with initial format"); + // warm by formatting a file twice, the 3rd time is when it gets really fast + this.formatFile("public class ClassName { }", "Test.cs"); + this.formatFile("public class ClassName { }", "Test.cs"); + } + + private void startProcess() { + try { + var processBuilder = new ProcessBuilder( + this.csharpierPath, + "--pipe-multiple-files" + ); + processBuilder.environment().put("DOTNET_NOLOGO", "1"); + processBuilder + .environment() + .put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); + this.process = processBuilder.start(); + + var charset = this.useUtf8 + ? "utf-8" + : Charset.defaultCharset().toString(); + + this.stdin = new OutputStreamWriter( + this.process.getOutputStream(), + charset + ); + this.stdOut = new BufferedReader( + new InputStreamReader(this.process.getInputStream(), charset) + ); + + // if we don't read the error stream, eventually too much is buffered on it and the plugin hangs + var errorGobbler = new StreamGobbler(this.process.getErrorStream()); + errorGobbler.start(); + } catch (Exception e) { + this.logger.warn( + "Failed to spawn the needed csharpier process. Formatting cannot occur.", + e + ); + this.processFailedToStart = true; + } + } + + @Override + public String getVersion() { + return this.version; + } + + @Override + public boolean getProcessFailedToStart() { + return this.processFailedToStart; + } + + @Override + public String formatFile(String content, String filePath) { + if (this.processFailedToStart) { + this.logger.warn( + "CSharpier proccess failed to start. Formatting cannot occur." + ); + return ""; } - private void startProcess() { - try { - var processBuilder = new ProcessBuilder(this.csharpierPath, "--pipe-multiple-files"); - processBuilder.environment().put("DOTNET_NOLOGO", "1"); - processBuilder.environment().put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); - this.process = processBuilder.start(); - - var charset = this.useUtf8 ? "utf-8" : Charset.defaultCharset().toString(); - - this.stdin = new OutputStreamWriter(this.process.getOutputStream(), charset); - this.stdOut = new BufferedReader(new InputStreamReader(this.process.getInputStream(), charset)); - - // if we don't read the error stream, eventually too much is buffered on it and the plugin hangs - var errorGobbler = new StreamGobbler(this.process.getErrorStream()); - errorGobbler.start(); - } catch (Exception e) { - this.logger.warn("Failed to spawn the needed csharpier process. Formatting cannot occur.", e); - this.processFailedToStart = true; + var stringBuilder = new StringBuilder(); + + Runnable task = () -> { + try { + this.stdin.write(filePath); + this.stdin.write('\u0003'); + this.stdin.write(content); + this.stdin.write('\u0003'); + this.stdin.flush(); + + var nextCharacter = this.stdOut.read(); + while (nextCharacter != -1) { + if (nextCharacter == '\u0003') { + break; + } + stringBuilder.append((char) nextCharacter); + nextCharacter = this.stdOut.read(); } + } catch (Exception e) { + this.logger.error(e); + e.printStackTrace(); + } + }; + + // csharpier will freeze in some instances when "Format on Save" is also installed and the file has compilation errors + // this detects that and recovers from it + var thread = new Thread(task); + thread.start(); + try { + thread.join(3000); + } catch (InterruptedException e) { + // if we interrupt it we shouldn't log it } - @Override - public String getVersion() { - return this.version; + if (thread.isAlive()) { + this.logger.warn("CSharpier process appears to be hung, restarting it."); + thread.interrupt(); + this.process.destroy(); + this.startProcess(); + return ""; } - @Override - public boolean getProcessFailedToStart() { - return this.processFailedToStart; - } + var result = stringBuilder.toString(); - @Override - public String formatFile(String content, String filePath) { - if (this.processFailedToStart) { - this.logger.warn("CSharpier proccess failed to start. Formatting cannot occur."); - return ""; - } + if (result == null || result.isEmpty()) { + this.logger.info( + "File is ignored by .csharpierignore or there was an error" + ); + return ""; + } - var stringBuilder = new StringBuilder(); - - Runnable task = () -> { - try { - this.stdin.write(filePath); - this.stdin.write('\u0003'); - this.stdin.write(content); - this.stdin.write('\u0003'); - this.stdin.flush(); - - var nextCharacter = this.stdOut.read(); - while (nextCharacter != -1) { - if (nextCharacter == '\u0003') { - break; - } - stringBuilder.append((char) nextCharacter); - nextCharacter = this.stdOut.read(); - } - } catch (Exception e) { - this.logger.error(e); - e.printStackTrace(); - } - }; - - // csharpier will freeze in some instances when "Format on Save" is also installed and the file has compilation errors - // this detects that and recovers from it - var thread = new Thread(task); - thread.start(); - try { - thread.join(3000); - } catch (InterruptedException e) { - // if we interrupt it we shouldn't log it - } + return result; + } - if (thread.isAlive()) { - this.logger.warn("CSharpier process appears to be hung, restarting it."); - thread.interrupt(); - this.process.destroy(); - this.startProcess(); - return ""; - } + @Override + public void dispose() { + if (this.process != null) { + this.process.destroy(); + } + } - var result = stringBuilder.toString(); + private class StreamGobbler extends Thread { - if (result == null || result.isEmpty()) { - this.logger.info("File is ignored by .csharpierignore or there was an error"); - return ""; - } + InputStream inputStream; - return result; + private StreamGobbler(InputStream inputStream) { + this.inputStream = inputStream; } @Override - public void dispose() { - if (this.process != null) { - this.process.destroy(); - } - } - - private class StreamGobbler extends Thread { - InputStream inputStream; - - private StreamGobbler(InputStream inputStream) { - this.inputStream = inputStream; - } - - @Override - public void run() { - try { - var streamReader = new InputStreamReader(this.inputStream); - var reader = new BufferedReader(streamReader); - while (reader.readLine() != null) {} - } - catch (IOException ioe) { } - } + public void run() { + try { + var streamReader = new InputStreamReader(this.inputStream); + var reader = new BufferedReader(streamReader); + while (reader.readLine() != null) {} + } catch (IOException ioe) {} } + } } 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 db01a7bd1..3cee010f3 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 @@ -12,12 +12,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.w3c.dom.Node; - -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathFactory; import java.io.File; import java.nio.file.Files; import java.nio.file.Path; @@ -28,251 +22,357 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; +import org.jetbrains.annotations.NotNull; +import org.w3c.dom.Node; -public class CSharpierProcessProvider implements DocumentListener, Disposable, IProcessKiller { - private final CustomPathInstaller customPathInstaller; - private final Logger logger = CSharpierLogger.getInstance(); - private final Project project; - - private boolean warnedForOldVersion; - private final HashMap lastWarmedByDirectory = new HashMap<>(); - private final HashMap csharpierVersionByDirectory = new HashMap<>(); - private final HashMap csharpierProcessesByVersion = new HashMap<>(); - - public CSharpierProcessProvider(@NotNull Project project) { - this.project = project; - this.customPathInstaller = new CustomPathInstaller(project); - - EditorFactory.getInstance().getEventMulticaster().addDocumentListener(this, this); +public class CSharpierProcessProvider + implements DocumentListener, Disposable, IProcessKiller { + + private final CustomPathInstaller customPathInstaller; + private final Logger logger = CSharpierLogger.getInstance(); + private final Project project; + + private boolean warnedForOldVersion; + private final HashMap lastWarmedByDirectory = new HashMap<>(); + private final HashMap csharpierVersionByDirectory = + new HashMap<>(); + private final HashMap csharpierProcessesByVersion = + new HashMap<>(); + + public CSharpierProcessProvider(@NotNull Project project) { + this.project = project; + this.customPathInstaller = new CustomPathInstaller(project); + + EditorFactory.getInstance() + .getEventMulticaster() + .addDocumentListener(this, this); + } + + @NotNull + static CSharpierProcessProvider getInstance(@NotNull Project project) { + return project.getService(CSharpierProcessProvider.class); + } + + @Override + 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") + ) { + return; } - - @NotNull - static CSharpierProcessProvider getInstance(@NotNull Project project) { - return project.getService(CSharpierProcessProvider.class); + var filePath = file.getPath(); + this.findAndWarmProcess(filePath); + } + + private void findAndWarmProcess(String filePath) { + // if we didn't find dotnet bail out so we don't get extra errors about being unable to format + if (!DotNetProvider.getInstance(this.project).foundDotNet()) { + return; } - - @Override - 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")) { - return; - } - var filePath = file.getPath(); - this.findAndWarmProcess(filePath); + var directory = Path.of(filePath).getParent().toString(); + var now = Instant.now().toEpochMilli(); + var lastWarmed = + this.lastWarmedByDirectory.getOrDefault(directory, Long.valueOf(0)); + if (lastWarmed + 5000 > now) { + return; } - - private void findAndWarmProcess(String filePath) { - // if we didn't find dotnet bail out so we don't get extra errors about being unable to format - if (!DotNetProvider.getInstance(this.project).foundDotNet()) { - return; - } - var directory = Path.of(filePath).getParent().toString(); - var now = Instant.now().toEpochMilli(); - var lastWarmed = this.lastWarmedByDirectory.getOrDefault(directory, Long.valueOf(0)); - if (lastWarmed + 5000 > now) { - return; - } - this.logger.debug("Ensure there is a csharpier process for " + directory); - this.lastWarmedByDirectory.put(directory, now); - var version = this.csharpierVersionByDirectory.getOrDefault(directory, null); - if (version == null) { - version = this.getCSharpierVersion(directory); - if (version == null || version.isEmpty()) { - InstallerService.getInstance(this.project).displayInstallNeededMessage(directory, this); - } - this.csharpierVersionByDirectory.put(directory, version); - } - - if (!this.csharpierProcessesByVersion.containsKey(version)) { - this.csharpierProcessesByVersion.put(version, this.setupCSharpierProcess(directory, version)); - } + this.logger.debug("Ensure there is a csharpier process for " + directory); + this.lastWarmedByDirectory.put(directory, now); + var version = + this.csharpierVersionByDirectory.getOrDefault(directory, null); + if (version == null) { + version = this.getCSharpierVersion(directory); + if (version == null || version.isEmpty()) { + InstallerService.getInstance(this.project).displayInstallNeededMessage( + directory, + this + ); + } + this.csharpierVersionByDirectory.put(directory, version); } - public ICSharpierProcess getProcessFor(String filePath) { - // if we didn't find dotnet bail out so we don't get extra errors about being unable to format - if (!DotNetProvider.getInstance(this.project).foundDotNet()) { - return NullCSharpierProcess.Instance; - } + if (!this.csharpierProcessesByVersion.containsKey(version)) { + this.csharpierProcessesByVersion.put( + version, + this.setupCSharpierProcess(directory, version) + ); + } + } - var directory = Path.of(filePath).getParent().toString(); - var version = this.csharpierVersionByDirectory.getOrDefault(directory, null); - if (version == null) { - this.findAndWarmProcess(filePath); - version = this.csharpierVersionByDirectory.get(directory); - } + public ICSharpierProcess getProcessFor(String filePath) { + // if we didn't find dotnet bail out so we don't get extra errors about being unable to format + if (!DotNetProvider.getInstance(this.project).foundDotNet()) { + return NullCSharpierProcess.Instance; + } - if (version == null || !this.csharpierProcessesByVersion.containsKey(version)) { - // this shouldn't really happen, but just in case - return NullCSharpierProcess.Instance; - } + var directory = Path.of(filePath).getParent().toString(); + var version = + this.csharpierVersionByDirectory.getOrDefault(directory, null); + if (version == null) { + this.findAndWarmProcess(filePath); + version = this.csharpierVersionByDirectory.get(directory); + } - return this.csharpierProcessesByVersion.get(version); + if ( + version == null || !this.csharpierProcessesByVersion.containsKey(version) + ) { + // this shouldn't really happen, but just in case + return NullCSharpierProcess.Instance; } - private String getCSharpierVersion(String directoryThatContainsFile) { - var csharpierVersion = FunctionRunner.runUntilNonNull( - () -> this.findVersionInCsProjOfParentsDirectories(directoryThatContainsFile), - () -> this.findCSharpierVersionInToolOutput(directoryThatContainsFile, false), - () -> this.findCSharpierVersionInToolOutput(directoryThatContainsFile, true) - ); + return this.csharpierProcessesByVersion.get(version); + } + + private String getCSharpierVersion(String directoryThatContainsFile) { + var csharpierVersion = FunctionRunner.runUntilNonNull( + () -> + this.findVersionInCsProjOfParentsDirectories(directoryThatContainsFile), + () -> + this.findCSharpierVersionInToolOutput(directoryThatContainsFile, false), + () -> + this.findCSharpierVersionInToolOutput(directoryThatContainsFile, true) + ); + + if (csharpierVersion == null) { + return ""; + } - 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]; } - - 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); + if (csProjVersion != null) { + return csProjVersion; } - return null; + if (currentDirectory.getParent() == null) { + break; + } + currentDirectory = currentDirectory.getParent(); + } + } catch (Exception ex) { + this.logger.error(ex); } - 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); - if (csProjVersion != null) { - return csProjVersion; - } - - if (currentDirectory.getParent() == null) { - break; - } - currentDirectory = currentDirectory.getParent(); - } - } catch (Exception ex) { - this.logger.error(ex); + return null; + } + + 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 selector = XPathFactory.newInstance().newXPath(); + var node = (Node) selector + .compile("//PackageReference[@Include='CSharpier.MsBuild']") + .evaluate(xmlDocument, XPathConstants.NODE); + if (node == null) { + continue; } - return null; + var versionOfMsBuildPackage = node + .getAttributes() + .getNamedItem("Version") + .getNodeValue(); + if (versionOfMsBuildPackage != null) { + this.logger.debug( + "Found version " + versionOfMsBuildPackage + " in " + pathToCsProj + ); + return versionOfMsBuildPackage; + } + } catch (Exception e) { + this.logger.warn( + "The csproj at " + + pathToCsProj + + " failed to load with the following exception " + + e.getMessage() + ); + } } - 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 selector = XPathFactory.newInstance().newXPath(); - var node = (Node) selector.compile("//PackageReference[@Include='CSharpier.MsBuild']").evaluate(xmlDocument, XPathConstants.NODE); - if (node == null) { - continue; - } - - var versionOfMsBuildPackage = node.getAttributes().getNamedItem("Version").getNodeValue(); - if (versionOfMsBuildPackage != null) { - this.logger.debug("Found version " + versionOfMsBuildPackage + " in " + pathToCsProj); - return versionOfMsBuildPackage; - } - } catch (Exception e) { - this.logger.warn("The csproj at " + pathToCsProj + " failed to load with the following exception " + e.getMessage()); - } - } + return null; + } - return null; + private ICSharpierProcess setupCSharpierProcess( + String directory, + String version + ) { + if (version == null || version.equals("")) { + return NullCSharpierProcess.Instance; } - private ICSharpierProcess setupCSharpierProcess(String directory, String version) { - if (version == null || version.equals("")) { - return NullCSharpierProcess.Instance; + try { + if (!this.customPathInstaller.ensureVersionInstalled(version)) { + this.displayFailureMessage(); + return NullCSharpierProcess.Instance; + } + + var customPath = this.customPathInstaller.getPathForVersion(version); + + this.logger.debug( + "Adding new version " + version + " process for " + directory + ); + + // ComparableVersion was unhappy in rider 2023, this code should probably just go away + // but there are still 0.12 and 0.14 downloads happening + var installedVersion = version.split("\\."); + var versionWeCareAbout = Integer.parseInt(installedVersion[1]); + var serverVersion = 29; + + ICSharpierProcess csharpierProcess; + 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" + ); } - try { - if (!this.customPathInstaller.ensureVersionInstalled(version)) { - this.displayFailureMessage(); - return NullCSharpierProcess.Instance; - } - - var customPath = this.customPathInstaller.getPathForVersion(version); - - this.logger.debug("Adding new version " + version + " process for " + directory); - - // ComparableVersion was unhappy in rider 2023, this code should probably just go away - // but there are still 0.12 and 0.14 downloads happening - var installedVersion = version.split("\\."); - var versionWeCareAbout = Integer.parseInt(installedVersion[1]); - var serverVersion = 29; - - ICSharpierProcess csharpierProcess; - 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"); - } - - 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); - - this.warnedForOldVersion = true; - } - - csharpierProcess = new CSharpierProcessSingleFile(customPath, version, this.project); - } - - if (csharpierProcess.getProcessFailedToStart()) { - this.displayFailureMessage(); - } - - return csharpierProcess; - - } catch (Exception ex) { - this.logger.error(ex); + 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); + + this.warnedForOldVersion = true; } - return NullCSharpierProcess.Instance; - } + csharpierProcess = new CSharpierProcessSingleFile( + customPath, + version, + this.project + ); + } - 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); - notification.addAction(new OpenUrlAction("Read More", "https://csharpier.com/docs/EditorsTroubleshooting")); - notification.notify(this.project); - } + if (csharpierProcess.getProcessFailedToStart()) { + this.displayFailureMessage(); + } - @Override - public void dispose() { - this.killRunningProcesses(); + return csharpierProcess; + } catch (Exception ex) { + this.logger.error(ex); } - public void killRunningProcesses() { - for (var key : this.csharpierProcessesByVersion.keySet()) { - this.logger.debug("disposing of process for version " + (key == "" ? "null" : key)); - this.csharpierProcessesByVersion.get(key).dispose(); - } - this.lastWarmedByDirectory.clear(); - this.csharpierVersionByDirectory.clear(); - this.csharpierProcessesByVersion.clear(); + return NullCSharpierProcess.Instance; + } + + 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); + notification.addAction( + new OpenUrlAction( + "Read More", + "https://csharpier.com/docs/EditorsTroubleshooting" + ) + ); + notification.notify(this.project); + } + + @Override + public void dispose() { + this.killRunningProcesses(); + } + + public void killRunningProcesses() { + for (var key : this.csharpierProcessesByVersion.keySet()) { + this.logger.debug( + "disposing of process for version " + (key == "" ? "null" : key) + ); + this.csharpierProcessesByVersion.get(key).dispose(); } + this.lastWarmedByDirectory.clear(); + this.csharpierVersionByDirectory.clear(); + this.csharpierProcessesByVersion.clear(); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessServer.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessServer.java index 04ed4471e..8f7a30835 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessServer.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessServer.java @@ -1,8 +1,9 @@ package com.intellij.csharpier; +import com.google.gson.Gson; import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; - +import com.intellij.openapi.project.Project; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; @@ -10,150 +11,164 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import com.google.gson.Gson; -import com.intellij.openapi.project.Project; - public class CSharpierProcessServer implements ICSharpierProcess2, Disposable { - private final Gson gson = new Gson(); - private final String csharpierPath; - private final DotNetProvider dotNetProvider; - private final String version; - private Logger logger = CSharpierLogger.getInstance(); - private int port; - private Process process = null; - private boolean processFailedToStart; - - public CSharpierProcessServer(String csharpierPath, String version, Project project) { - this.csharpierPath = csharpierPath; - this.dotNetProvider = DotNetProvider.getInstance(project); - this.version = version; - - if (!this.startProcess()) { - this.processFailedToStart = true; - return; - } - - this.logger.debug("Warm CSharpier with initial format"); - // warm by formatting a file twice, the 3rd time is when it gets really fast - this.formatFile("public class ClassName { }", "/Temp/Test.cs"); - this.formatFile("public class ClassName { }", "/Temp/Test.cs"); - } - private boolean startProcess() { - try { - var processBuilder = new ProcessBuilder(this.csharpierPath, "--server"); - processBuilder.redirectErrorStream(true); - processBuilder.environment().put("DOTNET_NOLOGO", "1"); - processBuilder.environment().put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); - this.process = processBuilder.start(); - - var reader = new BufferedReader(new InputStreamReader(this.process.getInputStream())); - - var executor = Executors.newSingleThreadExecutor(); - var future = executor.submit(() -> reader.readLine()); - - String output; - try { - output = future.get(2, TimeUnit.SECONDS); - } catch (TimeoutException e) { - this.logger.warn("Spawning the csharpier server timed out. Formatting cannot occur."); - this.process.destroy(); - return false; - } - - if (!this.process.isAlive()) { - this.logger.warn("Spawning the csharpier server failed because it exited. " + output); - return false; - } - - var portString = output.replace("Started on ", ""); - this.port = Integer.parseInt(portString); - - this.logger.debug("Connecting via port " + portString); - return true; - - } catch (Exception e) { - this.logger.warn("Failed to spawn the needed csharpier server.", e); - return false; - } + private final Gson gson = new Gson(); + private final String csharpierPath; + private final DotNetProvider dotNetProvider; + private final String version; + private Logger logger = CSharpierLogger.getInstance(); + private int port; + private Process process = null; + private boolean processFailedToStart; + + public CSharpierProcessServer( + String csharpierPath, + String version, + Project project + ) { + this.csharpierPath = csharpierPath; + this.dotNetProvider = DotNetProvider.getInstance(project); + this.version = version; + + if (!this.startProcess()) { + this.processFailedToStart = true; + return; } - @Override - public FormatFileResult formatFile(FormatFileParameter parameter) { - if (this.processFailedToStart) { - this.logger.warn("CSharpier process failed to start. Formatting cannot occur."); - return null; - } - - var url = "http://127.0.0.1:" + this.port + "/format"; - - - try { - var url1 = new URL(url); - - var connection = (HttpURLConnection) url1.openConnection(); - - connection.setRequestMethod("POST"); - - connection.setRequestProperty("Content-Type", "application/json; utf-8"); + this.logger.debug("Warm CSharpier with initial format"); + // warm by formatting a file twice, the 3rd time is when it gets really fast + this.formatFile("public class ClassName { }", "/Temp/Test.cs"); + this.formatFile("public class ClassName { }", "/Temp/Test.cs"); + } + + private boolean startProcess() { + try { + var processBuilder = new ProcessBuilder(this.csharpierPath, "--server"); + processBuilder.redirectErrorStream(true); + processBuilder.environment().put("DOTNET_NOLOGO", "1"); + processBuilder + .environment() + .put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); + this.process = processBuilder.start(); + + var reader = new BufferedReader( + new InputStreamReader(this.process.getInputStream()) + ); + + var executor = Executors.newSingleThreadExecutor(); + var future = executor.submit(() -> reader.readLine()); + + String output; + try { + output = future.get(2, TimeUnit.SECONDS); + } catch (TimeoutException e) { + this.logger.warn( + "Spawning the csharpier server timed out. Formatting cannot occur." + ); + this.process.destroy(); + return false; + } + + if (!this.process.isAlive()) { + this.logger.warn( + "Spawning the csharpier server failed because it exited. " + output + ); + return false; + } + + var portString = output.replace("Started on ", ""); + this.port = Integer.parseInt(portString); + + this.logger.debug("Connecting via port " + portString); + return true; + } catch (Exception e) { + this.logger.warn("Failed to spawn the needed csharpier server.", e); + return false; + } + } + + @Override + public FormatFileResult formatFile(FormatFileParameter parameter) { + if (this.processFailedToStart) { + this.logger.warn( + "CSharpier process failed to start. Formatting cannot occur." + ); + return null; + } - connection.setConnectTimeout(2000); - connection.setDoOutput(true); - connection.setDoInput(true); + var url = "http://127.0.0.1:" + this.port + "/format"; - var outputStream = connection.getOutputStream(); - var writer = new OutputStreamWriter(outputStream, "UTF-8"); - writer.write(this.gson.toJson(parameter)); - writer.flush(); - writer.close(); - outputStream.close(); + try { + var url1 = new URL(url); - var responseCode = connection.getResponseCode(); - if (responseCode != 200) { - this.logger.warn("Csharpier server returned non-200 status code of " + responseCode); - connection.disconnect(); - return null; - } + var connection = (HttpURLConnection) url1.openConnection(); - InputStreamReader reader = new InputStreamReader(connection.getInputStream(), "UTF-8"); - var result = gson.fromJson(reader, FormatFileResult.class); - reader.close(); + connection.setRequestMethod("POST"); - connection.disconnect(); + connection.setRequestProperty("Content-Type", "application/json; utf-8"); - return result; + connection.setConnectTimeout(2000); + connection.setDoOutput(true); + connection.setDoInput(true); - } catch (Exception e) { - this.logger.warn("Failed posting to the csharpier server.", e); - } + var outputStream = connection.getOutputStream(); + var writer = new OutputStreamWriter(outputStream, "UTF-8"); + writer.write(this.gson.toJson(parameter)); + writer.flush(); + writer.close(); + outputStream.close(); + var responseCode = connection.getResponseCode(); + if (responseCode != 200) { + this.logger.warn( + "Csharpier server returned non-200 status code of " + responseCode + ); + connection.disconnect(); return null; - } - - @Override - public String getVersion() { - return this.version; - } + } - @Override - public boolean getProcessFailedToStart() { - return this.processFailedToStart; - } + InputStreamReader reader = new InputStreamReader( + connection.getInputStream(), + "UTF-8" + ); + var result = gson.fromJson(reader, FormatFileResult.class); + reader.close(); - @Override - public String formatFile(String content, String fileName) { - var parameter = new FormatFileParameter(); - parameter.fileName = fileName; - parameter.fileContents = content; + connection.disconnect(); - var result = this.formatFile(parameter); - return result == null ? null : result.formattedFile; + return result; + } catch (Exception e) { + this.logger.warn("Failed posting to the csharpier server.", e); } - @Override - public void dispose() { - if (this.process != null) { - this.process.destroy(); - } + return null; + } + + @Override + public String getVersion() { + return this.version; + } + + @Override + public boolean getProcessFailedToStart() { + return this.processFailedToStart; + } + + @Override + public String formatFile(String content, String fileName) { + var parameter = new FormatFileParameter(); + parameter.fileName = fileName; + parameter.fileContents = content; + + var result = this.formatFile(parameter); + return result == null ? null : result.formattedFile; + } + + @Override + public void dispose() { + if (this.process != null) { + this.process.destroy(); } + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessSingleFile.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessSingleFile.java index c09d1ffc7..11330485b 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessSingleFile.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierProcessSingleFile.java @@ -2,73 +2,84 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; - import java.io.BufferedReader; import java.io.InputStreamReader; public class CSharpierProcessSingleFile implements ICSharpierProcess { - private final DotNetProvider dotNetProvider; - private final Logger logger = CSharpierLogger.getInstance(); - private final String csharpierPath; - private final String version; - - public CSharpierProcessSingleFile(String csharpierPath, String version, Project project) { - this.csharpierPath = csharpierPath; - this.dotNetProvider = DotNetProvider.getInstance(project); - this.version = version; - } - - @Override - public String getVersion() { - return this.version; - } - - @Override - public boolean getProcessFailedToStart() { - return false; - } - - @Override - public String formatFile(String content, String fileName) { - try { - this.logger.debug("Running " + this.csharpierPath + " --write-stdout"); - var processBuilder = new ProcessBuilder(this.csharpierPath, "--write-stdout"); - processBuilder.environment().put("DOTNET_NOLOGO", "1"); - processBuilder.environment().put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); - processBuilder.redirectErrorStream(true); - var process = processBuilder.start(); - - var stdin = process.getOutputStream(); - var stdOut = new BufferedReader(new InputStreamReader(process.getInputStream())); - - stdin.write(content.getBytes()); - stdin.close(); - - var output = new StringBuilder(); - - var nextCharacter = stdOut.read(); - while (nextCharacter != -1) { - output.append((char) nextCharacter); - nextCharacter = stdOut.read(); - } - - var result = output.toString(); - - if (process.exitValue() == 0 && !result.contains("Failed to compile so was not formatted.")) { - return result; - } else { - this.logger.error(result); - } - } catch (Exception e) { - this.logger.error("error", e); - } - - return ""; + private final DotNetProvider dotNetProvider; + private final Logger logger = CSharpierLogger.getInstance(); + private final String csharpierPath; + private final String version; + + public CSharpierProcessSingleFile( + String csharpierPath, + String version, + Project project + ) { + this.csharpierPath = csharpierPath; + this.dotNetProvider = DotNetProvider.getInstance(project); + this.version = version; + } + + @Override + public String getVersion() { + return this.version; + } + + @Override + public boolean getProcessFailedToStart() { + return false; + } + + @Override + public String formatFile(String content, String fileName) { + try { + this.logger.debug("Running " + this.csharpierPath + " --write-stdout"); + var processBuilder = new ProcessBuilder( + this.csharpierPath, + "--write-stdout" + ); + processBuilder.environment().put("DOTNET_NOLOGO", "1"); + processBuilder + .environment() + .put("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); + processBuilder.redirectErrorStream(true); + var process = processBuilder.start(); + + var stdin = process.getOutputStream(); + var stdOut = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + + stdin.write(content.getBytes()); + stdin.close(); + + var output = new StringBuilder(); + + var nextCharacter = stdOut.read(); + while (nextCharacter != -1) { + output.append((char) nextCharacter); + nextCharacter = stdOut.read(); + } + + var result = output.toString(); + + if ( + process.exitValue() == 0 && + !result.contains("Failed to compile so was not formatted.") + ) { + return result; + } else { + this.logger.error(result); + } + } catch (Exception e) { + this.logger.error("error", e); } - @Override - public void dispose() { + return ""; + } - } + @Override + public void dispose() {} } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettings.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettings.java index a339250ab..234e33f3b 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettings.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettings.java @@ -8,54 +8,54 @@ import org.jetbrains.annotations.NotNull; @State( - name = "com.intellij.csharpier", - storages = @Storage("CSharpierPlugin.xml") + name = "com.intellij.csharpier", + storages = @Storage("CSharpierPlugin.xml") ) public class CSharpierSettings - implements PersistentStateComponent { + implements PersistentStateComponent { - @NotNull - static CSharpierSettings getInstance(@NotNull Project project) { - return project.getService(CSharpierSettings.class); - } + @NotNull + static CSharpierSettings getInstance(@NotNull Project project) { + return project.getService(CSharpierSettings.class); + } - private boolean runOnSave; + private boolean runOnSave; - public boolean getRunOnSave() { - return this.runOnSave; - } + public boolean getRunOnSave() { + return this.runOnSave; + } - public void setRunOnSave(boolean value) { - this.runOnSave = value; - } + public void setRunOnSave(boolean value) { + this.runOnSave = value; + } - private String customPath; + private String customPath; - public String getCustomPath() { - return this.customPath; - } + public String getCustomPath() { + return this.customPath; + } - public void setCustomPath(String value) { - this.customPath = value; - } + public void setCustomPath(String value) { + this.customPath = value; + } - private boolean disableCSharpierServer; + private boolean disableCSharpierServer; - public boolean getDisableCSharpierServer() { - return this.disableCSharpierServer; - } + public boolean getDisableCSharpierServer() { + return this.disableCSharpierServer; + } - public void setDisableCSharpierServer(boolean value) { - this.disableCSharpierServer = value; - } + public void setDisableCSharpierServer(boolean value) { + this.disableCSharpierServer = value; + } - @Override - public CSharpierSettings getState() { - return this; - } + @Override + public CSharpierSettings getState() { + return this; + } - @Override - public void loadState(@NotNull CSharpierSettings state) { - XmlSerializerUtil.copyBean(state, this); - } + @Override + public void loadState(@NotNull CSharpierSettings state) { + XmlSerializerUtil.copyBean(state, this); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettingsComponent.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettingsComponent.java index fc9bf1f2e..d1f48d56b 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettingsComponent.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CSharpierSettingsComponent.java @@ -7,98 +7,113 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; +import java.awt.*; +import javax.swing.*; +import javax.swing.border.EmptyBorder; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; - - public class CSharpierSettingsComponent implements SearchableConfigurable { - private final Project project; - private JBCheckBox runOnSaveCheckBox = new JBCheckBox("Run on Save"); - private JBCheckBox disableCSharpierServerCheckBox = new JBCheckBox("Disable CSharpier Server"); - private JBTextField customPathTextField = new JBTextField(); - - public CSharpierSettingsComponent(@NotNull Project project) { - this.project = project; - } - - @NotNull - @Override - public String getId() { - return "CSharpier"; - } - - @Nls - @Override - public String getDisplayName() { - return "CSharpier"; - } - - private JComponent createSectionHeader(String label) { - var panel = new JPanel(new GridBagLayout()); - var gbc = new GridBagConstraints(); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.anchor = GridBagConstraints.WEST; - - panel.add(new JBLabel(label), gbc); - - gbc = new GridBagConstraints(); - gbc.gridy = 0; - gbc.gridx = 1; - gbc.weightx = 1.0; - gbc.insets = new Insets(1, 6, 0, 0); - gbc.fill = GridBagConstraints.HORIZONTAL; - - var separator = new JSeparator(SwingConstants.HORIZONTAL); - panel.add(separator, gbc); - - return panel; - } - - @Override - public @Nullable JComponent createComponent() { - var leftIndent = 20; - var topInset = 10; - - return FormBuilder.createFormBuilder() - .addComponent(createSectionHeader("General Settings")) - .setFormLeftIndent(leftIndent) - .addComponent(this.runOnSaveCheckBox, topInset) - .setFormLeftIndent(0) - .addComponent(createSectionHeader("Developer Settings"), 20) - .setFormLeftIndent(leftIndent) - .addLabeledComponent(new JBLabel("Directory of custom dotnet-csharpier:"), this.customPathTextField, topInset, false) - .addComponent(this.disableCSharpierServerCheckBox, topInset) - .addComponentFillVertically(new JPanel(), 0) - .getPanel(); - } - - @Override - public boolean isModified() { - return CSharpierSettings.getInstance(this.project).getRunOnSave() != this.runOnSaveCheckBox.isSelected() - || CSharpierSettings.getInstance(this.project).getCustomPath() != this.customPathTextField.getText() - || CSharpierSettings.getInstance(this.project).getDisableCSharpierServer() != this.disableCSharpierServerCheckBox.isSelected(); - } - - @Override - public void apply() { - var settings = CSharpierSettings.getInstance(this.project); - - settings.setRunOnSave(this.runOnSaveCheckBox.isSelected()); - settings.setCustomPath(this.customPathTextField.getText()); - settings.setDisableCSharpierServer(this.disableCSharpierServerCheckBox.isSelected()); - } - - @Override - public void reset() { - var settings = CSharpierSettings.getInstance(this.project); - this.runOnSaveCheckBox.setSelected(settings.getRunOnSave()); - this.customPathTextField.setText(settings.getCustomPath()); - this.disableCSharpierServerCheckBox.setSelected(settings.getDisableCSharpierServer()); - } + + private final Project project; + private JBCheckBox runOnSaveCheckBox = new JBCheckBox("Run on Save"); + private JBCheckBox disableCSharpierServerCheckBox = new JBCheckBox( + "Disable CSharpier Server" + ); + private JBTextField customPathTextField = new JBTextField(); + + public CSharpierSettingsComponent(@NotNull Project project) { + this.project = project; + } + + @NotNull + @Override + public String getId() { + return "CSharpier"; + } + + @Nls + @Override + public String getDisplayName() { + return "CSharpier"; + } + + private JComponent createSectionHeader(String label) { + var panel = new JPanel(new GridBagLayout()); + var gbc = new GridBagConstraints(); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.anchor = GridBagConstraints.WEST; + + panel.add(new JBLabel(label), gbc); + + gbc = new GridBagConstraints(); + gbc.gridy = 0; + gbc.gridx = 1; + gbc.weightx = 1.0; + gbc.insets = new Insets(1, 6, 0, 0); + gbc.fill = GridBagConstraints.HORIZONTAL; + + var separator = new JSeparator(SwingConstants.HORIZONTAL); + panel.add(separator, gbc); + + return panel; + } + + @Override + public @Nullable JComponent createComponent() { + var leftIndent = 20; + var topInset = 10; + + return FormBuilder.createFormBuilder() + .addComponent(createSectionHeader("General Settings")) + .setFormLeftIndent(leftIndent) + .addComponent(this.runOnSaveCheckBox, topInset) + .setFormLeftIndent(0) + .addComponent(createSectionHeader("Developer Settings"), 20) + .setFormLeftIndent(leftIndent) + .addLabeledComponent( + new JBLabel("Directory of custom dotnet-csharpier:"), + this.customPathTextField, + topInset, + false + ) + .addComponent(this.disableCSharpierServerCheckBox, topInset) + .addComponentFillVertically(new JPanel(), 0) + .getPanel(); + } + + @Override + public boolean isModified() { + return ( + CSharpierSettings.getInstance(this.project).getRunOnSave() != + this.runOnSaveCheckBox.isSelected() || + CSharpierSettings.getInstance(this.project).getCustomPath() != + this.customPathTextField.getText() || + CSharpierSettings.getInstance(this.project).getDisableCSharpierServer() != + this.disableCSharpierServerCheckBox.isSelected() + ); + } + + @Override + public void apply() { + var settings = CSharpierSettings.getInstance(this.project); + + settings.setRunOnSave(this.runOnSaveCheckBox.isSelected()); + settings.setCustomPath(this.customPathTextField.getText()); + settings.setDisableCSharpierServer( + this.disableCSharpierServerCheckBox.isSelected() + ); + } + + @Override + public void reset() { + var settings = CSharpierSettings.getInstance(this.project); + this.runOnSaveCheckBox.setSelected(settings.getRunOnSave()); + this.customPathTextField.setText(settings.getCustomPath()); + this.disableCSharpierServerCheckBox.setSelected( + settings.getDisableCSharpierServer() + ); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CustomPathInstaller.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CustomPathInstaller.java index 02e78b677..057f5e659 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CustomPathInstaller.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/CustomPathInstaller.java @@ -2,105 +2,138 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import org.apache.commons.lang.SystemUtils; - import java.io.File; import java.nio.file.Path; import java.util.List; import java.util.Map; import java.util.regex.Pattern; +import org.apache.commons.lang.SystemUtils; public class CustomPathInstaller { - private final DotNetProvider dotNetProvider; - Logger logger = CSharpierLogger.getInstance(); - String customPath; - - public CustomPathInstaller(Project project) { - this.customPath = CSharpierSettings.getInstance(project).getCustomPath(); - this.dotNetProvider = DotNetProvider.getInstance(project); - } - - public boolean ensureVersionInstalled(String version) throws Exception { - if (version == null || version.equals("")) { - return true; - } - if (this.customPath != "" && this.customPath != null) { - this.logger.debug("Using csharpier at a custom path of " + this.customPath); - return true; - } - - var pathToDirectoryForVersion = getDirectoryForVersion(version); - var directoryForVersion = new File(pathToDirectoryForVersion); - if (directoryForVersion.exists()) { - if (this.validateInstall(pathToDirectoryForVersion, version)) { - return true; - } - - this.logger.debug( - "Removing directory at " + pathToDirectoryForVersion + " because it appears to be corrupted" - ); - deleteDirectory(directoryForVersion); - } - - var command = List.of("tool", "install", "csharpier", "--version", version, "--tool-path", pathToDirectoryForVersion); - this.dotNetProvider.execDotNet(command, null); - - return this.validateInstall(pathToDirectoryForVersion, version); - } - private boolean validateInstall(String pathToDirectoryForVersion, String version) { - try { - var env = Map.of("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); + private final DotNetProvider dotNetProvider; + Logger logger = CSharpierLogger.getInstance(); + String customPath; - var command = List.of(this.getPathForVersion(version), "--version" ); - var output = ProcessHelper.executeCommand(command, env, new File(pathToDirectoryForVersion)).trim(); + public CustomPathInstaller(Project project) { + this.customPath = CSharpierSettings.getInstance(project).getCustomPath(); + this.dotNetProvider = DotNetProvider.getInstance(project); + } - this.logger.debug(this.getPathForVersion(version) +"--version output: " + version); - var versionWithoutHash = output.split(Pattern.quote("+"))[0]; - this.logger.debug("Using " + versionWithoutHash + " as the version number."); - - if (versionWithoutHash.equals(version)) - { - this.logger.debug("CSharpier at " + pathToDirectoryForVersion + " already exists"); - return true; - } - } - catch (Exception ex) { - this.logger.warn("Exception while running 'dotnet csharpier --version' in " + pathToDirectoryForVersion, ex); - } + public boolean ensureVersionInstalled(String version) throws Exception { + if (version == null || version.equals("")) { + return true; + } + if (this.customPath != "" && this.customPath != null) { + this.logger.debug( + "Using csharpier at a custom path of " + this.customPath + ); + return true; + } - return false; + var pathToDirectoryForVersion = getDirectoryForVersion(version); + var directoryForVersion = new File(pathToDirectoryForVersion); + if (directoryForVersion.exists()) { + if (this.validateInstall(pathToDirectoryForVersion, version)) { + return true; + } + + this.logger.debug( + "Removing directory at " + + pathToDirectoryForVersion + + " because it appears to be corrupted" + ); + deleteDirectory(directoryForVersion); } - private boolean deleteDirectory(File directoryToBeDeleted) { - File[] allContents = directoryToBeDeleted.listFiles(); - if (allContents != null) { - for (File file : allContents) { - deleteDirectory(file); - } - } - return directoryToBeDeleted.delete(); + var command = List.of( + "tool", + "install", + "csharpier", + "--version", + version, + "--tool-path", + pathToDirectoryForVersion + ); + this.dotNetProvider.execDotNet(command, null); + + return this.validateInstall(pathToDirectoryForVersion, version); + } + + private boolean validateInstall( + String pathToDirectoryForVersion, + String version + ) { + try { + var env = Map.of("DOTNET_ROOT", this.dotNetProvider.getDotNetRoot()); + + var command = List.of(this.getPathForVersion(version), "--version"); + var output = ProcessHelper.executeCommand( + command, + env, + new File(pathToDirectoryForVersion) + ).trim(); + + this.logger.debug( + this.getPathForVersion(version) + "--version output: " + version + ); + var versionWithoutHash = output.split(Pattern.quote("+"))[0]; + this.logger.debug( + "Using " + versionWithoutHash + " as the version number." + ); + + if (versionWithoutHash.equals(version)) { + this.logger.debug( + "CSharpier at " + pathToDirectoryForVersion + " already exists" + ); + return true; + } + } catch (Exception ex) { + this.logger.warn( + "Exception while running 'dotnet csharpier --version' in " + + pathToDirectoryForVersion, + ex + ); } - private String getDirectoryForVersion(String version) throws Exception { - if (this.customPath != "" && this.customPath != null) { - return this.customPath; - } + return false; + } - if (SystemUtils.IS_OS_WINDOWS) { - return Path.of(System.getenv("LOCALAPPDATA"), "CSharpier", version).toString(); - } + private boolean deleteDirectory(File directoryToBeDeleted) { + File[] allContents = directoryToBeDeleted.listFiles(); + if (allContents != null) { + for (File file : allContents) { + deleteDirectory(file); + } + } + return directoryToBeDeleted.delete(); + } - var userHome = System.getProperty("user.home"); - if (userHome == null) { - throw new Exception("There was no user.home property and the OS was not windows"); - } + private String getDirectoryForVersion(String version) throws Exception { + if (this.customPath != "" && this.customPath != null) { + return this.customPath; + } - return Path.of(userHome, ".cache/csharpier", version).toString(); + if (SystemUtils.IS_OS_WINDOWS) { + return Path.of( + System.getenv("LOCALAPPDATA"), + "CSharpier", + version + ).toString(); } - public String getPathForVersion(String version) throws Exception { - var path = Path.of(getDirectoryForVersion(version), "dotnet-csharpier"); - return path.toString(); + var userHome = System.getProperty("user.home"); + if (userHome == null) { + throw new Exception( + "There was no user.home property and the OS was not windows" + ); } + + return Path.of(userHome, ".cache/csharpier", version).toString(); + } + + public String getPathForVersion(String version) throws Exception { + var path = Path.of(getDirectoryForVersion(version), "dotnet-csharpier"); + return path.toString(); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/DotNetProvider.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/DotNetProvider.java index 26ebb0de3..3ec6cb3a2 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/DotNetProvider.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/DotNetProvider.java @@ -5,73 +5,89 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.jetbrains.rider.runtime.RiderDotNetActiveRuntimeHost; -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.nio.file.Paths; import java.util.*; +import org.jetbrains.annotations.NotNull; public class DotNetProvider { - private final Logger logger = CSharpierLogger.getInstance(); - private final Project project; - private String dotNetRoot; - private String cliExePath; - - public DotNetProvider(@NotNull Project project) { - this.project = project; - } - - static DotNetProvider getInstance(@NotNull Project project) { - return project.getService(DotNetProvider.class); - } - - void initialize() { - var foundDotNet = this.findDotNet(); - if (!foundDotNet) { - var title = "CSharpier unable to run dotnet commands"; - var message = "CSharpier was unable to determine how to run dotnet commands. Ensure that '.NET CLI executable path' is set properly in your settings or dotnet is available on PATH and restart."; - var notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier").createNotification(title, message, NotificationType.WARNING); - notification.notify(this.project); - } - } - - private boolean findDotNet() { - try { - var dotNetCoreRuntime = RiderDotNetActiveRuntimeHost.Companion.getInstance(project).getDotNetCoreRuntime().getValue(); - - if (dotNetCoreRuntime != null && dotNetCoreRuntime.getCliExePath() != null) { - this.logger.debug("Using dotnet found from RiderDotNetActiveRuntimeHost at " + dotNetCoreRuntime.getCliExePath()); - this.cliExePath = dotNetCoreRuntime.getCliExePath(); - } else { - return false; - } - this.dotNetRoot = Paths.get(this.cliExePath).getParent().toString(); - - return true; - } catch (Exception ex) { - logger.error(ex); - - return false; - } + private final Logger logger = CSharpierLogger.getInstance(); + private final Project project; + private String dotNetRoot; + private String cliExePath; + + public DotNetProvider(@NotNull Project project) { + this.project = project; + } + + static DotNetProvider getInstance(@NotNull Project project) { + return project.getService(DotNetProvider.class); + } + + void initialize() { + var foundDotNet = this.findDotNet(); + if (!foundDotNet) { + var title = "CSharpier unable to run dotnet commands"; + var message = + "CSharpier was unable to determine how to run dotnet commands. Ensure that '.NET CLI executable path' is set properly in your settings or dotnet is available on PATH and restart."; + var notification = NotificationGroupManager.getInstance() + .getNotificationGroup("CSharpier") + .createNotification(title, message, NotificationType.WARNING); + notification.notify(this.project); } - - public String execDotNet(List command, File workingDirectory) { - var commands = new ArrayList<>(command); - commands.add(0, this.cliExePath); - - var env = Map.of("DOTNET_NOLOGO", "1", "DOTNET_CLI_TELEMETRY_OPTOUT", "1", "DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1"); - - return ProcessHelper.executeCommand(commands, env, workingDirectory); + } + + private boolean findDotNet() { + try { + var dotNetCoreRuntime = + RiderDotNetActiveRuntimeHost.Companion.getInstance(project) + .getDotNetCoreRuntime() + .getValue(); + + if ( + dotNetCoreRuntime != null && dotNetCoreRuntime.getCliExePath() != null + ) { + this.logger.debug( + "Using dotnet found from RiderDotNetActiveRuntimeHost at " + + dotNetCoreRuntime.getCliExePath() + ); + this.cliExePath = dotNetCoreRuntime.getCliExePath(); + } else { + return false; + } + + this.dotNetRoot = Paths.get(this.cliExePath).getParent().toString(); + + return true; + } catch (Exception ex) { + logger.error(ex); + + return false; } - - public String getDotNetRoot() { - return this.dotNetRoot; - } - - public boolean foundDotNet() { - return this.cliExePath != null; - } - - + } + + public String execDotNet(List command, File workingDirectory) { + var commands = new ArrayList<>(command); + commands.add(0, this.cliExePath); + + var env = Map.of( + "DOTNET_NOLOGO", + "1", + "DOTNET_CLI_TELEMETRY_OPTOUT", + "1", + "DOTNET_SKIP_FIRST_TIME_EXPERIENCE", + "1" + ); + + return ProcessHelper.executeCommand(commands, env, workingDirectory); + } + + public String getDotNetRoot() { + return this.dotNetRoot; + } + + public boolean foundDotNet() { + return this.cliExePath != null; + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FormattingService.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FormattingService.java index f561bdbeb..7ea243c15 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FormattingService.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FormattingService.java @@ -6,99 +6,135 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.psi.PsiDocumentManager; -import org.jetbrains.annotations.NotNull; - import java.time.Duration; import java.time.Instant; import java.util.Collections; +import org.jetbrains.annotations.NotNull; public class FormattingService { - Logger logger = CSharpierLogger.getInstance(); - @NotNull - static FormattingService getInstance(@NotNull Project project) { - return project.getService(FormattingService.class); - } + Logger logger = CSharpierLogger.getInstance(); - public void format(@NotNull Document document, @NotNull Project project) { - var psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); - if (psiFile == null) { - return; - } - - if (!(psiFile.getLanguage().getID().equals("C#") - // while testing in intellij it doesn't know about c# - || (psiFile.getLanguage().getID().equals("TEXT") && psiFile.getName().endsWith(".cs"))) - ) { - this.logger.debug("Skipping formatting because language was " + psiFile.getLanguage().getDisplayName()); - return; - } - - var virtualFile = psiFile.getVirtualFile(); - if (ReadonlyStatusHandler.getInstance(project) - .ensureFilesWritable(Collections.singletonList(virtualFile)) - .hasReadonlyFiles() - ) { - return; - } + @NotNull + static FormattingService getInstance(@NotNull Project project) { + return project.getService(FormattingService.class); + } - var filePath = virtualFile.getPath(); - - if (!this.getCanFormat(filePath, project)) { - return; - } - - var currentDocumentText = document.getText(); - - var csharpierProcess = CSharpierProcessProvider.getInstance(project).getProcessFor(filePath); - this.logger.info("Formatting started for " + filePath + " using CSharpier " + csharpierProcess.getVersion()); - var start = Instant.now(); - if (csharpierProcess instanceof ICSharpierProcess2) { - var csharpierProcess2 = (ICSharpierProcess2) csharpierProcess; - var parameter = new FormatFileParameter(); - parameter.fileContents = currentDocumentText; - parameter.fileName = filePath; - var result = csharpierProcess2.formatFile(parameter); - - var end = Instant.now(); - this.logger.info("Formatted in " + (Duration.between(start, end).toMillis()) + "ms"); - - if (result != null) { - switch (result.status) { - case Formatted -> updateText(document, project, result.formattedFile, currentDocumentText); - case Ignored -> this.logger.info("File is ignored by csharpier cli."); - case Failed -> this.logger.warn("CSharpier cli failed to format the file and returned the following error: " + result.errorMessage); - } - } - } - else { - var result = csharpierProcess.formatFile(currentDocumentText, filePath); + public void format(@NotNull Document document, @NotNull Project project) { + var psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); + if (psiFile == null) { + return; + } - var end = Instant.now(); - this.logger.info("Formatted in " + (Duration.between(start, end).toMillis()) + "ms"); + if ( + !(psiFile.getLanguage().getID().equals("C#") || + // while testing in intellij it doesn't know about c# + (psiFile.getLanguage().getID().equals("TEXT") && + psiFile.getName().endsWith(".cs"))) + ) { + this.logger.debug( + "Skipping formatting because language was " + + psiFile.getLanguage().getDisplayName() + ); + return; + } - if (result.length() == 0 || currentDocumentText.equals(result)) { - this.logger.debug("Skipping write because " + (result.length() == 0 ? "result is empty" : "current document equals result")); - } else { - updateText(document, project, result, currentDocumentText); - } - } + var virtualFile = psiFile.getVirtualFile(); + if ( + ReadonlyStatusHandler.getInstance(project) + .ensureFilesWritable(Collections.singletonList(virtualFile)) + .hasReadonlyFiles() + ) { + return; } - private static void updateText(@NotNull Document document, @NotNull Project project, String result, String currentDocumentText) { - WriteCommandAction.runWriteCommandAction(project, () -> { - var finalResult = result; - if (result.indexOf('\r') >= 0) { - // rider always wants \n in files so remove any \r - finalResult = result.replaceAll("\\r", ""); - } + var filePath = virtualFile.getPath(); - document.replaceString(0, currentDocumentText.length(), finalResult); - }); + if (!this.getCanFormat(filePath, project)) { + return; } - public boolean getCanFormat(String filePath, Project project) { - var cSharpierProcess = CSharpierProcessProvider.getInstance(project).getProcessFor(filePath); - return !NullCSharpierProcess.Instance.equals(cSharpierProcess); + var currentDocumentText = document.getText(); + + var csharpierProcess = CSharpierProcessProvider.getInstance( + project + ).getProcessFor(filePath); + this.logger.info( + "Formatting started for " + + filePath + + " using CSharpier " + + csharpierProcess.getVersion() + ); + var start = Instant.now(); + if (csharpierProcess instanceof ICSharpierProcess2) { + var csharpierProcess2 = (ICSharpierProcess2) csharpierProcess; + var parameter = new FormatFileParameter(); + parameter.fileContents = currentDocumentText; + parameter.fileName = filePath; + var result = csharpierProcess2.formatFile(parameter); + + var end = Instant.now(); + this.logger.info( + "Formatted in " + (Duration.between(start, end).toMillis()) + "ms" + ); + + if (result != null) { + switch (result.status) { + case Formatted -> updateText( + document, + project, + result.formattedFile, + currentDocumentText + ); + case Ignored -> this.logger.info("File is ignored by csharpier cli."); + case Failed -> this.logger.warn( + "CSharpier cli failed to format the file and returned the following error: " + + result.errorMessage + ); + } + } + } else { + var result = csharpierProcess.formatFile(currentDocumentText, filePath); + + var end = Instant.now(); + this.logger.info( + "Formatted in " + (Duration.between(start, end).toMillis()) + "ms" + ); + + if (result.length() == 0 || currentDocumentText.equals(result)) { + this.logger.debug( + "Skipping write because " + + (result.length() == 0 + ? "result is empty" + : "current document equals result") + ); + } else { + updateText(document, project, result, currentDocumentText); + } } + } + + private static void updateText( + @NotNull Document document, + @NotNull Project project, + String result, + String currentDocumentText + ) { + WriteCommandAction.runWriteCommandAction(project, () -> { + var finalResult = result; + if (result.indexOf('\r') >= 0) { + // rider always wants \n in files so remove any \r + finalResult = result.replaceAll("\\r", ""); + } + + document.replaceString(0, currentDocumentText.length(), finalResult); + }); + } + + public boolean getCanFormat(String filePath, Project project) { + var cSharpierProcess = CSharpierProcessProvider.getInstance( + project + ).getProcessFor(filePath); + return !NullCSharpierProcess.Instance.equals(cSharpierProcess); + } } 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 index f78109e11..3a886b2c8 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/FunctionRunner.java @@ -3,13 +3,14 @@ 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; + + 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/ICSharpierProcess.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ICSharpierProcess.java index d7cff4eb2..e859fbc1a 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ICSharpierProcess.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ICSharpierProcess.java @@ -1,31 +1,32 @@ package com.intellij.csharpier; interface ICSharpierProcess { - String getVersion(); - boolean getProcessFailedToStart(); - String formatFile(String content, String fileName); - void dispose(); + String getVersion(); + boolean getProcessFailedToStart(); + String formatFile(String content, String fileName); + void dispose(); } interface ICSharpierProcess2 extends ICSharpierProcess { - FormatFileResult formatFile(FormatFileParameter parameter); - void dispose(); + FormatFileResult formatFile(FormatFileParameter parameter); + void dispose(); } class FormatFileParameter { - public String fileContents; - public String fileName; + + public String fileContents; + public String fileName; } class FormatFileResult { - public String formattedFile; - public Status status; - public String errorMessage; + + public String formattedFile; + public Status status; + public String errorMessage; } -enum Status -{ - Formatted, - Ignored, - Failed +enum Status { + Formatted, + Ignored, + Failed, } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/IProcessKiller.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/IProcessKiller.java index d725f5f30..e761b2217 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/IProcessKiller.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/IProcessKiller.java @@ -1,5 +1,5 @@ package com.intellij.csharpier; public interface IProcessKiller { - public void killRunningProcesses(); + public void killRunningProcesses(); } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallGlobalAction.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallGlobalAction.java index 1c2393f21..8d3c15d04 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallGlobalAction.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallGlobalAction.java @@ -5,33 +5,34 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; +import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - public class InstallGlobalAction extends NotificationAction { - private final IProcessKiller processKiller; - private final DotNetProvider dotNetProvider; - - public InstallGlobalAction( - @Nullable @NlsContexts.NotificationContent String text, - IProcessKiller processKiller, - Project project - ) { - super(text); - this.processKiller = processKiller; - this.dotNetProvider = DotNetProvider.getInstance(project); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { - var command = List.of("tool", "install", "-g", "csharpier"); - this.dotNetProvider.execDotNet(command, null); - this.processKiller.killRunningProcesses(); - - notification.expire(); - } + private final IProcessKiller processKiller; + private final DotNetProvider dotNetProvider; + + public InstallGlobalAction( + @Nullable @NlsContexts.NotificationContent String text, + IProcessKiller processKiller, + Project project + ) { + super(text); + this.processKiller = processKiller; + this.dotNetProvider = DotNetProvider.getInstance(project); + } + + @Override + public void actionPerformed( + @NotNull AnActionEvent e, + @NotNull Notification notification + ) { + var command = List.of("tool", "install", "-g", "csharpier"); + this.dotNetProvider.execDotNet(command, null); + this.processKiller.killRunningProcesses(); + + notification.expire(); + } } - diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallLocalAction.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallLocalAction.java index 900f537db..39ab9002b 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallLocalAction.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/InstallLocalAction.java @@ -6,44 +6,49 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import java.io.File; import java.nio.file.Path; import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class InstallLocalAction extends NotificationAction { - private final DotNetProvider dotNetProvider; - private Logger logger = CSharpierLogger.getInstance(); - private final IProcessKiller processKiller; - private final String projectPath; - public InstallLocalAction( - @Nullable @NlsContexts.NotificationContent String text, - IProcessKiller processKiller, - Project project - ) { - super(text); - this.projectPath = project.getBasePath(); - this.processKiller = processKiller; - this.dotNetProvider = DotNetProvider.getInstance(project); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { - var manifestPath = Path.of(this.projectPath, ".config/dotnet-tools.json").toString(); - this.logger.info("Installing csharpier in " + manifestPath); - if (!new File(manifestPath).exists()) - { - var command = List.of("new", "tool-manifest"); - this.dotNetProvider.execDotNet(command, new File(this.projectPath)); - } + private final DotNetProvider dotNetProvider; + private Logger logger = CSharpierLogger.getInstance(); + private final IProcessKiller processKiller; + private final String projectPath; - var command2 = List.of("tool", "install", "csharpier"); - this.dotNetProvider.execDotNet(command2, new File(this.projectPath)); - this.processKiller.killRunningProcesses(); + public InstallLocalAction( + @Nullable @NlsContexts.NotificationContent String text, + IProcessKiller processKiller, + Project project + ) { + super(text); + this.projectPath = project.getBasePath(); + this.processKiller = processKiller; + this.dotNetProvider = DotNetProvider.getInstance(project); + } - notification.expire(); + @Override + public void actionPerformed( + @NotNull AnActionEvent e, + @NotNull Notification notification + ) { + var manifestPath = Path.of( + this.projectPath, + ".config/dotnet-tools.json" + ).toString(); + this.logger.info("Installing csharpier in " + manifestPath); + if (!new File(manifestPath).exists()) { + var command = List.of("new", "tool-manifest"); + this.dotNetProvider.execDotNet(command, new File(this.projectPath)); } + + var command2 = List.of("tool", "install", "csharpier"); + this.dotNetProvider.execDotNet(command2, new File(this.projectPath)); + this.processKiller.killRunningProcesses(); + + notification.expire(); + } } 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 d4ee3885d..1bed1f3c9 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 @@ -9,49 +9,75 @@ import org.jetbrains.annotations.NotNull; public class InstallerService { - private final Logger logger = CSharpierLogger.getInstance(); - private final Project project; - private boolean warnedAlready; - public InstallerService(@NotNull Project project) { - this.project = project; - } + private final Logger logger = CSharpierLogger.getInstance(); + private final Project project; + private boolean warnedAlready; - @NotNull - static InstallerService getInstance(@NotNull Project project) { - return project.getService(InstallerService.class); - } + public InstallerService(@NotNull Project project) { + this.project = project; + } - public void displayInstallNeededMessage(String directoryThatContainsFile, IProcessKiller processKiller) { - if (this.warnedAlready || ignoreDirectory(directoryThatContainsFile)) { - return; - } - this.warnedAlready = true; - this.logger.warn("CSharpier was not found so files may not be formatted."); + @NotNull + static InstallerService getInstance(@NotNull Project project) { + return project.getService(InstallerService.class); + } - var isOnlyGlobal = !directoryThatContainsFile.replace('\\', '/').startsWith(this.project.getBasePath()); + public void displayInstallNeededMessage( + String directoryThatContainsFile, + IProcessKiller processKiller + ) { + if (this.warnedAlready || ignoreDirectory(directoryThatContainsFile)) { + return; + } + this.warnedAlready = true; + this.logger.warn("CSharpier was not found so files may not be formatted."); - var message = isOnlyGlobal - ? ("CSharpier needs to be installed globally to format files in " + directoryThatContainsFile) - : "CSharpier needs to be installed to support formatting files"; + var isOnlyGlobal = !directoryThatContainsFile + .replace('\\', '/') + .startsWith(this.project.getBasePath()); - var notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier") - .createNotification(message, NotificationType.WARNING); + var message = isOnlyGlobal + ? ("CSharpier needs to be installed globally to format files in " + + directoryThatContainsFile) + : "CSharpier needs to be installed to support formatting files"; - notification.addAction(new InstallGlobalAction("Install CSharpier Globally", processKiller, this.project)); - if (!isOnlyGlobal) { - notification.addAction( - new InstallLocalAction("Install CSharpier Locally", processKiller, project) - ); - } + var notification = NotificationGroupManager.getInstance() + .getNotificationGroup("CSharpier") + .createNotification(message, NotificationType.WARNING); - notification.notify(this.project); + notification.addAction( + new InstallGlobalAction( + "Install CSharpier Globally", + processKiller, + this.project + ) + ); + if (!isOnlyGlobal) { + notification.addAction( + new InstallLocalAction( + "Install CSharpier Locally", + processKiller, + project + ) + ); } - private boolean ignoreDirectory(String directoryThatContainsFile) { - var normalizedPath = directoryThatContainsFile.replace('\\', '/'); - return StringUtils.containsIgnoreCase(normalizedPath, "resharper-host/DecompilerCache") - || StringUtils.containsIgnoreCase(normalizedPath, "resharper-host/SourcesCache") - || directoryThatContainsFile.equals("/"); - } + notification.notify(this.project); + } + + private boolean ignoreDirectory(String directoryThatContainsFile) { + var normalizedPath = directoryThatContainsFile.replace('\\', '/'); + return ( + StringUtils.containsIgnoreCase( + normalizedPath, + "resharper-host/DecompilerCache" + ) || + StringUtils.containsIgnoreCase( + normalizedPath, + "resharper-host/SourcesCache" + ) || + directoryThatContainsFile.equals("/") + ); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/NullCSharpierProcess.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/NullCSharpierProcess.java index d7370049e..64fa3731a 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/NullCSharpierProcess.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/NullCSharpierProcess.java @@ -1,27 +1,26 @@ package com.intellij.csharpier; public class NullCSharpierProcess implements ICSharpierProcess { - public static ICSharpierProcess Instance = new NullCSharpierProcess(); - private NullCSharpierProcess() {} + public static ICSharpierProcess Instance = new NullCSharpierProcess(); - @Override - public String getVersion() { - return "NULL"; - } + private NullCSharpierProcess() {} - @Override - public boolean getProcessFailedToStart() { - return false; - } + @Override + public String getVersion() { + return "NULL"; + } - @Override - public String formatFile(String content, String fileName) { - return ""; - } + @Override + public boolean getProcessFailedToStart() { + return false; + } - @Override - public void dispose() { + @Override + public String formatFile(String content, String fileName) { + return ""; + } - } + @Override + public void dispose() {} } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/OpenUrlAction.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/OpenUrlAction.java index 296e6d266..dbfc6c723 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/OpenUrlAction.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/OpenUrlAction.java @@ -5,16 +5,17 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import org.jetbrains.annotations.NotNull; -public class OpenUrlAction extends AnAction { - private String url; +public class OpenUrlAction extends AnAction { - public OpenUrlAction(String title, String url) { - super(title); - this.url = url; - } + private String url; - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - BrowserUtil.browse(this.url); - } + public OpenUrlAction(String title, String url) { + super(title); + this.url = url; + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + BrowserUtil.browse(this.url); + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ProcessHelper.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ProcessHelper.java index 374f936b1..5d9eab3aa 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ProcessHelper.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ProcessHelper.java @@ -7,50 +7,59 @@ public class ProcessHelper { - public static String executeCommand(List command, Map env, File workingDirectory) { - var logger = CSharpierLogger.getInstance(); - try { - var directoryToLog = workingDirectory == null ? "" : " in " + workingDirectory; - - logger.debug("Running " + String.join(" ", command) + directoryToLog); - var processBuilder = new ProcessBuilder(command); - - if (env == null) { - env = new HashMap<>(); - } - else { - env = new HashMap<>(env); - } - - processBuilder.environment().putAll(env); - - if (workingDirectory != null) { - processBuilder.directory(workingDirectory); - } - - var process = processBuilder.start(); - var output = new BufferedReader(new InputStreamReader(process.getInputStream())); - var error = new BufferedReader(new InputStreamReader(process.getErrorStream())); - var result = new StringBuilder(); - var errorResult = new StringBuilder(); - String line; - while ((line = output.readLine()) != null) { - result.append(line); - result.append("\n"); - } - while ((line = error.readLine()) != null) { - errorResult.append(line); - errorResult.append("\n"); - } - - var exitVal = process.waitFor(); - if (exitVal == 0){ - return result.toString().trim(); - } - logger.debug(errorResult.toString()); - } catch (Exception e) { - logger.error(e); - } - return null; + public static String executeCommand( + List command, + Map env, + File workingDirectory + ) { + var logger = CSharpierLogger.getInstance(); + try { + var directoryToLog = workingDirectory == null + ? "" + : " in " + workingDirectory; + + logger.debug("Running " + String.join(" ", command) + directoryToLog); + var processBuilder = new ProcessBuilder(command); + + if (env == null) { + env = new HashMap<>(); + } else { + env = new HashMap<>(env); + } + + processBuilder.environment().putAll(env); + + if (workingDirectory != null) { + processBuilder.directory(workingDirectory); + } + + var process = processBuilder.start(); + var output = new BufferedReader( + new InputStreamReader(process.getInputStream()) + ); + var error = new BufferedReader( + new InputStreamReader(process.getErrorStream()) + ); + var result = new StringBuilder(); + var errorResult = new StringBuilder(); + String line; + while ((line = output.readLine()) != null) { + result.append(line); + result.append("\n"); + } + while ((line = error.readLine()) != null) { + errorResult.append(line); + errorResult.append("\n"); + } + + var exitVal = process.waitFor(); + if (exitVal == 0) { + return result.toString().trim(); + } + logger.debug(errorResult.toString()); + } catch (Exception e) { + logger.error(e); } + return null; + } } diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierAction.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierAction.java index 7b7197a95..8db5a527e 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierAction.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierAction.java @@ -7,54 +7,67 @@ import org.jetbrains.annotations.NotNull; public class ReformatWithCSharpierAction extends AnAction { - Logger logger = CSharpierLogger.getInstance(); - @Override - public @NotNull ActionUpdateThread getActionUpdateThread() { - return ActionUpdateThread.BGT; - } + Logger logger = CSharpierLogger.getInstance(); - @Override - public void actionPerformed(@NotNull AnActionEvent e) { - this.logger.info("Running ReformatWithCSharpierAction"); - var project = e.getProject(); - if (project == null) { - return; - } - var editor = e.getData(CommonDataKeys.EDITOR); - if (editor != null) { - processFileInEditor(project, editor.getDocument()); - } - } + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.BGT; + } - @Override - public void update(@NotNull AnActionEvent e) { - var virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE); - - if (virtualFile == null - // this update can get hit before CSharpierStartup is done - // so bail early if we didn't find dotnet yet, it should be there when startup is done - || !DotNetProvider.getInstance(e.getProject()).foundDotNet()) { - e.getPresentation().setVisible(false); - return; - } - var virtualFileString = virtualFile.toString(); - var filePrefix = "file://"; - if (!virtualFileString.startsWith(filePrefix)) { - this.logger.debug("VIRTUAL_FILE did not start with file://, was: " + virtualFileString); - e.getPresentation().setVisible(false); - return; - } - - var file = virtualFileString.substring(filePrefix.length()); - var isCSharpFile = file.toLowerCase().endsWith(".cs"); - e.getPresentation().setVisible(isCSharpFile); - var canFormat = isCSharpFile && FormattingService.getInstance(e.getProject()).getCanFormat(file, e.getProject()); - e.getPresentation().setEnabled(canFormat); + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + this.logger.info("Running ReformatWithCSharpierAction"); + var project = e.getProject(); + if (project == null) { + return; } + var editor = e.getData(CommonDataKeys.EDITOR); + if (editor != null) { + processFileInEditor(project, editor.getDocument()); + } + } + + @Override + public void update(@NotNull AnActionEvent e) { + var virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE); - private static void processFileInEditor(@NotNull Project project, @NotNull Document document) { - var formattingService = FormattingService.getInstance(project); - formattingService.format(document, project); + if ( + virtualFile == null || + // this update can get hit before CSharpierStartup is done + // so bail early if we didn't find dotnet yet, it should be there when startup is done + !DotNetProvider.getInstance(e.getProject()).foundDotNet() + ) { + e.getPresentation().setVisible(false); + return; } -} \ No newline at end of file + var virtualFileString = virtualFile.toString(); + var filePrefix = "file://"; + if (!virtualFileString.startsWith(filePrefix)) { + this.logger.debug( + "VIRTUAL_FILE did not start with file://, was: " + virtualFileString + ); + e.getPresentation().setVisible(false); + return; + } + + var file = virtualFileString.substring(filePrefix.length()); + var isCSharpFile = file.toLowerCase().endsWith(".cs"); + e.getPresentation().setVisible(isCSharpFile); + var canFormat = + isCSharpFile && + FormattingService.getInstance(e.getProject()).getCanFormat( + file, + e.getProject() + ); + e.getPresentation().setEnabled(canFormat); + } + + private static void processFileInEditor( + @NotNull Project project, + @NotNull Document document + ) { + var formattingService = FormattingService.getInstance(project); + formattingService.format(document, project); + } +} diff --git a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierOnSaveListener.java b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierOnSaveListener.java index 2d893f458..8ee7aa546 100644 --- a/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierOnSaveListener.java +++ b/Src/CSharpier.Rider/src/main/java/com/intellij/csharpier/ReformatWithCSharpierOnSaveListener.java @@ -8,33 +8,34 @@ import com.intellij.openapi.project.ProjectUtil; import org.jetbrains.annotations.NotNull; -public class ReformatWithCSharpierOnSaveListener implements FileDocumentManagerListener { - Logger logger = CSharpierLogger.getInstance(); - - @Override - public void beforeDocumentSaving(@NotNull Document document) { - var project = this.GetProject(document); - if (project == null) { - return; - } - - var cSharpierSettings = CSharpierSettings.getInstance(project); - if (!cSharpierSettings.getRunOnSave()) { - return; - } - - var formattingService = FormattingService.getInstance(project); - this.logger.debug("beforeDocumentSaving for " + document); - formattingService.format(document, project); +public class ReformatWithCSharpierOnSaveListener + implements FileDocumentManagerListener { + + Logger logger = CSharpierLogger.getInstance(); + + @Override + public void beforeDocumentSaving(@NotNull Document document) { + var project = this.GetProject(document); + if (project == null) { + return; + } + + var cSharpierSettings = CSharpierSettings.getInstance(project); + if (!cSharpierSettings.getRunOnSave()) { + return; } - private Project GetProject(@NotNull Document document) { - var virtualFile = FileDocumentManager.getInstance().getFile(document); - if (virtualFile == null) { - return null; - } + var formattingService = FormattingService.getInstance(project); + this.logger.debug("beforeDocumentSaving for " + document); + formattingService.format(document, project); + } - return ProjectUtil.guessProjectForContentFile(virtualFile); + private Project GetProject(@NotNull Document document) { + var virtualFile = FileDocumentManager.getInstance().getFile(document); + if (virtualFile == null) { + return null; } + return ProjectUtil.guessProjectForContentFile(virtualFile); + } }