Skip to content

Commit

Permalink
Making plugin version aware and detect if csharpier is installed
Browse files Browse the repository at this point in the history
  • Loading branch information
belav committed Dec 13, 2021
1 parent 6b1db81 commit cbfb7f6
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 122 deletions.
15 changes: 11 additions & 4 deletions Src/CSharpier.Cli/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,17 @@ CancellationToken cancellationToken
[Conditional("DEBUG")]
private static void Log(string message)
{
File.AppendAllText(
@"C:\projects\csharpier\Src\CSharpier.Cli\bin\Debug\net6.0\log.txt",
message + "\n"
);
try
{
File.AppendAllText(
@"C:\projects\csharpier\Src\CSharpier.Cli\bin\Debug\net6.0\log.txt",
message + "\n"
);
}
catch (Exception)
{
// we don't care if this fails
}
}

private static async Task<int> PipeMultipleFiles(
Expand Down
5 changes: 5 additions & 0 deletions Src/CSharpier.Rider/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
TODO clean this up

- https://plugins.jetbrains.com/docs/intellij/welcome.html
- https://developerlife.com/2021/03/13/ij-idea-plugin-advanced/

# csharpier-rider

![Build](https://github.com/belav/csharpier-rider/workflows/Build/badge.svg)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.intellij.csharpierrider;

import com.esotericsoftware.minlog.Log;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;

// TODO only work with c#?
// TODO workflows?

public class CSharpierProcessPipeMultipleFiles implements ICSharpierProcess, Disposable {
Logger LOG = Logger.getInstance(CSharpierProcessPipeMultipleFiles.class);
String csharpierPath;

Process process = null;
OutputStream stdin;
BufferedReader stdOut;
BufferedReader stdError;

public CSharpierProcessPipeMultipleFiles(String csharpierPath) {
this.csharpierPath = csharpierPath;
try {
process = new ProcessBuilder("dotnet", csharpierPath, "--pipe-multiple-files")
.start();

stdin = process.getOutputStream();
stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
} catch (Exception e) {
LOG.error("error", e);
}

this.formatFile("public class ClassName { }", "Test.cs");
}

@Override
public String formatFile(String content, String filePath) {

try {
LOG.info(filePath);
// TODO we need the real file path
stdin.write("C:\\projects\\csharpier\\Src\\CSharpier\\DocSerializer.cs".getBytes());
stdin.write('\u0003');
stdin.write(content.getBytes());
stdin.write('\u0003');
stdin.flush();

StringBuilder output = new StringBuilder();
StringBuilder errorOutput = new StringBuilder();

AtomicBoolean done = new AtomicBoolean(false);

Thread outputReaderThread = CreateReadingThread(stdOut, output, done);
outputReaderThread.start();

Thread errorReaderThread = CreateReadingThread(stdError, errorOutput, done);
errorReaderThread.start();

while (!done.get()) {
Thread.sleep(1);
}

errorReaderThread.interrupt();
outputReaderThread.interrupt();

Log.info("Output:");
Log.info(output.toString());
Log.info("Error:");
Log.info(errorOutput.toString());

return output.toString();

} catch (Exception e) {
LOG.error("error", e);
e.printStackTrace();
return "";
}
}

private Thread CreateReadingThread(BufferedReader reader, StringBuilder stringBuilder, AtomicBoolean done) {
return new Thread(() -> {
try {
var nextCharacter = reader.read();
while (nextCharacter != -1) {
LOG.info("Got Error " + nextCharacter);
if (nextCharacter == '\u0003') {
done.set(true);
return;
}
stringBuilder.append((char) nextCharacter);
nextCharacter = reader.read();
}
} catch (Exception e) {
LOG.error("error", e);
done.set(true);
}
});
}

@Override
public void dispose() {
process.destroy();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.intellij.csharpierrider;

import com.esotericsoftware.minlog.Log;
import com.intellij.openapi.diagnostic.Logger;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;

public class CSharpierProcessSingleFile implements ICSharpierProcess {
Logger LOG = Logger.getInstance(CSharpierProcessSingleFile.class);
String csharpierPath;

public CSharpierProcessSingleFile(String csharpierPath) {
this.csharpierPath = csharpierPath;
}

@Override
public String formatFile(String content, String fileName) {
try {
ProcessBuilder processBuilder = new ProcessBuilder("dotnet", csharpierPath, "--write-stdout");
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();

OutputStream stdin = process.getOutputStream();
BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));

stdin.write(content.getBytes());
stdin.close();

StringBuilder output = new StringBuilder();

var nextCharacter = stdOut.read();
while (nextCharacter != -1) {
LOG.info("Got Output " + nextCharacter);
output.append((char)nextCharacter);
nextCharacter = stdOut.read();
}

String result = output.toString();

if (process.exitValue() == 0 && !result.contains("Failed to compile so was not formatted.")) {
return result;
}
else {
Log.error(result);
}
} catch (Exception e) {
LOG.error("error", e);
}

return "";
}
}
Original file line number Diff line number Diff line change
@@ -1,114 +1,107 @@
package com.intellij.csharpierrider;

import com.esotericsoftware.minlog.Log;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationGroupManager;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import org.apache.maven.artifact.versioning.ComparableVersion;
import org.jetbrains.annotations.NotNull;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;

// TODO https://plugins.jetbrains.com/docs/intellij/disposers.html#choosing-a-disposable-parent
import java.io.InputStream;
import java.util.Scanner;

public class CSharpierService {
Logger LOG = Logger.getInstance(ReformatWithCSharpierAction.class);
Process process = null;
OutputStream stdin = null;
BufferedReader stdOut = null;
BufferedReader stdError = null;
String csharpierPath;
ICSharpierProcess csharpierProcess;
Project project;

public CSharpierService(@NotNull Project project) {
this.project = project;
csharpierPath = getCSharpierPath();

public CSharpierService() {
Log.info("Using command dotnet " + csharpierPath);

csharpierProcess = setupCSharpierProcess();
}

public String getCSharpierPath() {
try {
// TODO
String csharpierPath = "C:\\projects\\csharpier\\Src\\CSharpier.Cli\\bin\\Debug\\net6.0\\dotnet-csharpier.dll";
String csharpierDebugPath = "C:\\projects\\csharpier\\Src\\CSharpier.Cli\\bin\\Debug\\net6.0\\dotnet-csharpier.dll";
String csharpierReleasePath = csharpierDebugPath.replace("Debug", "Release");

process = new ProcessBuilder("dotnet", csharpierPath, "--pipe-multiple-files").start();
} catch (IOException e) {
LOG.error("error", e);
e.printStackTrace();
if (new File(csharpierDebugPath).exists()) {
return csharpierDebugPath;
} else if (new File(csharpierReleasePath).exists()) {
return csharpierReleasePath;
}
} catch (Exception ex) {
Log.debug("Could not find local csharpier " + ex.getMessage());
}
stdin = process.getOutputStream();
stdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

return "csharpier";
}

@NotNull
static CSharpierService getInstance(@NotNull Project project) {
return project.getService(CSharpierService.class);
}

public String format(@NotNull String content, @NotNull String filePath) {
StringBuilder output = new StringBuilder();
StringBuilder errorOutput = new StringBuilder();
public String execCmd(String cmd) {
String result = null;
try (InputStream inputStream = Runtime.getRuntime().exec(cmd).getInputStream();
Scanner s = new Scanner(inputStream).useDelimiter("\\A")
) {
result = s.hasNext() ? s.next() : null;
} catch (IOException e) {
Log.error(e.getMessage());
e.printStackTrace();
}
return result;
}

private ICSharpierProcess setupCSharpierProcess() {
try {
LOG.info(filePath);
// TODO real path
stdin.write("C:\\projects\\csharpier\\Src\\CSharpier\\DocSerializer.cs".getBytes());
stdin.write('\u0003');
stdin.write(content.getBytes());
stdin.write('\u0003');
stdin.flush();

int end = '\u0003';

AtomicBoolean done = new AtomicBoolean(false);

Thread outStreamReader = new Thread(() -> {
try {
var nextCharacter = stdOut.read();
while (nextCharacter != -1) {
LOG.info("Got Output " + nextCharacter);
if (nextCharacter == end) {
done.set(true);
return;
}
output.append((char)nextCharacter);
nextCharacter = stdOut.read();
}
} catch (Exception e) {
LOG.error("error", e);
e.printStackTrace();
}
});
outStreamReader.start();

Thread errorStreamReader = new Thread(() -> {
try {
var nextCharacter = stdError.read();
while (nextCharacter != -1) {
LOG.info("Got Error " + nextCharacter);
errorOutput.append((char)nextCharacter);
nextCharacter = stdError.read();
}
} catch (Exception e) {
LOG.error("error", e);
e.printStackTrace();
String version = execCmd("dotnet " + this.csharpierPath + " --version");
Log.info("CSharpier version: " + version);
if (version == null) {
this.displayInstallNeededMessage();
}
else {
ComparableVersion installedVersion = new ComparableVersion(version);
ComparableVersion pipeFilesVersion = new ComparableVersion("0.12.0");
if (installedVersion.compareTo(pipeFilesVersion) < 0) {
String content = "Please upgrade to CSharpier >= 0.12.0 for bug fixes and improved formatting speed.";
NotificationGroupManager.getInstance().getNotificationGroup("CSharpier")
.createNotification(content, NotificationType.INFORMATION)
.notify(project);

return new CSharpierProcessSingleFile(this.csharpierPath);
}
});

errorStreamReader.start();

int lastLength = errorOutput.length();
while (!done.get()) {
Thread.sleep(1);
return new CSharpierProcessPipeMultipleFiles(this.csharpierPath);
}
} catch (Exception ex) {
LOG.error(ex);
}

errorStreamReader.interrupt();
return new NullCSharpierProcess();
}

} catch (Exception e) {
LOG.error("error", e);
e.printStackTrace();
}
private void displayInstallNeededMessage() {
Notification notification = NotificationGroupManager.getInstance().getNotificationGroup("CSharpier")
.createNotification("CSharpier must be installed globally to support formatting.", NotificationType.WARNING);

Log.info("Output:");
Log.info(output.toString());
Log.info("Error:");
Log.info(errorOutput.toString());
// TODO why can't an action be displayed in this???
// notification.addAction(new EditAction());

return output.toString();
notification.notify(project);
}

public String format(@NotNull String content, @NotNull String filePath) {
return this.csharpierProcess.formatFile(content, filePath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.intellij.csharpierrider;

import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.startup.StartupActivity;
import org.jetbrains.annotations.NotNull;

public class CSharpierStartup implements StartupActivity, DumbAware {
@Override
public void runActivity(@NotNull Project project) {
CSharpierService.getInstance(project);
}
}
Loading

0 comments on commit cbfb7f6

Please sign in to comment.