-
-
Notifications
You must be signed in to change notification settings - Fork 30
Formatting based on external formatter #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
e2d42bc
794f80a
b25a9eb
8074155
791f5b4
8d08642
0a67c4f
cb81940
b3c2898
68d26f1
bf1c923
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package org.nixos.idea.format; | ||
|
||
import com.intellij.execution.ExecutionException; | ||
import com.intellij.execution.configurations.GeneralCommandLine; | ||
import com.intellij.execution.process.CapturingProcessAdapter; | ||
import com.intellij.execution.process.OSProcessHandler; | ||
import com.intellij.execution.process.ProcessEvent; | ||
import com.intellij.formatting.service.AsyncDocumentFormattingService; | ||
import com.intellij.formatting.service.AsyncFormattingRequest; | ||
import com.intellij.openapi.util.NlsSafe; | ||
import com.intellij.psi.PsiFile; | ||
import com.intellij.util.execution.ParametersListUtil; | ||
import org.jetbrains.annotations.NonNls; | ||
import org.jetbrains.annotations.NotNull; | ||
import org.jetbrains.annotations.Nullable; | ||
import org.nixos.idea.file.NixFile; | ||
import org.nixos.idea.settings.NixLangSettings; | ||
|
||
import java.io.IOException; | ||
import java.io.OutputStream; | ||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.util.EnumSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public final class NixExternalFormatter extends AsyncDocumentFormattingService { | ||
|
||
@Override | ||
protected @NotNull String getNotificationGroupId() { | ||
return "NixIDEA"; | ||
JojOatXGME marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
@Override | ||
protected @NotNull @NlsSafe String getName() { | ||
return "NixIDEA"; | ||
} | ||
|
||
@Override | ||
public @NotNull Set<Feature> getFeatures() { | ||
return EnumSet.noneOf(Feature.class); | ||
} | ||
|
||
@Override | ||
public boolean canFormat(@NotNull PsiFile psiFile) { | ||
return psiFile instanceof NixFile; | ||
} | ||
|
||
|
||
@Override | ||
protected @Nullable FormattingTask createFormattingTask(@NotNull AsyncFormattingRequest request) { | ||
NixLangSettings nixSettings = NixLangSettings.getInstance(); | ||
System.out.println("started fmt task"); | ||
JojOatXGME marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!nixSettings.isFormatEnabled()) { | ||
return null; | ||
} | ||
|
||
var ioFile = request.getIOFile(); | ||
if (ioFile == null) return null; | ||
|
||
@NonNls | ||
var command = nixSettings.getFormatCommand(); | ||
List<String> argv = ParametersListUtil.parse(command, false, true); | ||
|
||
try { | ||
var commandLine = new GeneralCommandLine(argv); | ||
|
||
OSProcessHandler handler = new OSProcessHandler(commandLine.withCharset(StandardCharsets.UTF_8)); | ||
OutputStream processInput = handler.getProcessInput(); | ||
Files.copy(ioFile.toPath(), processInput); | ||
JojOatXGME marked this conversation as resolved.
Show resolved
Hide resolved
|
||
processInput.flush(); | ||
processInput.close(); | ||
return new FormattingTask() { | ||
@Override | ||
public void run() { | ||
handler.addProcessListener(new CapturingProcessAdapter() { | ||
|
||
@Override | ||
public void processTerminated(@NotNull ProcessEvent event) { | ||
int exitCode = event.getExitCode(); | ||
if (exitCode == 0) { | ||
request.onTextReady(getOutput().getStdout()); | ||
} else { | ||
request.onError("NixIDEA", getOutput().getStderr()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question (non-blocking): Should we maybe use “Nix External Formatter” for the title? (Same in catch block.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is clearer to use the plugin name as title, so it is clear who is 'to blame' for the error, but I do not have a strong opinion. I can change it to Nix External Formatter if you prefer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am fine with both. I think both have their pros and cons. 😅 |
||
} | ||
} | ||
}); | ||
handler.startNotify(); | ||
} | ||
|
||
@Override | ||
public boolean cancel() { | ||
handler.destroyProcess(); | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean isRunUnderProgress() { | ||
return true; | ||
} | ||
}; | ||
} catch (ExecutionException | IOException e) { | ||
request.onError("NixIDEA", e.getMessage()); | ||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package org.nixos.idea.settings; | ||
|
||
import com.intellij.openapi.application.ApplicationManager; | ||
import com.intellij.openapi.components.PersistentStateComponent; | ||
import com.intellij.openapi.components.RoamingType; | ||
import com.intellij.openapi.components.State; | ||
import com.intellij.openapi.components.Storage; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Deque; | ||
|
||
@State(name = "NixLangSettings", storages = @Storage(value = "nix-idea-tools.xml", roamingType = RoamingType.DISABLED)) | ||
public final class NixLangSettings implements PersistentStateComponent<NixLangSettings.State> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. polish: I would prefer if you keep this state class more specific for now. Like
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for reason (1), if you add an non-external formatter, I think it would appropriate to add its settings to this class also (rather than the existing settings in the "Build" section) for reason (2), if you prefer a different file to New settings can go underneath like in the picture below: ![]() I renamed 'Formatter Configuration' to ' External Formatter Configuration' If you still feel like the external formatter deserves its own section, I am happy to make another one too though! Again, no strong opinions from me on this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My concern was mostly about where and how to store the configuration, not the UI. The configuration of a built-in formatter would be system-independent and can be synchronized across operating systems. External formatters should not be synchronized across operating systems. To make this separation, these two cases need to be stored in separate XML files. The <application>
<component name="NixLspSettings">
<!-- LSP settings -->
</component>
<component name="NixExternalFormatterSettings">
<!-- External formatter settings -->
</component>
</application> instead of <application>
<component name="NixLspSettings">
<!-- LSP settings -->
</component>
<component name="NixLangSettings">
<!-- Other system-dependent settings -->
</component>
</application> There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see, thanks for the explanation! That makes sense, I will apply your suggestion then. |
||
|
||
// TODO: Use RoamingType.LOCAL with 2024.1 | ||
|
||
// Documentation: | ||
// https://plugins.jetbrains.com/docs/intellij/persisting-state-of-components.html | ||
|
||
private static final int MAX_HISTORY_SIZE = 5; | ||
|
||
private @NotNull State myState = new State(); | ||
|
||
public static @NotNull NixLangSettings getInstance() { | ||
return ApplicationManager.getApplication().getService(NixLangSettings.class); | ||
} | ||
|
||
public boolean isFormatEnabled() { | ||
return myState.formatEnabled; | ||
} | ||
|
||
public void setFormatEnabled(boolean enabled) { | ||
myState.formatEnabled = enabled; | ||
} | ||
|
||
public @NotNull String getFormatCommand() { | ||
return myState.formatCommand; | ||
} | ||
|
||
public void setFormatCommand(@NotNull String command) { | ||
myState.formatCommand = command; | ||
addFormatCommandToHistory(command); | ||
} | ||
|
||
public @NotNull Collection<String> getCommandHistory() { | ||
return Collections.unmodifiableCollection(myState.formatCommandHistory); | ||
} | ||
|
||
private void addFormatCommandToHistory(@NotNull String command) { | ||
Deque<String> history = myState.formatCommandHistory; | ||
history.remove(command); | ||
history.addFirst(command); | ||
while (history.size() > MAX_HISTORY_SIZE) { | ||
history.removeLast(); | ||
} | ||
} | ||
|
||
@SuppressWarnings("ClassEscapesDefinedScope") | ||
@Override | ||
public void loadState(@NotNull State state) { | ||
myState = state; | ||
} | ||
|
||
@SuppressWarnings("ClassEscapesDefinedScope") | ||
@Override | ||
public @NotNull State getState() { | ||
return myState; | ||
} | ||
|
||
static final class State { | ||
public boolean formatEnabled = false; | ||
public @NotNull String formatCommand = ""; | ||
public Deque<String> formatCommandHistory = new ArrayDeque<>(); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.