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

Compare commits

..

28 Commits

Author SHA1 Message Date
Alex Plate
3881b905be Update CHANGES.md 2020-11-12 10:51:57 +03:00
Alex Plate
073c62f868 Update version on TeamCity 2020-11-12 10:50:25 +03:00
Alex Plate
d8e0f26bea Revert "Unmute falling tests"
This reverts commit 0296cae7
2020-11-12 10:48:54 +03:00
Alex Plate
04c24ab5d0 Run manual tests 2020-11-12 10:38:02 +03:00
Alex Plate
bfb0ba1ab9 Update emulated plugins 2020-11-12 10:31:11 +03:00
Alex Plate
fb7d48af1f Update IDE actions section in README 2020-11-12 10:11:33 +03:00
Alex Plate
559989ce4b Remove all To Be Released labels 2020-11-12 10:03:56 +03:00
Alex Plate
81f59e3c18 Update plugin.xml 2020-11-12 10:03:04 +03:00
Alex Plate
b6adf9f7a9 Rename ideaenabledbufs to ideavimsupport 2020-11-12 09:45:29 +03:00
Alex Plate
b972a01cf0 Add merged PR to changes 2020-11-12 09:41:57 +03:00
Alex Plate
0296cae712 Unmute falling tests 2020-11-12 09:39:09 +03:00
Matt Ellis
c38b18e16b Prevent inlays causing scrolling to stick 2020-11-12 09:37:55 +03:00
Matt Ellis
8d65c3ed26 Limit how much of an inlay is shown when scrolling 2020-11-12 09:37:55 +03:00
Matt Ellis
995bb966ad Reposition cursor when scrolloff changes 2020-11-12 09:37:55 +03:00
Matt Ellis
dbda1a76ca [VIM-2158] Fix scrolling when scrolloff is greater than half screen height, but less than full screen height 2020-11-12 09:37:55 +03:00
Alex Plate
ed6f990d9a Remove some qodana inspections 2020-11-06 21:39:37 +03:00
Alex Plate
4f86d9cc77 Correct visual mode exiting when after line end 2020-11-06 20:26:26 +03:00
Alex Plate
d55774abab Use vimForEachCaret 2020-11-06 20:21:05 +03:00
Alex Plate
d5591ba08d Update qodana profile 2020-11-06 20:00:21 +03:00
Alex Plate
f67d483c4e Exclude next method from property based testing 2020-11-06 19:48:33 +03:00
Alex Plate
f26ddd4a27 Fix exception in aW 2020-11-06 19:12:40 +03:00
Alex Plate
dbbea642bc Add inspection profiles to the repository 2020-11-06 18:40:02 +03:00
Alex Plate
0539e39977 Fix <C-\><C-N> 2020-11-06 11:16:13 +03:00
Alex Plate
65235d32a1 Fix exception for ]b command 2020-11-06 10:36:03 +03:00
Alex Plate
ecfcdf5a8c Update intellij gradle plugin 2020-11-06 10:06:25 +03:00
Alex Plate
cf127ba7f9 Update plugin description 2020-11-06 09:37:01 +03:00
Alex Plate
1fba77d925 Update getName description 2020-11-05 11:31:41 +03:00
Alex Plate
5752b116f6 Fix plugin.xml 2020-11-04 13:04:36 +03:00
33 changed files with 709 additions and 102 deletions

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@
!/.idea/scopes
!/.idea/copyright
!/.idea/icon.png
!/.idea/inspectionProfiles
/build/
/out/

View File

@@ -0,0 +1,9 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_requireAnnotationsFirst" value="true" />
</inspection_tool>
<inspection_tool class="StaticMethodOnlyUsedInOneClass" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

23
.idea/inspectionProfiles/Qodana.xml generated Normal file
View File

@@ -0,0 +1,23 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<description>Inspections profile for Qodana</description>
<option name="myName" value="Qodana" />
<inspection_tool class="CanBeFinal" enabled="false" level="WARNING" enabled_by_default="false">
<option name="REPORT_CLASSES" value="false" />
<option name="REPORT_METHODS" value="false" />
<option name="REPORT_FIELDS" value="true" />
</inspection_tool>
<inspection_tool class="GrUnresolvedAccess" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_requireAnnotationsFirst" value="true" />
</inspection_tool>
<inspection_tool class="SameReturnValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="StaticMethodOnlyUsedInOneClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View File

