IdeaVIMの適用範囲をテキストエディタ下のEditorのみにしてみた

休み明けとか言っときながら,しんぼうたまらずやっちゃった。:-)


IdeaVIM使ってる人にしかわからんネタだけど,IdeaVIMはIDEAのエディタをホゲるため大抵のエディタ領域でvimモードが有効になるんだけど,ちょっとやり過ぎなところが気になっていた。たとえば,Refactor->Renameの入力エリアやデバッガ中の"Evaluate Expression"や"Set Value"などがそう。ここでvimモードが有効でもうれしい事は無く,むしろ迷惑(だって大抵の場合,ESCキーはキャンセルなんでの)だった。
ためしに,Renameダイアログで":"を入力してみたら,Exモードになったのには笑った。


ちょいと調べてみたら,IdeaVIMが拡張した*1IDEAのコンポーネントはEditorで,主な用途はテキストエディタなんだけど,汎用コンポーネントなんで他でも使われている(それが前述のRename, Evaluate Expression, Set Valueなど)。どうもね,コード補完が必要な入力んときに使ってるみたい。>Editor


そんなわけで,テキストエディタ以外でもvimモードが有効になる理由はわかった。でも「テキストエディタ外じゃ有効になってほしくない」んで,あれこれ調べてみた。結果,IDEAのテキストエディタ部分はFileEditor->TextEditor->Editorという階層構造になっていて,FileEditorにリスナを設定できるってのがわかった。ちょうどIdeaVIMの各種キーイベントハンドラが,「IdeaVIMが有効かどうか?」という判定が入っていたんで,これとFileEditorのリスナを利用して,こんなパッチを当ててみたよ。

Index: src/com/maddyhome/idea/vim/VimPlugin.java
===================================================================
--- src/com/maddyhome/idea/vim/VimPlugin.java	(revision 1.19.2.12.2.1)
+++ src/com/maddyhome/idea/vim/VimPlugin.java	Sun Mar 23 12:47:50 JST 2008
@@ -32,6 +32,7 @@
 import com.intellij.openapi.editor.event.EditorFactoryEvent;
 import com.intellij.openapi.fileEditor.FileEditorManager;
 import com.intellij.openapi.fileEditor.FileEditorManagerListener;
+import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.project.ProjectManager;
 import com.intellij.openapi.project.ProjectManagerAdapter;
@@ -40,6 +41,7 @@
 import com.intellij.openapi.util.WriteExternalException;
 import com.intellij.openapi.wm.StatusBar;
 import com.intellij.openapi.wm.WindowManager;
+import com.intellij.openapi.vfs.VirtualFile;
 import com.maddyhome.idea.vim.command.CommandState;
 import com.maddyhome.idea.vim.ex.CommandParser;
 import com.maddyhome.idea.vim.group.ChangeGroup;
@@ -60,6 +62,9 @@
 
 import java.awt.Toolkit;
 import java.util.ArrayList;
