mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c4eb8e5752 | ||
![]() |
7702d3a695 | ||
![]() |
fbff8ed2c0 | ||
![]() |
5b5b6188da | ||
![]() |
6a1707a7e6 | ||
![]() |
b625fd3df9 | ||
![]() |
ffaf2cc451 | ||
![]() |
dcc9b5a37e | ||
![]() |
1b4a51f770 | ||
![]() |
f8d2ee2dd7 | ||
![]() |
348d88d543 | ||
![]() |
38b859e555 | ||
![]() |
ca8026f612 | ||
![]() |
befcf4298f | ||
![]() |
2e0bbcc75d | ||
![]() |
2204fe6451 | ||
![]() |
12bc68bc02 | ||
![]() |
290370f744 | ||
![]() |
79acef46ce | ||
![]() |
0054eb5279 | ||
![]() |
e0eb919e51 | ||
![]() |
7c3d5a7946 | ||
![]() |
38cb20d1bd | ||
![]() |
505fcf5b31 | ||
![]() |
d744d45ccd | ||
![]() |
2e2e7a7df6 | ||
![]() |
db5ff05bb3 | ||
![]() |
566273fe83 | ||
![]() |
decb17e665 | ||
![]() |
46c27bbd10 | ||
![]() |
72f98cef14 | ||
![]() |
0f8cfa289d | ||
![]() |
0cb420c557 | ||
![]() |
79df0b0594 | ||
![]() |
dfbddb4050 | ||
![]() |
0f88495f29 |
@@ -21,6 +21,7 @@ Contributors:
|
|||||||
* [Masanobu Imai](mailto:masanobu.imai@gmail.com)
|
* [Masanobu Imai](mailto:masanobu.imai@gmail.com)
|
||||||
* [poxu](mailto:poxvuibr@gmail.com)
|
* [poxu](mailto:poxvuibr@gmail.com)
|
||||||
* [Alexander Zolotov](mailto:alexander.zolotov@jetbrains.com)
|
* [Alexander Zolotov](mailto:alexander.zolotov@jetbrains.com)
|
||||||
|
* [John Lindquist](mailto:johnlindquist@gmail.com)
|
||||||
|
|
||||||
If you are a contributor and your name is not listed here, feel free to
|
If you are a contributor and your name is not listed here, feel free to
|
||||||
contact the maintainer.
|
contact the maintainer.
|
||||||
|
26
CHANGES.md
26
CHANGES.md
@@ -4,6 +4,28 @@ The Changelog
|
|||||||
History of changes in IdeaVim for the IntelliJ platform.
|
History of changes in IdeaVim for the IntelliJ platform.
|
||||||
|
|
||||||
|
|
||||||
|
0.27, 2013-04-03
|
||||||
|
----------------
|
||||||
|
|
||||||
|
New Vim keymap generator creates better keymaps, especially for Mac OS X.
|
||||||
|
Restart after reconfiguring the keymap is no longer required.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
|
||||||
|
* VIM-92 Better Vim keymaps for Mac OS X
|
||||||
|
* VIM-286 Ask if the plugin should enable repeating keys in Mac OS X
|
||||||
|
|
||||||
|
Bug fixes:
|
||||||
|
|
||||||
|
* VIM-42 Fixed code completion for the `.` command
|
||||||
|
* VIM-421 Fixed `cw` on the last character in line
|
||||||
|
* VIM-419 Fixed resetting cursor position after 'gt' and 'gT'
|
||||||
|
* VIM-233 Fixed code completion for edits in visual block mode
|
||||||
|
* VIM-404 Fixed `O` command at the first line
|
||||||
|
* VIM-472 Fixed right selection in visual mode to be one char more
|
||||||
|
* Fixed command window font size to match editor font size
|
||||||
|
|
||||||
|
|
||||||
0.26, 2012-12-26
|
0.26, 2012-12-26
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@@ -17,11 +39,11 @@ Features:
|
|||||||
* VIM-262 Support for paste from register in command mode
|
* VIM-262 Support for paste from register in command mode
|
||||||
* VIM-214 Key bindings for paste into command line
|
* VIM-214 Key bindings for paste into command line
|
||||||
* VIM-43 Added support for the last change position mark
|
* VIM-43 Added support for the last change position mark
|
||||||
* VIM-177 Added <C-]> and <C-T> to the keymap
|
* VIM-177 Added `<C-]>` and `<C-T>` to the keymap
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
|
|
||||||
* VIM-302 Fixed tab switching order for 'gt' and 'gT'
|
* VIM-302 Fixed tab switching order for `gt` and `gT`
|
||||||
|
|
||||||
|
|
||||||
0.25, 2012-12-19
|
0.25, 2012-12-19
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
version-id:0.26
|
version-id:0.27
|
||||||
platform-version:110.0
|
platform-version:110.0
|
||||||
idea.download.url=http://download.jetbrains.com/idea/ideaIU-12.0.zip
|
idea.download.url=http://download.jetbrains.com/idea/ideaIU-12.0.zip
|
||||||
build.number=x
|
build.number=x
|
||||||
|
@@ -65,6 +65,8 @@ tag char note action in Normal mode ~
|
|||||||
|count| 9 "
|
|count| 9 "
|
||||||
|F| F{char} 1 cursor to the Nth occurrence of {char} to
|
|F| F{char} 1 cursor to the Nth occurrence of {char} to
|
||||||
the left
|
the left
|
||||||
|
|O| O 2 begin a new line above the cursor and
|
||||||
|
insert text, repeat N times
|
||||||
|P| ["x]P 2 put the text [from buffer x] before the
|
|P| ["x]P 2 put the text [from buffer x] before the
|
||||||
cursor N times
|
cursor N times
|
||||||
|T| T{char} 1 cursor till after Nth occurrence of {char}
|
|T| T{char} 1 cursor till after Nth occurrence of {char}
|
||||||
|
@@ -3,6 +3,13 @@
|
|||||||
<id>IdeaVIM</id>
|
<id>IdeaVIM</id>
|
||||||
<change-notes>
|
<change-notes>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
<p>0.27:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Better Vim keymaps for Mac OS X</li>
|
||||||
|
<li>Ask if the plugin should enable repeating keys in Mac OS X</li>
|
||||||
|
<li>Fixed a long-standing bug with code completion and repeat last change command ('<code>.</code>')</li>
|
||||||
|
<li>Various bug fixes</li>
|
||||||
|
</ul>
|
||||||
<p>0.26:</p>
|
<p>0.26:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Added support for paste in the command mode: from a register using <code><C-R></code>, from the clipboard using <code><S-Insert></code> or <code><M-V></code></li>
|
<li>Added support for paste in the command mode: from a register using <code><C-R></code>, from the clipboard using <code><S-Insert></code> or <code><M-V></code></li>
|
||||||
@@ -64,11 +71,11 @@
|
|||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.VimPluginToggleAction" text="VIM Emulator" description="Toggle the Vim Plugin On/Off">
|
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.VimPluginToggleAction" text="Vim Emulator" description="Toggle the Vim Plugin On/Off">
|
||||||
<keyboard-shortcut first-keystroke="control alt V" keymap="$default"/>
|
<keyboard-shortcut first-keystroke="control alt V" keymap="$default"/>
|
||||||
<add-to-group group-id="ToolsMenu" anchor="last"/>
|
<add-to-group group-id="ToolsMenu" anchor="last"/>
|
||||||
</action>
|
</action>
|
||||||
<action id="VimReconfigureKeymap" class="com.maddyhome.idea.vim.VimReconfigureKeymapAction" text="Reconfigure VIM Keymap" description="Reconfigure VIM keymap">
|
<action id="VimReconfigureKeymap" class="com.maddyhome.idea.vim.VimReconfigureKeymapAction" text="Reconfigure Vim Keymap" description="Reconfigure Vim keymap">
|
||||||
<add-to-group group-id="ToolsMenu" relative-to-action="after VimPluginToggle"/>
|
<add-to-group group-id="ToolsMenu" relative-to-action="after VimPluginToggle"/>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
@@ -1,129 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<keymap version="1" name="Vim" disable-mnemonics="false" parent="$default">
|
<keymap version="1" name="Vim" disable-mnemonics="false" parent="$default">
|
||||||
<action id="$Copy">
|
|
||||||
<keyboard-shortcut first-keystroke="control INSERT" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta C" />
|
|
||||||
</action>
|
|
||||||
<action id="$Cut">
|
|
||||||
<keyboard-shortcut first-keystroke="shift DELETE" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta X" />
|
|
||||||
</action>
|
|
||||||
<action id="$Paste">
|
|
||||||
<keyboard-shortcut first-keystroke="shift INSERT" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta V" />
|
|
||||||
</action>
|
|
||||||
<action id="$Redo">
|
|
||||||
<keyboard-shortcut first-keystroke="shift alt BACK_SPACE" />
|
|
||||||
</action>
|
|
||||||
<action id="$SelectAll" />
|
|
||||||
<action id="$Undo">
|
|
||||||
<keyboard-shortcut first-keystroke="alt BACK_SPACE" />
|
|
||||||
</action>
|
|
||||||
<action id="CheckinProject" />
|
|
||||||
<action id="ClassNameCompletion" />
|
|
||||||
<action id="ClearCase.CheckInProject" />
|
|
||||||
<action id="CodeCompletion" />
|
|
||||||
<action id="CommanderSwapPanels" />
|
|
||||||
<action id="ContextHelp" />
|
|
||||||
<action id="EditorBackSpace">
|
|
||||||
<keyboard-shortcut first-keystroke="BACK_SPACE" />
|
|
||||||
</action>
|
|
||||||
<action id="EditorCodeBlockEnd" />
|
|
||||||
<action id="EditorCodeBlockStart" />
|
|
||||||
<action id="EditorCopy">
|
|
||||||
<keyboard-shortcut first-keystroke="control INSERT" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta C" />
|
|
||||||
</action>
|
|
||||||
<action id="EditorCut">
|
|
||||||
<keyboard-shortcut first-keystroke="shift DELETE" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta X" />
|
|
||||||
</action>
|
|
||||||
<action id="EditorDeleteLine" />
|
|
||||||
<action id="EditorDeleteToWordEnd" />
|
|
||||||
<action id="EditorDeleteToWordStart" />
|
|
||||||
<action id="EditorDownWithSelection" />
|
|
||||||
<action id="EditorDuplicate" />
|
|
||||||
<action id="EditorIndentSelection" />
|
|
||||||
<action id="EditorJoinLines" />
|
|
||||||
<action id="EditorLeftWithSelection" />
|
|
||||||
<action id="EditorLineEndWithSelection" />
|
|
||||||
<action id="EditorLineStartWithSelection" />
|
|
||||||
<action id="EditorMoveToPageBottom" />
|
|
||||||
<action id="EditorMoveToPageBottomWithSelection" />
|
|
||||||
<action id="EditorMoveToPageTop" />
|
|
||||||
<action id="EditorMoveToPageTopWithSelection" />
|
|
||||||
<action id="EditorNextWord" />
|
|
||||||
<action id="EditorNextWordWithSelection" />
|
|
||||||
<action id="EditorPageDownWithSelection" />
|
|
||||||
<action id="EditorPageUpWithSelection" />
|
|
||||||
<action id="EditorPaste">
|
|
||||||
<keyboard-shortcut first-keystroke="shift INSERT" />
|
|
||||||
<keyboard-shortcut first-keystroke="meta V" />
|
|
||||||
</action>
|
|
||||||
<action id="EditorPreviousWord" />
|
|
||||||
<action id="EditorPreviousWordWithSelection" />
|
|
||||||
<action id="EditorRedo" />
|
|
||||||
<action id="EditorRightWithSelection" />
|
|
||||||
<action id="EditorScrollDown" />
|
|
||||||
<action id="EditorScrollToCenter" />
|
|
||||||
<action id="EditorScrollUp" />
|
|
||||||
<action id="EditorSelectAll" />
|
|
||||||
<action id="EditorSelectWord" />
|
|
||||||
<action id="EditorSplitLine" />
|
|
||||||
<action id="EditorStartNewLine" />
|
|
||||||
<action id="EditorTextEnd" />
|
|
||||||
<action id="EditorTextEndWithSelection" />
|
|
||||||
<action id="EditorTextStart" />
|
|
||||||
<action id="EditorTextStartWithSelection" />
|
|
||||||
<action id="EditorToggleCase" />
|
|
||||||
<action id="EditorUnSelectWord" />
|
|
||||||
<action id="EditorUndo" />
|
|
||||||
<action id="EditorUnindentSelection" />
|
|
||||||
<action id="EditorUpWithSelection" />
|
|
||||||
<action id="Find" />
|
|
||||||
<action id="FindNext">
|
|
||||||
<keyboard-shortcut first-keystroke="F3" />
|
|
||||||
</action>
|
|
||||||
<action id="FindPrevious">
|
|
||||||
<keyboard-shortcut first-keystroke="shift F3" />
|
|
||||||
</action>
|
|
||||||
<action id="GotoBookmark2" />
|
|
||||||
<action id="GotoClass">
|
|
||||||
<keyboard-shortcut first-keystroke="shift alt N" />
|
|
||||||
</action>
|
|
||||||
<action id="GotoDeclaration">
|
|
||||||
<mouse-shortcut keystroke="control button1" />
|
|
||||||
<mouse-shortcut keystroke="button2" />
|
|
||||||
</action>
|
|
||||||
<action id="GotoLine" />
|
|
||||||
<action id="GotoSuperMethod">
|
|
||||||
<keyboard-shortcut first-keystroke="shift control U" />
|
|
||||||
</action>
|
|
||||||
<action id="ImplementMethods">
|
|
||||||
<keyboard-shortcut first-keystroke="shift control I" />
|
|
||||||
</action>
|
|
||||||
<action id="InsertLiveTemplate" />
|
|
||||||
<action id="MethodHierarchy.ImplementMethodAction" />
|
|
||||||
<action id="MethodHierarchy.OverrideMethodAction" />
|
|
||||||
<action id="OverrideMethods">
|
|
||||||
<keyboard-shortcut first-keystroke="shift control O" />
|
|
||||||
</action>
|
|
||||||
<action id="ParameterInfo">
|
|
||||||
<keyboard-shortcut first-keystroke="shift control P" />
|
|
||||||
</action>
|
|
||||||
<action id="QuickJavaDoc">
|
|
||||||
<mouse-shortcut keystroke="alt button2" />
|
|
||||||
</action>
|
|
||||||
<action id="RecentFiles" />
|
|
||||||
<action id="Replace" />
|
|
||||||
<action id="SaveAll" />
|
|
||||||
<action id="SmartTypeCompletion" />
|
|
||||||
<action id="Starteam.CheckinProject" />
|
|
||||||
<action id="Subversion.CommitProject" />
|
|
||||||
<action id="ToggleBookmark2" />
|
|
||||||
<action id="TypeHierarchy">
|
|
||||||
<keyboard-shortcut first-keystroke="shift control alt H" />
|
|
||||||
</action>
|
|
||||||
<action id="VimKeyHandler">
|
<action id="VimKeyHandler">
|
||||||
<keyboard-shortcut first-keystroke="control 2" />
|
<keyboard-shortcut first-keystroke="control 2" />
|
||||||
<keyboard-shortcut first-keystroke="control A" />
|
<keyboard-shortcut first-keystroke="control A" />
|
||||||
@@ -145,7 +21,6 @@
|
|||||||
<keyboard-shortcut first-keystroke="control L" />
|
<keyboard-shortcut first-keystroke="control L" />
|
||||||
<keyboard-shortcut first-keystroke="control LEFT" />
|
<keyboard-shortcut first-keystroke="control LEFT" />
|
||||||
<keyboard-shortcut first-keystroke="control M" />
|
<keyboard-shortcut first-keystroke="control M" />
|
||||||
<keyboard-shortcut first-keystroke="control N" />
|
|
||||||
<keyboard-shortcut first-keystroke="control O" />
|
<keyboard-shortcut first-keystroke="control O" />
|
||||||
<keyboard-shortcut first-keystroke="control OPEN_BRACKET" />
|
<keyboard-shortcut first-keystroke="control OPEN_BRACKET" />
|
||||||
<keyboard-shortcut first-keystroke="control P" />
|
<keyboard-shortcut first-keystroke="control P" />
|
||||||
|
@@ -56,10 +56,6 @@ public class VimKeyMapUtil {
|
|||||||
*/
|
*/
|
||||||
public static boolean installKeyBoardBindings() {
|
public static boolean installKeyBoardBindings() {
|
||||||
LOG.debug("Check for keyboard bindings");
|
LOG.debug("Check for keyboard bindings");
|
||||||
if (isVimKeymapInstalled()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
|
final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
|
||||||
if (localFileSystem.refreshAndFindFileByPath(KEYMAPS_PATH) == null) {
|
if (localFileSystem.refreshAndFindFileByPath(KEYMAPS_PATH) == null) {
|
||||||
reportError("Failed to install vim keymap. Empty keymaps folder");
|
reportError("Failed to install vim keymap. Empty keymaps folder");
|
||||||
@@ -73,7 +69,7 @@ public class VimKeyMapUtil {
|
|||||||
final Document document = StorageUtil.loadDocument(bytes);
|
final Document document = StorageUtil.loadDocument(bytes);
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
// Prompt user to select the parent for the Vim keyboard
|
// Prompt user to select the parent for the Vim keyboard
|
||||||
configureVimParentKeymap(INSTALLED_VIM_KEYMAP_PATH, document, false);
|
configureVimParentKeymap(INSTALLED_VIM_KEYMAP_PATH, document, true);
|
||||||
}
|
}
|
||||||
installKeymap(document);
|
installKeymap(document);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -135,13 +131,14 @@ public class VimKeyMapUtil {
|
|||||||
final Keymap selectedKeymap = vimKeymapDialog.getSelectedKeymap();
|
final Keymap selectedKeymap = vimKeymapDialog.getSelectedKeymap();
|
||||||
final String keymapName = selectedKeymap.getName();
|
final String keymapName = selectedKeymap.getName();
|
||||||
rootElement.setAttribute("parent", keymapName);
|
rootElement.setAttribute("parent", keymapName);
|
||||||
|
VimPlugin.getInstance().setPreviousKeyMap(keymapName);
|
||||||
|
VimKeymapConflictResolveUtil.resolveConflicts(rootElement, selectedKeymap);
|
||||||
// Save modified keymap to the file
|
// Save modified keymap to the file
|
||||||
JDOMUtil.writeDocument(document, path, "\n");
|
JDOMUtil.writeDocument(document, path, "\n");
|
||||||
if (showNotification) {
|
if (showNotification) {
|
||||||
Notifications.Bus.notify(new Notification(VimPlugin.IDEAVIM_NOTIFICATION_ID, VimPlugin.IDEAVIM_NOTIFICATION_TITLE,
|
Notifications.Bus.notify(new Notification(VimPlugin.IDEAVIM_NOTIFICATION_ID, VimPlugin.IDEAVIM_NOTIFICATION_TITLE,
|
||||||
"Successfully configured vim keymap to be based on " +
|
"Successfully configured vim keymap to be based on " +
|
||||||
selectedKeymap.getPresentableName(),
|
selectedKeymap.getPresentableName(),
|
||||||
NotificationType.INFORMATION));
|
NotificationType.INFORMATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,30 +191,6 @@ public class VimKeyMapUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void reconfigureParentKeymap(final Project project) {
|
|
||||||
final VirtualFile vimKeymapFile = getVimKeymapFile();
|
|
||||||
if (vimKeymapFile == null) {
|
|
||||||
reportError("Failed to find Vim keymap");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final Document document = StorageUtil.loadDocument(new FileInputStream(vimKeymapFile.getPath()));
|
|
||||||
// Prompt user to select the parent for the Vim keyboard
|
|
||||||
if (configureVimParentKeymap(vimKeymapFile.getPath(), document, true)) {
|
|
||||||
installKeymap(document);
|
|
||||||
requestRestartOrShutdown(project);
|
|
||||||
}
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
reportError("Failed to install vim keymap.", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
reportError("Failed to install vim keymap.", e);
|
|
||||||
} catch (InvalidDataException e) {
|
|
||||||
reportError("Failed to install vim keymap. Vim.xml file is corrupted", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static InputStream retrieveSourceKeymapStream() throws IOException {
|
private static InputStream retrieveSourceKeymapStream() throws IOException {
|
||||||
String keymapPath = PATH_JOINER.join(PathManager.getPluginsPath(), VimPlugin.IDEAVIM_NOTIFICATION_TITLE, VIM_XML);
|
String keymapPath = PATH_JOINER.join(PathManager.getPluginsPath(), VimPlugin.IDEAVIM_NOTIFICATION_TITLE, VIM_XML);
|
||||||
|
217
src/com/maddyhome/idea/vim/VimKeymapConflictResolveUtil.java
Normal file
217
src/com/maddyhome/idea/vim/VimKeymapConflictResolveUtil.java
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package com.maddyhome.idea.vim;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.intellij.openapi.actionSystem.KeyboardShortcut;
|
||||||
|
import com.intellij.openapi.actionSystem.MouseShortcut;
|
||||||
|
import com.intellij.openapi.actionSystem.Shortcut;
|
||||||
|
import com.intellij.openapi.keymap.Keymap;
|
||||||
|
import com.intellij.openapi.keymap.impl.KeymapImpl;
|
||||||
|
import com.intellij.openapi.util.SystemInfo;
|
||||||
|
import org.jdom.Element;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.InputEvent;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Lists.newLinkedList;
|
||||||
|
import static com.google.common.collect.Maps.newHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: zolotov
|
||||||
|
* Date: 1/8/13
|
||||||
|
*/
|
||||||
|
public class VimKeymapConflictResolveUtil {
|
||||||
|
private static final String SHIFT = "shift";
|
||||||
|
private static final String CONTROL = "control";
|
||||||
|
private static final String META = "meta";
|
||||||
|
private static final String ALT = "alt";
|
||||||
|
private static final String ALT_GRAPH = "altGraph";
|
||||||
|
private static final String DOUBLE_CLICK = "doubleClick";
|
||||||
|
|
||||||
|
private static final String VIM_KEY_HANDLER_ACTION_ID = "VimKeyHandler";
|
||||||
|
private static final String ACTION_TAG = "action";
|
||||||
|
private static final String KEYBOARD_SHORTCUT_TAG = "keyboard-shortcut";
|
||||||
|
private static final String MOUSE_SHORTCUT_TAG = "mouse-shortcut";
|
||||||
|
private static final String ID_ATTRIBUTE = "id";
|
||||||
|
private static final String FIRST_KEYSTROKE_ATTRIBUTE = "first-keystroke";
|
||||||
|
private static final String KEYSTROKE_ATTRIBUTE = "keystroke";
|
||||||
|
private static final String SECOND_KEYSTROKE_ATTRIBUTE = "second-keystroke";
|
||||||
|
|
||||||
|
private static final List<Integer> ALTERNATIVE_MODIFIERS = ImmutableList.of(
|
||||||
|
InputEvent.ALT_DOWN_MASK,
|
||||||
|
InputEvent.CTRL_DOWN_MASK,
|
||||||
|
InputEvent.SHIFT_DOWN_MASK,
|
||||||
|
InputEvent.META_DOWN_MASK,
|
||||||
|
InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK,
|
||||||
|
InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK,
|
||||||
|
InputEvent.ALT_DOWN_MASK | InputEvent.META_DOWN_MASK,
|
||||||
|
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK,
|
||||||
|
InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||||
|
|
||||||
|
public static void resolveConflicts(Element targetKeymapRoot, Keymap parentKeymap) {
|
||||||
|
final Collection<String> vimHandlingShortcuts = getVimHandlingShortcuts(targetKeymapRoot);
|
||||||
|
final Map<String, List<Shortcut>> shortcutsToOverride = retrieveShortcutsToOverride(vimHandlingShortcuts, parentKeymap);
|
||||||
|
overrideShortcuts(targetKeymapRoot, shortcutsToOverride);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param targetKeymapRoot root element of Vim keymap
|
||||||
|
* @return all shortcuts in string representation that should be handled by Vim plugin
|
||||||
|
*/
|
||||||
|
private static Collection<String> getVimHandlingShortcuts(Element targetKeymapRoot) {
|
||||||
|
Element vimKeyHandlerAction = retrieveActionElement(targetKeymapRoot);
|
||||||
|
if (vimKeyHandlerAction != null) {
|
||||||
|
Collection<String> result = newLinkedList();
|
||||||
|
for (Object childAction : vimKeyHandlerAction.getChildren()) {
|
||||||
|
if (childAction instanceof Element) {
|
||||||
|
Element shortcut = (Element)childAction;
|
||||||
|
result.add(shortcut.getAttributeValue(FIRST_KEYSTROKE_ATTRIBUTE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param targetKeymapRoot root element of Vim keymap
|
||||||
|
* @return Retrieve VimKeyHandler action element
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private static Element retrieveActionElement(Element targetKeymapRoot) {
|
||||||
|
Element vimKeyHandlerAction = null;
|
||||||
|
for (Object child : targetKeymapRoot.getChildren(ACTION_TAG)) {
|
||||||
|
if (child instanceof Element) {
|
||||||
|
Element action = (Element)child;
|
||||||
|
if (VIM_KEY_HANDLER_ACTION_ID.equals(action.getAttributeValue(ID_ATTRIBUTE))) {
|
||||||
|
vimKeyHandlerAction = action;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vimKeyHandlerAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param vimHandlingShortcuts collection of shortcuts that should be handled by Vim plugin
|
||||||
|
* @param parentKeymap selected parent keymap for vim keymap
|
||||||
|
* @return mapping of action names to its shortcuts that we should save in Vim-keymap
|
||||||
|
* (or empty list of shortcuts if action just should be disabled)
|
||||||
|
*/
|
||||||
|
private static Map<String, List<Shortcut>> retrieveShortcutsToOverride(Collection<String> vimHandlingShortcuts, Keymap parentKeymap) {
|
||||||
|
Map<String, List<Shortcut>> result = newHashMap();
|
||||||
|
for (String shortcut : vimHandlingShortcuts) {
|
||||||
|
final Map<String, ArrayList<KeyboardShortcut>> conflicts = parentKeymap.getConflicts("", KeyboardShortcut.fromString(shortcut));
|
||||||
|
for (Map.Entry<String, ArrayList<KeyboardShortcut>> conflict : conflicts.entrySet()) {
|
||||||
|
String actionName = conflict.getKey();
|
||||||
|
final ArrayList<KeyboardShortcut> conflictedShortcuts = conflict.getValue();
|
||||||
|
if (result.containsKey(actionName)) {
|
||||||
|
// found another conflict for already overridden action
|
||||||
|
List<Shortcut> overridesShortcuts = result.get(actionName);
|
||||||
|
for (KeyboardShortcut conflictedShortcut : conflictedShortcuts) {
|
||||||
|
overridesShortcuts.remove(conflictedShortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// let's override action with all non-conflict shortcuts
|
||||||
|
List<Shortcut> overriddenShortcuts = newLinkedList();
|
||||||
|
for (Shortcut actionShortcut : parentKeymap.getShortcuts(actionName)) {
|
||||||
|
if (!(actionShortcut instanceof KeyboardShortcut) || !conflictedShortcuts.contains(actionShortcut)) {
|
||||||
|
overriddenShortcuts.add(actionShortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (overriddenShortcuts.isEmpty()) {
|
||||||
|
for (Integer modifier : ALTERNATIVE_MODIFIERS) {
|
||||||
|
if (!SystemInfo.isMac && (modifier & InputEvent.META_DOWN_MASK) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final KeyStroke originalStroke = KeyStroke.getKeyStroke(shortcut);
|
||||||
|
final int modifiers = originalStroke.getModifiers() | modifier;
|
||||||
|
//noinspection MagicConstant
|
||||||
|
final KeyStroke stroke = KeyStroke.getKeyStroke(originalStroke.getKeyCode(), modifiers);
|
||||||
|
final KeyboardShortcut alternativeShortcut = new KeyboardShortcut(stroke, null);
|
||||||
|
if (parentKeymap.getConflicts("", alternativeShortcut).isEmpty()) {
|
||||||
|
overriddenShortcuts.add(alternativeShortcut);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
result.put(actionName, overriddenShortcuts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill vim keymap with overridden actions.
|
||||||
|
* Only keyboard and mouse shortcuts will be overridden.
|
||||||
|
*
|
||||||
|
* @param targetKeymapRoot root element of Vim keymap
|
||||||
|
* @param shortcutsToOverride overriding mapping: actions -> shortcuts_should_be_saved
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static void overrideShortcuts(Element targetKeymapRoot, Map<String, List<Shortcut>> shortcutsToOverride) {
|
||||||
|
for (Map.Entry<String, List<Shortcut>> action : shortcutsToOverride.entrySet()) {
|
||||||
|
targetKeymapRoot.addContent(createActionElement(action.getKey(), action.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Element createActionElement(String actionName, List<Shortcut> shortcuts) {
|
||||||
|
final Element overridesAction = new Element(ACTION_TAG);
|
||||||
|
overridesAction.setAttribute(ID_ATTRIBUTE, actionName);
|
||||||
|
for (Shortcut shortcut : shortcuts) {
|
||||||
|
if (shortcut instanceof KeyboardShortcut) {
|
||||||
|
KeyboardShortcut keyboardShortcut = (KeyboardShortcut)shortcut;
|
||||||
|
overridesAction
|
||||||
|
.addContent(createShortcutElement(KEYBOARD_SHORTCUT_TAG, FIRST_KEYSTROKE_ATTRIBUTE, KeymapImpl.getKeyShortcutString(keyboardShortcut.getFirstKeyStroke())));
|
||||||
|
}
|
||||||
|
else if (shortcut instanceof MouseShortcut) {
|
||||||
|
overridesAction.addContent(createShortcutElement(MOUSE_SHORTCUT_TAG, KEYSTROKE_ATTRIBUTE, getMouseShortcutString((MouseShortcut)shortcut)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return overridesAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Element createShortcutElement(String elementName, String shortcutAttributeName, String shortcut) {
|
||||||
|
final Element shortcutElement = new Element(elementName);
|
||||||
|
shortcutElement.setAttribute(shortcutAttributeName, shortcut);
|
||||||
|
return shortcutElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create string representation of mouse shortcut
|
||||||
|
* KeymapImpl has implementation for mouse shortcut marshaling, but it is private :-(
|
||||||
|
*
|
||||||
|
* @param shortcut mouse shortcut
|
||||||
|
* @return string representation of mouse shortcut
|
||||||
|
*/
|
||||||
|
private static String getMouseShortcutString(MouseShortcut shortcut) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
int modifiers = shortcut.getModifiers();
|
||||||
|
if ((MouseEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
|
||||||
|
builder.append(SHIFT).append(' ');
|
||||||
|
}
|
||||||
|
if ((MouseEvent.CTRL_DOWN_MASK & modifiers) > 0) {
|
||||||
|
builder.append(CONTROL).append(' ');
|
||||||
|
}
|
||||||
|
if ((MouseEvent.META_DOWN_MASK & modifiers) > 0) {
|
||||||
|
builder.append(META).append(' ');
|
||||||
|
}
|
||||||
|
if ((MouseEvent.ALT_DOWN_MASK & modifiers) > 0) {
|
||||||
|
builder.append(ALT).append(' ');
|
||||||
|
}
|
||||||
|
if ((MouseEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
|
||||||
|
builder.append(ALT_GRAPH).append(' ');
|
||||||
|
}
|
||||||
|
builder.append("button").append(shortcut.getButton()).append(' ');
|
||||||
|
if (shortcut.getClickCount() > 1) {
|
||||||
|
builder.append(DOUBLE_CLICK);
|
||||||
|
}
|
||||||
|
return builder.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -18,8 +18,10 @@ package com.maddyhome.idea.vim;
|
|||||||
import com.intellij.notification.Notification;
|
import com.intellij.notification.Notification;
|
||||||
import com.intellij.notification.NotificationType;
|
import com.intellij.notification.NotificationType;
|
||||||
import com.intellij.notification.Notifications;
|
import com.intellij.notification.Notifications;
|
||||||
|
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
|
||||||
import com.intellij.openapi.application.Application;
|
import com.intellij.openapi.application.Application;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||||
import com.intellij.openapi.command.CommandProcessor;
|
import com.intellij.openapi.command.CommandProcessor;
|
||||||
import com.intellij.openapi.components.ApplicationComponent;
|
import com.intellij.openapi.components.ApplicationComponent;
|
||||||
@@ -39,15 +41,13 @@ import com.intellij.openapi.project.Project;
|
|||||||
import com.intellij.openapi.project.ProjectManager;
|
import com.intellij.openapi.project.ProjectManager;
|
||||||
import com.intellij.openapi.project.ProjectManagerAdapter;
|
import com.intellij.openapi.project.ProjectManagerAdapter;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
|
import com.intellij.openapi.util.SystemInfo;
|
||||||
import com.intellij.openapi.wm.StatusBar;
|
import com.intellij.openapi.wm.StatusBar;
|
||||||
import com.intellij.openapi.wm.WindowManager;
|
import com.intellij.openapi.wm.WindowManager;
|
||||||
import com.maddyhome.idea.vim.command.CommandState;
|
import com.maddyhome.idea.vim.command.CommandState;
|
||||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||||
import com.maddyhome.idea.vim.group.*;
|
import com.maddyhome.idea.vim.group.*;
|
||||||
import com.maddyhome.idea.vim.helper.DelegateCommandListener;
|
import com.maddyhome.idea.vim.helper.*;
|
||||||
import com.maddyhome.idea.vim.helper.DocumentManager;
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorData;
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
|
||||||
import com.maddyhome.idea.vim.key.RegisterActions;
|
import com.maddyhome.idea.vim.key.RegisterActions;
|
||||||
import com.maddyhome.idea.vim.option.Options;
|
import com.maddyhome.idea.vim.option.Options;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
@@ -81,6 +81,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
private static final String IDEAVIM_COMPONENT_NAME = "VimPlugin";
|
private static final String IDEAVIM_COMPONENT_NAME = "VimPlugin";
|
||||||
public static final String IDEAVIM_NOTIFICATION_ID = "ideavim";
|
public static final String IDEAVIM_NOTIFICATION_ID = "ideavim";
|
||||||
public static final String IDEAVIM_NOTIFICATION_TITLE = "IdeaVim";
|
public static final String IDEAVIM_NOTIFICATION_TITLE = "IdeaVim";
|
||||||
|
public static final int STATE_VERSION = 2;
|
||||||
|
|
||||||
private static final boolean BLOCK_CURSOR_VIM_VALUE = true;
|
private static final boolean BLOCK_CURSOR_VIM_VALUE = true;
|
||||||
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
||||||
@@ -93,6 +94,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
private boolean isRefrainFromScrolling = false;
|
private boolean isRefrainFromScrolling = false;
|
||||||
private boolean error = false;
|
private boolean error = false;
|
||||||
|
|
||||||
|
private int previousStateVersion = 0;
|
||||||
private String previousKeyMap = "";
|
private String previousKeyMap = "";
|
||||||
|
|
||||||
// It is enabled by default to avoid any special configuration after plugin installation
|
// It is enabled by default to avoid any special configuration after plugin installation
|
||||||
@@ -142,6 +144,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
|
|
||||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
updateState();
|
||||||
checkAndInstallKeymap();
|
checkAndInstallKeymap();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -161,6 +164,41 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
LOG.debug("done");
|
LOG.debug("done");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateState() {
|
||||||
|
if (isEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
|
boolean requiresRestart = false;
|
||||||
|
if (previousStateVersion < 1 && VimKeyMapUtil.isVimKeymapInstalled()) {
|
||||||
|
if (Messages.showYesNoDialog("Vim keymap generator has been updated to create keymaps more compatible " +
|
||||||
|
"with base keymaps.\n\nDo you want to reconfigure your Vim keymap?\n\n" +
|
||||||
|
"Warning: Any custom shortcuts will be lost!\n\n" +
|
||||||
|
"(You can do it later using Tools | Reconfigure Vim Keymap).",
|
||||||
|
IDEAVIM_NOTIFICATION_TITLE,
|
||||||
|
Messages.getQuestionIcon()) == Messages.YES) {
|
||||||
|
KeyHandler.executeAction("VimReconfigureKeymap", SimpleDataContext.getProjectContext(null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (previousStateVersion < 2 && SystemInfo.isMac) {
|
||||||
|
final MacKeyRepeat keyRepeat = MacKeyRepeat.getInstance();
|
||||||
|
final Boolean enabled = keyRepeat.isEnabled();
|
||||||
|
if (enabled == null || !enabled) {
|
||||||
|
if (Messages.showYesNoDialog("Do you want to enable repeating keys in Mac OS X on press and hold " +
|
||||||
|
"(requires restart)?\n\n" +
|
||||||
|
"(You can do it manually by running 'defaults write -g " +
|
||||||
|
"ApplePressAndHoldEnabled 0' in the console).",
|
||||||
|
IDEAVIM_NOTIFICATION_TITLE,
|
||||||
|
Messages.getQuestionIcon()) == Messages.YES) {
|
||||||
|
keyRepeat.setEnabled(true);
|
||||||
|
requiresRestart = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requiresRestart) {
|
||||||
|
final ApplicationEx app = ApplicationManagerEx.getApplicationEx();
|
||||||
|
app.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void checkAndInstallKeymap() {
|
private static void checkAndInstallKeymap() {
|
||||||
// Ensure that Vim keymap is installed and install if not.
|
// Ensure that Vim keymap is installed and install if not.
|
||||||
// Moreover we can use installed keymap as indicator of the first time installed plugin
|
// Moreover we can use installed keymap as indicator of the first time installed plugin
|
||||||
@@ -264,6 +302,11 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
// Restore whether the plugin is enabled or not
|
// Restore whether the plugin is enabled or not
|
||||||
Element state = element.getChild("state");
|
Element state = element.getChild("state");
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
|
try {
|
||||||
|
previousStateVersion = Integer.valueOf(state.getAttributeValue("version"));
|
||||||
|
}
|
||||||
|
catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
enabled = Boolean.valueOf(state.getAttributeValue("enabled"));
|
enabled = Boolean.valueOf(state.getAttributeValue("enabled"));
|
||||||
previousKeyMap = state.getAttributeValue("keymap");
|
previousKeyMap = state.getAttributeValue("keymap");
|
||||||
}
|
}
|
||||||
@@ -278,6 +321,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
|||||||
final Element element = new Element("ideavim");
|
final Element element = new Element("ideavim");
|
||||||
// Save whether the plugin is enabled or not
|
// Save whether the plugin is enabled or not
|
||||||
final Element state = new Element("state");
|
final Element state = new Element("state");
|
||||||
|
state.setAttribute("version", Integer.toString(STATE_VERSION));
|
||||||
state.setAttribute("enabled", Boolean.toString(enabled));
|
state.setAttribute("enabled", Boolean.toString(enabled));
|
||||||
state.setAttribute("keymap", previousKeyMap);
|
state.setAttribute("keymap", previousKeyMap);
|
||||||
element.addContent(state);
|
element.addContent(state);
|
||||||
|
@@ -3,7 +3,6 @@ package com.maddyhome.idea.vim;
|
|||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||||
import com.intellij.openapi.project.DumbAware;
|
import com.intellij.openapi.project.DumbAware;
|
||||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,6 +17,7 @@ public class VimReconfigureKeymapAction extends AnAction implements DumbAware {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||||
VimKeyMapUtil.reconfigureParentKeymap(e.getData(PlatformDataKeys.PROJECT));
|
VimKeyMapUtil.installKeyBoardBindings();
|
||||||
|
VimKeyMapUtil.switchKeymapBindings(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ public class DeleteCharacterAction extends EditorAction {
|
|||||||
|
|
||||||
private static class Handler extends ChangeEditorActionHandler {
|
private static class Handler extends ChangeEditorActionHandler {
|
||||||
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
||||||
return CommandGroups.getInstance().getChange().deleteCharacter(editor, 1);
|
return CommandGroups.getInstance().getChange().deleteCharacter(editor, 1, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ public class DeleteCharacterLeftAction extends EditorAction {
|
|||||||
|
|
||||||
private static class Handler extends ChangeEditorActionHandler {
|
private static class Handler extends ChangeEditorActionHandler {
|
||||||
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
||||||
return CommandGroups.getInstance().getChange().deleteCharacter(editor, -count);
|
return CommandGroups.getInstance().getChange().deleteCharacter(editor, -count, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -36,7 +36,7 @@ public class DeleteCharacterRightAction extends EditorAction {
|
|||||||
|
|
||||||
private static class Handler extends ChangeEditorActionHandler {
|
private static class Handler extends ChangeEditorActionHandler {
|
||||||
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
public boolean execute(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
||||||
return CommandGroups.getInstance().getChange().deleteCharacter(editor, count);
|
return CommandGroups.getInstance().getChange().deleteCharacter(editor, count, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -47,7 +47,7 @@ public class DeleteVisualAction extends EditorAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
||||||
SelectionType.fromSubMode(mode));
|
SelectionType.fromSubMode(mode), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,13 +43,13 @@ public class DeleteVisualLinesAction extends EditorAction {
|
|||||||
CommandState.SubMode mode = CommandState.getInstance(editor).getSubMode();
|
CommandState.SubMode mode = CommandState.getInstance(editor).getSubMode();
|
||||||
if (mode == CommandState.SubMode.VISUAL_BLOCK) {
|
if (mode == CommandState.SubMode.VISUAL_BLOCK) {
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
||||||
SelectionType.fromSubMode(mode));
|
SelectionType.fromSubMode(mode), false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
|
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
|
||||||
EditorHelper.getLineEndForOffset(editor, range.getEndOffset()) + 1);
|
EditorHelper.getLineEndForOffset(editor, range.getEndOffset()) + 1);
|
||||||
|
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.LINE_WISE);
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.LINE_WISE, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,14 +50,14 @@ public class DeleteVisualLinesEndAction extends EditorAction {
|
|||||||
|
|
||||||
range = new TextRange(starts, ends);
|
range = new TextRange(starts, ends);
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
||||||
SelectionType.BLOCK_WISE);
|
SelectionType.BLOCK_WISE, false);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
|
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
|
||||||
EditorHelper.getLineEndForOffset(editor, range.getEndOffset()) + 1);
|
EditorHelper.getLineEndForOffset(editor, range.getEndOffset()) + 1);
|
||||||
|
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
|
||||||
SelectionType.LINE_WISE);
|
SelectionType.LINE_WISE, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ public class MotionLastColumnAction extends MotionEditorAction {
|
|||||||
private static class Handler extends MotionEditorActionHandler {
|
private static class Handler extends MotionEditorActionHandler {
|
||||||
public int getOffset(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
public int getOffset(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
||||||
boolean allow = false;
|
boolean allow = false;
|
||||||
if (CommandState.inInsertMode(editor)) {
|
if (CommandState.inInsertMode(editor) || CommandState.inRepeatMode(editor)) {
|
||||||
allow = true;
|
allow = true;
|
||||||
}
|
}
|
||||||
else if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
else if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
||||||
|
@@ -18,7 +18,7 @@ public class MotionNextTabAction extends MotionEditorAction {
|
|||||||
|
|
||||||
private static class Handler extends MotionEditorActionHandler {
|
private static class Handler extends MotionEditorActionHandler {
|
||||||
public int getOffset(final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
public int getOffset(final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
||||||
return CommandGroups.getInstance().getMotion().moveCaretGotoNextTab(context);
|
return CommandGroups.getInstance().getMotion().moveCaretGotoNextTab(editor, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,7 @@ public class MotionPreviousTabAction extends MotionEditorAction {
|
|||||||
|
|
||||||
private static class Handler extends MotionEditorActionHandler {
|
private static class Handler extends MotionEditorActionHandler {
|
||||||
public int getOffset(final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
public int getOffset(final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
||||||
return CommandGroups.getInstance().getMotion().moveCaretGotoPreviousTab(context);
|
return CommandGroups.getInstance().getMotion().moveCaretGotoPreviousTab(editor, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -58,7 +58,6 @@ public class CommandState {
|
|||||||
/**
|
/**
|
||||||
* Gets the command state singleton
|
* Gets the command state singleton
|
||||||
*
|
*
|
||||||
* @param editor
|
|
||||||
* @return The singleton instance
|
* @return The singleton instance
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -76,11 +75,21 @@ public class CommandState {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean inInsertMode(Editor editor) {
|
public static boolean inInsertMode(@Nullable Editor editor) {
|
||||||
final Mode mode = getInstance(editor).getMode();
|
final Mode mode = getInstance(editor).getMode();
|
||||||
return mode == Mode.INSERT || mode == Mode.REPLACE;
|
return mode == Mode.INSERT || mode == Mode.REPLACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean inRepeatMode(@Nullable Editor editor) {
|
||||||
|
final Mode mode = getInstance(editor).getMode();
|
||||||
|
return mode == Mode.REPEAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean inVisualCharacterMode(@Nullable Editor editor) {
|
||||||
|
final CommandState state = getInstance(editor);
|
||||||
|
return state.getMode() == Mode.VISUAL && state.getSubMode() == SubMode.VISUAL_CHARACTER;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the currently executing command
|
* Gets the currently executing command
|
||||||
*
|
*
|
||||||
|
@@ -51,6 +51,6 @@ public class DeleteLinesHandler extends CommandHandler {
|
|||||||
|
|
||||||
TextRange range = cmd.getTextRange(editor, context, true);
|
TextRange range = cmd.getTextRange(editor, context, true);
|
||||||
|
|
||||||
return CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.LINE_WISE);
|
return CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.LINE_WISE, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,10 +23,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
|||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.*;
|
import com.intellij.openapi.editor.*;
|
||||||
import com.intellij.openapi.editor.event.EditorFactoryAdapter;
|
import com.intellij.openapi.editor.event.*;
|
||||||
import com.intellij.openapi.editor.event.EditorFactoryEvent;
|
|
||||||
import com.intellij.openapi.editor.event.EditorMouseAdapter;
|
|
||||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
import com.intellij.openapi.fileTypes.FileType;
|
import com.intellij.openapi.fileTypes.FileType;
|
||||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||||
@@ -167,13 +164,8 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
initInsert(editor, context, CommandState.Mode.INSERT);
|
initInsert(editor, context, CommandState.Mode.INSERT);
|
||||||
|
|
||||||
if (!editor.isOneLineMode()) {
|
if (!editor.isOneLineMode()) {
|
||||||
CommandState state = CommandState.getInstance(editor);
|
runEnterAction(editor, context);
|
||||||
if (state.getMode() != CommandState.Mode.REPEAT) {
|
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretVertical(editor, -1));
|
||||||
KeyHandler.executeAction("VimEditorEnter", context);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretVertical(editor, -1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -191,10 +183,18 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
public void insertNewLineBelow(@NotNull final Editor editor, @NotNull final DataContext context) {
|
public void insertNewLineBelow(@NotNull final Editor editor, @NotNull final DataContext context) {
|
||||||
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretToLineEnd(editor, true));
|
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretToLineEnd(editor, true));
|
||||||
initInsert(editor, context, CommandState.Mode.INSERT);
|
initInsert(editor, context, CommandState.Mode.INSERT);
|
||||||
|
runEnterAction(editor, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runEnterAction(Editor editor, DataContext context) {
|
||||||
CommandState state = CommandState.getInstance(editor);
|
CommandState state = CommandState.getInstance(editor);
|
||||||
if (state.getMode() != CommandState.Mode.REPEAT) {
|
if (state.getMode() != CommandState.Mode.REPEAT) {
|
||||||
KeyHandler.executeAction("VimEditorEnter", context);
|
final ActionManager actionManager = ActionManager.getInstance();
|
||||||
|
final AnAction action = actionManager.getAction("VimEditorEnter");
|
||||||
|
if (action != null) {
|
||||||
|
strokes.add(action);
|
||||||
|
KeyHandler.executeAction(action, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (deleteTo != -1) {
|
if (deleteTo != -1) {
|
||||||
deleteRange(editor, new TextRange(deleteTo, offset), SelectionType.CHARACTER_WISE);
|
deleteRange(editor, new TextRange(deleteTo, offset), SelectionType.CHARACTER_WISE, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -321,7 +321,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
final TextRange range = new TextRange(deleteTo, editor.getCaretModel().getOffset());
|
final TextRange range = new TextRange(deleteTo, editor.getCaretModel().getOffset());
|
||||||
deleteRange(editor, range, SelectionType.CHARACTER_WISE);
|
deleteRange(editor, range, SelectionType.CHARACTER_WISE, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,6 +358,13 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
else {
|
else {
|
||||||
lastInsert = state.getCommand();
|
lastInsert = state.getCommand();
|
||||||
strokes.clear();
|
strokes.clear();
|
||||||
|
if (document != null && documentListener != null) {
|
||||||
|
document.removeDocumentListener(documentListener);
|
||||||
|
}
|
||||||
|
document = editor.getDocument();
|
||||||
|
documentListener = new InsertActionsDocumentListener();
|
||||||
|
editor.getDocument().addDocumentListener(documentListener);
|
||||||
|
oldOffset = -1;
|
||||||
inInsert = true;
|
inInsert = true;
|
||||||
if (mode == CommandState.Mode.REPLACE) {
|
if (mode == CommandState.Mode.REPLACE) {
|
||||||
processInsert(editor, context);
|
processInsert(editor, context);
|
||||||
@@ -368,6 +375,54 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class InsertActionsDocumentListener extends DocumentAdapter {
|
||||||
|
@Override
|
||||||
|
public void documentChanged(DocumentEvent e) {
|
||||||
|
final String newFragment = e.getNewFragment().toString();
|
||||||
|
final String oldFragment = e.getOldFragment().toString();
|
||||||
|
|
||||||
|
// <Enter> is added to strokes as an action during processing in order to indent code properly in the repeat
|
||||||
|
// command
|
||||||
|
if (newFragment.startsWith("\n") && newFragment.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore multi-character indents as they should be inserted automatically while repeating <Enter> actions
|
||||||
|
if (newFragment.length() > 1 && newFragment.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int delta = e.getOffset() + oldFragment.length() - oldOffset;
|
||||||
|
if (oldOffset >= 0 && delta != 0) {
|
||||||
|
final String motionName = delta < 0 ? "VimMotionLeft" : "VimMotionRight";
|
||||||
|
final AnAction action = ActionManager.getInstance().getAction(motionName);
|
||||||
|
final int count = Math.abs(delta);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
strokes.add(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldFragment.length() > 0) {
|
||||||
|
final AnAction editorBackSpace = ActionManager.getInstance().getAction("VimEditorBackSpace");
|
||||||
|
for (int i = 0; i < oldFragment.length(); i++) {
|
||||||
|
strokes.add(editorBackSpace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (char c : newFragment.toCharArray()) {
|
||||||
|
strokes.add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newFragment.length() > 0) {
|
||||||
|
// TODO: If newFragment is shorter than oldFragment?
|
||||||
|
oldOffset = e.getOffset() + newFragment.length();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
oldOffset = e.getOffset() - oldFragment.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This repeats the previous insert count times
|
* This repeats the previous insert count times
|
||||||
*
|
*
|
||||||
@@ -456,6 +511,11 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
cnt = 1;
|
cnt = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (document != null && documentListener != null) {
|
||||||
|
document.removeDocumentListener(documentListener);
|
||||||
|
documentListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
// Save off current list of keystrokes
|
// Save off current list of keystrokes
|
||||||
lastStrokes = new ArrayList<Object>(strokes);
|
lastStrokes = new ArrayList<Object>(strokes);
|
||||||
|
|
||||||
@@ -535,10 +595,6 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (key.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
|
if (key.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
|
||||||
// Regular characters are not handled by us, pass them back to Idea. We just keep track of the keystroke
|
|
||||||
// for repeating later.
|
|
||||||
strokes.add(key.getKeyChar());
|
|
||||||
|
|
||||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
KeyHandler.getInstance().getOriginalHandler().execute(editor, key.getKeyChar(), context);
|
KeyHandler.getInstance().getOriginalHandler().execute(editor, key.getKeyChar(), context);
|
||||||
@@ -562,7 +618,6 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
public boolean processCommand(@NotNull Editor editor, @NotNull Command cmd) {
|
public boolean processCommand(@NotNull Editor editor, @NotNull Command cmd) {
|
||||||
if ((cmd.getFlags() & Command.FLAG_SAVE_STROKE) != 0) {
|
if ((cmd.getFlags() & Command.FLAG_SAVE_STROKE) != 0) {
|
||||||
strokes.add(cmd.getAction());
|
strokes.add(cmd.getAction());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ((cmd.getFlags() & Command.FLAG_CLEAR_STROKES) != 0) {
|
else if ((cmd.getFlags() & Command.FLAG_CLEAR_STROKES) != 0) {
|
||||||
@@ -591,12 +646,12 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
* @param count The number of characters to delete
|
* @param count The number of characters to delete
|
||||||
* @return true if able to delete, false if not
|
* @return true if able to delete, false if not
|
||||||
*/
|
*/
|
||||||
public boolean deleteCharacter(@NotNull Editor editor, int count) {
|
public boolean deleteCharacter(@NotNull Editor editor, int count, boolean isChange) {
|
||||||
int offset = CommandGroups.getInstance().getMotion().moveCaretHorizontal(editor, count, true);
|
int offset = CommandGroups.getInstance().getMotion().moveCaretHorizontal(editor, count, true);
|
||||||
if (offset != -1) {
|
if (offset != -1) {
|
||||||
boolean res = deleteText(editor, new TextRange(editor.getCaretModel().getOffset(), offset), SelectionType.CHARACTER_WISE);
|
boolean res = deleteText(editor, new TextRange(editor.getCaretModel().getOffset(), offset), SelectionType.CHARACTER_WISE);
|
||||||
int pos = editor.getCaretModel().getOffset();
|
int pos = editor.getCaretModel().getOffset();
|
||||||
int norm = EditorHelper.normalizeOffset(editor, editor.getCaretModel().getLogicalPosition().line, pos, false);
|
int norm = EditorHelper.normalizeOffset(editor, editor.getCaretModel().getLogicalPosition().line, pos, isChange);
|
||||||
if (norm != pos) {
|
if (norm != pos) {
|
||||||
MotionGroup.moveCaret(editor, norm);
|
MotionGroup.moveCaret(editor, norm);
|
||||||
}
|
}
|
||||||
@@ -743,25 +798,11 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
* @return true if able to delete the text, false if not
|
* @return true if able to delete the text, false if not
|
||||||
*/
|
*/
|
||||||
public boolean deleteMotion(@NotNull Editor editor, DataContext context, int count, int rawCount, @NotNull Argument argument, boolean isChange) {
|
public boolean deleteMotion(@NotNull Editor editor, DataContext context, int count, int rawCount, @NotNull Argument argument, boolean isChange) {
|
||||||
TextRange range = MotionGroup.getMotionRange(editor, context, count, rawCount, argument, true, false);
|
final TextRange range = getDeleteMotionRange(editor, context, count, rawCount, argument);
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
return (EditorHelper.getFileSize(editor) == 0);
|
return (EditorHelper.getFileSize(editor) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is deleted when it shouldn't be.
|
|
||||||
String text = editor.getDocument().getCharsSequence().subSequence(range.getStartOffset(),
|
|
||||||
range.getEndOffset()).toString();
|
|
||||||
final int lastNewLine = text.lastIndexOf('\n');
|
|
||||||
if (lastNewLine > 0) {
|
|
||||||
final String id = ActionManager.getInstance().getId(argument.getMotion().getAction());
|
|
||||||
if (id.equals("VimMotionWordRight") || id.equals("VimMotionBigWordRight") || id.equals("VimMotionCamelRight")) {
|
|
||||||
if (!SearchHelper.anyNonWhitespace(editor, range.getEndOffset(), -1)) {
|
|
||||||
final int start = range.getStartOffset();
|
|
||||||
range = new TextRange(start, start + lastNewLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete motion commands that are not linewise become linewise if all the following are true:
|
// Delete motion commands that are not linewise become linewise if all the following are true:
|
||||||
// 1) The range is across multiple lines
|
// 1) The range is across multiple lines
|
||||||
// 2) There is only whitespace before the start of the range
|
// 2) There is only whitespace before the start of the range
|
||||||
@@ -780,7 +821,31 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return deleteRange(editor, range, SelectionType.fromCommandFlags(argument.getMotion().getFlags()));
|
return deleteRange(editor, range, SelectionType.fromCommandFlags(argument.getMotion().getFlags()), isChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextRange getDeleteMotionRange(Editor editor,
|
||||||
|
DataContext context,
|
||||||
|
int count,
|
||||||
|
int rawCount,
|
||||||
|
Argument argument) {
|
||||||
|
TextRange range = MotionGroup.getMotionRange(editor, context, count, rawCount, argument, true, false);
|
||||||
|
// This is a kludge for dw, dW, and d[w. Without this kludge, an extra newline is deleted when it shouldn't be.
|
||||||
|
if (range != null) {
|
||||||
|
String text = editor.getDocument().getCharsSequence().subSequence(range.getStartOffset(),
|
||||||
|
range.getEndOffset()).toString();
|
||||||
|
final int lastNewLine = text.lastIndexOf('\n');
|
||||||
|
if (lastNewLine > 0) {
|
||||||
|
final String id = ActionManager.getInstance().getId(argument.getMotion().getAction());
|
||||||
|
if (id.equals("VimMotionWordRight") || id.equals("VimMotionBigWordRight") || id.equals("VimMotionCamelRight")) {
|
||||||
|
if (!SearchHelper.anyNonWhitespace(editor, range.getEndOffset(), -1)) {
|
||||||
|
final int start = range.getStartOffset();
|
||||||
|
range = new TextRange(start, start + lastNewLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -789,9 +854,13 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
* @param editor The editor to delete the text from
|
* @param editor The editor to delete the text from
|
||||||
* @param range The range to delete
|
* @param range The range to delete
|
||||||
* @param type The type of deletion
|
* @param type The type of deletion
|
||||||
|
* @param isChange is from a change action
|
||||||
* @return true if able to delete the text, false if not
|
* @return true if able to delete the text, false if not
|
||||||
*/
|
*/
|
||||||
public boolean deleteRange(@NotNull Editor editor, @Nullable TextRange range, @Nullable SelectionType type) {
|
public boolean deleteRange(@NotNull Editor editor,
|
||||||
|
@Nullable TextRange range,
|
||||||
|
@Nullable SelectionType type,
|
||||||
|
boolean isChange) {
|
||||||
if (range == null) {
|
if (range == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -804,7 +873,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
pos = size - 1;
|
pos = size - 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pos = EditorHelper.normalizeOffset(editor, range.getStartOffset(), false);
|
pos = EditorHelper.normalizeOffset(editor, range.getStartOffset(), isChange);
|
||||||
}
|
}
|
||||||
MotionGroup.moveCaret(editor, pos);
|
MotionGroup.moveCaret(editor, pos);
|
||||||
}
|
}
|
||||||
@@ -914,7 +983,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
return changeEndOfLine(editor, context, 1);
|
return changeEndOfLine(editor, context, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean res = deleteCharacter(editor, count);
|
boolean res = deleteCharacter(editor, count, true);
|
||||||
if (res) {
|
if (res) {
|
||||||
initInsert(editor, context, CommandState.Mode.INSERT);
|
initInsert(editor, context, CommandState.Mode.INSERT);
|
||||||
}
|
}
|
||||||
@@ -990,7 +1059,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
final ImmutableSet<String> wordMotions = ImmutableSet.of(
|
final ImmutableSet<String> wordMotions = ImmutableSet.of(
|
||||||
"VimMotionWordRight", "VimMotionBigWordRight", "VimMotionCamelRight");
|
"VimMotionWordRight", "VimMotionBigWordRight", "VimMotionCamelRight");
|
||||||
if (wordMotions.contains(id) && lastWordChar) {
|
if (wordMotions.contains(id) && lastWordChar) {
|
||||||
final boolean res = deleteCharacter(editor, 1);
|
final boolean res = deleteCharacter(editor, 1, true);
|
||||||
if (res) {
|
if (res) {
|
||||||
insertBeforeCursor(editor, context);
|
insertBeforeCursor(editor, context);
|
||||||
}
|
}
|
||||||
@@ -1107,7 +1176,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean after = range.getEndOffset() >= EditorHelper.getFileSize(editor);
|
boolean after = range.getEndOffset() >= EditorHelper.getFileSize(editor);
|
||||||
boolean res = deleteRange(editor, range, type);
|
boolean res = deleteRange(editor, range, type, true);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (type == SelectionType.LINE_WISE) {
|
if (type == SelectionType.LINE_WISE) {
|
||||||
if (after) {
|
if (after) {
|
||||||
@@ -1226,7 +1295,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
if (stroke instanceof Character) {
|
if (stroke instanceof Character) {
|
||||||
Character key = (Character)stroke;
|
Character key = (Character)stroke;
|
||||||
if (key == '0') {
|
if (key == '0') {
|
||||||
deleteCharacter(editor, -1);
|
deleteCharacter(editor, -1, false);
|
||||||
cnt = 99;
|
cnt = 99;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1559,6 +1628,9 @@ public class ChangeGroup extends AbstractActionGroup {
|
|||||||
private int repeatColumn;
|
private int repeatColumn;
|
||||||
private boolean repeatAppend;
|
private boolean repeatAppend;
|
||||||
private boolean lastLower = true;
|
private boolean lastLower = true;
|
||||||
|
private Document document;
|
||||||
|
private DocumentAdapter documentListener;
|
||||||
|
private int oldOffset = -1;
|
||||||
|
|
||||||
private static Logger logger = Logger.getInstance(ChangeGroup.class.getName());
|
private static Logger logger = Logger.getInstance(ChangeGroup.class.getName());
|
||||||
}
|
}
|
||||||
|
@@ -202,7 +202,7 @@ public class CopyGroup extends AbstractActionGroup {
|
|||||||
Math.min(range.getEndOffset() + 1, EditorHelper.getFileSize(editor)));
|
Math.min(range.getEndOffset() + 1, EditorHelper.getFileSize(editor)));
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.fromSubMode(subMode));
|
CommandGroups.getInstance().getChange().deleteRange(editor, range, SelectionType.fromSubMode(subMode), false);
|
||||||
|
|
||||||
editor.getCaretModel().moveToOffset(start);
|
editor.getCaretModel().moveToOffset(start);
|
||||||
|
|
||||||
|
@@ -1273,9 +1273,11 @@ public class MotionGroup extends AbstractActionGroup {
|
|||||||
|
|
||||||
public static void moveCaret(@NotNull Editor editor, int offset) {
|
public static void moveCaret(@NotNull Editor editor, int offset) {
|
||||||
if (offset >= 0 && offset <= editor.getDocument().getTextLength()) {
|
if (offset >= 0 && offset <= editor.getDocument().getTextLength()) {
|
||||||
editor.getCaretModel().moveToOffset(offset);
|
if (editor.getCaretModel().getOffset() != offset) {
|
||||||
EditorData.setLastColumn(editor, editor.getCaretModel().getVisualPosition().column);
|
editor.getCaretModel().moveToOffset(offset);
|
||||||
scrollCaretIntoView(editor);
|
EditorData.setLastColumn(editor, editor.getCaretModel().getVisualPosition().column);
|
||||||
|
scrollCaretIntoView(editor);
|
||||||
|
}
|
||||||
|
|
||||||
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
||||||
CommandGroups.getInstance().getMotion().updateSelection(editor, offset);
|
CommandGroups.getInstance().getMotion().updateSelection(editor, offset);
|
||||||
@@ -1286,31 +1288,20 @@ public class MotionGroup extends AbstractActionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int moveCaretGotoPreviousTab(@NotNull final DataContext context) {
|
public int moveCaretGotoPreviousTab(@NotNull Editor editor, @NotNull DataContext context) {
|
||||||
final AnAction previousTab = ActionManager.getInstance().getAction("PreviousTab");
|
final AnAction previousTab = ActionManager.getInstance().getAction("PreviousTab");
|
||||||
previousTab.actionPerformed(new AnActionEvent(
|
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
|
||||||
null,
|
previousTab.actionPerformed(e);
|
||||||
context,
|
return editor.getCaretModel().getOffset();
|
||||||
"",
|
|
||||||
new Presentation(),
|
|
||||||
ActionManager.getInstance(),
|
|
||||||
0));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int moveCaretGotoNextTab(@NotNull final DataContext context) {
|
public int moveCaretGotoNextTab(@NotNull Editor editor, @NotNull DataContext context) {
|
||||||
final AnAction previousTab = ActionManager.getInstance().getAction("NextTab");
|
final AnAction nextTab = ActionManager.getInstance().getAction("NextTab");
|
||||||
previousTab.actionPerformed(new AnActionEvent(
|
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
|
||||||
null,
|
nextTab.actionPerformed(e);
|
||||||
context,
|
return editor.getCaretModel().getOffset();
|
||||||
"",
|
|
||||||
new Presentation(),
|
|
||||||
ActionManager.getInstance(),
|
|
||||||
0));
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void scrollCaretIntoView(@NotNull Editor editor) {
|
public static void scrollCaretIntoView(@NotNull Editor editor) {
|
||||||
int cline = editor.getCaretModel().getVisualPosition().line;
|
int cline = editor.getCaretModel().getVisualPosition().line;
|
||||||
int vline = EditorHelper.getVisualLineAtTopOfScreen(editor);
|
int vline = EditorHelper.getVisualLineAtTopOfScreen(editor);
|
||||||
|
@@ -44,7 +44,8 @@ public abstract class MotionEditorActionHandler extends AbstractEditorActionHand
|
|||||||
if ((cmd.getFlags() & Command.FLAG_SAVE_JUMP) != 0) {
|
if ((cmd.getFlags() & Command.FLAG_SAVE_JUMP) != 0) {
|
||||||
CommandGroups.getInstance().getMark().saveJumpLocation(editor);
|
CommandGroups.getInstance().getMark().saveJumpLocation(editor);
|
||||||
}
|
}
|
||||||
if (!CommandState.inInsertMode(editor)) {
|
if (!CommandState.inInsertMode(editor) && !CommandState.inRepeatMode(editor) &&
|
||||||
|
!CommandState.inVisualCharacterMode(editor)) {
|
||||||
offset = EditorHelper.normalizeOffset(editor, offset, false);
|
offset = EditorHelper.normalizeOffset(editor, offset, false);
|
||||||
}
|
}
|
||||||
MotionGroup.moveCaret(editor, offset);
|
MotionGroup.moveCaret(editor, offset);
|
||||||
|
64
src/com/maddyhome/idea/vim/helper/MacKeyRepeat.java
Normal file
64
src/com/maddyhome/idea/vim/helper/MacKeyRepeat.java
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package com.maddyhome.idea.vim.helper;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author vlan
|
||||||
|
*/
|
||||||
|
public class MacKeyRepeat {
|
||||||
|
public static final String FMT = "defaults %s -globalDomain ApplePressAndHoldEnabled";
|
||||||
|
@NotNull private static final MacKeyRepeat INSTANCE = new MacKeyRepeat();
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static MacKeyRepeat getInstance() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Boolean isEnabled() {
|
||||||
|
final String command = String.format(FMT, "read");
|
||||||
|
try {
|
||||||
|
final Process process = Runtime.getRuntime().exec(command);
|
||||||
|
final String data = read(process.getInputStream()).trim();
|
||||||
|
try {
|
||||||
|
return Integer.valueOf(data) == 0;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(@Nullable Boolean value) {
|
||||||
|
final String command;
|
||||||
|
if (value == null) {
|
||||||
|
command = String.format(FMT, "delete");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final String arg = value ? "0" : "1";
|
||||||
|
command = String.format(FMT, "write") + " " + arg;
|
||||||
|
}
|
||||||
|
final Process process;
|
||||||
|
try {
|
||||||
|
process = Runtime.getRuntime().exec(command);
|
||||||
|
process.waitFor();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String read(@NotNull InputStream stream) throws IOException {
|
||||||
|
return CharStreams.toString(new InputStreamReader(stream));
|
||||||
|
}
|
||||||
|
}
|
@@ -38,18 +38,18 @@ public class RegisterActions {
|
|||||||
|
|
||||||
// ******************* Insert Mode Actions **********************
|
// ******************* Insert Mode Actions **********************
|
||||||
// Delegation actions
|
// Delegation actions
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimClassNameCompletion", Command.Type.COMPLETION, Command.FLAG_SAVE_STROKE,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimClassNameCompletion", Command.Type.COMPLETION,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimCodeCompletion", Command.Type.COMPLETION, Command.FLAG_SAVE_STROKE,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimCodeCompletion", Command.Type.COMPLETION,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimSmartTypeCompletion", Command.Type.COMPLETION, Command.FLAG_SAVE_STROKE,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimSmartTypeCompletion", Command.Type.COMPLETION,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, KeyEvent.CTRL_MASK | KeyEvent.SHIFT_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimInsertLiveTemplate", Command.Type.COMPLETION, Command.FLAG_SAVE_STROKE,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimInsertLiveTemplate", Command.Type.COMPLETION,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_J, KeyEvent.CTRL_MASK)));
|
||||||
|
|
||||||
// Other insert actions
|
// Other insert actions
|
||||||
parser
|
parser
|
||||||
.registerAction(KeyParser.MAPPING_INSERT, "VimEditorBackSpace", Command.Type.INSERT, Command.FLAG_SAVE_STROKE | Command.FLAG_IS_BACKSPACE,
|
.registerAction(KeyParser.MAPPING_INSERT, "VimEditorBackSpace", Command.Type.INSERT, Command.FLAG_IS_BACKSPACE,
|
||||||
new Shortcut[]{
|
new Shortcut[]{
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0))
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0))
|
||||||
@@ -107,19 +107,19 @@ public class RegisterActions {
|
|||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimInsertSingleCommand", Command.Type.INSERT,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimInsertSingleCommand", Command.Type.INSERT,
|
||||||
Command.FLAG_CLEAR_STROKES | Command.FLAG_EXPECT_MORE,
|
Command.FLAG_CLEAR_STROKES | Command.FLAG_EXPECT_MORE,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionFirstColumn", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionFirstColumn", Command.Type.INSERT, Command.FLAG_SAVE_STROKE,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineFirst", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineFirst", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, KeyEvent.CTRL_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, KeyEvent.CTRL_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineLastEnd", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineLastEnd", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, KeyEvent.CTRL_MASK)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, KeyEvent.CTRL_MASK)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionLastColumn", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionLastColumn", Command.Type.INSERT, Command.FLAG_SAVE_STROKE,
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0)));
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0)));
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionLeft", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES, new Shortcut[]{
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionLeft", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, 0))
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, 0))
|
||||||
});
|
});
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionRight", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES, new Shortcut[]{
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionRight", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0))
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0))
|
||||||
});
|
});
|
||||||
@@ -137,13 +137,13 @@ public class RegisterActions {
|
|||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_MASK))
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_MASK))
|
||||||
});
|
});
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionWordLeft", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES, new Shortcut[]{
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionWordLeft", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_MASK))
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_MASK))
|
||||||
});
|
});
|
||||||
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionWordRight", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES, new Shortcut[]{
|
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionWordRight", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_MASK)),
|
||||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK)),
|
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK)),
|
||||||
|
@@ -23,6 +23,8 @@ import com.intellij.openapi.actionSystem.DataContext;
|
|||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||||
|
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -46,8 +48,9 @@ public class ExEntryPanel extends JPanel {
|
|||||||
|
|
||||||
private ExEntryPanel() {
|
private ExEntryPanel() {
|
||||||
setBorder(BorderFactory.createEtchedBorder());
|
setBorder(BorderFactory.createEtchedBorder());
|
||||||
|
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
|
||||||
|
|
||||||
Font font = new Font("Monospaced", Font.PLAIN, 12);
|
Font font = new Font(scheme.getEditorFontName(), Font.PLAIN, scheme.getEditorFontSize());
|
||||||
label = new JLabel(" ");
|
label = new JLabel(" ");
|
||||||
label.setFont(font);
|
label.setFont(font);
|
||||||
entry = new ExTextField();
|
entry = new ExTextField();
|
||||||
|
87
test/org/jetbrains/plugins/ideavim/KeymapGenerationTest.java
Normal file
87
test/org/jetbrains/plugins/ideavim/KeymapGenerationTest.java
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package org.jetbrains.plugins.ideavim;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.intellij.openapi.actionSystem.KeyboardShortcut;
|
||||||
|
import com.intellij.openapi.actionSystem.Shortcut;
|
||||||
|
import com.intellij.openapi.components.impl.stores.StorageUtil;
|
||||||
|
import com.intellij.openapi.keymap.Keymap;
|
||||||
|
import com.intellij.openapi.keymap.impl.KeymapImpl;
|
||||||
|
import com.intellij.openapi.util.InvalidDataException;
|
||||||
|
import com.maddyhome.idea.vim.VimKeymapConflictResolveUtil;
|
||||||
|
import org.jdom.Document;
|
||||||
|
import org.jdom.Element;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Lists.newArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User: zolotov
|
||||||
|
* Date: 1/10/13
|
||||||
|
*/
|
||||||
|
public class KeymapGenerationTest extends VimTestCase {
|
||||||
|
public void testOverrideSingleConflict() throws Exception {
|
||||||
|
final KeymapImpl parentKeymap = new KeymapImpl();
|
||||||
|
parentKeymap.addShortcut("action1", KeyboardShortcut.fromString("control C")); //should be overridden
|
||||||
|
parentKeymap.addShortcut("action1", KeyboardShortcut.fromString("control X"));
|
||||||
|
parentKeymap.addShortcut("action2", KeyboardShortcut.fromString("control V")); //should be overridden
|
||||||
|
parentKeymap.addShortcut("action3", KeyboardShortcut.fromString("control Z"));
|
||||||
|
|
||||||
|
final KeymapImpl resultKeymap = resolveConflicts(parentKeymap);
|
||||||
|
final ArrayList<String> allShortcuts = newArrayList(resultKeymap.getActionIds());
|
||||||
|
assertContainsElements(allShortcuts, "VimKeyHandler", "action1", "action2");
|
||||||
|
assertDoesntContain(allShortcuts, "action3");
|
||||||
|
|
||||||
|
final Shortcut[] action1Shortcuts = resultKeymap.getShortcuts("action1");
|
||||||
|
assertEquals(1, action1Shortcuts.length);
|
||||||
|
assertEquals(KeyboardShortcut.fromString("control X"), action1Shortcuts[0]);
|
||||||
|
final Shortcut[] action2Shortcuts = resultKeymap.getShortcuts("action2");
|
||||||
|
assertEquals(1, action2Shortcuts.length);
|
||||||
|
assertEquals(KeyboardShortcut.fromString("control alt V"), action2Shortcuts[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverrideDoubleConflict() throws Exception {
|
||||||
|
final KeymapImpl parentKeymap = new KeymapImpl();
|
||||||
|
parentKeymap.addShortcut("action1", KeyboardShortcut.fromString("control C")); //should be overridden
|
||||||
|
parentKeymap.addShortcut("action1", KeyboardShortcut.fromString("control V")); //should be overridden
|
||||||
|
parentKeymap.addShortcut("action2", KeyboardShortcut.fromString("control V")); //should be overridden
|
||||||
|
parentKeymap.addShortcut("action3", KeyboardShortcut.fromString("control Z"));
|
||||||
|
|
||||||
|
final KeymapImpl resultKeymap = resolveConflicts(parentKeymap);
|
||||||
|
final ArrayList<String> allShortcuts = newArrayList(resultKeymap.getActionIds());
|
||||||
|
assertContainsElements(allShortcuts, "VimKeyHandler", "action1", "action2");
|
||||||
|
assertDoesntContain(allShortcuts, "action3");
|
||||||
|
|
||||||
|
final Shortcut[] action1Shortcuts = resultKeymap.getShortcuts("action1");
|
||||||
|
assertEquals(0, action1Shortcuts.length);
|
||||||
|
final Shortcut[] action2Shortcuts = resultKeymap.getShortcuts("action2");
|
||||||
|
assertEquals(1, action2Shortcuts.length);
|
||||||
|
assertEquals(KeyboardShortcut.fromString("control alt V"), action2Shortcuts[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static KeymapImpl resolveConflicts(KeymapImpl parentKeymap) throws InvalidDataException {
|
||||||
|
final Element stubKeymap = createStubKeymap();
|
||||||
|
VimKeymapConflictResolveUtil.resolveConflicts(stubKeymap, parentKeymap);
|
||||||
|
KeymapImpl resultKeymap = new KeymapImpl();
|
||||||
|
resultKeymap.readExternal(stubKeymap, new Keymap[0]);
|
||||||
|
return resultKeymap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create simple keymap with bound Ctrl+V and Ctrl+C shortcuts
|
||||||
|
*
|
||||||
|
* @return root element of keymap in xml representation
|
||||||
|
*/
|
||||||
|
private static Element createStubKeymap() {
|
||||||
|
Document result = StorageUtil.loadDocument(("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||||
|
"<keymap version=\"1\" name=\"Vim\" disable-mnemonics=\"false\" parent=\"$default\">\n" +
|
||||||
|
" <action id=\"VimKeyHandler\">\n" +
|
||||||
|
" <keyboard-shortcut first-keystroke=\"control C\"/>\n" +
|
||||||
|
" <keyboard-shortcut first-keystroke=\"control V\"/>\n" +
|
||||||
|
" </action>\n" +
|
||||||
|
" \n" +
|
||||||
|
"</keymap>").getBytes(Charsets.UTF_8));
|
||||||
|
assert result != null;
|
||||||
|
return result.getRootElement();
|
||||||
|
}
|
||||||
|
}
|
@@ -219,6 +219,41 @@ public class ChangeActionTest extends VimTestCase {
|
|||||||
"Hello, World!\n");
|
"Hello, World!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VIM-421 |c| |w|
|
||||||
|
public void testChangeLastWordInLine() {
|
||||||
|
doTest(stringToKeys("cw"),
|
||||||
|
"ab.<caret>cd\n",
|
||||||
|
"ab.<caret>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIM-421 |c| |iw|
|
||||||
|
public void testChangeLastInnerWordInLine() {
|
||||||
|
doTest(stringToKeys("ciwbaz"),
|
||||||
|
"foo bar bo<caret>o\n",
|
||||||
|
"foo bar baz\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIM-421 |c| |w|
|
||||||
|
public void testChangeLastCharInLine() {
|
||||||
|
doTest(stringToKeys("cw"),
|
||||||
|
"fo<caret>o\n",
|
||||||
|
"fo<caret>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIM-404 |O|
|
||||||
|
public void testInsertNewLineAboveFirstLine() {
|
||||||
|
doTest(stringToKeys("Obar"),
|
||||||
|
"fo<caret>o\n",
|
||||||
|
"bar\nfoo\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// VIM-472 |v|
|
||||||
|
public void testVisualSelectionRightMargin() {
|
||||||
|
doTest(stringToKeys("vk$d"),
|
||||||
|
"foo\n<caret>bar\n",
|
||||||
|
"fooar\n");
|
||||||
|
}
|
||||||
|
|
||||||
private void doTest(final List<KeyStroke> keys, String before, String after) {
|
private void doTest(final List<KeyStroke> keys, String before, String after) {
|
||||||
myFixture.configureByText("a.java", before);
|
myFixture.configureByText("a.java", before);
|
||||||
final Editor editor = myFixture.getEditor();
|
final Editor editor = myFixture.getEditor();
|
||||||
|
Reference in New Issue
Block a user