Skip to content

Commit

Permalink
Rider - Better error handling and logging for failures running csharp…
Browse files Browse the repository at this point in the history
…ier (#1032)

* Working on rider issues

#926

* Updating doc

* formatting files

* some cleanup

* limit to compatible versions
  • Loading branch information
belav authored Nov 28, 2023
1 parent 337f984 commit 08b575c
Show file tree
Hide file tree
Showing 21 changed files with 17,231 additions and 2,928 deletions.
7 changes: 7 additions & 0 deletions CSharpier.sln
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shell", "Shell\Shell.csproj
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Src", "Src", "{0D12A95B-A237-4181-937C-DF6D0265975E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Website", "Src\Website\Website.csproj", "{A5573FE8-F544-4F18-B554-A8F9CC16A5DB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -109,6 +111,10 @@ Global
{497B849B-044C-4350-BDE8-13CA3E6C7845}.Debug|Any CPU.Build.0 = Debug|Any CPU
{497B849B-044C-4350-BDE8-13CA3E6C7845}.Release|Any CPU.ActiveCfg = Release|Any CPU
{497B849B-044C-4350-BDE8-13CA3E6C7845}.Release|Any CPU.Build.0 = Release|Any CPU
{A5573FE8-F544-4F18-B554-A8F9CC16A5DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5573FE8-F544-4F18-B554-A8F9CC16A5DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5573FE8-F544-4F18-B554-A8F9CC16A5DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5573FE8-F544-4F18-B554-A8F9CC16A5DB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -128,5 +134,6 @@ Global
{3E1D074B-A4F3-4010-84E7-820459214202} = {0D12A95B-A237-4181-937C-DF6D0265975E}
{19874634-1A44-4831-BB43-6E2B864AB3ED} = {0D12A95B-A237-4181-937C-DF6D0265975E}
{0B075DD3-1FEE-483E-8665-43C226188287} = {0D12A95B-A237-4181-937C-DF6D0265975E}
{A5573FE8-F544-4F18-B554-A8F9CC16A5DB} = {0D12A95B-A237-4181-937C-DF6D0265975E}
EndGlobalSection
EndGlobal
26 changes: 17 additions & 9 deletions Docs/About.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ The printing process was ported from [prettier](https://github.com/prettier/pret
CSharpier provides a few basic options that affect formatting and has no plans to add more. It follows the [Option Philosophy](https://prettier.io/docs/en/option-philosophy.html) of prettier.

### Quick Start
Install CSharpier globally using the following command.
Install CSharpier in a project with the following command.
```bash
dotnet tool install csharpier -g
dotnet tool install csharpier
```
Then format the contents of a directory and its children with the following command.
Then format the contents of the project
```bash
dotnet csharpier .
```

See [Install a local tool](https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool) and [CLI Usage](https://csharpier.com/docs/CLI) for more information

CSharpier can also format [on save in your editor](https://csharpier.com/docs/Editors), as a [pre-commit hook](https://csharpier.com/docs/Pre-commit), as [part of your build](https://csharpier.com/docs/MSBuild) or even [programatically](https://csharpier.com/docs/API). Then you can ensure code was formatted with a [CI/CD tool](https://csharpier.com/docs/ContinuousIntegration).

---
Expand All @@ -31,7 +33,7 @@ CSharpier can also format [on save in your editor](https://csharpier.com/docs/Ed
```c#
public class ClassName {
public void CallMethod() {
this.LongUglyMethod("1234567890", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
var shuffle = shuffle.Skip(26).LogQuery("Bottom Half").InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"), shuffle.Skip(26).LogQuery("Bottom Half")).LogQuery("Shuffle").ToArray();
}
}
```
Expand All @@ -42,11 +44,17 @@ public class ClassName
{
public void CallMethod()
{
this.LongUglyMethod(
"1234567890",
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
);
var shuffle = shuffle
.Skip(26)
.LogQuery("Bottom Half")
.InterleaveSequenceWith(
shuffle.Take(26).LogQuery("Top Half"),
shuffle.Skip(26).LogQuery("Bottom Half")
)
.LogQuery("Shuffle")
.ToArray();
}
}


```
10 changes: 9 additions & 1 deletion Docs/Editors.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@ title: Editor Integration
hide_table_of_contents: true
---

Running CSharpier on save is recommended. It will speed up your development time.
Running CSharpier on save is recommended. It will speed up your development time. All of the official extensions support it.

**Note** It is important to use CSharpier as a [local dotnet tool](https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool) to ensure files are formatted consistently across team members and environments.

### Visual Studio
Use the [official 2022 extension](https://marketplace.visualstudio.com/items?itemName=csharpier.CSharpier)
\
Use the [official 2019 extension](https://marketplace.visualstudio.com/items?itemName=csharpier.CSharpier2019)

They can both be installed from within the VisualStudio Manage Extensions dialog.
### Visual Studio Code
Use the [official extension](https://marketplace.visualstudio.com/items?itemName=csharpier.csharpier-vscode)

It can be installed via the Extensions sidebar. It is called "CSharpier - Code Formatter"
### Rider
Use the [official plugin](https://plugins.jetbrains.com/plugin/18243-csharpier)

It can be installed via the Plugins dialog.
### Neovim
Use [neoformat](https://github.com/sbdchd/neoformat)
52 changes: 52 additions & 0 deletions Docs/EditorsTroubleshooting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Troubleshooting
hide_table_of_contents: true
---

The editor extensions run CSharpier to facilitate format files.
CSharpier is kept running to speed up formatting results.
The extensions install CSharpier to a custom path to avoid commands like `dotnet tool update -g csharpier` failing because a CSharpier is running.

When the extension is unable to format files, it is generally a problem with being unable to install or execute the extension. You can use the information from the logs it outputs to understand the failure and then attempt the troubleshooting steps below.

## Enabling Debug Logs
### VisualStudio
- Navigate to `Tools - Options - CSharpier`
- Change `Log Debug Messages` to `true`

### VSCode
- Navigate to `File - Preferences - Settings - Extensions - CSharpier`
- Check `Enable debug logs`
- Restart VSCode

### Rider
- Execute the action `Debug Log Settings`
- Add an entry for `#com.intellij.csharpier.CSharpierLogger`
- Restart Rider

## Locating Logs
### Visual Studio
- Navigate to `View - Output`
- Change the `Show output from:` dropdown to `CSharpier`

### VSCode
- Navigate to `View - Output`
- Change the dropdown to `CSharpier`

### Rider
- Execute the action `Show Log in Explorer
- Look for lines that contain `#c.i.c.CSharpierLogger`

## Troubleshooting Steps
The following can help track down issues with the extension being unable to install/run CSharpier

1. Validate the following command can run in the directory of your project\
`dotnet csharpier --version`
2. If the extension was able to install CSharpier it should exist at a path such as\
`C:\Users\[UserName]\AppData\Local\CSharpier\[CSharpierVersion]` or\
`$Home/.cache/csharpier/[CSharpierVersion]`
3. Assuming the directory above exists, attempt to run the following in that directory\
`dotnet-csharpier --version`
4. If the installation appears to be corrupt, delete the directory and install CSharpier there yourself\
`dotnet tool install csharpier --version [CSharpierVersion] --tool-path [PathFromStep2]`
5. Repeat step 3 to validate the install
3 changes: 3 additions & 0 deletions Src/CSharpier.Rider/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

# csharpier-rider Changelog

## [1.5.0]
- Improved error handling and reporting around csharpier failing to install or run

## [1.3.10]
- Read from error stream to prevent the plugin hanging when the csharpier process writes too much to the error stream.

Expand Down
2 changes: 1 addition & 1 deletion Src/CSharpier.Rider/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
id("org.jetbrains.intellij") version "1.3.0"
id("org.jetbrains.changelog") version "1.3.1"
id("org.jetbrains.qodana") version "0.1.13"
id("com.jetbrains.rdgen") version "0.213.394"
id("com.jetbrains.rdgen") version "2021.3.4"
}

group = properties("pluginGroup")
Expand Down
5 changes: 2 additions & 3 deletions Src/CSharpier.Rider/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@

pluginGroup = com.intellij.csharpier
pluginName = csharpier
# SemVer format -> https://semver.org
pluginVersion = 1.3.10
pluginVersion = 1.5.0

# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
# for insight into build numbers and IntelliJ Platform versions.
pluginSinceBuild = 203
pluginSinceBuild = 212

# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
platformType = RD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,22 @@
public class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess, Disposable {
private final boolean useUtf8;
private final String csharpierPath;
Logger logger = CSharpierLogger.getInstance();
private Logger logger = CSharpierLogger.getInstance();

Process process = null;
OutputStreamWriter stdin;
BufferedReader stdOut;
private Process process = null;
private OutputStreamWriter stdin;
private BufferedReader stdOut;
public boolean processFailedToStart;

public CSharpierProcessPipeMultipleFiles(String csharpierPath, boolean useUtf8) {
this.csharpierPath = csharpierPath;
this.useUtf8 = useUtf8;
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() {
Expand All @@ -35,20 +41,19 @@ private void startProcess() {
// 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.error("error", e);
this.logger.warn("Failed to spawn the needed csharpier process. Formatting cannot occur.", e);
this.processFailedToStart = true;
}

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");
}

@Override
public String formatFile(String content, String filePath) {
if (this.processFailedToStart) {
this.logger.warn("CSharpier proccess failed to start. Formatting cannot occur.");
return "";
}

var stringBuilder = new StringBuilder();

Runnable task = () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

public class CSharpierProcessProvider implements DocumentListener, Disposable, IProcessKiller {
private final CustomPathInstaller customPathInstaller = new CustomPathInstaller();
private final CustomPathInstaller customPathInstaller;
private final Logger logger = CSharpierLogger.getInstance();
private final Project project;

Expand All @@ -38,6 +39,7 @@ public class CSharpierProcessProvider implements DocumentListener, Disposable, I

public CSharpierProcessProvider(@NotNull Project project) {
this.project = project;
this.customPathInstaller = new CustomPathInstaller(CSharpierSettings.getInstance(project));

for (var fileEditor : FileEditorManager.getInstance(project).getAllEditors()) {
var path = fileEditor.getFile().getPath();
Expand Down Expand Up @@ -161,9 +163,14 @@ private String getCSharpierVersion(String directoryThatContainsFile) {
var command = new String[]{"dotnet", "csharpier", "--version"};
var version = ProcessHelper.ExecuteCommand(command, env, new File(directoryThatContainsFile));

if (version == null) {
version = "";
}
this.logger.debug("dotnet csharpier --version output: " + version);
var versionWithoutHash = version.split(Pattern.quote("+"))[0];
this.logger.debug("Using " + versionWithoutHash + " as the version number.");

return version == null ? "" : version;
return versionWithoutHash;
}

private String FindVersionInCsProj(Path currentDirectory) {
Expand Down Expand Up @@ -200,7 +207,11 @@ private ICSharpierProcess setupCSharpierProcess(String directory, String version
}

try {
this.customPathInstaller.ensureVersionInstalled(version);
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);
Expand All @@ -226,7 +237,12 @@ private ICSharpierProcess setupCSharpierProcess(String directory, String version

var useUtf8 = versionWeCareAbout >= 14;

return new CSharpierProcessPipeMultipleFiles(customPath, useUtf8);
var csharpierProcess = new CSharpierProcessPipeMultipleFiles(customPath, useUtf8);
if (csharpierProcess.processFailedToStart) {
this.displayFailureMessage();
}

return csharpierProcess;

} catch (Exception ex) {
this.logger.error(ex);
Expand All @@ -235,6 +251,15 @@ private ICSharpierProcess setupCSharpierProcess(String directory, String version
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ public void setRunOnSave(boolean runOnSave) {
this.runOnSave = runOnSave;
}

private String customPath;

public String getCustomPath() {
return this.customPath;
}

public void setCustomPath(String customPath) {
this.customPath = customPath;
}

@Override
public CSharpierSettings getState() {
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
public class CSharpierSettingsComponent implements SearchableConfigurable {
private final Project project;
private JCheckBox runOnSaveCheckBox = new JCheckBox("Run on Save");
private JTextField customPathTextField = new JTextField("Path to directory containing dotnet-csharpier - used for testing the extension with new versions of csharpier.");

public CSharpierSettingsComponent(@NotNull Project project) {
this.project = project;
Expand All @@ -34,25 +35,29 @@ public String getDisplayName() {
public @Nullable JComponent createComponent() {
return FormBuilder.createFormBuilder()
.addComponent(this.runOnSaveCheckBox)
.addComponent(this.customPathTextField)
.addComponentFillVertically(new JPanel(), 0)
.getPanel();
}

@Override
public boolean isModified() {
return CSharpierSettings.getInstance(this.project).getRunOnSave() != this.runOnSaveCheckBox.isSelected();
return CSharpierSettings.getInstance(this.project).getRunOnSave() != this.runOnSaveCheckBox.isSelected()
|| CSharpierSettings.getInstance(this.project).getCustomPath() != this.customPathTextField.getText();
}

@Override
public void apply() throws ConfigurationException {
var settings = CSharpierSettings.getInstance(this.project);

settings.setRunOnSave(this.runOnSaveCheckBox.isSelected());
settings.setCustomPath(this.customPathTextField.getText());
}

@Override
public void reset() {
var settings = CSharpierSettings.getInstance(this.project);
this.runOnSaveCheckBox.setSelected(settings.getRunOnSave());
this.customPathTextField.setText(settings.getCustomPath());
}
}
Loading

0 comments on commit 08b575c

Please sign in to comment.