+import java.util.Set;
+import java.util.Collections;
+import java.util.HashSet;
 
 /**
  * This plugin attempts to emulate the keybinding and general functionality of Vim and gVim. See the supplied
@@ -79,6 +84,7 @@
     public VimPlugin()
     {
         logger.debug("VimPlugin ctr");
+        editorSet = Collections.synchronizedSet(new HashSet<Editor>());
 
         /*
         java.net.URL resource = getClass().getResource("/icons/vim32x32.png");
@@ -166,6 +172,8 @@
                 EditorData.uninitializeEditor(event.getEditor());
                 event.getEditor().getSettings().setAnimatedScrolling(isSmoothScrolling);
                 DocumentManager.getInstance().removeListeners(event.getEditor().getDocument());
+
+                editorSet.remove(event.getEditor());
             }
         });
 
@@ -193,6 +201,14 @@
                 {
                     FileEditorManager.getInstance(project).addFileEditorManagerListener(listener);
                 }
+                FileEditorManager.getInstance(project).addFileEditorManagerListener(new FileEditorManagerListener() {
+                    public void fileOpened(FileEditorManager fileEditorManager, VirtualFile virtualFile) {
+                        Editor editor = fileEditorManager.getSelectedTextEditor();
+                        editorSet.add(editor);
+                    }
+                    public void fileClosed(FileEditorManager fileEditorManager, VirtualFile virtualFile) { }
+                    public void selectionChanged(FileEditorManagerEvent event) { }
+                });
 
                 //DocumentManager.getInstance().openProject(project);
 
@@ -303,6 +319,12 @@
         //KeyParser.getInstance().saveData(element);
     }
 
+    private Set<Editor> editorSet;
+
+    public static boolean isEnabled(Editor editor) {
+        return getInstance().editorSet.contains(editor) && isEnabled();
+    }
+
     /**
      * Indicates whether the user has enabled or disabled the plugin
      *
Index: src/com/maddyhome/idea/vim/handler/key/EditorKeyHandler.java
===================================================================
--- src/com/maddyhome/idea/vim/handler/key/EditorKeyHandler.java	(revision 1.2.4.3.4.1)
+++ src/com/maddyhome/idea/vim/handler/key/EditorKeyHandler.java	Sat Mar 22 21:09:16 JST 2008
@@ -52,7 +52,7 @@
     {
         logger.debug("execute");
         //if (isEnabled(editor, context))
-        if (editor != null && VimPlugin.isEnabled())
+        if (editor != null && VimPlugin.isEnabled(editor))
         {
             handle(editor, context);
         }
Index: src/com/maddyhome/idea/vim/VimTypedActionHandler.java
===================================================================
--- src/com/maddyhome/idea/vim/VimTypedActionHandler.java	(revision 1.1.1.1.4.1.8.1)
+++ src/com/maddyhome/idea/vim/VimTypedActionHandler.java	Sun Mar 23 12:47:49 JST 2008
@@ -63,7 +63,7 @@
     public void execute(Editor editor, char charTyped, DataContext context)
     {
         // If the plugin is disabled we simply resend the character to the original handler
-        if (!VimPlugin.isEnabled())
+        if (!VimPlugin.isEnabled(editor))
         {
             origHandler.execute(editor, charTyped, context);
             return;

ちょっとダーティハックだけど,今んとこ結果オーライで動いている(カーソル形状がブロックカーソルのままなのはご愛嬌)。


(追記)さらに,こんなパッチあててブロックカーソルもなんとかしたよ。

Index: src/com/maddyhome/idea/vim/VimPlugin.java
===================================================================
--- src/com/maddyhome/idea/vim/VimPlugin.java	Mon Mar 24 14:38:58 JST 2008
+++ src/com/maddyhome/idea/vim/VimPlugin.java	Mon Mar 24 14:38:58 JST 2008
@@ -154,12 +154,6 @@
                 isBlockCursor = event.getEditor().getSettings().isBlockCursor();
                 isSmoothScrolling = event.getEditor().getSettings().isAnimatedScrolling();
 
-                if (VimPlugin.isEnabled())
-                {
-                    event.getEditor().getSettings().setBlockCursor(!CommandState.inInsertMode(event.getEditor()));
-                    event.getEditor().getSettings().setAnimatedScrolling(false);
-                }
-
                 EditorData.initializeEditor(event.getEditor());
                 //if (EditorData.getVirtualFile(event.getEditor()) == null)
                 //{
@@ -204,6 +198,10 @@
                 FileEditorManager.getInstance(project).addFileEditorManagerListener(new FileEditorManagerListener() {
                     public void fileOpened(FileEditorManager fileEditorManager, VirtualFile virtualFile) {
                         Editor editor = fileEditorManager.getSelectedTextEditor();
+                        if (VimPlugin.isEnabled()) {
+                            editor.getSettings().setBlockCursor(!CommandState.inInsertMode(editor));
+                            editor.getSettings().setAnimatedScrolling(false);
+                        }
                         editorSet.add(editor);
                     }
                     public void fileClosed(FileEditorManager fileEditorManager, VirtualFile virtualFile) { }

*1:正確にはちょっと違う