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

Compare commits

...

17 Commits

Author SHA1 Message Date
Alex Plate
55f54b2e82 Prepare for 0.56.1 release 2020-04-22 09:31:38 +03:00
Alex Plate
1b18065e68 Small refactoring of handlers 2020-04-18 17:43:50 +03:00
Alex Plate
053dc02152 EditorActionHandlerBase doesn't take null as caret 2020-04-18 16:40:07 +03:00
Alex Plate
b8cb4a1295 Move IdeaVim icon in the README 2020-04-17 11:34:28 +03:00
Alex Plate
cd2cbf68a1 Update README 2020-04-17 11:31:17 +03:00
Alex Plate
73f3be8af0 Include copyright into repository 2020-04-17 11:17:34 +03:00
Alex Plate
8cce059fb4 Write tests for yanking and pasting with number register 2020-04-17 10:48:09 +03:00
Alex Plate
db641ec6f6 Add runforprogram to contributors list 2020-04-17 10:27:11 +03:00
Alex Plate
9d8239b68d Update changelog 2020-04-17 10:19:56 +03:00
Alex Pláte
4ec0bac275 Merge pull request #234 from runforprogram/master
[VIM-1991] fix >0 number register not work
2020-04-17 10:19:49 +03:00
Alex Plate
613c234cfb Fix related tests 2020-04-16 11:31:22 +03:00
Alex Plate
83dca71f69 [VIM-1992] Fix shift-letter mappings 2020-04-16 11:20:26 +03:00
Alex Plate
f7ea9cdb6e Convert mapping tests to kotlin 2020-04-16 11:03:40 +03:00
Alex Plate
762cb1804f Rename .java to .kt 2020-04-16 11:02:38 +03:00
run
962cfb7ae2 [VIM-1991] fix >0 number register not work 2020-04-16 15:45:57 +08:00
Alex Plate
8415d104e9 Clear registers before test 2020-04-10 11:03:14 +03:00
Alex Plate
abd0f9b961 Update dependencies 2020-04-10 11:00:47 +03:00
28 changed files with 769 additions and 596 deletions

4
.gitignore vendored
View File

@@ -1,6 +1,10 @@
*.swp
/.gradle/
/.idea/
!/.idea/scopes
!/.idea/copyright
/build/
/out/
/tmp/

6
.idea/copyright/IdeaVim.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<component name="CopyrightManager">
<copyright>
<option name="notice" value="IdeaVim - Vim emulator for IDEs based on the IntelliJ platform&#10;Copyright (C) 2003-&amp;#36;today.year The IdeaVim authors&#10;&#10;This program is free software: you can redistribute it and/or modify&#10;it under the terms of the GNU General Public License as published by&#10;the Free Software Foundation, either version 2 of the License, or&#10;(at your option) any later version.&#10;&#10;This program is distributed in the hope that it will be useful,&#10;but WITHOUT ANY WARRANTY; without even the implied warranty of&#10;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&#10;GNU General Public License for more details.&#10;&#10;You should have received a copy of the GNU General Public License&#10;along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;." />
<option name="myName" value="IdeaVim" />
</copyright>
</component>

7
.idea/copyright/profiles_settings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<component name="CopyrightManager">
<settings>
<module2copyright>
<element module="Copyright" copyright="IdeaVim" />
</module2copyright>
</settings>
</component>

3
.idea/scopes/Copyright.xml generated Normal file
View File

@@ -0,0 +1,3 @@
<component name="DependencyValidationManager">
<scope name="Copyright" pattern="file[IdeaVIM.main]:com//*||file[IdeaVIM.test]:*/" />
</component>

View File

