diff --git a/neovim-intellij-complete.iml b/neovim-intellij-complete.iml
index f78988f..227d8f9 100644
--- a/neovim-intellij-complete.iml
+++ b/neovim-intellij-complete.iml
@@ -9,8 +9,7 @@
-
-
+
\ No newline at end of file
diff --git a/src/NeovimIntellijComplete.java b/src/NeovimIntellijComplete.java
index 9b87359..bad43b0 100644
--- a/src/NeovimIntellijComplete.java
+++ b/src/NeovimIntellijComplete.java
@@ -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 {
@@ -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 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 lines, final int row, final int col) {
+ final String fileContent = String.join("\n", lines) ;
+ List allFixes = Inspect.getFixes(path, fileContent, row, col);
+ List 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 lines) {
final String fileContent = String.join("\n", lines);
@@ -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() {
@@ -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.'");
diff --git a/src/codeinspect/Inspect.java b/src/codeinspect/Inspect.java
new file mode 100644
index 0000000..f75ae85
--- /dev/null
+++ b/src/codeinspect/Inspect.java
@@ -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 getFixes(
+ final String path, @Nullable final String fileContent, final int row, final int col) {
+ List fixes = new ArrayList<>();
+ Pair> problems = getProblems(path, fileContent);
+ Document doc = problems.getFirst();
+ for (HighlightInfo h : problems.getSecond()) {
+ if (h.quickFixActionRanges == null) continue;
+ for (Pair 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> getProblems(final String path, @Nullable final String fileContent) {
+ final Ref psiFileRef = new Ref<>();
+ final Ref editorRef = new Ref<>();
+ final Ref docRef = new Ref<>();
+ final Ref 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> 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;
+ }
+}
diff --git a/src/complete/EmbeditorUtil.java b/src/complete/EmbeditorUtil.java
index d9aabc1..1faccec 100644
--- a/src/complete/EmbeditorUtil.java
+++ b/src/complete/EmbeditorUtil.java
@@ -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;
}