1
0
mirror of https://github.com/chylex/IntelliJ-IdeaVim.git synced 2025-08-17 16:31:45 +02:00

Compare commits

...

36 Commits
0.26 ... 0.27

Author SHA1 Message Date
Andrey Vlasovskikh
c4eb8e5752 Updated changelog 2013-04-03 15:57:22 +04:00
Andrey Vlasovskikh
7702d3a695 Fixed broken test 2013-04-03 04:05:37 +04:00
Andrey Vlasovskikh
fbff8ed2c0 VIM-419 Keep cursor position after 'gt' and 'gT' 2013-04-03 03:55:02 +04:00
Andrey Vlasovskikh
5b5b6188da VIM-42 Ignore multi-character indents while recoding last insert actions 2013-04-03 00:30:26 +04:00
Andrey Vlasovskikh
6a1707a7e6 Minor style edits 2013-04-01 20:36:41 +04:00
Andrey Vlasovskikh
b625fd3df9 Updated changelog 2013-04-01 20:35:56 +04:00
Andrey Vlasovskikh
ffaf2cc451 VIM-42 Handle moving cursor back and forward during autocompletion
Expressions like static methods (Foo.bar()) and shortcut completions (f.b for
foo.bar()) are completed using several document edits with cursor movements.
Now IdeaVim records and replays these cursor movements.
2013-04-01 20:17:30 +04:00
Andrey Vlasovskikh
dcc9b5a37e VIM-42 Record char strokes as regular document edits 2013-04-01 17:23:33 +04:00
Andrey Vlasovskikh
1b4a51f770 VIM-42 Basic support for repeating code completion using '.' command 2013-04-01 17:10:56 +04:00
Andrey Vlasovskikh
f8d2ee2dd7 VIM-286 Note about switching key repeating on Mac OS X manually 2013-03-27 18:15:46 +04:00
Andrey Vlasovskikh
348d88d543 VIM-92 Note about resetting custom shortcuts 2013-03-27 17:54:51 +04:00
Andrey Vlasovskikh
38b859e555 VIM-472 Fixed right selection in visual character mode to be one char past line 2013-03-26 16:36:15 +04:00
Andrey Vlasovskikh
ca8026f612 VIM-404 Fixed 'O' at the first line 2013-03-26 15:51:15 +04:00
Andrey Vlasovskikh
befcf4298f Updated changelog 2013-03-26 02:43:46 +04:00
Andrey Vlasovskikh
2e0bbcc75d Moved MacKeyRepeat to helpers 2013-03-26 02:23:27 +04:00
Andrey Vlasovskikh
2204fe6451 Fixed unit tests for updated plugin state 2013-03-26 02:21:09 +04:00
Andrey Vlasovskikh
12bc68bc02 VIM-286 Suggest enabling Mac OS X keys autorepeat only if it is not enabled 2013-03-26 02:20:30 +04:00
Andrey Vlasovskikh
290370f744 VIM-92 Don't suggest reconfiguring Vim keymap if it is not installed yet 2013-03-25 22:45:02 +04:00
Andrey Vlasovskikh
79acef46ce VIM-286 Suggest enabling repeating keys on press and hold for Mac OS X 2013-03-25 22:44:25 +04:00
Andrey Vlasovskikh
0054eb5279 Disabled overriding second keystrokes as a workaround for Emacs keymap 2013-03-25 21:27:14 +04:00
Andrey Vlasovskikh
e0eb919e51 VIM-92 Save parent keymap after reconfiguring Vim keymap and show notification 2013-03-25 20:51:30 +04:00
Andrey Vlasovskikh
7c3d5a7946 VIM-92 Suggest to reconfigure Vim keymap using the new generation algorithm 2013-03-25 19:49:31 +04:00
Andrey Vlasovskikh
38cb20d1bd Changed capitalization of "Vim" 2013-03-25 19:48:25 +04:00
Andrey Vlasovskikh
505fcf5b31 VIM-421 Fixed 'cw' at the last word in line 2013-03-13 13:48:11 -07:00
Andrey Vlasovskikh
d744d45ccd VIM-92 Reconfigure Vim keymap by overwriting it and switching bindings without restart 2013-03-12 21:40:18 +04:00
Andrey Vlasovskikh
2e2e7a7df6 VIM-92 Free Ctrl+N that is critical to navigate actions on Linux 2013-03-12 21:39:19 +04:00
Andrey Vlasovskikh
db5ff05bb3 VIM-92 Do not use Meta key on systems other than Mac 2013-03-12 21:38:28 +04:00
Andrey Vlasovskikh
566273fe83 Merge branch 'vim-92' 2013-03-12 16:48:31 +04:00
Andrey Vlasovskikh
decb17e665 VIM-92 Alternative shortcuts for actions with shortcuts bound to IdeaVim
Alternative action shortcuts are obtained from standard shortcuts by
adding Alt+, Ctrl+, Shift+, Meta+, Alt+Ctrl+, etc. to them.
2013-03-12 16:48:17 +04:00
Andrey Vlasovskikh
46c27bbd10 Added John Lindquist to contributors 2013-03-11 15:41:23 +04:00
Andrey Vlasovskikh
72f98cef14 Merge pull request #14 from johnlindquist/master
Matching command-line font and font size to editor font and font size
2013-03-11 04:38:33 -07:00
John Lindquist
0f8cfa289d Command-line font and font size now match editor font and font size 2013-03-10 23:47:53 -06:00
Alexander Zolotov
0cb420c557 VIM-92: Fix Vim keymap to work better in Mac OS
- add extra shortcuts for Linux keymaps
2013-01-11 00:50:15 +04:00
Alexander Zolotov
79df0b0594 VIM-92: Fix Vim keymap to work better in Mac OS
- ability to define extra shortcuts
2013-01-11 00:42:31 +04:00
Alexander Zolotov
dfbddb4050 VIM-92: Fix Vim keymap to work better in Mac OS
- tests for keymap generation
2013-01-10 23:47:20 +04:00
Alexander Zolotov
0f88495f29 VIM-92: Fix Vim keymap to work better in Mac OS
- remove all static overrides from vim.xml
- introduce conflicts resolving
2013-01-08 23:19:41 +04:00
30 changed files with 664 additions and 261 deletions