@@ -287,6 +287,10 @@ Contributors:
[![icon][github]](https://github.com/kevin70)
&nbsp;
kk
* [![icon][mail]](mailto:runforprogram@163.com)
[![icon][github]](https://github.com/runforprogram)
&nbsp;
runforprogram
If you are a contributor and your name is not listed here, feel free to
contact the maintainers.

View File

@@ -22,6 +22,20 @@ It is important to distinguish EAP from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even
usual beta standards.
[To Be Released]
-------------
_Available since 0.56.1 EAP:_
**Fixes:**
* [VIM-1992](https://youtrack.jetbrains.com/issue/VIM-1992) Fix mappings to `<S-Letter>`
* [VIM-1991](https://youtrack.jetbrains.com/issue/VIM-1991) Fix working with number registers
**Merged PRs:**
* [234](https://github.com/JetBrains/ideavim/pull/234) by [runforprogram](https://github.com/runforprogram): [VIM-1991] fix >0 number register not work
_To Be Released..._
0.56, 2020-04-09
--------------

View File

@@ -79,36 +79,6 @@ in the issue tracker.
* You can install this file by selecting "Settings | Plugins | Install plugin
from disk...".
### Copyright
1. Go to `Preferences | Appearance & Behavior | Scopes`, press "+" button, `Shared`.
Name: Copyright scope
Pattern: `file[IdeaVIM.main]:com//*||file[IdeaVIM.test]:*/`
2. Go to `Preferences | Editor | Copyright | Copyright Profiles` and click the "+" button.
Name: IdeaVim
Text:
IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
Copyright (C) 2003-$today.year The IdeaVim authors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
3. Go to `Preferences | Editor | Copyright`, click the "+" button.
Scope: Copyright scope
Copyright: IdeaVim
### Testing
1. Read about the `@VimBehaviorDiffers` annotation.

119
README.md
View File

@@ -25,23 +25,20 @@ Resources:
* [@IdeaVim](https://twitter.com/ideavim) in Twitter
Installation
Setup
------------
Use the IDE's plugin manager to install the latest version of the plugin.
Start the IDE normally and enable the Vim emulation using "Tools | Vim
Emulator" menu item. At this point you must use Vim keystrokes in all editors.
- IdeaVim can be installed via `Settings | Plugins`.
See [detailed instructions](https://www.jetbrains.com/help/idea/managing-plugins.html#).
If you wish to disable the plugin, select the "Tools | Vim Emulator" menu so
it is unchecked. At this point your IDE will work with its regular keyboard
shortcuts.
- Use `Tools | Vim Emulator` to enable or disable emulation.
Keyboard shortcut conflicts between the Vim emulation and the IDE can be
resolved via "File | Settings | Editor | Vim Emulation", "File | Settings |
Keymap" on Linux & Windows, and via "Preferences | Editor | Vim Emulation",
"Preferences | Keymap" on macOS. They can also be resolved by key-mapping
commands in your ~/.ideavimrc file.
- Use `~/.ideavimrc` file as an analog of `~/.vimrc` ([details](#Files)). XGD standard is supported as well.
- Shortcut conflicts can be resolved using:
- Linux & Windows: `File | Settings | Editor | Vim Emulation` & `File | Settings | Keymap`,
- macOS: `Preferences | Editor | Vim Emulation` & `Preferences | Keymap`,
- regular vim mappings in the `~/.ideavimrc` file.
Get Early Access
-------------------
@@ -49,7 +46,9 @@ Get Early Access
Would you like to try new features and fixes? Join the Early Access Program and
receive EAP builds as updates!
1. Click the IdeaVim icon in the status bar | `EAP` | `Get Early Access...`
1. Click the IdeaVim icon <img src="resources/META-INF/pluginIcon_noBorders.svg" width="16" height="16" alt="icon"/>
in the status bar | `EAP` | `Get Early Access...`
Or subscribe to EAP updates manually:
@@ -57,7 +56,7 @@ Or subscribe to EAP updates manually:
2. Click the gear icon :gear:, select `Manage Plugin Repositories`, and add the following url:
`https://plugins.jetbrains.com/plugins/eap/ideavim`
See [the changelog](CHANGES.md) for the list of hot unreleased features.
See [the changelog](CHANGES.md) for the list of unreleased features.
It is important to distinguish EAP builds from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even
@@ -91,7 +90,7 @@ Supported:
* Vim web help
* Select mode
Emulated Vim plugins:
[Emulated Vim plugins](doc/emulated-plugins.md):
* vim-easymotion
* vim-surround
@@ -115,19 +114,19 @@ See also:
Files
-----
* ~/.ideavimrc
* `~/.ideavimrc`
* Your IdeaVim-specific Vim initialization commands
You can read your ~/.vimrc file from ~/.ideavimrc with this command:
You can read your `~/.vimrc` file from `~/.ideavimrc` with this command:
source ~/.vimrc
Note, that IdeaVim currently parses ~/.ideavimrc file via simple pattern matching.
Note, that IdeaVim currently parses `~/.ideavimrc` file via simple pattern matching.
See [VIM-669](https://youtrack.jetbrains.com/issue/VIM-669) for proper parsing
of VimL files.
Also note that if you have overridden the `user.home` JVM option, this
will affect where IdeaVim looks for your .ideavimrc file. For example, if you
will affect where IdeaVim looks for your `.ideavimrc` file. For example, if you
have `-Duser.home=/my/alternate/home` then IdeaVim will source
`/my/alternate/home/.ideavimrc` instead of `~/.ideavimrc`.
@@ -138,62 +137,25 @@ Put your settings to `$XDG_CONFIG_HOME$/ideavim/ideavimrc` file.
Emulated Vim Plugins
--------------------
IdeaVim extensions emulate some plugins of the original Vim. In order to use
IdeaVim extensions, you have to enable them via this command in your `~/.ideavimrc`:
set <extension-name>
Available extensions:
* easymotion
* Setup:
* Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/)
and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins.
* `set easymotion`
* Emulates [vim-easymotion](https://github.com/easymotion/vim-easymotion)
* Commands: All commands with the mappings are supported. See the [full list of supported commands](https://github.com/AlexPl292/IdeaVim-EasyMotion#supported-commands).
* surround
* Setup: `set surround`
* Emulates [vim-surround](https://github.com/tpope/vim-surround)
* Commands: `ys`, `cs`, `ds`, `S`
* multiple-cursors
* Setup: `set multiple-cursors`
* Emulates [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors)
* Commands: `<A-n>`, `<A-x>`, `<A-p>`, `g<A-n>`
* commentary
* Setup: `set commentary`
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
* Commands: `gcc`, `gc + motion`, `v_gc`
* ReplaceWithRegister
* Setup: `set ReplaceWithRegister`
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
* Commands: `gr`, `grr`
* argtextobj
* Setup:
* `set argtextobj`
* By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
* Additional text objects: `aa`, `ia`
* textobj-entire
* Setup: `set textobj-entire`
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
* Additional text objects: `ae`, `ie`
See [doc/emulated-plugins.md](doc/emulated-plugins.md)
Changes to the IDE
------------------
### Executing IDE Actions
IdeaVim adds two commands for listing and executing arbitrary IDE actions as
Ex commands or via `:map` command mappings:
* `:actionlist [pattern]`
* Find IDE actions by name or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`)
* `:action {name}`
* Execute an action named `NAME`
For example, here `\r` is mapped to the Reformat Code action:
:map \r :action ReformatCode<CR>
### Undo/Redo
The IdeaVim plugin uses the undo/redo functionality of the IntelliJ Platform,
@@ -212,21 +174,6 @@ improvement.
See also [unresolved escape issues](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+Help+topic%3A+i_Esc).
### Executing IDE Actions
IdeaVim adds two commands for listing and executing arbitrary IDE actions as
Ex commands or via `:map` command mappings:
* `:actionlist [pattern]`
* Find IDE actions by name or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`)
* `:action {name}`
* Execute an action named `NAME`
For example, here `\r` is mapped to the Reformat Code action:
:map \r :action ReformatCode<CR>
Contributing
------------

View File

@@ -9,7 +9,7 @@ buildscript {
}
plugins {
id 'org.jetbrains.intellij' version '0.4.16'
id 'org.jetbrains.intellij' version '0.4.18'
}
apply plugin: 'java'
@@ -80,13 +80,13 @@ tasks.register("slackEapNotification") {
changeLog = changeLog.replaceAll("\\[(.+)]\\(([^)]+)\\)", '<$2|$1>') // Enable links
def message ="""
{
"text": "Danny Torrence left a 1 star review for your property.",
"text": "New version of IdeaVim",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "New EAP released: $version\\n$changeLog"
"text": "IdeaVim EAP $version has been relesed\\n$changeLog"
}
}
]

62
doc/emulated-plugins.md Normal file
View File

@@ -0,0 +1,62 @@
Emulated Vim Plugins
--------------------
IdeaVim extensions emulate plugins of the original Vim. In order to use
IdeaVim extensions, you have to enable them via this command in your `~/.ideavimrc`:
set <extension-name>
Available extensions:
## easymotion
* Setup:
* Install [IdeaVim-EasyMotion](https://plugins.jetbrains.com/plugin/13360-ideavim-easymotion/)
and [AceJump](https://plugins.jetbrains.com/plugin/7086-acejump/) plugins.
* `set easymotion`
* Emulates [vim-easymotion](https://github.com/easymotion/vim-easymotion)
* Commands: All commands with the mappings are supported. See the [full list of supported commands](https://github.com/AlexPl292/IdeaVim-EasyMotion#supported-commands).
## surround
* Setup: `set surround`
* Emulates [vim-surround](https://github.com/tpope/vim-surround)
* Commands: `ys`, `cs`, `ds`, `S`
## multiple-cursors
* Setup: `set multiple-cursors`
* Emulates [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors)
* Commands: `<A-n>`, `<A-x>`, `<A-p>`, `g<A-n>`
## commentary
* Setup: `set commentary`
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
* Commands: `gcc`, `gc + motion`, `v_gc`
## ReplaceWithRegister
* Setup: `set ReplaceWithRegister`
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
* Commands: `gr`, `grr`
## argtextobj
* Setup:
* `set argtextobj`
* By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
this syntax parenthesis, must come before angle brackets in the list.
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
* Additional text objects: `aa`, `ia`
## textobj-entire
* Setup: `set textobj-entire`
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
* Additional text objects: `ae`, `ie`

View File

@@ -1,11 +1,11 @@
# suppress inspection "UnusedProperty" for whole file
ideaVersion=201-EAP-SNAPSHOT
ideaVersion=2020.1
downloadIdeaSources=true
instrumentPluginCode=true
version=SNAPSHOT
javaVersion=1.8
kotlinVersion=1.3.70
kotlinVersion=1.3.71
publishUsername=username
publishToken=token
publishChannels=eap

View File

@@ -1,5 +1,5 @@
#Fri Mar 20 11:41:45 MSK 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
#Fri Apr 10 10:57:10 MSK 2020
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStorePath=wrapper/dists

View File

@@ -3,12 +3,8 @@
<id>IdeaVIM</id>
<change-notes><![CDATA[
<ul>
<li>Support ReplaceWithRegister plugin emulation</li>
<li>Support argtextobj.vim plugin emulation</li>
<li>Support vim-textobj-entire plugin emulation</li>
<li>Support showcmd command</li>
<li>Support ls/buffers/files command</li>
<li>Control the icon in the status bar using an `ideastatusicon` option</li>
<li>Fix mappings for `<S-Letter>`</li>
<li>Fix yank/paste with number registers</li>
<li>Various bug fixes</li>
</ul>
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>

View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<defs>
<linearGradient id="ideavim_plugin-a" x1="-6.748%" x2="47.286%" y1="33.61%" y2="85.907%">
<stop offset="0%" stop-color="#3BEA62"/>
<stop offset="100%" stop-color="#087CFA"/>
</linearGradient>
</defs>
<polygon fill="url(#ideavim_plugin-a)" fill-rule="evenodd" points="29.019 0 13.988 26.119 13.988 0 0 0 0 40 16.953 40 40 0"/>
</svg>

After

Width:  |  Height:  |  Size: 450 B

View File

@@ -563,7 +563,9 @@ public class KeyHandler {
private boolean isCommandCountKey(char chKey, @NotNull CommandState editorState) {
// Make sure to avoid handling '0' as the start of a count.
final CommandBuilder commandBuilder = editorState.getCommandBuilder();
return (editorState.getMode() == CommandState.Mode.COMMAND || editorState.getMode() == CommandState.Mode.VISUAL)
return ((editorState.getMode() == CommandState.Mode.COMMAND
&&editorState.getSubMode()!=CommandState.SubMode.REGISTER_PENDING)
|| editorState.getMode() == CommandState.Mode.VISUAL)
&& commandBuilder.isExpectingCount() && Character.isDigit(chKey) && (commandBuilder.getCount() > 0 || chKey != '0');
}

View File

@@ -29,17 +29,35 @@ import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.helper.vimChangeActionSwitchMode
import com.maddyhome.idea.vim.helper.vimLastColumn
sealed class ChangeEditorActionHandler : VimActionHandler.SingleExecution() {
/**
* Base handler for commands that performs change actions.
* This handler stores the commands and they can be repeated later with dot command.
*
* Use subclasses of this handler:
* - [ChangeEditorActionHandler.SingleExecution]
* - [ChangeEditorActionHandler.ForEachCaret]
*/
sealed class ChangeEditorActionHandler : EditorActionHandlerBase(false) {
/**
* This handler executes an action for each caret. That means that if you have 5 carets, [execute] will be
* called 5 times.
* @see [ChangeEditorActionHandler.SingleExecution] for only one execution.
*/
abstract class ForEachCaret : ChangeEditorActionHandler() {
abstract fun execute(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean
}
/**
* This handler executes an action only once for all carets. That means that if you have 5 carets,
* [execute] will be called 1 time.
* @see [ChangeEditorActionHandler.ForEachCaret] for per-caret execution
*/
abstract class SingleExecution : ChangeEditorActionHandler() {
abstract fun execute(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean
}
final override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
// Here we have to save the last changed command. This should be done separately for each
// call of the task, not for each caret. Currently there is no way to schedule any action
// to be worked after each task. So here we override the deprecated execute function which
@@ -50,9 +68,9 @@ sealed class ChangeEditorActionHandler : VimActionHandler.SingleExecution() {
val worked = Ref.create(true)
when (this) {
is ForEachCaret -> {
editor.caretModel.runForEachCaret({ caret ->
if (!caret.isValid) return@runForEachCaret
if (!execute(editor, caret, context, cmd.count, cmd.rawCount, cmd.argument)) {
editor.caretModel.runForEachCaret({ current ->
if (!current.isValid) return@runForEachCaret
if (!execute(editor, current, context, cmd.count, cmd.rawCount, cmd.argument)) {
worked.set(false)
}
}, true)

View File

@@ -36,52 +36,22 @@ import java.util.*
import javax.swing.KeyStroke
/**
* Structure of handlers
* `~` - this symbol means that this handler cannot be used directly (only its children)
* Almost each handler isn't usable by itself and has two children - "SingleExecution" and "ForEachCaret"
* which should be used
* All the commands in IdeaVim should implement one of the following handlers and be registered in VimActions.xml
* Check the KtDocs of handlers for the details.
*
* ~ EditorActionHandlerBase ~
* |
* ----------------------------------------------------------------------------
* | | |
* ~ ForEachCaret ~ ~ SingleExecution ~ ~ VimActionHandler ~
* | | / \
* TextObjectActionHandler MotionActionHandler / \
* SingleExecution ForEachCaret
* |
* -------------------------------------------------------------
* | |
* ~ ChangeEditorActionHandler ~ ~ VisualOperatorActionHandler ~
* / \ / \
* SingleExecution ForEachCaret SingleExecution ForEachCaret
* Structure of handlers:
*
* - [EditorActionHandlerBase]: Base handler for all handlers. Please don't use it directly.
* - [VimActionHandler]: .............. Common vim commands.. E.g.: u, <C-W>s, <C-D>.
* - [TextObjectActionHandler]: ....... Text objects. ....... E.g.: iw, a(, i>
* - [MotionActionHandler]: ........... Motion commands. .... E.g.: k, w, <Up>
* - [ChangeEditorActionHandler]: ..... Change commands. .... E.g.: s, r, gU
* - [VisualOperatorActionHandler]: ... Visual commands.
*
* SpecialKeyHandlers are not presented here because these handlers are created to a limited set of commands and they
* are already implemented
* are already implemented.
*/
/**
* Handler for common usage
*/
sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerBase(myRunForEachCaret) {
abstract class ForEachCaret : VimActionHandler(true) {
abstract fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean
}
abstract class SingleExecution : VimActionHandler(false) {
abstract fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean
}
final override fun baseExecute(editor: Editor, caret: Caret?, context: DataContext, cmd: Command): Boolean {
return when (this) {
is ForEachCaret -> caret == null || execute(editor, caret, context, cmd)
is SingleExecution -> execute(editor, context, cmd)
}
}
}
sealed class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
val id: String = getActionId(this::class.java.name)
abstract val type: Command.Type
@@ -97,25 +67,7 @@ sealed class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
*/
open val flags: EnumSet<CommandFlags> = noneOfEnum()
abstract class ForEachCaret : EditorActionHandlerBase(true) {
abstract fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean
final override fun baseExecute(editor: Editor, caret: Caret?, context: DataContext, cmd: Command): Boolean {
if (caret == null) return false
return execute(editor, caret, context, cmd)
}
}
abstract class SingleExecution : EditorActionHandlerBase(false) {
abstract fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean
final override fun baseExecute(editor: Editor, caret: Caret?, context: DataContext, cmd: Command): Boolean {
return execute(editor, context, cmd)
}
}
abstract fun baseExecute(editor: Editor, caret: Caret?, context: DataContext, cmd: Command): Boolean
abstract fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean
fun execute(editor: Editor, context: DataContext) {
val hostEditor: Editor = CommonDataKeys.HOST_EDITOR.getData(context) ?: editor

View File

@@ -42,7 +42,7 @@ import com.maddyhome.idea.vim.helper.vimSelectionStart
* Base class for motion handlers.
* @see [MotionActionHandler.SingleExecution] and [MotionActionHandler.ForEachCaret]
*/
sealed class MotionActionHandler : EditorActionHandlerBase.SingleExecution() {
sealed class MotionActionHandler : EditorActionHandlerBase(false) {
/**
* Base class for motion handlers.
@@ -127,7 +127,7 @@ sealed class MotionActionHandler : EditorActionHandlerBase.SingleExecution() {
}
}
final override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
val blockSubmodeActive = editor.inBlockSubMode
when (this) {

View File

@@ -41,10 +41,16 @@ import com.maddyhome.idea.vim.helper.vimSelectionStart
*
* This handler gets executed for each caret.
*/
abstract class TextObjectActionHandler : EditorActionHandlerBase.ForEachCaret() {
abstract class TextObjectActionHandler : EditorActionHandlerBase(true) {
final override val type: Command.Type = Command.Type.MOTION
abstract fun getRange(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): TextRange?
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
/**
* This code is called when user executes text object in visual mode. E.g. `va(a(a(`
*/
final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
if (!editor.inVisualMode) return true
val range = getRange(editor, caret, context, cmd.count, cmd.rawCount, cmd.argument) ?: return false

View File

@@ -0,0 +1,58 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.handler
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.command.Command
/**
* Handler for common usage.
*
* Use subclasses of this handler:
* - [VimActionHandler.SingleExecution]
* - [VimActionHandler.ForEachCaret]
*/
sealed class VimActionHandler(myRunForEachCaret: Boolean) : EditorActionHandlerBase(myRunForEachCaret) {
/**
* This handler executes an action for each caret. That means that if you have 5 carets,
* [execute] will be called 5 times.
* @see [VimActionHandler.SingleExecution] for only one execution.
*/
abstract class ForEachCaret : VimActionHandler(true) {
abstract fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean
}
/**
* This handler executes an action only once for all carets. That means that if you have 5 carets,
* [execute] will be called 1 time.
* @see [VimActionHandler.ForEachCaret] for per-caret execution.
*/
abstract class SingleExecution : VimActionHandler(false) {
abstract fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean
}
final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
return when (this) {
is ForEachCaret -> execute(editor, caret, context, cmd)
is SingleExecution -> execute(editor, context, cmd)
}
}
}

View File

@@ -52,14 +52,17 @@ import com.maddyhome.idea.vim.helper.vimSelectionStart
* @author Alex Plate
*
* Base class for visual operation handlers.
* @see [VisualOperatorActionHandler.SingleExecution] and [VisualOperatorActionHandler.ForEachCaret]
*
* Use subclasses of this handler:
* - [VisualOperatorActionHandler.SingleExecution]
* - [VisualOperatorActionHandler.ForEachCaret]
*/
sealed class VisualOperatorActionHandler : VimActionHandler.SingleExecution() {
sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
/**
* Base class for visual operation handlers.
* This handler executes an action for each caret. That means that if you have 5 carets,
* [executeAction] will be called 5 times.
* @see [VisualOperatorActionHandler.SingleExecution] for only one execution
* @see [VisualOperatorActionHandler.SingleExecution] for only one execution.
*/
abstract class ForEachCaret : VisualOperatorActionHandler() {
@@ -90,7 +93,7 @@ sealed class VisualOperatorActionHandler : VimActionHandler.SingleExecution() {
/**
* Base class for visual operation handlers.
* This handler executes an action only once for all carets. That means that if you have 5 carets,
* [executeAction] will be called 1 time.
* [executeForAllCarets] will be called 1 time.
* @see [VisualOperatorActionHandler.ForEachCaret] for per-caret execution
*/
abstract class SingleExecution : VisualOperatorActionHandler() {
@@ -104,7 +107,7 @@ sealed class VisualOperatorActionHandler : VimActionHandler.SingleExecution() {
abstract fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean
}
final override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean {
logger.info("Execute visual command $cmd")
editor.vimChangeActionSwitchMode = null
@@ -131,9 +134,9 @@ sealed class VisualOperatorActionHandler : VimActionHandler.SingleExecution() {
when {
selections.keys.isEmpty() -> return false
selections.keys.size == 1 -> res.set(executeAction(editor, selections.keys.first(), context, cmd, selections.values.first()))
else -> editor.caretModel.runForEachCaret({ caret ->
val range = selections.getValue(caret)
val loopRes = executeAction(editor, caret, context, cmd, range)
else -> editor.caretModel.runForEachCaret({ currentCaret ->
val range = selections.getValue(currentCaret)
val loopRes = executeAction(editor, currentCaret, context, cmd, range)
res.set(loopRes and res.get())
}, true)
}

View File

@@ -535,8 +535,8 @@ public class StringHelper {
if (modifiers == 0) {
return getKeyStroke(c);
}
else if (modifiers == SHIFT_DOWN_MASK) {
return getKeyStroke(Character.toUpperCase(c), modifiers);
else if (modifiers == SHIFT_DOWN_MASK && Character.isLetter(c)) {
return getKeyStroke(Character.toUpperCase(c));
}
else {
return getKeyStroke(Character.toUpperCase(c), modifiers);

View File

@@ -0,0 +1,33 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.action.copy
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.common.Register
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import org.jetbrains.plugins.ideavim.VimTestCase
class PutTestAfterCursorActionTest : VimTestCase() {
fun `test put from number register`() {
VimPlugin.getRegister().saveRegister('4', Register('4', SelectionType.CHARACTER_WISE, "XXX ", ArrayList()))
doTest(parseKeys("\"4p"), "This is my${c} text", "This is my XXX${c} text", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
}
}

View File

@@ -0,0 +1,37 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.action.copy
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import junit.framework.Assert
import org.jetbrains.plugins.ideavim.VimTestCase
class YankLineActionTest : VimTestCase() {
fun `test yank to number register`() {
val before = """
${c}I found it in a legendary land
all rocks and lavender and tufted grass,
""".trimIndent()
configureByText(before)
typeText(parseKeys("\"4yy"))
val register = VimPlugin.getRegister().getRegister('4')!!
Assert.assertEquals("I found it in a legendary land\n", register.text)
}
}

View File

@@ -1,379 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.ex.handler;
import com.maddyhome.idea.vim.command.CommandState;
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
import org.jetbrains.plugins.ideavim.VimTestCase;
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
import static com.maddyhome.idea.vim.helper.StringHelper.stringToKeys;
/**
* @author vlan
*/
public class MapCommandTest extends VimTestCase {
public void testMapKtoJ() {
configureByText("<caret>foo\n" +
"bar\n");
typeText(commandToKeys("nmap k j"));
assertPluginError(false);
assertOffset(0);
typeText(parseKeys("k"));
assertOffset(4);
}
public void testInsertMapJKtoEsc() {
configureByText("<caret>World!\n");
typeText(commandToKeys("imap jk <Esc>"));
assertPluginError(false);
typeText(parseKeys("i", "Hello, ", "jk"));
myFixture.checkResult("Hello, World!\n");
assertMode(CommandState.Mode.COMMAND);
assertOffset(6);
}
public void testBackslashEscape() {
configureByText("\n");
typeText(commandToKeys("imap \\\\,\\<,\\n foo"));
assertPluginError(false);
typeText(stringToKeys("i\\,<,\\n"));
myFixture.checkResult("foo\n");
}
public void testBackslashAtEnd() {
configureByText("\n");
typeText(commandToKeys("imap foo\\ bar"));
assertPluginError(false);
typeText(stringToKeys("ifoo\\"));
myFixture.checkResult("bar\n");
}
public void testUnfinishedSpecialKey() {
configureByText("\n");
typeText(commandToKeys("imap <Esc foo"));
typeText(stringToKeys("i<Esc"));
myFixture.checkResult("foo\n");
}
public void testUnknownSpecialKey() {
configureByText("\n");
typeText(commandToKeys("imap <foo> bar"));
typeText(stringToKeys("i<foo>"));
myFixture.checkResult("bar\n");
}
public void testMapTable() {
configureByText("\n");
typeText(commandToKeys("map <C-Down> gt"));
typeText(commandToKeys("imap foo bar"));
typeText(commandToKeys("imap bar <Esc>"));
typeText(commandToKeys("imap <C-Down> <C-O>gt"));
typeText(commandToKeys("nmap ,f <Plug>Foo"));
typeText(commandToKeys("nmap <Plug>Foo iHello<Esc>"));
typeText(commandToKeys("imap"));
assertExOutput("i <C-Down> <C-O>gt\n" +
"i bar <Esc>\n" +
"i foo bar\n");
typeText(commandToKeys("map"));
assertExOutput(" <C-Down> gt\n" +
"n <Plug>Foo iHello<Esc>\n" +
"n ,f <Plug>Foo\n");
}
public void testRecursiveMapping() {
configureByText("\n");
typeText(commandToKeys("imap foo bar"));
typeText(commandToKeys("imap bar baz"));
typeText(commandToKeys("imap baz quux"));
typeText(parseKeys("i", "foo"));
myFixture.checkResult("quux\n");
}
public void testNonRecursiveMapping() {
configureByText("\n");
typeText(commandToKeys("inoremap a b"));
assertPluginError(false);
typeText(commandToKeys("inoremap b a"));
typeText(parseKeys("i", "ab"));
myFixture.checkResult("ba\n");
}
public void testNonRecursiveMapTable() {
configureByText("\n");
typeText(commandToKeys("inoremap jj <Esc>"));
typeText(commandToKeys("imap foo bar"));
typeText(commandToKeys("imap"));
assertExOutput("i foo bar\n" +
"i jj * <Esc>\n");
}
public void testNop() {
configureByText("<caret>foo\n" +
"bar\n");
typeText(commandToKeys("noremap <Right> <nop>"));
assertPluginError(false);
typeText(parseKeys("l", "<Right>"));
assertPluginError(false);
myFixture.checkResult("foo\n" +
"bar\n");
assertOffset(1);
typeText(commandToKeys("nmap"));
assertExOutput("n <Right> * <Nop>\n");
}
public void testIgnoreModifiers() {
configureByText("\n");
typeText(commandToKeys("nmap <buffer> ,a /a<CR>"));
typeText(commandToKeys("nmap <nowait> ,b /b<CR>"));
typeText(commandToKeys("nmap <silent> ,c /c<CR>"));
typeText(commandToKeys("nmap <special> ,d /d<CR>"));
typeText(commandToKeys("nmap <script> ,e /e<CR>"));
typeText(commandToKeys("nmap <expr> ,f /f<CR>"));
typeText(commandToKeys("nmap <unique> ,g /g<CR>"));
typeText(commandToKeys("nmap"));
assertExOutput("n ,a /a<CR>\n" +
"n ,b /b<CR>\n" +
"n ,c /c<CR>\n" +
"n ,d /d<CR>\n" +
"n ,g /g<CR>\n");
}
// VIM-645 |:nmap|
public void testMapSpace() {
configureByText("foo\n");
typeText(commandToKeys("nmap <space> dw"));
typeText(parseKeys(" "));
myFixture.checkResult("\n");
typeText(parseKeys("i", " ", "<Esc>"));
myFixture.checkResult(" \n");
}
// VIM-661 |:noremap| |r|
public void testNoMappingInReplaceCharacterArgument() {
configureByText("<caret>foo\n");
typeText(commandToKeys("noremap A Z"));
typeText(parseKeys("rA"));
myFixture.checkResult("Aoo\n");
}
// VIM-661 |:omap| |d| |t|
public void testNoMappingInNonFirstCharOfOperatorPendingMode() {
configureByText("<caret>foo, bar\n");
typeText(commandToKeys("omap , ?"));
typeText(parseKeys("dt,"));
myFixture.checkResult(", bar\n");
}
// VIM-666 |:imap|
public void testIgnoreEverythingAfterBar() {
configureByText("<caret>foo\n");
typeText(commandToKeys("imap a b |c \" Something else"));
typeText(parseKeys("ia"));
myFixture.checkResult("b foo\n");
}
// VIM-666 |:imap|
public void testBarEscaped() {
configureByText("<caret>foo\n");
typeText(commandToKeys("imap a b \\| c"));
typeText(parseKeys("ia"));
myFixture.checkResult("b | cfoo\n");
}
// VIM-666 |:imap|
public void testBarEscapedSeveralSpaces() {
configureByText("<caret>foo\n");
typeText(commandToKeys("imap a b \\| c |"));
typeText(parseKeys("ia"));
myFixture.checkResult("b | c foo\n");
}
// VIM-670 |:map|
public void testFirstCharIsNonRecursive() {
configureByText("\n");
typeText(commandToKeys("map ab abcd"));
typeText(parseKeys("ab"));
myFixture.checkResult("bcd\n");
}
// VIM-676 |:map|
public void testBackspaceCharacterInVimRc() {
configureByText("\n");
VimScriptParser.executeText("inoremap # X\u0008#\n");
typeText(parseKeys("i", "#", "<Esc>"));
myFixture.checkResult("#\n");
assertMode(CommandState.Mode.COMMAND);
typeText(commandToKeys("imap"));
assertExOutput("i # * X<C-H>#\n");
}
// VIM-679 |:map|
public void testCancelCharacterInVimRc() {
configureByText("<caret>foo\n" +
"bar\n");
VimScriptParser.executeText("map \u0018i dd\n");
typeText(parseKeys("i", "#", "<Esc>"));
myFixture.checkResult("#foo\n" +
"bar\n");
assertMode(CommandState.Mode.COMMAND);
typeText(commandToKeys("map"));
assertExOutput(" <C-X>i dd\n");
typeText(parseKeys("<C-X>i"));
myFixture.checkResult("bar\n");
}
// VIM-679 |:map|
public void testBarCtrlVEscaped() {
configureByText("<caret>foo\n");
VimScriptParser.executeText("imap a b \u0016|\u0016| c |\n");
typeText(parseKeys("ia"));
myFixture.checkResult("b || c foo\n");
}
// VIM-679 |:map|
public void testCtrlMCtrlLAsNewLine() {
configureByText("<caret>foo\n");
VimScriptParser.executeText("map A :%s/foo/bar/g\r\u000C\n");
typeText(parseKeys("A"));
myFixture.checkResult("bar\n");
}
// VIM-700 |:map|
public void testRemappingZero() {
configureByText("x<caret>yz\n");
VimScriptParser.executeText("map 0 ~");
typeText(parseKeys("0"));
myFixture.checkResult("xYz\n");
}
// VIM-700 |:map|
public void testRemappingZeroStillAllowsZeroToBeUsedInCount() {
configureByText("a<caret>bcdefghijklmnop\n");
VimScriptParser.executeText("map 0 ^");
typeText(parseKeys("10~"));
myFixture.checkResult("aBCDEFGHIJKlmnop\n");
}
// VIM-700 |:map|
public void testRemappingDeleteOverridesRemovingLastDigitFromCount() {
configureByText("a<caret>bcdefghijklmnop\n");
VimScriptParser.executeText("map <Del> ~");
typeText(parseKeys("10<Del>"));
myFixture.checkResult("aBCDEFGHIJKlmnop\n");
}
// VIM-650 |mapleader|
public void testMapLeader() {
configureByText("\n");
typeText(commandToKeys("let mapleader = \",\""));
typeText(commandToKeys("nmap <Leader>z izzz<Esc>"));
typeText(parseKeys(",z"));
myFixture.checkResult("zzz\n");
}
public void testAmbiguousMapping() {
configureByText("\n");
typeText(commandToKeys("nmap ,f iHello<Esc>"));
typeText(commandToKeys("nmap ,fc iBye<Esc>"));
typeText(parseKeys(",fdh"));
myFixture.checkResult("Helo\n");
typeText(parseKeys("diw"));
myFixture.checkResult("\n");
typeText(parseKeys(",fch"));
myFixture.checkResult("Bye\n");
}
public void testLongAmbiguousMapping() {
configureByText("\n");
typeText(commandToKeys("nmap ,foo iHello<Esc>"));
typeText(commandToKeys("nmap ,fooc iBye<Esc>"));
typeText(parseKeys(",foodh"));
myFixture.checkResult("Helo\n");
typeText(parseKeys("diw"));
myFixture.checkResult("\n");
typeText(parseKeys(",fooch"));
myFixture.checkResult("Bye\n");
}
public void testPlugMapping() {
configureByText("\n");
typeText(commandToKeys("nmap ,f <Plug>Foo"));
typeText(commandToKeys("nmap <Plug>Foo iHello<Esc>"));
typeText(parseKeys(",fa!<Esc>"));
myFixture.checkResult("Hello!\n");
}
public void testIntersectingCommands() {
configureByText("123<caret>4567890");
typeText(commandToKeys("map ds h"));
typeText(commandToKeys("map I 3l"));
typeText(parseKeys("dI"));
myFixture.checkResult("123<caret>7890");
}
public void testIncompleteMapping() {
configureByText("123<caret>4567890");
typeText(commandToKeys("map <Plug>(Hi)l lll"));
typeText(commandToKeys("map I <Plug>(Hi)"));
typeText(parseKeys("Ih"));
myFixture.checkResult("12<caret>34567890");
}
public void testIntersectingCommands2() {
configureByText("123<caret>4567890");
typeText(commandToKeys("map as x"));
typeText(parseKeys("gas"));
myFixture.checkResult("123<caret>567890");
}
public void testMapZero() {
configureByText("A quick <caret>brown fox jumps over the lazy dog");
typeText(commandToKeys("nmap 0 w"));
typeText(parseKeys("0"));
assertOffset(14);
}
public void testMapZeroIgnoredInCount() {
configureByText("A quick <caret>brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog");
typeText(commandToKeys("nmap 0 w"));
typeText(parseKeys("10w"));
assertOffset(51);
}
public void testMapNonZeroDigit() {
configureByText("A quick <caret>brown fox jumps over the lazy dog");
typeText(commandToKeys("nmap 2 w"));
typeText(parseKeys("2"));
assertOffset(14);
}
public void testMapNonZeroDigitNotIncludedInCount() {
configureByText("A quick <caret>brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog");
typeText(commandToKeys("nmap 2 w"));
typeText(parseKeys("92"));
assertOffset(45);
}
}

View File

@@ -0,0 +1,426 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.ex.handler
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser
import com.maddyhome.idea.vim.helper.StringHelper
import org.jetbrains.plugins.ideavim.VimTestCase
/**
* @author vlan
*/
class MapCommandTest : VimTestCase() {
fun testMapKtoJ() {
configureByText("""
${c}foo
bar
""".trimIndent())
typeText(commandToKeys("nmap k j"))
assertPluginError(false)
assertOffset(0)
typeText(StringHelper.parseKeys("k"))
assertOffset(4)
}
fun testInsertMapJKtoEsc() {
configureByText("${c}World!\n")
typeText(commandToKeys("imap jk <Esc>"))
assertPluginError(false)
typeText(StringHelper.parseKeys("i", "Hello, ", "jk"))
myFixture.checkResult("Hello, World!\n")
assertMode(CommandState.Mode.COMMAND)
assertOffset(6)
}
fun testBackslashEscape() {
configureByText("\n")
typeText(commandToKeys("imap \\\\,\\<,\\n foo"))
assertPluginError(false)
typeText(StringHelper.stringToKeys("i\\,<,\\n"))
myFixture.checkResult("foo\n")
}
fun testBackslashAtEnd() {
configureByText("\n")
typeText(commandToKeys("imap foo\\ bar"))
assertPluginError(false)
typeText(StringHelper.stringToKeys("ifoo\\"))
myFixture.checkResult("bar\n")
}
fun testUnfinishedSpecialKey() {
configureByText("\n")
typeText(commandToKeys("imap <Esc foo"))
typeText(StringHelper.stringToKeys("i<Esc"))
myFixture.checkResult("foo\n")
}
fun testUnknownSpecialKey() {
configureByText("\n")
typeText(commandToKeys("imap <foo> bar"))
typeText(StringHelper.stringToKeys("i<foo>"))
myFixture.checkResult("bar\n")
}
fun testMapTable() {
configureByText("\n")
typeText(commandToKeys("map <C-Down> gt"))
typeText(commandToKeys("imap foo bar"))
typeText(commandToKeys("imap bar <Esc>"))
typeText(commandToKeys("imap <C-Down> <C-O>gt"))
typeText(commandToKeys("nmap ,f <Plug>Foo"))
typeText(commandToKeys("nmap <Plug>Foo iHello<Esc>"))
typeText(commandToKeys("imap"))
assertExOutput("""
i <C-Down> <C-O>gt
i bar <Esc>
i foo bar
""".trimIndent())
typeText(commandToKeys("map"))
assertExOutput(""" <C-Down> gt
n <Plug>Foo iHello<Esc>
n ,f <Plug>Foo
""")
}
fun testRecursiveMapping() {
configureByText("\n")
typeText(commandToKeys("imap foo bar"))
typeText(commandToKeys("imap bar baz"))
typeText(commandToKeys("imap baz quux"))
typeText(StringHelper.parseKeys("i", "foo"))
myFixture.checkResult("quux\n")
}
fun testNonRecursiveMapping() {
configureByText("\n")
typeText(commandToKeys("inoremap a b"))
assertPluginError(false)
typeText(commandToKeys("inoremap b a"))
typeText(StringHelper.parseKeys("i", "ab"))
myFixture.checkResult("ba\n")
}
fun testNonRecursiveMapTable() {
configureByText("\n")
typeText(commandToKeys("inoremap jj <Esc>"))
typeText(commandToKeys("imap foo bar"))
typeText(commandToKeys("imap"))
assertExOutput("""
i foo bar
i jj * <Esc>
""".trimIndent())
}
fun testNop() {
configureByText("""
${c}foo
bar
""".trimIndent())
typeText(commandToKeys("noremap <Right> <nop>"))
assertPluginError(false)
typeText(StringHelper.parseKeys("l", "<Right>"))
assertPluginError(false)
myFixture.checkResult("""
foo
bar
""".trimIndent())
assertOffset(1)
typeText(commandToKeys("nmap"))
assertExOutput("n <Right> * <Nop>\n")
}
fun testIgnoreModifiers() {
configureByText("\n")
typeText(commandToKeys("nmap <buffer> ,a /a<CR>"))
typeText(commandToKeys("nmap <nowait> ,b /b<CR>"))
typeText(commandToKeys("nmap <silent> ,c /c<CR>"))
typeText(commandToKeys("nmap <special> ,d /d<CR>"))
typeText(commandToKeys("nmap <script> ,e /e<CR>"))
typeText(commandToKeys("nmap <expr> ,f /f<CR>"))
typeText(commandToKeys("nmap <unique> ,g /g<CR>"))
typeText(commandToKeys("nmap"))
assertExOutput("""
n ,a /a<CR>
n ,b /b<CR>
n ,c /c<CR>
n ,d /d<CR>
n ,g /g<CR>
""".trimIndent())
}
// VIM-645 |:nmap|
fun testMapSpace() {
configureByText("foo\n")
typeText(commandToKeys("nmap <space> dw"))
typeText(StringHelper.parseKeys(" "))
myFixture.checkResult("\n")
typeText(StringHelper.parseKeys("i", " ", "<Esc>"))
myFixture.checkResult(" \n")
}
// VIM-661 |:noremap| |r|
fun testNoMappingInReplaceCharacterArgument() {
configureByText("${c}foo\n")
typeText(commandToKeys("noremap A Z"))
typeText(StringHelper.parseKeys("rA"))
myFixture.checkResult("Aoo\n")
}
// VIM-661 |:omap| |d| |t|
fun testNoMappingInNonFirstCharOfOperatorPendingMode() {
configureByText("${c}foo, bar\n")
typeText(commandToKeys("omap , ?"))
typeText(StringHelper.parseKeys("dt,"))
myFixture.checkResult(", bar\n")
}
// VIM-666 |:imap|
fun testIgnoreEverythingAfterBar() {
configureByText("${c}foo\n")
typeText(commandToKeys("imap a b |c \" Something else"))
typeText(StringHelper.parseKeys("ia"))
myFixture.checkResult("b foo\n")
}
// VIM-666 |:imap|
fun testBarEscaped() {
configureByText("${c}foo\n")
typeText(commandToKeys("imap a b \\| c"))
typeText(StringHelper.parseKeys("ia"))
myFixture.checkResult("b | cfoo\n")
}
// VIM-666 |:imap|
fun testBarEscapedSeveralSpaces() {
configureByText("${c}foo\n")
typeText(commandToKeys("imap a b \\| c |"))
typeText(StringHelper.parseKeys("ia"))
myFixture.checkResult("b | c foo\n")
}
// VIM-670 |:map|
fun testFirstCharIsNonRecursive() {
configureByText("\n")
typeText(commandToKeys("map ab abcd"))
typeText(StringHelper.parseKeys("ab"))
myFixture.checkResult("bcd\n")
}
// VIM-676 |:map|
fun testBackspaceCharacterInVimRc() {
configureByText("\n")
VimScriptParser.executeText("inoremap # X\u0008#\n")
typeText(StringHelper.parseKeys("i", "#", "<Esc>"))
myFixture.checkResult("#\n")
assertMode(CommandState.Mode.COMMAND)
typeText(commandToKeys("imap"))
assertExOutput("i # * X<C-H>#\n")
}
// VIM-679 |:map|
fun testCancelCharacterInVimRc() {
configureByText("""
${c}foo
bar
""".trimIndent())
VimScriptParser.executeText("map \u0018i dd\n")
typeText(StringHelper.parseKeys("i", "#", "<Esc>"))
myFixture.checkResult("""
#foo
bar
""".trimIndent())
assertMode(CommandState.Mode.COMMAND)
typeText(commandToKeys("map"))
assertExOutput(" <C-X>i dd\n")
typeText(StringHelper.parseKeys("<C-X>i"))
myFixture.checkResult("bar\n")
}
// VIM-679 |:map|
fun testBarCtrlVEscaped() {
configureByText("${c}foo\n")
VimScriptParser.executeText("imap a b \u0016|\u0016| c |\n")
typeText(StringHelper.parseKeys("ia"))
myFixture.checkResult("b || c foo\n")
}
// VIM-679 |:map|
fun testCtrlMCtrlLAsNewLine() {
configureByText("${c}foo\n")
VimScriptParser.executeText("map A :%s/foo/bar/g\r\u000C\n")
typeText(StringHelper.parseKeys("A"))
myFixture.checkResult("bar\n")
}
// VIM-700 |:map|
fun testRemappingZero() {
configureByText("x${c}yz\n")
VimScriptParser.executeText("map 0 ~")
typeText(StringHelper.parseKeys("0"))
myFixture.checkResult("xYz\n")
}
// VIM-700 |:map|
fun testRemappingZeroStillAllowsZeroToBeUsedInCount() {
configureByText("a${c}bcdefghijklmnop\n")
VimScriptParser.executeText("map 0 ^")
typeText(StringHelper.parseKeys("10~"))
myFixture.checkResult("aBCDEFGHIJKlmnop\n")
}
// VIM-700 |:map|
fun testRemappingDeleteOverridesRemovingLastDigitFromCount() {
configureByText("a${c}bcdefghijklmnop\n")
VimScriptParser.executeText("map <Del> ~")
typeText(StringHelper.parseKeys("10<Del>"))
myFixture.checkResult("aBCDEFGHIJKlmnop\n")
}
// VIM-650 |mapleader|
fun testMapLeader() {
configureByText("\n")
typeText(commandToKeys("let mapleader = \",\""))
typeText(commandToKeys("nmap <Leader>z izzz<Esc>"))
typeText(StringHelper.parseKeys(",z"))
myFixture.checkResult("zzz\n")
}
fun testAmbiguousMapping() {
configureByText("\n")
typeText(commandToKeys("nmap ,f iHello<Esc>"))
typeText(commandToKeys("nmap ,fc iBye<Esc>"))
typeText(StringHelper.parseKeys(",fdh"))
myFixture.checkResult("Helo\n")
typeText(StringHelper.parseKeys("diw"))
myFixture.checkResult("\n")
typeText(StringHelper.parseKeys(",fch"))
myFixture.checkResult("Bye\n")
}
fun testLongAmbiguousMapping() {
configureByText("\n")
typeText(commandToKeys("nmap ,foo iHello<Esc>"))
typeText(commandToKeys("nmap ,fooc iBye<Esc>"))
typeText(StringHelper.parseKeys(",foodh"))
myFixture.checkResult("Helo\n")
typeText(StringHelper.parseKeys("diw"))
myFixture.checkResult("\n")
typeText(StringHelper.parseKeys(",fooch"))
myFixture.checkResult("Bye\n")
}
fun testPlugMapping() {
configureByText("\n")
typeText(commandToKeys("nmap ,f <Plug>Foo"))
typeText(commandToKeys("nmap <Plug>Foo iHello<Esc>"))
typeText(StringHelper.parseKeys(",fa!<Esc>"))
myFixture.checkResult("Hello!\n")
}
fun testIntersectingCommands() {
configureByText("123${c}4567890")
typeText(commandToKeys("map ds h"))
typeText(commandToKeys("map I 3l"))
typeText(StringHelper.parseKeys("dI"))
myFixture.checkResult("123${c}7890")
}
fun testIncompleteMapping() {
configureByText("123${c}4567890")
typeText(commandToKeys("map <Plug>(Hi)l lll"))
typeText(commandToKeys("map I <Plug>(Hi)"))
typeText(StringHelper.parseKeys("Ih"))
myFixture.checkResult("12${c}34567890")
}
fun testIntersectingCommands2() {
configureByText("123${c}4567890")
typeText(commandToKeys("map as x"))
typeText(StringHelper.parseKeys("gas"))
myFixture.checkResult("123${c}567890")
}
fun testMapZero() {
configureByText("A quick ${c}brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap 0 w"))
typeText(StringHelper.parseKeys("0"))
assertOffset(14)
}
fun testMapZeroIgnoredInCount() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap 0 w"))
typeText(StringHelper.parseKeys("10w"))
assertOffset(51)
}
fun testMapNonZeroDigit() {
configureByText("A quick ${c}brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap 2 w"))
typeText(StringHelper.parseKeys("2"))
assertOffset(14)
}
fun testMapNonZeroDigitNotIncludedInCount() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap 2 w"))
typeText(StringHelper.parseKeys("92"))
assertOffset(45)
}
fun testShiftSpace() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap <S-Space> w"))
typeText(StringHelper.parseKeys("<S-Space>"))
myFixture.checkResult("A quick brown ${c}fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
}
fun testShiftLetter() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap <S-D> w"))
typeText(StringHelper.parseKeys("<S-D>"))
myFixture.checkResult("A quick brown ${c}fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
}
fun testUppercaseLetter() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap D w"))
typeText(StringHelper.parseKeys("D"))
myFixture.checkResult("A quick brown ${c}fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
}
fun `test shift letter doesn't break insert mode`() {
configureByText("A quick ${c}brown fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(commandToKeys("nmap <S-D> w"))
typeText(StringHelper.parseKeys("<S-D>"))
myFixture.checkResult("A quick brown ${c}fox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
typeText(StringHelper.parseKeys("iD<Esc>"))
myFixture.checkResult("A quick brown ${c}Dfox jumps over the lazy dog. A quick brown fox jumps over the lazy dog")
}
}

View File

@@ -37,6 +37,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
fun `test replace with empty register`() {
val text = "one ${c}two three"
VimPlugin.getRegister().resetRegisters()
configureByText(text)
typeText(parseKeys("griw"))

View File

@@ -24,7 +24,6 @@ import org.jetbrains.plugins.ideavim.VimTestCase;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
/**
@@ -32,7 +31,7 @@ import java.util.List;
*/
public class StringHelperTest extends VimTestCase {
public void testParseKeyModifiers() {
assertTypedKeyStroke('C', "<S-C>");
assertTypedKeyStroke('C', "C");
assertTypedKeyStroke('c', "c");
assertPressedKeyStroke("control C", "<C-C>");
@@ -113,13 +112,8 @@ public class StringHelperTest extends VimTestCase {
}
private void assertTypedKeyStroke(char expected, @NotNull String actual) {
if (Character.isUpperCase(expected)) {
assertEquals(KeyStroke.getKeyStroke(expected, KeyEvent.SHIFT_DOWN_MASK), parseKeyStroke(actual));
}
else {
assertEquals(KeyStroke.getKeyStroke(expected), parseKeyStroke(actual));
}
}
@NotNull
private KeyStroke parseKeyStroke(@NotNull String s) {