@@ -10,7 +10,7 @@ object Release : BuildType({
description = "Build and publish IdeaVim plugin"
artifactRules = "build/distributions/*"
buildNumberPattern = "0.60"
buildNumberPattern = "0.61"
params {
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2")

View File

@@ -13,7 +13,7 @@ object ReleaseEap : BuildType({
description = "Build and publish EAP of IdeaVim plugin"
artifactRules = "build/distributions/*"
buildNumberPattern = "0.60.%build.counter%"
buildNumberPattern = "0.61.%build.counter%"
params {
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2")

View File

@@ -24,6 +24,10 @@ usual beta standards.
## To Be Released
...
## 0.61, 2020-11-12
### Features:
* Ability to map IDE actions via the `<Action>` keyword. E.g. `map <C-K> <Action>(CommentByLineComment)`.
Check out `README.md` for the details.
@@ -44,14 +48,18 @@ usual beta standards.
### Changes:
* Fix `<Esc>` for dialogs. Now `<Esc>` will exit insert / visual mode and close the dialog from normal mode.
* Add option to disable IdeaVim in dialogs / single line editors. [VIM-765](https://youtrack.jetbrains.com/issue/VIM-765)
Use `set ideaenabledbufs=` to disable IdeaVim in dialog editors.
_Note for EAP users: the option name can be changed for the stable release_
Use `set ideavimsupport=` to disable IdeaVim in dialog editors.
* Reposition cursor when `scrolloff` changes
### Fixes:
* [VIM-2150](https://youtrack.jetbrains.com/issue/VIM-2150) `Shift-D` should not delete an empty line
* [VIM-2157](https://youtrack.jetbrains.com/issue/VIM-2157) Fix tab with an active template
* [VIM-2156](https://youtrack.jetbrains.com/issue/VIM-2156) Correct up/down motions with inlays
* [VIM-2144](https://youtrack.jetbrains.com/issue/VIM-2144) Correct text position after block insert with inlays
* [VIM-2158](https://youtrack.jetbrains.com/issue/VIM-2158) Fix scrolling when `scrolloff` is over half screen height, but less than full height
### Merged PRs:
* [255](https://github.com/JetBrains/ideavim/pull/255) by [Matt Ellis](https://github.com/citizenmatt): Fix various scrolling issues
## 0.60, 2020-10-09

View File

@@ -11,7 +11,7 @@ IdeaVim
[![Gitter][gitter-svg]][gitter]
[![Twitter][twitter-svg]][twitter]
IdeaVim is a Vim emulation plugin for IDEs based on the IntelliJ Platform.
IdeaVim is a Vim emulation plugin for IntelliJ Platform-based IDEs.
##### Contact maintainers:
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
@@ -188,42 +188,38 @@ Changes to the IDE
### Executing IDE Actions
IdeaVim adds two commands for listing and executing arbitrary IDE actions as
IdeaVim adds various commands for listing and executing arbitrary IDE actions as
Ex commands or via `:map` command mappings:
**Executing actions:**
* `:action {action_id}`
* Execute an action by id. Works from Ex command line.
* `<Action>(*action_id*)`
* For the mappings you can use a special `<Action>` keyword. Don't forget the parentheses.
**Finding actions:**
* `:actionlist [pattern]`
* Find IDE actions by name or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`)
* `:action {name}`
* Execute an action named `NAME`
* Find IDE actions by id or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`)
In addition to `:actionlist` command, IdeaVim provides `IdeaVim: track action Ids` option to
extract the ids of executed command. This option can be found in "Search everywhere" (double `shift`). **[To Be Released]**
* In addition to `:actionlist` command, IdeaVim provides `IdeaVim: track action Ids` option to
extract the ids of executed command. This option can be found in "Search everywhere" (double `shift`).
<details>
<summary><strong>"Track aciton Ids" Details</strong> (click to see)</summary>
<img src="resources/readme/track_action_id.gif" alt="track actioin ids"/>
</details>
For the mappings you can use a special `<Action>` keyword. Use `<Action>(*action_id*)` to map keys to some action. Don't
forget the parentheses. This keyword works for insert mode as well. **[To Be Released]**
<details>
<summary><strong>"Track aciton Ids" Details</strong> (click to see)</summary>
<img src="resources/readme/track_action_id.gif" alt="track action ids"/>
</details>
Examples:
```vim
" Map \r to the Reformat Code action
:map \r :action ReformatCode<CR>
" or
:map \r <Action>(ReformatCode) " [To Be Released]
:map \r <Action>(ReformatCode)
" Map <leader>d to start debug
:map <leader>d :action Debug<CR>
" or
:map <leader>d <Action>(Debug) " [To Be Released]
:map <leader>d <Action>(Debug)
" Map \b to toggle the breakpoint on the current line
:map \b :action ToggleLineBreakpoint<CR>
" or
:map \b <Action>(ToggleLineBreakpoint) " [To Be Released]
:map \b <Action>(ToggleLineBreakpoint)
```
### Undo/Redo

View File

@@ -13,7 +13,7 @@ buildscript {
}
plugins {
id 'org.jetbrains.intellij' version '0.6.1'
id 'org.jetbrains.intellij' version '0.6.2'
id 'io.gitlab.arturbosch.detekt' version '1.14.1'
id "org.jetbrains.changelog" version "0.6.2"
}

View File

@@ -4,7 +4,15 @@ 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>
```
set <extension-name>
```
If you reuse your existing `.vimrc` file using `source ~/.vimrc`, IdeaVim can parse and enable plugins that are defined
using [vim-plug](https://github.com/junegunn/vim-plug) or [vundle](https://github.com/VundleVim/Vundle.vim).
No additional set commands in `~/.ideavimrc` are required.
If you'd like to disable some plugin that's enabled in `.vimrc`, you can use `set no<extension-name>`
in `~/.ideavimrc`.
Available extensions:
@@ -20,18 +28,52 @@ Available extensions:
## surround
* Setup: `set surround`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tpope/vim-surround'</code>
<br/>
<code>Plug 'tpope/vim-surround'</code>
<br/>
<code>Plug 'vim-surround'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
</details>
* Emulates [vim-surround](https://github.com/tpope/vim-surround)
* Commands: `ys`, `cs`, `ds`, `S`
## multiple-cursors
* Setup: `set multiple-cursors`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/terryma/vim-multiple-cursors'</code>
<br/>
<code>Plug 'terryma/vim-multiple-cursors'</code>
<br/>
<code>Plug 'vim-multiple-cursors'</code>
</details>
* 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`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/>
<code>Plug 'tpope/vim-commentary'</code>
<br/>
<code>Plug 'vim-commentary'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=3695'</code>
<br/>
<code>Plug 'tomtom/tcomment_vim'</code>
<br/>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1173'</code>
</details>
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
* Commands: `gcc`, `gc + motion`, `v_gc`
* By [Daniel Leong](https://github.com/dhleong)
@@ -39,6 +81,22 @@ Available extensions:
## ReplaceWithRegister
* Setup: `set ReplaceWithRegister`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/vim-scripts/ReplaceWithRegister'</code>
<br/>
<code>Plug 'vim-scripts/ReplaceWithRegister'</code>
<br/>
<code>Plug 'ReplaceWithRegister'</code>
<br/>
<code>Plug 'https://github.com/inkarkat/vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'inkarkat/vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2703'</code>
</details>
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
* Commands: `gr`, `grr`
* By [igrekster](https://github.com/igrekster)
@@ -47,6 +105,16 @@ Available extensions:
* Setup:
* `set argtextobj`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'argtextobj.vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
</details>
* 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
@@ -59,6 +127,14 @@ Available extensions:
## exchange
* Setup: `set exchange`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'vim-exchange'</code>
</details>
* Emulates [vim-exchange](https://github.com/tommcdo/vim-exchange)
* Commands: `cx`, `cxx`, `X`, `cxc`
* By [fan-tom](https://github.com/fan-tom)
@@ -66,6 +142,16 @@ Available extensions:
## textobj-entire
* Setup: `set textobj-entire`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/kana/vim-textobj-entire'</code>
<br/>
<code>Plug 'kana/vim-textobj-entire'</code>
<br/>
<code>Plug 'vim-textobj-entire'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2610'</code>
</details>
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
* Additional text objects: `ae`, `ie`
* By [Alexandre Grison](https://github.com/agrison)
@@ -74,6 +160,14 @@ Available extensions:
* Setup:
* `set highlightedyank`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'vim-highlightedyank'</code>
</details>
* if you want to optimize highlight duration, assign a time in milliseconds:
`let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent.

View File

@@ -125,7 +125,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
"<C-Down>", "<C-Up>", "<PageUp>", "<PageDown>",
"<C-J>", "<C-Q>"
`ideaenabledbufs` `ideaenabledbufs` List of strings (default "dialog") [To Be Released]
`ideavimsupport` `ideavimsupport` List of strings (default "dialog")
Define the list of additional buffers where IdeaVim is enabled.

View File

@@ -1,19 +1,54 @@
<idea-plugin url="https://plugins.jetbrains.com/plugin/164" xmlns:xi="http://www.w3.org/2001/XInclude">
<name>IdeaVim</name>
<id>IdeaVIM</id>
<change-notes><![CDATA[
<ul>
<li>Fix ESC in dialogs</li>
<li>Add option to disable IdeaVim in dialogs and single line editors</li>
<li>Ability to map IDE actions via the &lt;Action&gt; keyword.</li>
<li>"IdeaVim: track action Ids" command to find action ids for the :action command.
<li>Add new option to register IdeaVim extensions. Please read the changelog for details</li>
</ul>
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
]]></change-notes>
<change-notes>
&lt;h3&gt;Features:&lt;/h3&gt;
&lt;br/&gt;
&lt;ul&gt;&lt;li&gt;Ability to map IDE actions via the &lt;code&gt;&amp;lt;Action&amp;gt;&lt;/code&gt; keyword. E.g.
&lt;code&gt;map &amp;lt;C-K&amp;gt; &amp;lt;Action&amp;gt;(CommentByLineComment)&lt;/code&gt;.
Check out &lt;code&gt;README.md&lt;/code&gt; for the details.&lt;/li&gt;&lt;li&gt;&lt;code&gt;IdeaVim: track action
Ids&lt;/code&gt; command to find action ids for the &lt;code&gt;:action&lt;/code&gt; command.
Enable this option in &amp;quot;Search everywhere&amp;quot; (double shift).&lt;/li&gt;&lt;li&gt;Ability to enable
extensions using &lt;code&gt;vim-plug&lt;/code&gt; or &lt;code&gt;vundle&lt;/code&gt; syntax.&lt;br /&gt;
E.g. to enable commentary extension you can use one of the following commands:&lt;pre&gt;&lt;code
class="language-vim"&gt;set commentary
Plug 'tpope/vim-commentary'
Plug 'https://github.com/tpope/vim-commentary'
Plugin 'tpope/vim-commentary'
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This approach is especially handy if you have &lt;code&gt;.vimrc&lt;/code&gt; with
plugins registered via &lt;code&gt;vim-plug&lt;/code&gt; or &lt;code&gt;vundle&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;br/&gt;
&lt;h3&gt;Changes:&lt;/h3&gt;
&lt;br/&gt;
&lt;ul&gt;&lt;li&gt;Fix &lt;code&gt;&amp;lt;Esc&amp;gt;&lt;/code&gt; for dialogs. Now &lt;code&gt;&amp;lt;Esc&amp;gt;&lt;/code&gt;
will exit insert / visual mode and close the dialog from normal mode.&lt;/li&gt;&lt;li&gt;Add option to disable
IdeaVim in dialogs / single line editors. &lt;a href="https://youtrack.jetbrains.com/issue/VIM-765"&gt;VIM-765&lt;/a&gt;&lt;br
/&gt;
Use &lt;code&gt;set ideavimsupport=&lt;/code&gt; to disable IdeaVim in dialog editors. &lt;/li&gt;&lt;li&gt;Reposition
cursor when &lt;code&gt;scrolloff&lt;/code&gt; changes&lt;/li&gt;&lt;/ul&gt;
&lt;br/&gt;
&lt;h3&gt;Fixes:&lt;/h3&gt;
&lt;br/&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/VIM-2150"&gt;VIM-2150&lt;/a&gt; &lt;code&gt;Shift-D&lt;/code&gt;
should not delete an empty line&lt;/li&gt;&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/VIM-2157"&gt;VIM-2157&lt;/a&gt;
Fix tab with an active template&lt;/li&gt;&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/VIM-2156"&gt;VIM-2156&lt;/a&gt;
Correct up/down motions with inlays&lt;/li&gt;&lt;li&gt;&lt;a href="https://youtrack.jetbrains.com/issue/VIM-2144"&gt;VIM-2144&lt;/a&gt;
Correct text position after block insert with inlays&lt;/li&gt;&lt;li&gt;&lt;a
href="https://youtrack.jetbrains.com/issue/VIM-2158"&gt;VIM-2158&lt;/a&gt; Fix scrolling when &lt;code&gt;scrolloff&lt;/code&gt;
is over half screen height, but less than full height&lt;/li&gt;&lt;/ul&gt;
&lt;br/&gt;
&lt;p&gt;See also the complete &lt;a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md"&gt;changelog&lt;/a&gt;.&lt;/p&gt;
</change-notes>
<description><![CDATA[
<p>Vim emulation plug-in for IDEs based on the IntelliJ platform.</p>
<p>IdeaVim supports many Vim features including normal/insert/visual modes, motion keys, deletion/changing, marks, registers, some Ex commands, Vim regexps, configuration via ~/.ideavimrc, macros, window commands, etc.</p>
<p>Vim emulation plugin for IntelliJ Platform-based IDEs.</p>
<br/>
<p>IdeaVim supports many Vim features including normal/insert/visual modes, motion keys, deletion/changing,
marks, registers, some Ex commands, Vim regexps, configuration via ~/.ideavimrc, macros, Vim plugins, etc.</p>
<br/>
<p>See also:</p>
<ul>
<li><a href="https://github.com/JetBrains/ideavim">GitHub repository</a>: documentation and contributing</li>

View File

@@ -20,15 +20,29 @@ package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.mode
import com.maddyhome.idea.vim.helper.vimForEachCaret
class ResetModeAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
val modeBeforeReset = editor.mode
KeyHandler.getInstance().fullReset(editor.getTopLevelEditor())
if (modeBeforeReset == CommandState.Mode.INSERT) {
editor.vimForEachCaret { caret ->
val position = VimPlugin.getMotion().getOffsetOfHorizontalMotion(editor, caret, -1, false)
MotionGroup.moveCaret(editor, caret, position)
}
}
return true
}
}

View File

@@ -128,7 +128,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
private fun isEnabledForEscape(editor: Editor): Boolean {
return editor.isPrimaryEditor()
|| EditorHelper.isFileEditor(editor) && !editor.inNormalMode
|| OptionsManager.ideaenabledbufs.contains("dialog") && !editor.inNormalMode
|| OptionsManager.ideavimsupport.contains("dialog") && !editor.inNormalMode
}
private fun isShortcutConflict(keyStroke: KeyStroke): Boolean {

View File

@@ -19,10 +19,14 @@ package com.maddyhome.idea.vim.action.motion.visual
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.vimForEachCaret
/**
* @author vlan
@@ -32,6 +36,16 @@ class VisualExitModeAction : VimActionHandler.SingleExecution() {
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
editor.getTopLevelEditor().exitVisualMode()
// Should it be in [exitVisualMode]?
editor.vimForEachCaret { caret ->
val lineEnd = EditorHelper.getLineEndForOffset(editor, caret.offset)
if (lineEnd == caret.offset) {
val position = VimPlugin.getMotion().getOffsetOfHorizontalMotion(editor, caret, -1, false)
MotionGroup.moveCaret(editor, caret, position)
}
}
return true
}
}

View File

@@ -178,7 +178,7 @@ class CommandState private constructor() {
executingCommand = null
resetModes()
commandBuilder.resetInProgressCommandPart(getKeyRootNode(mappingState.mappingMode))
startDigraphSequence()
digraphSequence.reset()
updateStatus()
}

View File

@@ -31,10 +31,11 @@ public interface VimExtension {
@NotNull ExtensionPointName<ExtensionBeanClass> EP_NAME = ExtensionPointName.create("IdeaVIM.vimExtension");
/**
* @deprecated Please set extension name in <vimExtension> tag
* @deprecated This property is not used anymore, but we'll remove it much later to keep the compatibility of IdeaVim
* extensions with previous versions of IdeaVim.
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "0.63")
@ApiStatus.ScheduledForRemoval(inVersion = "0.65")
@NotNull String getName();
default MappingOwner getOwner() {

View File

@@ -42,10 +42,12 @@ import com.maddyhome.idea.vim.handler.MotionActionHandler;
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.option.NumberOption;
import com.maddyhome.idea.vim.option.OptionChangeListener;
import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.ui.ExEntryPanel;
import kotlin.Pair;
import kotlin.ranges.IntProgression;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -184,7 +186,7 @@ public class MotionGroup {
if (caretVisualLine < topVisualLine + scrollOffset) {
newVisualLine = normalizeVisualLine(editor, topVisualLine + scrollOffset);
}
else if (caretVisualLine >= bottomVisualLine - scrollOffset) {
else if (caretVisualLine > bottomVisualLine - scrollOffset) {
newVisualLine = normalizeVisualLine(editor, bottomVisualLine - scrollOffset);
}
else {
@@ -631,10 +633,10 @@ public class MotionGroup {
private static void scrollCaretIntoViewVertically(@NotNull Editor editor, final int caretLine) {
// TODO: Make this work with soft wraps
// Vim's algorithm works counts line heights for wrapped lines. We're using visual lines, which handles collapsed
// folds, but treats soft wrapped lines as individual lines.
// Ironically, after figuring out how Vim's algorithm works (although not *why*), it looks likely to be rewritten as
// a dumb line for line reimplementation.
// Vim's algorithm works by counting line heights for wrapped lines. We're using visual lines, which handles
// collapsed folds, but treats soft wrapped lines as individual lines.
// Ironically, after figuring out how Vim's algorithm works (although not *why*) and reimplementing, it looks likely
// that this needs to be replaced as a more or less dumb line for line rewrite.
final int topLine = getVisualLineAtTopOfScreen(editor);
final int bottomLine = getVisualLineAtBottomOfScreen(editor);
@@ -642,7 +644,7 @@ public class MotionGroup {
// We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
final int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value();
final int topBound = topLine + scrollOffset;
final int bottomBound = Math.max(topBound + 1, bottomLine - scrollOffset);
final int bottomBound = Math.max(topBound, bottomLine - scrollOffset);
// If we need to scroll the current line more than half a screen worth of lines then we just centre the new
// current line. This mimics vim behavior of e.g. 100G in a 300 line file with a screen size of 25 centering line
@@ -650,7 +652,6 @@ public class MotionGroup {
// Note that block inlays means that the pixel height we are scrolling can be larger than half the screen, even if
// the number of lines is less. I'm not sure what impact this has.
final int height = bottomLine - topLine + 1;
final int halfHeight = Math.max(2, (height / 2) - 1);
// Scrolljump isn't handled as you might expect. It is the minimal number of lines to scroll, but that doesn't mean
// newLine = caretLine +/- MAX(sj, so)
@@ -663,7 +664,7 @@ public class MotionGroup {
// (See move.c:scroll_cursor_top)
//
// When scrolling down (`j` - scrolling window down in the buffer; more lines are visible at the bottom), Vim again
// expands lines above and below the new bottom line, but calcualtes things a little differently. The total number
// expands lines above and below the new bottom line, but calculates things a little differently. The total number
// of lines expanded is at least scrolljump and there must be at least scrolloff lines below.
// Since the lines are advancing simultaneously, it is only possible to get scrolljump/2 above the new cursor line.
// If there are fewer than scrolljump/2 lines between the current bottom line and the new cursor line, the extra
@@ -678,10 +679,26 @@ public class MotionGroup {
// out correct scroll locations
final int scrollJump = getScrollJump(editor, height);
if (caretLine < topBound) {
// Unavoidable fudge value. Multiline rendered doc comments can mean we have very few actual lines, and scrolling
// can get stuck in a loop as we re-centre the cursor instead of actually moving it. But if we ignore all inlays
// and use the approximate screen height instead of the actual screen height (in lines), we make incorrect
// assumptions about the top/bottom line numbers and can scroll to the wrong location. E.g. if there are enough doc
// comments (String.java) it's possible to get 12 lines of actual code on screen. Given scrolloff=5, it's very easy
// to hit problems, and have (scrolloffset > height / 2) and scroll to the middle of the screen. We'll use this
// fudge value to make sure we're working with sensible values. Note that this problem doesn't affect code without
// block inlays as positioning the cursor in the middle of the screen always positions it in a deterministic manner,
// relative to other text in the file.
final int inlayAwareMinHeightFudge = getApproximateScreenHeight(editor) / 2;
// Note that while these calculations do the same thing that Vim does, it processes them differently. E.g. it
// optionally checks and moves the top line, then optionally checks the bottom line. This gives us the same results
// via the tests.
if (height > inlayAwareMinHeightFudge && scrollOffset > height / 2) {
scrollVisualLineToMiddleOfScreen(editor, caretLine);
} else if (caretLine < topBound) {
// Scrolling up, put the cursor at the top of the window (minus scrolloff)
// Initial approximation in move.c:update_topline
if (topLine + scrollOffset - caretLine >= halfHeight) {
// Initial approximation in move.c:update_topline (including same calculation for halfHeight)
if (topLine + scrollOffset - caretLine >= Math.max(2, (height / 2) - 1)) {
scrollVisualLineToMiddleOfScreen(editor, caretLine);
}
else {
@@ -692,9 +709,12 @@ public class MotionGroup {
final int scrollOffsetTopLine = Math.max(0, caretLine - scrollOffset);
final int newTopLine = Math.min(scrollOffsetTopLine, scrollJumpTopLine);
// Used is set to the line height of caretLine, and then incremented by line height of the lines above and
// below caretLine (up to scrolloff or end of file)
final int used = 1 + (newTopLine - topLine) + Math.min(scrollOffset, getVisualLineCount(editor) - topLine);
// Used is set to the line height of caretLine (1 or how many lines soft wraps take up), and then incremented by
// the line heights of the lines above and below caretLine (up to scrolloff or end of file).
// Our implementation ignores soft wrap line heights. Folds already have a line height of 1.
final int usedAbove = caretLine - newTopLine;
final int usedBelow = Math.min(scrollOffset, getVisualLineCount(editor) - caretLine);
final int used = 1 + usedAbove + usedBelow;
if (used > height) {
scrollVisualLineToMiddleOfScreen(editor, caretLine);
}
@@ -1374,4 +1394,21 @@ public class MotionGroup {
return moveCaretToLineEnd(editor, visualLineToLogicalLine(editor, line), allowPastEnd);
}
}
public static class ScrollOptionsChangeListener implements OptionChangeListener<String> {
public static ScrollOptionsChangeListener INSTANCE = new ScrollOptionsChangeListener();
@Contract(pure = true)
private ScrollOptionsChangeListener() {
}
@Override
public void valueChange(String oldValue, String newValue) {
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
if (UserDataManager.getVimEditorGroup(editor)) {
MotionGroup.scrollCaretIntoView(editor);
}
}
}
}
}

View File

@@ -43,6 +43,12 @@ import static java.lang.Integer.max;
* This is a set of helper methods for working with editors. All line and column values are zero based.
*/
public class EditorHelper {
// Set a max height on block inlays to be made visible at the top/bottom of a line when scrolling up/down. This
// mitigates the visible area bouncing around too much and even pushing the cursor line off screen with large
// multiline rendered doc comments, while still providing some visibility of the block inlay (e.g. Rider's single line
// Code Vision)
private static final int BLOCK_INLAY_MAX_LINE_HEIGHT = 3;
public static @NotNull Rectangle getVisibleArea(final @NotNull Editor editor) {
return editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
}
@@ -201,7 +207,7 @@ public class EditorHelper {
* @param editor The editor
* @return The number of screen lines
*/
private static int getApproximateScreenHeight(final @NotNull Editor editor) {
public static int getApproximateScreenHeight(final @NotNull Editor editor) {
return getVisibleArea(editor).height / editor.getLineHeight();
}
@@ -641,7 +647,10 @@ public class EditorHelper {
* @return Returns true if the window was moved
*/
public static boolean scrollVisualLineToTopOfScreen(final @NotNull Editor editor, int visualLine) {
int y = EditorUtil.getVisualLineAreaStartY(editor, normalizeVisualLine(editor, visualLine));
final int inlayHeight = EditorUtil.getInlaysHeight(editor, visualLine, true);
final int maxInlayHeight = BLOCK_INLAY_MAX_LINE_HEIGHT * editor.getLineHeight();
int y = editor.visualLineToY(visualLine) - Math.min(inlayHeight, maxInlayHeight);
// Normalise Y so that we don't try to scroll the editor to a location it can't reach. The editor will handle this,
// but when we ask for the target location to move the caret to match, we'll get the incorrect value.
@@ -653,7 +662,7 @@ public class EditorHelper {
// Get the max line number that can sit at the top of the screen
final int editorHeight = getVisibleArea(editor).height;
final int virtualSpaceHeight = editor.getSettings().getAdditionalLinesCount() * editor.getLineHeight();
final int yLastLine = editor.visualLineToY(EditorHelper.getLineCount(editor)); // last line + 1
final int yLastLine = editor.visualLineToY(getLineCount(editor)); // last line + 1
y = Math.min(y, yLastLine + virtualSpaceHeight - editorHeight);
}
return scrollVertically(editor, y);
@@ -662,21 +671,25 @@ public class EditorHelper {
/**
* Scrolls the editor to place the given visual line in the middle of the current window.
*
* <p>Snaps the line to the nearest standard line height grid, which gives a good position for both an odd and even
* number of lines and mimics what Vim does.</p>
*
* @param editor The editor to scroll
* @param visualLine The visual line to place in the middle of the current window
*/
public static void scrollVisualLineToMiddleOfScreen(@NotNull Editor editor, int visualLine) {
int y = editor.visualLineToY(normalizeVisualLine(editor, visualLine));
int lineHeight = editor.getLineHeight();
int height = getVisibleArea(editor).height;
scrollVertically(editor, y - ((height - lineHeight) / 2));
final int y = editor.visualLineToY(normalizeVisualLine(editor, visualLine));
final int screenHeight = getVisibleArea(editor).height;
final int lineHeight = editor.getLineHeight();
scrollVertically(editor, y - ((screenHeight - lineHeight) / lineHeight / 2 * lineHeight));
}
/**
* Scrolls the editor to place the given visual line at the bottom of the screen.
*
* When we're moving the caret down a few lines and want to scroll to keep this visible, we need to be able to place a
* line at the bottom of the screen. Due to block inlays, we can't do this by specifying a top line to scroll to.
* <p>When we're moving the caret down a few lines and want to scroll to keep this visible, we need to be able to
* place a line at the bottom of the screen. Due to block inlays, we can't do this by specifying a top line to scroll
* to.</p>
*
* @param editor The editor to scroll
* @param visualLine The visual line to place at the bottom of the current window
@@ -690,7 +703,12 @@ public class EditorHelper {
if (ExEntryPanel.getInstanceWithoutShortcuts().isActive()) {
exPanelHeight += ExEntryPanel.getInstanceWithoutShortcuts().getHeight();
}
final int y = EditorUtil.getVisualLineAreaEndY(editor, normalizeVisualLine(editor, visualLine)) + exPanelHeight;
final int normalizedVisualLine = normalizeVisualLine(editor, visualLine);
final int lineHeight = editor.getLineHeight();
final int inlayHeight = EditorUtil.getInlaysHeight(editor, normalizedVisualLine, false);
final int maxInlayHeight = BLOCK_INLAY_MAX_LINE_HEIGHT * lineHeight;
final int y = editor.visualLineToY(normalizedVisualLine) + lineHeight + Math.min(inlayHeight, maxInlayHeight) + exPanelHeight;
final Rectangle visibleArea = getVisibleArea(editor);
return scrollVertically(editor, max(0, y - visibleArea.height));
}
@@ -714,19 +732,19 @@ public class EditorHelper {
}
final int columnLeftX = editor.visualPositionToXY(new VisualPosition(visualLine, targetVisualColumn)).x;
EditorHelper.scrollHorizontally(editor, columnLeftX);
scrollHorizontally(editor, columnLeftX);
}
public static void scrollColumnToMiddleOfScreen(@NotNull Editor editor, int visualLine, int visualColumn) {
final Point point = editor.visualPositionToXY(new VisualPosition(visualLine, visualColumn));
final int screenWidth = EditorHelper.getVisibleArea(editor).width;
final int screenWidth = getVisibleArea(editor).width;
// Snap the column to the nearest standard column grid. This positions us nicely if there are an odd or even number
// of columns. It also works with inline inlays and folds. It is slightly inaccurate for proportional fonts, but is
// still a good solution. Besides, what kind of monster uses Vim with proportional fonts?
final int standardColumnWidth = EditorUtil.getPlainSpaceWidth(editor);
final int x = point.x - (screenWidth / standardColumnWidth / 2 * standardColumnWidth);
EditorHelper.scrollHorizontally(editor, x);
scrollHorizontally(editor, x);
}
public static void scrollColumnToRightOfScreen(@NotNull Editor editor, int visualLine, int visualColumn) {
@@ -751,8 +769,8 @@ public class EditorHelper {
// Scroll to the left edge of the target column, minus a screenwidth, and adjusted for inlays
final int targetColumnRightX = editor.visualPositionToXY(new VisualPosition(visualLine, targetVisualColumn + 1)).x;
final int screenWidth = EditorHelper.getVisibleArea(editor).width;
EditorHelper.scrollHorizontally(editor, targetColumnRightX - screenWidth);
final int screenWidth = getVisibleArea(editor).width;
scrollHorizontally(editor, targetColumnRightX - screenWidth);
}
/**

View File

@@ -46,11 +46,11 @@ val Editor.isIdeaVimDisabledHere: Boolean
val timeForCalculation = measureTimeMillis {
res = (disabledInDialog.apply { times += System.currentTimeMillis() to "Disabled in dialog" }
|| (!OptionsManager.ideaenabledbufs.contains("singleline")
|| (!OptionsManager.ideavimsupport.contains("singleline")
.apply { times += System.currentTimeMillis() to "first single line check" }
&& isDatabaseCell(times).apply { times += System.currentTimeMillis() to "is db cell" })
|| (!OptionsManager.ideaenabledbufs.contains("singleline")
|| (!OptionsManager.ideavimsupport.contains("singleline")
.apply { times += System.currentTimeMillis() to "second single line check" }
&& isOneLineMode.apply { times += System.currentTimeMillis() to "is one line" })
)
@@ -68,7 +68,7 @@ private fun Editor.isDatabaseCell(times: MutableList<Pair<Long, String>>): Boole
}
private val Editor.disabledInDialog: Boolean
get() = (!OptionsManager.ideaenabledbufs.contains("dialog") && !OptionsManager.ideaenabledbufs.contains("dialoglegacy"))
get() = (!OptionsManager.ideavimsupport.contains("dialog") && !OptionsManager.ideavimsupport.contains("dialoglegacy"))
&& (!this.isPrimaryEditor() && !EditorHelper.isFileEditor(this))
/**

View File

@@ -786,7 +786,7 @@ public class SearchHelper {
if (pos == size - 1 ||
!Character.isLetter(chars.charAt(pos + 1)) ||
(Character.isUpperCase(chars.charAt(pos + 1)) &&
pos <= size - 2 &&
pos < size - 2 &&
Character.isLowerCase(chars.charAt(pos + 2)))) {
res = pos;
found++;
@@ -1375,7 +1375,7 @@ public class SearchHelper {
}
if (goForward && anyNonWhitespace(editor, end, 1)) {
while (end < max &&
while (end + 1 < max &&
CharacterHelper.charType(chars.charAt(end + 1), false) == CharacterHelper.CharacterType.WHITESPACE) {
end++;
}

View File

@@ -106,6 +106,7 @@ object VimListenerManager {
OptionsManager.number.addOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
OptionsManager.relativenumber.addOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
OptionsManager.scrolloff.addOptionChangeListener(MotionGroup.ScrollOptionsChangeListener.INSTANCE)
OptionsManager.showcmd.addOptionChangeListener(ShowCmdOptionChangeListener)
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance())
@@ -116,6 +117,7 @@ object VimListenerManager {
OptionsManager.number.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
OptionsManager.relativenumber.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
OptionsManager.scrolloff.removeOptionChangeListener(MotionGroup.ScrollOptionsChangeListener.INSTANCE)
OptionsManager.showcmd.removeOptionChangeListener(ShowCmdOptionChangeListener)
EventFacade.getInstance().removeEditorFactoryListener(VimEditorFactoryListener)

View File

@@ -88,7 +88,7 @@ object OptionsManager {
val ideastatusicon = addOption(BoundStringOption(IdeaStatusIcon.name, IdeaStatusIcon.name, IdeaStatusIcon.enabled, IdeaStatusIcon.allValues))
val ideastrictmode = addOption(ToggleOption("ideastrictmode", "ideastrictmode", false))
val ideawrite = addOption(BoundStringOption("ideawrite", "ideawrite", IdeaWriteData.all, IdeaWriteData.allValues))
val ideaenabledbufs = addOption(BoundListOption("ideaenabledbufs", "ideaenabledbufs", arrayOf("dialog"), arrayOf("dialog", "singleline", "dialoglegacy")))
val ideavimsupport = addOption(BoundListOption("ideavimsupport", "ideavimsupport", arrayOf("dialog"), arrayOf("dialog", "singleline", "dialoglegacy")))
fun isSet(name: String): Boolean {
val option = getOption(name)

View File

@@ -62,7 +62,7 @@
* |i_CTRL-Z| TO BE IMPLEMENTED
* |i_<Esc>| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-[| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-\_CTRL-N| {@link com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction}
* |i_CTRL-\_CTRL-N| {@link com.maddyhome.idea.vim.action.ResetModeAction}
* |i_CTRL-\_CTRL-G| TO BE IMPLEMENTED
* |i_CTRL-]} TO BE IMPLEMENTED
* |i_CTRL-^| TO BE IMPLEMENTED

View File

@@ -1,6 +1,6 @@
# Manual Tests
## #1 [Last run: 2020-10-09]
## #1 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -14,7 +14,7 @@ Word is selected, block-caret is placed on the word end (offset = `word end - 1`
![](resources/manualTests/1.png)
## #2 [Last run: 2020-10-09]
## #2 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -26,7 +26,7 @@ Last word is selected, block caret is placed on the word end without bouncing
![](resources/manualTests/2.png)
## #3 [Last run: 2020-10-09]
## #3 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -38,7 +38,7 @@ Line is selected. Caret is placed on the line end
![](resources/manualTests/3.png)
## #4 [Last run: 2020-10-09]
## #4 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -51,7 +51,7 @@ After mouse release, caret moves one character back and becomes block shape
![](resources/manualTests/4.gif)
## #5 [Last run: 2020-10-09]
## #5 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -65,7 +65,7 @@ After mouse release, caret moves one character back and becomes block shape
![](resources/manualTests/5.gif)
## #6 [Last run: 2020-10-09]
## #6 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -77,7 +77,7 @@ Line is selected, caret is on the first position
![](resources/manualTests/6.gif)
## #6 [Last run: 2020-10-09]
## #6 [Last run: 2020-11-12]
_Initial mode:_ NORMAL
@@ -94,7 +94,7 @@ Caret stays in _block_ shape with a normal mode
![](resources/manualTests/7.2.gif)
## #7 [Last run: 2020-10-09]
## #7 [Last run: 2020-11-12]
_Action:_
Turn emulation off and on
@@ -102,7 +102,7 @@ Turn emulation off and on
_Result:_
Vim emulator works as expected
## #8 [Last run: 2020-10-09
## #8 [Last run: 2020-11-12
_Action:_
Start up IJ with disabled emulator, turn it on
@@ -110,7 +110,7 @@ Start up IJ with disabled emulator, turn it on
_Result:_
Vim emulator works as expected
## #9 [Last run: 2020-10-09]
## #9 [Last run: 2020-11-12]
_Action:_
Wrap with if

View File

@@ -180,26 +180,30 @@ abstract class VimTestCase : UsefulTestCase() {
@JvmOverloads
protected fun setPositionAndScroll(scrollToLogicalLine: Int, caretLogicalLine: Int, caretLogicalColumn: Int = 0) {
val scrolloff = min(OptionsManager.scrolloff.value(), screenHeight / 2)
// Note that it is possible to request a position which would be invalid under normal Vim!
// We disable scrolloff + scrolljump, position as requested, and reset. When resetting scrolloff, Vim will
// recalculate the correct offsets, and that could move the top and/or caret line
val scrolloff = OptionsManager.scrolloff.value()
val scrolljump = OptionsManager.scrolljump.value()
OptionsManager.scrolloff.set(0)
OptionsManager.scrolljump.set(1)
// Convert to visual lines to handle any collapsed folds
val scrollToVisualLine = EditorHelper.logicalLineToVisualLine(myFixture.editor, scrollToLogicalLine)
val bottomVisualLine = scrollToVisualLine + screenHeight - 1
val bottomVisualLine = scrollToVisualLine + EditorHelper.getApproximateScreenHeight(myFixture.editor) - 1
val bottomLogicalLine = EditorHelper.visualLineToLogicalLine(myFixture.editor, bottomVisualLine)
// Make sure we're not trying to put caret in an invalid location
val boundsTop = EditorHelper.visualLineToLogicalLine(myFixture.editor,
if (scrollToVisualLine > scrolloff) scrollToVisualLine + scrolloff else scrollToVisualLine)
val boundsBottom = EditorHelper.visualLineToLogicalLine(myFixture.editor,
if (bottomVisualLine > EditorHelper.getVisualLineCount(myFixture.editor) - scrolloff - 1) bottomVisualLine - scrolloff else bottomVisualLine)
val boundsTop = EditorHelper.visualLineToLogicalLine(myFixture.editor, scrollToVisualLine)
val boundsBottom = EditorHelper.visualLineToLogicalLine(myFixture.editor, bottomVisualLine)
Assert.assertTrue("Caret line $caretLogicalLine not inside legal screen bounds (${boundsTop} - ${boundsBottom})",
caretLogicalLine in boundsTop..boundsBottom)
typeText(parseKeys("${scrollToLogicalLine+scrolloff+1}z<CR>", "${caretLogicalLine+1}G", "${caretLogicalColumn+1}|"))
typeText(parseKeys("${scrollToLogicalLine+1}z<CR>", "${caretLogicalLine+1}G", "${caretLogicalColumn+1}|"))
OptionsManager.scrolljump.set(scrolljump)
OptionsManager.scrolloff.set(scrolloff)
// Make sure we're where we want to be
assertVisibleArea(scrollToLogicalLine, bottomLogicalLine)

View File

@@ -25,6 +25,7 @@ import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
public class ReformatCodeTest extends VimTestCase {
/*
[VERSION UPDATE] 2020.2+
public void testEmpty() {
configureByJavaText("<caret>");
typeText(parseKeys("gqq"));

View File

@@ -47,6 +47,22 @@ class ResetModeActionTest : VimTestCase() {
TestCase.assertFalse(myFixture.editor.selectionModel.hasSelection())
}
fun `test reset from insert mode check position`() {
val keys = listOf("i", "<C-\\><C-N>")
val before = "A Disc${c}overy"
val after = "A Dis${c}covery"
doTest(keys, before, after, CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
TestCase.assertFalse(myFixture.editor.selectionModel.hasSelection())
}
fun `test reset and execute command`() {
val keys = listOf("i", "<C-\\><C-N>", "3l")
val before = "${c}A Discovery"
val after = "A D${c}iscovery"
doTest(keys, before, after, CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
TestCase.assertFalse(myFixture.editor.selectionModel.hasSelection())
}
fun `test reset from visual mode`() {
val keys = listOf("V", "<C-\\><C-N>")
val before = "A Discovery"

View File

@@ -26,7 +26,8 @@ import org.jetbrains.plugins.ideavim.VimTestCase
class MotionOuterBigWordActionTest : VimTestCase() {
@TestWithoutNeovim(SkipNeovimReason.UNCLEAR, description = "Wrong caret position, but in real neovim works fine")
fun `test on last dot`() {
doTest("<aW", """
doTest(
"<aW", """
I found it in a legendary land
all rocks and lavender and tufted grass,
where it was settled on some sodden sand
@@ -36,6 +37,19 @@ class MotionOuterBigWordActionTest : VimTestCase() {
all rocks and lavender and tufted grass,
where it was settled on some sodden sand
${c}hard by the torrent of a mountain pass.
""".trimIndent(), CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
""".trimIndent(), CommandState.Mode.COMMAND, CommandState.SubMode.NONE
)
}
fun `test past end in visual`() {
doTest(
"v\$aW", """
I found it in a ${c}legendary land
}
""".trimIndent(), """
I found it in a ${s}legendary land
${c}}${se}
""".trimIndent(), CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_CHARACTER
)
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.motion.text
import com.maddyhome.idea.vim.command.CommandState
import org.jetbrains.plugins.ideavim.VimTestCase
class MotionCamelEndLeftActionTest : VimTestCase() {
fun `test go with a single uppercase word`() {
doTest("]b", "TES${c}T", "TES${c}T", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
}
}

View File

@@ -0,0 +1,28 @@
/*
* 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.motion.visual
import com.maddyhome.idea.vim.command.CommandState
import org.jetbrains.plugins.ideavim.VimTestCase
class VisualExitModeAction : VimTestCase() {
fun `test exit visual mode after line end`() {
doTest("vl<Esc>", "12${c}3", "12${c}3", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
}
}

View File

@@ -90,6 +90,7 @@ class VisualToggleBlockModeActionTest : VimTestCase() {
}
/*
[VERSION UPDATE] 2020.2+
fun `test on empty file`() {
doTest("<C-V>", "", "",
CommandState.Mode.VISUAL, CommandState.SubMode.VISUAL_BLOCK)

View File

@@ -21,6 +21,8 @@
package org.jetbrains.plugins.ideavim.group.motion
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import com.maddyhome.idea.vim.option.OptionsManager
import com.maddyhome.idea.vim.option.ScrollJumpData
import com.maddyhome.idea.vim.option.ScrollOffData
import org.jetbrains.plugins.ideavim.SkipNeovimReason
@@ -63,6 +65,119 @@ class MotionGroup_scrolloff_Test : VimOptionTestCase(ScrollOffData.name) {
assertVisibleArea(24, 58)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["15"]))
fun `test move up when scrolloff is slightly less than half screen height`() {
// Screen height = 35. scrolloff=15. This gives 5 possible caret lines without scrolling (48, 49, 50, 51 + 52)
configureByPages(5)
setPositionAndScroll(33, 52)
typeText(parseKeys("k"))
assertPosition(51, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("k"))
assertPosition(50, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("k"))
assertPosition(48, 0)
assertVisibleArea(33, 67)
// Scroll
typeText(parseKeys("k"))
assertPosition(47, 0)
assertVisibleArea(32, 66)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["16"]))
fun `test move up when scrolloff is slightly less than half screen height 2`() {
// Screen height = 35. scrolloff=16. This gives 3 possible caret lines without scrolling (49, 50 + 51)
configureByPages(5)
setPositionAndScroll(33, 51)
typeText(parseKeys("k"))
assertPosition(50, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(33, 67)
// Scroll
typeText(parseKeys("k"))
assertPosition(48, 0)
assertVisibleArea(32, 66)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["16"]))
fun `test move up when scrolloff is slightly less than half screen height 3`() {
// Screen height = 34. scrolloff=16
// Even numbers. 2 possible caret lines without scrolling (49 + 50)
configureByPages(5)
setEditorVisibleSize(screenWidth, 34)
setPositionAndScroll(33, 50)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(33, 66)
// Scroll
typeText(parseKeys("k"))
assertPosition(48, 0)
assertVisibleArea(32, 65)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["17"]))
@VimBehaviorDiffers(description = "Moving up in Vim will always have 16 lines above the caret line. IdeaVim keeps 17")
fun `test move up when scrolloff is exactly screen height`() {
// Page height = 34. scrolloff=17
// 2 possible caret lines without scrolling (49 + 50)
configureByPages(5)
setEditorVisibleSize(screenWidth, 34)
setPositionAndScroll(33, 50)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(33, 66)
// Scroll
typeText(parseKeys("k"))
assertPosition(48, 0)
assertVisibleArea(32, 65)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["17"]))
fun `test move up when scrolloff is slightly greater than screen height keeps cursor in centre of screen`() {
// Page height = 35. scrolloff=17
configureByPages(5)
setPositionAndScroll(33, 50)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(32, 66)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["22"]))
fun `test move up when scrolloff is slightly greater than screen height keeps cursor in centre of screen 2`() {
// Page height = 35. scrolloff=17
configureByPages(5)
setPositionAndScroll(33, 50)
typeText(parseKeys("k"))
assertPosition(49, 0)
assertVisibleArea(32, 66)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["0"]))
fun `test move down shows no context with scrolloff=0`() {
@@ -93,14 +208,159 @@ class MotionGroup_scrolloff_Test : VimOptionTestCase(ScrollOffData.name) {
assertVisibleArea(26, 60)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["15"]))
fun `test move down when scrolloff is slightly less than half screen height`() {
// Screen height = 35. scrolloff=15. This gives 5 possible caret lines without scrolling (48, 49, 50, 51 + 52)
configureByPages(5)
setPositionAndScroll(33, 48)
typeText(parseKeys("j"))
assertPosition(49, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("j"))
assertPosition(50, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("j"))
assertPosition(52, 0)
assertVisibleArea(33, 67)
// Scroll
typeText(parseKeys("j"))
assertPosition(53, 0)
assertVisibleArea(34, 68)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["16"]))
fun `test move down when scrolloff is slightly less than half screen height 2`() {
// Screen height = 35. scrolloff=16. This gives 3 possible caret lines without scrolling (49, 50 + 51)
configureByPages(5)
setPositionAndScroll(33, 49)
typeText(parseKeys("j"))
assertPosition(50, 0)
assertVisibleArea(33, 67)
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(33, 67)
// Scroll
typeText(parseKeys("j"))
assertPosition(52, 0)
assertVisibleArea(34, 68)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["16"]))
fun `test move down when scrolloff is slightly less than half screen height 3`() {
// Screen height = 34. scrolloff=16
// Even numbers. 2 possible caret lines without scrolling (49 + 50)
configureByPages(5)
setEditorVisibleSize(screenWidth, 34)
setPositionAndScroll(33, 49)
typeText(parseKeys("j"))
assertPosition(50, 0)
assertVisibleArea(33, 66)
// Scroll
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(34, 67)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["17"]))
fun `test move down when scrolloff is exactly screen height`() {
// Page height = 34. scrolloff=17
// 2 possible caret lines without scrolling (49 + 50), but moving to line 51 will scroll 2 lines!
configureByPages(5)
setEditorVisibleSize(screenWidth, 34)
setPositionAndScroll(33, 49)
typeText(parseKeys("j"))
assertPosition(50, 0)
assertVisibleArea(33, 66)
// Scroll. By 2 lines!
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(35, 68)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["17"]))
fun `test move down when scrolloff is slightly greater than half screen height keeps cursor in centre of screen`() {
// Page height = 35. scrolloff=17
configureByPages(5)
setPositionAndScroll(33, 50)
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(34, 68)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["22"]))
fun `test move down when scrolloff is slightly greater than half screen height keeps cursor in centre of screen 2`() {
// Page height = 35. scrolloff=17
configureByPages(5)
setPositionAndScroll(33, 50)
typeText(parseKeys("j"))
assertPosition(51, 0)
assertVisibleArea(34, 68)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["999"]))
fun `test scrolloff=999 keeps cursor in centre of screen`() {
configureByPages(5)
setPositionAndScroll(25, 42)
typeText(parseKeys("j"))
assertPosition(43, 0)
assertVisibleArea(26, 60)
typeText(parseKeys("k"))
assertPosition(42, 0)
assertVisibleArea(25, 59)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["999"]))
fun `test scrolloff=999 keeps cursor in centre of screen with even screen height`() {
configureByPages(5)
setEditorVisibleSize(screenWidth, 34)
setPositionAndScroll(26, 42)
typeText(parseKeys("j"))
assertPosition(43, 0)
assertVisibleArea(27, 60)
typeText(parseKeys("k"))
assertPosition(42, 0)
assertVisibleArea(26, 59)
}
@TestWithoutNeovim(SkipNeovimReason.OPTION)
@VimOptionTestConfiguration(VimTestOption(ScrollOffData.name, VimTestOptionType.NUMBER, ["0"]))
fun `test reposition cursor when scrolloff is set`() {
configureByPages(5)
setPositionAndScroll(50, 50)
OptionsManager.scrolloff.set(999)
assertPosition(50, 0)
assertVisibleArea(33, 67)
}
}

View File

@@ -116,5 +116,8 @@ private val stinkyKeysList = arrayListOf("K", "u", "H", "<C-Y>",
// Temporally disabled due to issues in the platform
"<C-V>", "<C-Q>",
"<C-]>"
"<C-]>",
// Next / previous method fails because of vfs sync
"]"
)