View File

@@ -21,6 +21,7 @@ Contributors:
* [Masanobu Imai](mailto:masanobu.imai@gmail.com)
* [poxu](mailto:poxvuibr@gmail.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
contact the maintainer.

View File

@@ -4,6 +4,28 @@ The Changelog
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
----------------
@@ -17,11 +39,11 @@ Features:
* VIM-262 Support for paste from register in command mode
* VIM-214 Key bindings for paste into command line
* 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:
* 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

View File

@@ -1,4 +1,4 @@
version-id:0.26
version-id:0.27
platform-version:110.0
idea.download.url=http://download.jetbrains.com/idea/ideaIU-12.0.zip
build.number=x

View File

@@ -65,6 +65,8 @@ tag char note action in Normal mode ~
|count| 9 "
|F| F{char} 1 cursor to the Nth occurrence of {char} to
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
cursor N times
|T| T{char} 1 cursor till after Nth occurrence of {char}

View File

@@ -3,6 +3,13 @@
<id>IdeaVIM</id>
<change-notes>
<![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>
<ul>
<li>Added support for paste in the command mode: from a register using <code>&lt;C-R&gt;</code>, from the clipboard using <code>&lt;S-Insert&gt;</code> or <code>&lt;M-V&gt;</code></li>
@@ -64,11 +71,11 @@
</extensions>
<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"/>
<add-to-group group-id="ToolsMenu" anchor="last"/>
</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"/>
</action>

View File

@@ -1,129 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<keyboard-shortcut first-keystroke="control 2" />
<keyboard-shortcut first-keystroke="control A" />
@@ -145,7 +21,6 @@
<keyboard-shortcut first-keystroke="control L" />
<keyboard-shortcut first-keystroke="control LEFT" />
<keyboard-shortcut first-keystroke="control M" />
<keyboard-shortcut first-keystroke="control N" />
<keyboard-shortcut first-keystroke="control O" />
<keyboard-shortcut first-keystroke="control OPEN_BRACKET" />
<keyboard-shortcut first-keystroke="control P" />

View File

@@ -56,10 +56,6 @@ public class VimKeyMapUtil {
*/
public static boolean installKeyBoardBindings() {
LOG.debug("Check for keyboard bindings");
if (isVimKeymapInstalled()) {
return true;
}
final LocalFileSystem localFileSystem = LocalFileSystem.getInstance();
if (localFileSystem.refreshAndFindFileByPath(KEYMAPS_PATH) == null) {
reportError("Failed to install vim keymap. Empty keymaps folder");
@@ -73,7 +69,7 @@ public class VimKeyMapUtil {
final Document document = StorageUtil.loadDocument(bytes);
if (!ApplicationManager.getApplication().isUnitTestMode()) {
// 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);
} catch (IOException e) {
@@ -135,13 +131,14 @@ public class VimKeyMapUtil {
final Keymap selectedKeymap = vimKeymapDialog.getSelectedKeymap();
final String keymapName = selectedKeymap.getName();
rootElement.setAttribute("parent", keymapName);
VimPlugin.getInstance().setPreviousKeyMap(keymapName);
VimKeymapConflictResolveUtil.resolveConflicts(rootElement, selectedKeymap);
// Save modified keymap to the file
JDOMUtil.writeDocument(document, path, "\n");
if (showNotification) {
Notifications.Bus.notify(new Notification(VimPlugin.IDEAVIM_NOTIFICATION_ID, VimPlugin.IDEAVIM_NOTIFICATION_TITLE,
"Successfully configured vim keymap to be based on " +
selectedKeymap.getPresentableName(),
selectedKeymap.getPresentableName(),
NotificationType.INFORMATION));
}
@@ -194,30 +191,6 @@ public class VimKeyMapUtil {
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
private static InputStream retrieveSourceKeymapStream() throws IOException {
String keymapPath = PATH_JOINER.join(PathManager.getPluginsPath(), VimPlugin.IDEAVIM_NOTIFICATION_TITLE, VIM_XML);

View 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();
}
}

View File

@@ -18,8 +18,10 @@ package com.maddyhome.idea.vim;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.ApplicationEx;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
import com.intellij.openapi.command.CommandProcessor;
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.ProjectManagerAdapter;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.maddyhome.idea.vim.command.CommandState;
import com.maddyhome.idea.vim.ex.CommandParser;
import com.maddyhome.idea.vim.group.*;
import com.maddyhome.idea.vim.helper.DelegateCommandListener;
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.helper.*;
import com.maddyhome.idea.vim.key.RegisterActions;
import com.maddyhome.idea.vim.option.Options;
import org.jdom.Element;
@@ -81,6 +81,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
private static final String IDEAVIM_COMPONENT_NAME = "VimPlugin";
public static final String IDEAVIM_NOTIFICATION_ID = "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 ANIMATED_SCROLLING_VIM_VALUE = false;
@@ -93,6 +94,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
private boolean isRefrainFromScrolling = false;
private boolean error = false;
private int previousStateVersion = 0;
private String previousKeyMap = "";
// 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() {
public void run() {
updateState();
checkAndInstallKeymap();
}
});
@@ -161,6 +164,41 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
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() {
// Ensure that Vim keymap is installed and install if not.
// 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
Element state = element.getChild("state");
if (state != null) {
try {
previousStateVersion = Integer.valueOf(state.getAttributeValue("version"));
}
catch (NumberFormatException ignored) {
}
enabled = Boolean.valueOf(state.getAttributeValue("enabled"));
previousKeyMap = state.getAttributeValue("keymap");
}
@@ -278,6 +321,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
final Element element = new Element("ideavim");
// Save whether the plugin is enabled or not
final Element state = new Element("state");
state.setAttribute("version", Integer.toString(STATE_VERSION));
state.setAttribute("enabled", Boolean.toString(enabled));
state.setAttribute("keymap", previousKeyMap);
element.addContent(state);

View File

@@ -3,7 +3,6 @@ package com.maddyhome.idea.vim;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import org.jetbrains.annotations.NotNull;
/**
@@ -18,6 +17,7 @@ public class VimReconfigureKeymapAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(@NotNull final AnActionEvent e) {
VimKeyMapUtil.reconfigureParentKeymap(e.getData(PlatformDataKeys.PROJECT));
VimKeyMapUtil.installKeyBoardBindings();
VimKeyMapUtil.switchKeymapBindings(true);
}
}

View File

@@ -36,7 +36,7 @@ public class DeleteCharacterAction extends EditorAction {
private static class Handler extends ChangeEditorActionHandler {
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);
}
}
}

View File

@@ -36,7 +36,7 @@ public class DeleteCharacterLeftAction extends EditorAction {
private static class Handler extends ChangeEditorActionHandler {
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);
}
}
}

View File

@@ -36,7 +36,7 @@ public class DeleteCharacterRightAction extends EditorAction {
private static class Handler extends ChangeEditorActionHandler {
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);
}
}
}

View File

@@ -47,7 +47,7 @@ public class DeleteVisualAction extends EditorAction {
}
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
SelectionType.fromSubMode(mode));
SelectionType.fromSubMode(mode), false);
}
}
}

View File

@@ -43,13 +43,13 @@ public class DeleteVisualLinesAction extends EditorAction {
CommandState.SubMode mode = CommandState.getInstance(editor).getSubMode();
if (mode == CommandState.SubMode.VISUAL_BLOCK) {
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
SelectionType.fromSubMode(mode));
SelectionType.fromSubMode(mode), false);
}
else {
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
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);
}
}
}

View File

@@ -50,14 +50,14 @@ public class DeleteVisualLinesEndAction extends EditorAction {
range = new TextRange(starts, ends);
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
SelectionType.BLOCK_WISE);
SelectionType.BLOCK_WISE, false);
}
else {
range = new TextRange(EditorHelper.getLineStartForOffset(editor, range.getStartOffset()),
EditorHelper.getLineEndForOffset(editor, range.getEndOffset()) + 1);
return CommandGroups.getInstance().getChange().deleteRange(editor, range,
SelectionType.LINE_WISE);
SelectionType.LINE_WISE, false);
}
}
}

View File

@@ -43,7 +43,7 @@ public class MotionLastColumnAction extends MotionEditorAction {
private static class Handler extends MotionEditorActionHandler {
public int getOffset(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
boolean allow = false;
if (CommandState.inInsertMode(editor)) {
if (CommandState.inInsertMode(editor) || CommandState.inRepeatMode(editor)) {
allow = true;
}
else if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {

View File

@@ -18,7 +18,7 @@ public class MotionNextTabAction extends MotionEditorAction {
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) {
return CommandGroups.getInstance().getMotion().moveCaretGotoNextTab(context);
return CommandGroups.getInstance().getMotion().moveCaretGotoNextTab(editor, context);
}
}
}

View File

@@ -18,7 +18,7 @@ public class MotionPreviousTabAction extends MotionEditorAction {
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) {
return CommandGroups.getInstance().getMotion().moveCaretGotoPreviousTab(context);
return CommandGroups.getInstance().getMotion().moveCaretGotoPreviousTab(editor, context);
}
}
}

View File

@@ -58,7 +58,6 @@ public class CommandState {
/**
* Gets the command state singleton
*
* @param editor
* @return The singleton instance
*/
@NotNull
@@ -76,11 +75,21 @@ public class CommandState {
return res;
}
public static boolean inInsertMode(Editor editor) {
public static boolean inInsertMode(@Nullable Editor editor) {
final Mode mode = getInstance(editor).getMode();
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
*

View File

@@ -51,6 +51,6 @@ public class DeleteLinesHandler extends CommandHandler {
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);
}
}

View File

@@ -23,10 +23,7 @@ import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.EditorFactoryAdapter;
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.editor.event.*;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
@@ -167,13 +164,8 @@ public class ChangeGroup extends AbstractActionGroup {
initInsert(editor, context, CommandState.Mode.INSERT);
if (!editor.isOneLineMode()) {
CommandState state = CommandState.getInstance(editor);
if (state.getMode() != CommandState.Mode.REPEAT) {
KeyHandler.executeAction("VimEditorEnter", context);
}
else {
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretVertical(editor, -1));
}
runEnterAction(editor, context);
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretVertical(editor, -1));
}
}
else {
@@ -191,10 +183,18 @@ public class ChangeGroup extends AbstractActionGroup {
public void insertNewLineBelow(@NotNull final Editor editor, @NotNull final DataContext context) {
MotionGroup.moveCaret(editor, CommandGroups.getInstance().getMotion().moveCaretToLineEnd(editor, true));
initInsert(editor, context, CommandState.Mode.INSERT);
runEnterAction(editor, context);
}
private void runEnterAction(Editor editor, DataContext context) {
CommandState state = CommandState.getInstance(editor);
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) {
deleteRange(editor, new TextRange(deleteTo, offset), SelectionType.CHARACTER_WISE);
deleteRange(editor, new TextRange(deleteTo, offset), SelectionType.CHARACTER_WISE, false);
return true;
}
@@ -321,7 +321,7 @@ public class ChangeGroup extends AbstractActionGroup {
return false;
}
final TextRange range = new TextRange(deleteTo, editor.getCaretModel().getOffset());
deleteRange(editor, range, SelectionType.CHARACTER_WISE);
deleteRange(editor, range, SelectionType.CHARACTER_WISE, false);
return true;
}
@@ -358,6 +358,13 @@ public class ChangeGroup extends AbstractActionGroup {
else {
lastInsert = state.getCommand();
strokes.clear();
if (document != null && documentListener != null) {
document.removeDocumentListener(documentListener);
}
document = editor.getDocument();
documentListener = new InsertActionsDocumentListener();
editor.getDocument().addDocumentListener(documentListener);
oldOffset = -1;
inInsert = true;
if (mode == CommandState.Mode.REPLACE) {
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
*
@@ -456,6 +511,11 @@ public class ChangeGroup extends AbstractActionGroup {
cnt = 1;
}
if (document != null && documentListener != null) {
document.removeDocumentListener(documentListener);
documentListener = null;
}
// Save off current list of keystrokes
lastStrokes = new ArrayList<Object>(strokes);
@@ -535,10 +595,6 @@ public class ChangeGroup extends AbstractActionGroup {
}
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() {
public void run() {
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) {
if ((cmd.getFlags() & Command.FLAG_SAVE_STROKE) != 0) {
strokes.add(cmd.getAction());
return true;
}
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
* @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);
if (offset != -1) {
boolean res = deleteText(editor, new TextRange(editor.getCaretModel().getOffset(), offset), SelectionType.CHARACTER_WISE);
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) {
MotionGroup.moveCaret(editor, norm);
}
@@ -743,25 +798,11 @@ public class ChangeGroup extends AbstractActionGroup {
* @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) {
TextRange range = MotionGroup.getMotionRange(editor, context, count, rawCount, argument, true, false);
final TextRange range = getDeleteMotionRange(editor, context, count, rawCount, argument);
if (range == null) {
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:
// 1) The range is across multiple lines
// 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 range The range to delete
* @param type The type of deletion
* @param isChange is from a change action
* @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) {
return false;
}
@@ -804,7 +873,7 @@ public class ChangeGroup extends AbstractActionGroup {
pos = size - 1;
}
else {
pos = EditorHelper.normalizeOffset(editor, range.getStartOffset(), false);
pos = EditorHelper.normalizeOffset(editor, range.getStartOffset(), isChange);
}
MotionGroup.moveCaret(editor, pos);
}
@@ -914,7 +983,7 @@ public class ChangeGroup extends AbstractActionGroup {
return changeEndOfLine(editor, context, 1);
}
boolean res = deleteCharacter(editor, count);
boolean res = deleteCharacter(editor, count, true);
if (res) {
initInsert(editor, context, CommandState.Mode.INSERT);
}
@@ -990,7 +1059,7 @@ public class ChangeGroup extends AbstractActionGroup {
final ImmutableSet<String> wordMotions = ImmutableSet.of(
"VimMotionWordRight", "VimMotionBigWordRight", "VimMotionCamelRight");
if (wordMotions.contains(id) && lastWordChar) {
final boolean res = deleteCharacter(editor, 1);
final boolean res = deleteCharacter(editor, 1, true);
if (res) {
insertBeforeCursor(editor, context);
}
@@ -1107,7 +1176,7 @@ public class ChangeGroup extends AbstractActionGroup {
}
}
boolean after = range.getEndOffset() >= EditorHelper.getFileSize(editor);
boolean res = deleteRange(editor, range, type);
boolean res = deleteRange(editor, range, type, true);
if (res) {
if (type == SelectionType.LINE_WISE) {
if (after) {
@@ -1226,7 +1295,7 @@ public class ChangeGroup extends AbstractActionGroup {
if (stroke instanceof Character) {
Character key = (Character)stroke;
if (key == '0') {
deleteCharacter(editor, -1);
deleteCharacter(editor, -1, false);
cnt = 99;
}
}
@@ -1559,6 +1628,9 @@ public class ChangeGroup extends AbstractActionGroup {
private int repeatColumn;
private boolean repeatAppend;
private boolean lastLower = true;
private Document document;
private DocumentAdapter documentListener;
private int oldOffset = -1;
private static Logger logger = Logger.getInstance(ChangeGroup.class.getName());
}

View File

@@ -202,7 +202,7 @@ public class CopyGroup extends AbstractActionGroup {
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);

View File

@@ -1273,9 +1273,11 @@ public class MotionGroup extends AbstractActionGroup {
public static void moveCaret(@NotNull Editor editor, int offset) {
if (offset >= 0 && offset <= editor.getDocument().getTextLength()) {
editor.getCaretModel().moveToOffset(offset);
EditorData.setLastColumn(editor, editor.getCaretModel().getVisualPosition().column);
scrollCaretIntoView(editor);
if (editor.getCaretModel().getOffset() != offset) {
editor.getCaretModel().moveToOffset(offset);
EditorData.setLastColumn(editor, editor.getCaretModel().getVisualPosition().column);
scrollCaretIntoView(editor);
}
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
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");
previousTab.actionPerformed(new AnActionEvent(
null,
context,
"",
new Presentation(),
ActionManager.getInstance(),
0));
return 0;
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
previousTab.actionPerformed(e);
return editor.getCaretModel().getOffset();
}
public int moveCaretGotoNextTab(@NotNull final DataContext context) {
final AnAction previousTab = ActionManager.getInstance().getAction("NextTab");
previousTab.actionPerformed(new AnActionEvent(
null,
context,
"",
new Presentation(),
ActionManager.getInstance(),
0));
return 0;
public int moveCaretGotoNextTab(@NotNull Editor editor, @NotNull DataContext context) {
final AnAction nextTab = ActionManager.getInstance().getAction("NextTab");
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
nextTab.actionPerformed(e);
return editor.getCaretModel().getOffset();
}
public static void scrollCaretIntoView(@NotNull Editor editor) {
int cline = editor.getCaretModel().getVisualPosition().line;
int vline = EditorHelper.getVisualLineAtTopOfScreen(editor);

View File

@@ -44,7 +44,8 @@ public abstract class MotionEditorActionHandler extends AbstractEditorActionHand
if ((cmd.getFlags() & Command.FLAG_SAVE_JUMP) != 0) {
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);
}
MotionGroup.moveCaret(editor, offset);

View 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));
}
}

View File

@@ -38,18 +38,18 @@ public class RegisterActions {
// ******************* Insert Mode 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)));
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)));
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)));
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)));
// Other insert actions
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(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK)),
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,
Command.FLAG_CLEAR_STROKES | Command.FLAG_EXPECT_MORE,
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)));
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineFirst", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, KeyEvent.CTRL_MASK)));
parser.registerAction(KeyParser.MAPPING_INSERT, "VimMotionGotoLineLastEnd", Command.Type.INSERT, Command.FLAG_CLEAR_STROKES,
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)));
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_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_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_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_KP_LEFT, KeyEvent.CTRL_MASK)),
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_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_KP_RIGHT, KeyEvent.CTRL_MASK)),
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK)),

View File

@@ -23,6 +23,8 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
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.Nullable;
@@ -46,8 +48,9 @@ public class ExEntryPanel extends JPanel {
private ExEntryPanel() {
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.setFont(font);
entry = new ExTextField();

View 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();
}
}

View File

@@ -219,6 +219,41 @@ public class ChangeActionTest extends VimTestCase {
"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) {
myFixture.configureByText("a.java", before);
final Editor editor = myFixture.getEditor();