Skip to content
This repository has been archived by the owner on Feb 9, 2019. It is now read-only.

Commit

Permalink
Quickfixes (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
vhakulinen committed Apr 20, 2016
1 parent 0d9e4cc commit bb60183
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 17 deletions.
3 changes: 1 addition & 2 deletions neovim-intellij-complete.iml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="neovim-intellij-client" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.7.0" level="project" />
<orderEntry type="module" module-name="neovim-java-client" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.7.0" level="project" />
</component>
</module>
99 changes: 85 additions & 14 deletions src/NeovimIntellijComplete.java
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
import com.google.common.net.HostAndPort;
import codeinspect.Inspect;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.intellij.codeInsight.CodeSmellInfo;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DataKeys;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CodeSmellDetector;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.file.PsiPackageImpl;
import com.intellij.util.ui.UIUtil;
import com.neovim.*;
import com.neovim.Neovim;
import com.neovim.NeovimHandler;
import com.neovim.SocketNeovim;
import com.neovim.msgpack.MessagePackRPC;
import complete.DeopleteHelper;
import complete.DeopleteItem;
import complete.EmbeditorRequestHandler;
import complete.Problem;
import complete.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class NeovimIntellijComplete extends AnAction {
Expand All @@ -39,17 +37,60 @@ public static class Updater {

private Neovim mNeovim;
private EmbeditorRequestHandler mEmbeditorRequestHandler;
private Fix[] mCachedFixes = new Fix[0];

public Updater(Neovim nvim){
mNeovim = nvim;
mEmbeditorRequestHandler = new EmbeditorRequestHandler();
}

/**
* Hack to have up to date files when doing quickfix stuff...
* @param path
*/
@NeovimHandler("IntellijOnWrite")
public void intellijOnWrite(String path) {
ApplicationManager.getApplication().invokeAndWait(() -> {
PsiFile f = EmbeditorUtil.findTargetFile(path);
if (f != null) {
VirtualFile vf = f.getVirtualFile();
if (vf != null)
vf.refresh(false, true);
}
}, ModalityState.any());
}

@NeovimHandler("TextChanged")
public void changed(String args) {
LOG.info("Text changed");
}

@NeovimHandler("IntellijFixProblem")
public void intellijFixProblem(String path, List<String> lines, int fixId) {
final String fileContent = String.join("\n", lines) ;
for (Fix f : mCachedFixes) {
if (f.getFixId() == fixId) {
Inspect.doFix(path, fileContent, f.getAction());
break;
}
}

}

@NeovimHandler("IntellijProblems")
public Fix[] intellijProblems(String path, List<String> lines, final int row, final int col) {
final String fileContent = String.join("\n", lines) ;
List<HighlightInfo.IntentionActionDescriptor> allFixes = Inspect.getFixes(path, fileContent, row, col);
List<Fix> fixes = new ArrayList<>();
for (int i = 0; i < allFixes.size(); i++) {
HighlightInfo.IntentionActionDescriptor d = allFixes.get(i);
if (d.getAction().getText().length() == 0) continue;
fixes.add(new Fix(d.getAction().getText(), i, d));
}
mCachedFixes = fixes.toArray(new Fix[fixes.size()]);
return mCachedFixes;
}

@NeovimHandler("IntellijCodeSmell")
public Problem[] intellijCodeSmell(final String path, final List<String> lines) {
final String fileContent = String.join("\n", lines);
Expand Down Expand Up @@ -101,6 +142,34 @@ public DeopleteItem[] intellijComplete(final String path, final String bufferCon
});
return dh.getItems();
}

private class Fix {
@JsonProperty
private String description;
@JsonProperty
private int fixId;

@JsonIgnore
private HighlightInfo.IntentionActionDescriptor action;

public Fix(String description, int fixId, HighlightInfo.IntentionActionDescriptor action) {
this.description = description;
this.fixId = fixId;
this.action = action;
}

public String getDescription() {
return description;
}

public int getFixId() {
return fixId;
}

public HighlightInfo.IntentionActionDescriptor getAction() {
return action;
}
}
}

public NeovimIntellijComplete() {
Expand Down Expand Up @@ -128,6 +197,8 @@ public void actionPerformed(AnActionEvent e) {

long cid = mNeovim.getChannelId().join();
mNeovim.commandOutput("let g:intellijID=" + cid);
// Refresh file on intellij on write so we can have uptodate stuff when doing codeanalyzis
mNeovim.commandOutput("au BufWritePost * call rpcnotify(g:intellijID, \"IntellijOnWrite\", expand(\"%:p\"))");
mNeovim.register(new Updater(mNeovim));

mNeovim.sendVimCommand("echo 'Intellij connected.'");
Expand Down
143 changes: 143 additions & 0 deletions src/codeinspect/Inspect.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package codeinspect;

import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.file.impl.FileManager;
import com.intellij.util.ui.UIUtil;
import complete.EmbeditorUtil;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
* Created by ville on 4/19/16.
*/
public class Inspect {
private final static Logger LOG = Logger.getInstance(Inspect.class);

/**
* Runs code analyzis and returns all problems found under cursor (row and col).
*
* @param path
* @param fileContent
* @param row
* @param col
* @return
*/
public static List<HighlightInfo.IntentionActionDescriptor> getFixes(
final String path, @Nullable final String fileContent, final int row, final int col) {
List<HighlightInfo.IntentionActionDescriptor> fixes = new ArrayList<>();
Pair<Document, List<HighlightInfo>> problems = getProblems(path, fileContent);
Document doc = problems.getFirst();
for (HighlightInfo h : problems.getSecond()) {
if (h.quickFixActionRanges == null) continue;
for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> p : h.quickFixActionRanges) {
int offset = EmbeditorUtil.lineAndColumnToOffset(doc, row, col);
if (p.getSecond().contains(offset)) {
fixes.add(p.getFirst());
}
}
}
return fixes;
}

/**
* Runs code anlyzis on document found in path and returns all problems found with the document.
* @param path
* @param fileContent
* @return
*/
private static Pair<Document, List<HighlightInfo>> getProblems(final String path, @Nullable final String fileContent) {
final Ref<PsiFile> psiFileRef = new Ref<>();
final Ref<Editor> editorRef = new Ref<>();
final Ref<Document> docRef = new Ref<>();
final Ref<Project> projectRef = new Ref<>();

UIUtil.invokeAndWaitIfNeeded((Runnable)() -> {
PsiFile targetPsiFile = EmbeditorUtil.findTargetFile(path);
VirtualFile targetVirtualFile = targetPsiFile.getVirtualFile();
Project project = targetPsiFile.getProject();

PsiFile fileCopy = fileContent != null
? EmbeditorUtil.createDummyPsiFile(project, fileContent, targetPsiFile)
: EmbeditorUtil.createDummyPsiFile(project, targetPsiFile.getText(), targetPsiFile);

final Document document = fileCopy.getViewProvider().getDocument();

editorRef.set(EditorFactory.getInstance().createEditor(document, project, targetVirtualFile, false));
psiFileRef.set(targetPsiFile);
docRef.set(document);
projectRef.set(project);
});
Disposable context = Disposer.newDisposable();

Ref<List<HighlightInfo>> highlightInfoList = new Ref<>();

ApplicationManager.getApplication().runReadAction(() -> {
final DaemonProgressIndicator progress = new DaemonProgressIndicator();
Disposer.register(context, progress);

ProgressManager.getInstance().runProcess(() -> {

final DaemonCodeAnalyzerEx analyzer =
DaemonCodeAnalyzerEx.getInstanceEx(projectRef.get());
//analyzer.restart(psiFileRef.get());

// analyze!
highlightInfoList.set(analyzer.runMainPasses(
psiFileRef.get(), docRef.get(), progress));
}, progress);
});
return Pair.create(docRef.get(), highlightInfoList.get());
}

/**
* Invokes action in intentionActionDescriptor on file found in path and writes the file to disk.
*
* @param path
* @param fileContent
* @param intentionActionDescriptor
* @return
*/
public static String doFix(String path, @Nullable String fileContent, HighlightInfo.IntentionActionDescriptor intentionActionDescriptor) {
UIUtil.invokeAndWaitIfNeeded((Runnable)() -> {
PsiFile psiFile = EmbeditorUtil.findTargetFile(path);
Project project = psiFile.getProject();

PsiFile fileCopy = fileContent != null
? EmbeditorUtil.createDummyPsiFile(project, fileContent, psiFile)
: EmbeditorUtil.createDummyPsiFile(project, psiFile.getText(), psiFile);

VirtualFile targetVirtualFile = psiFile.getVirtualFile();
Document document = fileCopy.getViewProvider().getDocument();

Editor editor = EditorFactory.getInstance().createEditor(document, project, targetVirtualFile, false);

intentionActionDescriptor.getAction().invoke(project, editor, fileCopy);

FileDocumentManager.getInstance().saveDocument(psiFile.getViewProvider().getDocument());
});
return null;
}
}
2 changes: 1 addition & 1 deletion src/complete/EmbeditorUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public static PsiFile createDummyPsiFile(Project project, String contents, PsiFi
return psiFile;
}

private static int lineAndColumnToOffset(Document document, int line, int column) {
public static int lineAndColumnToOffset(Document document, int line, int column) {
return document.getLineStartOffset(line) + column;
}

Expand Down

0 comments on commit bb60183

Please sign in to comment.