mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-18 19:24:55 +02:00
Compare commits
157 Commits
0.55.2-EAP
...
0.57.1-EAP
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0adde753f4 | ||
![]() |
5f28a22666 | ||
![]() |
a975b53894 | ||
![]() |
98aee5d0ab | ||
![]() |
f57af8bf9e | ||
![]() |
c6c3b6643e | ||
![]() |
af94079b92 | ||
![]() |
7203cc5cb3 | ||
![]() |
028423cf58 | ||
![]() |
2ead6af96a | ||
![]() |
bf853e3c0c | ||
![]() |
c85f41e65b | ||
![]() |
2759bed1b2 | ||
![]() |
89c2a8ec9b | ||
![]() |
aa2c1257ac | ||
![]() |
f9fa15b7ac | ||
![]() |
93a9be41bc | ||
![]() |
ecd2f2032c | ||
![]() |
de5ce5f635 | ||
![]() |
2eb6fd6819 | ||
![]() |
22ea4e7ffa | ||
![]() |
3d98f3035f | ||
![]() |
ec1d6ac477 | ||
![]() |
0dc236cb5b | ||
![]() |
98349a49fd | ||
![]() |
ab8be2cada | ||
![]() |
b8c22d0928 | ||
![]() |
c6cf77e4b8 | ||
![]() |
6c0511a898 | ||
![]() |
366c862bcf | ||
![]() |
03493e2390 | ||
![]() |
8f9c71dd55 | ||
![]() |
11beb1e331 | ||
![]() |
01b4dc233a | ||
![]() |
9f1e80e969 | ||
![]() |
7e319e11c6 | ||
![]() |
d11bf1c4d2 | ||
![]() |
3e2f18b757 | ||
![]() |
61677aa811 | ||
![]() |
fb04e835ef | ||
![]() |
bb133922d6 | ||
![]() |
44dd5ef872 | ||
![]() |
bcc8e1c055 | ||
![]() |
71117ed335 | ||
![]() |
de07fb3b74 | ||
![]() |
e31d5a4dcf | ||
![]() |
e14aae761d | ||
![]() |
47db2a247c | ||
![]() |
e449bb9692 | ||
![]() |
b8fc72b6a7 | ||
![]() |
64c01c1bd1 | ||
![]() |
0a0e3df42b | ||
![]() |
949c69a7e9 | ||
![]() |
69caf7a604 | ||
![]() |
23860ad5f9 | ||
![]() |
ace5234d8d | ||
![]() |
4654f821a9 | ||
![]() |
927e0e7865 | ||
![]() |
d47c9735b5 | ||
![]() |
6100433636 | ||
![]() |
43f79e8183 | ||
![]() |
f58fda0c87 | ||
![]() |
64b49e37d7 | ||
![]() |
e44418d410 | ||
![]() |
ca8d05ff13 | ||
![]() |
626871e34d | ||
![]() |
4b659fe643 | ||
![]() |
d5055506b0 | ||
![]() |
55f54b2e82 | ||
![]() |
1b18065e68 | ||
![]() |
053dc02152 | ||
![]() |
b8cb4a1295 | ||
![]() |
cd2cbf68a1 | ||
![]() |
73f3be8af0 | ||
![]() |
8cce059fb4 | ||
![]() |
db641ec6f6 | ||
![]() |
9d8239b68d | ||
![]() |
4ec0bac275 | ||
![]() |
613c234cfb | ||
![]() |
83dca71f69 | ||
![]() |
f7ea9cdb6e | ||
![]() |
762cb1804f | ||
![]() |
962cfb7ae2 | ||
![]() |
9bc2ec7d8a | ||
![]() |
8415d104e9 | ||
![]() |
abd0f9b961 | ||
![]() |
0a4683d908 | ||
![]() |
4c280b0193 | ||
![]() |
e88a3deafd | ||
![]() |
7de08e08d0 | ||
![]() |
bd172b3300 | ||
![]() |
95c7a13cb5 | ||
![]() |
b1ddf03385 | ||
![]() |
a83c326736 | ||
![]() |
b1acb56247 | ||
![]() |
caa4731a13 | ||
![]() |
5b0ece7a91 | ||
![]() |
a4cd94847e | ||
![]() |
a0a7386b51 | ||
![]() |
535a72000f | ||
![]() |
60531b9cd2 | ||
![]() |
8f86ad696d | ||
![]() |
c9bda98a6a | ||
![]() |
9ea08da133 | ||
![]() |
5762ec0518 | ||
![]() |
7db74460fa | ||
![]() |
c8d64e0a06 | ||
![]() |
1a3dea0de6 | ||
![]() |
17b642280e | ||
![]() |
1c1717b78b | ||
![]() |
4bbbdf8108 | ||
![]() |
04a193309d | ||
![]() |
f106ffa176 | ||
![]() |
8d5d099542 | ||
![]() |
4849992ca9 | ||
![]() |
623105650e | ||
![]() |
5e2c01daa6 | ||
![]() |
58bf3a4d30 | ||
![]() |
2d434c38b9 | ||
![]() |
246f5cd8cf | ||
![]() |
5a174d21f1 | ||
![]() |
e632c653f6 | ||
![]() |
174d17b088 | ||
![]() |
3a35c931e4 | ||
![]() |
b768b26c85 | ||
![]() |
123ce6ebaf | ||
![]() |
276c8db512 | ||
![]() |
f898b8d181 | ||
![]() |
e9f9e531e4 | ||
![]() |
a7d813cb86 | ||
![]() |
75b6eedb12 | ||
![]() |
ec6860aa90 | ||
![]() |
5cf661c6ae | ||
![]() |
8c62caae7c | ||
![]() |
cc6fe21af6 | ||
![]() |
1902151efa | ||
![]() |
b7af1e6289 | ||
![]() |
0c77b320db | ||
![]() |
ee41adc4e9 | ||
![]() |
93462d7505 | ||
![]() |
2f148255f7 | ||
![]() |
cb00b8b335 | ||
![]() |
559b56c8a2 | ||
![]() |
6ec39314ee | ||
![]() |
158cea51db | ||
![]() |
33d34f35e9 | ||
![]() |
1f4f40fd7c | ||
![]() |
7c908b247e | ||
![]() |
41c822fde1 | ||
![]() |
2a6569742d | ||
![]() |
14c8b6a248 | ||
![]() |
9b71215cde | ||
![]() |
8be572f976 | ||
![]() |
4f43bcffb9 | ||
![]() |
29e4dc5fb5 | ||
![]() |
0dc95cb13c | ||
![]() |
9fad4a74ed |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,6 +1,13 @@
|
|||||||
*.swp
|
*.swp
|
||||||
/.gradle/
|
/.gradle/
|
||||||
|
|
||||||
/.idea/
|
/.idea/
|
||||||
|
!/.idea/scopes
|
||||||
|
!/.idea/copyright
|
||||||
|
!/.idea/icon.png
|
||||||
|
|
||||||
/build/
|
/build/
|
||||||
/out/
|
/out/
|
||||||
/tmp/
|
/tmp/
|
||||||
|
|
||||||
|
*.DS_Store
|
6
.idea/copyright/IdeaVim.xml
generated
Normal file
6
.idea/copyright/IdeaVim.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<copyright>
|
||||||
|
<option name="notice" value="IdeaVim - Vim emulator for IDEs based on the IntelliJ platform Copyright (C) 2003-&#36;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/>." />
|
||||||
|
<option name="myName" value="IdeaVim" />
|
||||||
|
</copyright>
|
||||||
|
</component>
|
7
.idea/copyright/profiles_settings.xml
generated
Normal file
7
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings>
|
||||||
|
<module2copyright>
|
||||||
|
<element module="Copyright" copyright="IdeaVim" />
|
||||||
|
</module2copyright>
|
||||||
|
</settings>
|
||||||
|
</component>
|
BIN
.idea/icon.png
generated
Normal file
BIN
.idea/icon.png
generated
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
3
.idea/scopes/Copyright.xml
generated
Normal file
3
.idea/scopes/Copyright.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<component name="DependencyValidationManager">
|
||||||
|
<scope name="Copyright" pattern="file[IdeaVIM.main]:com//*||file[IdeaVIM.test]:*/" />
|
||||||
|
</component>
|
16
AUTHORS.md
16
AUTHORS.md
@@ -283,6 +283,22 @@ Contributors:
|
|||||||
[![icon][github]](https://github.com/angelbot)
|
[![icon][github]](https://github.com/angelbot)
|
||||||
|
|
||||||
John Weigel
|
John Weigel
|
||||||
|
* [![icon][mail]](mailto:kevinz@weghst.com)
|
||||||
|
[![icon][github]](https://github.com/kevin70)
|
||||||
|
|
||||||
|
kk
|
||||||
|
* [![icon][mail]](mailto:runforprogram@163.com)
|
||||||
|
[![icon][github]](https://github.com/runforprogram)
|
||||||
|
|
||||||
|
runforprogram
|
||||||
|
* [![icon][mail]](mailto:valery.isaev@jetbrains.com)
|
||||||
|
[![icon][github]](https://github.com/valis)
|
||||||
|
|
||||||
|
valis
|
||||||
|
* [![icon][mail]](mailto:pmikulski@voleon.com)
|
||||||
|
[![icon][github]](https://github.com/pmnoxx)
|
||||||
|
|
||||||
|
Piotr Mikulski
|
||||||
|
|
||||||
If you are a contributor and your name is not listed here, feel free to
|
If you are a contributor and your name is not listed here, feel free to
|
||||||
contact the maintainers.
|
contact the maintainers.
|
||||||
|
86
CHANGES.md
86
CHANGES.md
@@ -3,6 +3,12 @@ The Changelog
|
|||||||
|
|
||||||
History of changes in IdeaVim for the IntelliJ platform.
|
History of changes in IdeaVim for the IntelliJ platform.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project DOES NOT adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
Stable versions use X.Y format.
|
||||||
|
EAP versions use X.Y.Z format.
|
||||||
|
|
||||||
|
|
||||||
Get an Early Access
|
Get an Early Access
|
||||||
-------------------
|
-------------------
|
||||||
@@ -17,30 +23,78 @@ Please note that the quality of EAP versions may at times be way below even
|
|||||||
usual beta standards.
|
usual beta standards.
|
||||||
|
|
||||||
To Be Released
|
To Be Released
|
||||||
--------------
|
-------------
|
||||||
|
|
||||||
_Available since 0.55.1 EAP:_
|
_Available since 0.57.1 EAP:_
|
||||||
|
|
||||||
**Fixes:**
|
|
||||||
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits
|
|
||||||
* Fix handling of counts on both operator and motion, e.g. `3d2w` deletes 6 words, instead of 32
|
|
||||||
* Allow mapping of `<C-K>` and `<C-V>`/`<C-Q>`
|
|
||||||
* [VIM-1899](https://youtrack.jetbrains.com/issue/VIM-1899) Add argument to `:registers` command
|
|
||||||
* [VIM-1835](https://youtrack.jetbrains.com/issue/VIM-1835) Macros record input keystrokes instead of mapped keystrokes
|
|
||||||
* [VIM-1900](https://youtrack.jetbrains.com/issue/VIM-1900) Ensure non-printable output for `:registers`, `:marks` and `:jumps` is encoded correctly
|
|
||||||
|
|
||||||
_Available since 0.55.2 EAP:_
|
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
* `argtextobj.vim` plugin emulation ([argtextobj.vim](https://vim.sourceforge.io/scripts/script.php?script_id=2699))
|
* `exchange` plugin emulation ([vim-exchange](https://github.com/tommcdo/vim-exchange)).
|
||||||
* `vim-textobj-entire` plugin emulation ([vim-textobj-entire](https://github.com/kana/vim-textobj-entire))
|
* `~/.ideavimrc` file can be reloaded using the new floating action.
|
||||||
* Support `ls/buffers/files` commands
|
|
||||||
|
* <details>
|
||||||
|
<summary><strong>Click to see details</strong></summary>
|
||||||
|
<img src="resources/changes/0.58/reload_ideavimrc.png" alt="IdeaVimRc reload"/>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
* Add `:buffer` command.
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
* Support IntelliJ's smooth scrolling. Use "Enable smooth scrolling" checkbox in _Preferences | Editor | General_ to disable.
|
||||||
|
|
||||||
|
**Fixes:**
|
||||||
|
* [VIM-1994](https://youtrack.jetbrains.com/issue/VIM-1994) Correct paste after `y}P` command.
|
||||||
|
* [VIM-1924](https://youtrack.jetbrains.com/issue/VIM-1924) Select next occurrence doesn't become block selection.
|
||||||
|
|
||||||
|
**Merged PRs:**
|
||||||
|
* [233](https://github.com/JetBrains/ideavim/pull/233) by [valis](https://github.com/valis): [VIM-1994] Correct paste after `y}P` command.
|
||||||
|
* [224](https://github.com/JetBrains/ideavim/pull/224) by [pmnoxx](https://github.com/pmnoxx): Populate intelij navigation history together with ideavim jumplist.
|
||||||
|
* [227](https://github.com/JetBrains/ideavim/pull/227) by [angelbot](https://github.com/angelbot): Add support for buffer command.
|
||||||
|
* [230](https://github.com/JetBrains/ideavim/pull/230) by [fan-tom](https://github.com/fan-tom): VIM-1924.
|
||||||
|
* [231](https://github.com/JetBrains/ideavim/pull/231) by [citizenmatt](https://github.com/citizenmatt): Support smooth scrolling.
|
||||||
|
|
||||||
|
_To Be Released..._
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
0.57, 2020-04-28
|
||||||
|
-------------
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
0.56, 2020-04-09
|
||||||
|
--------------
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
* `ReplaceWithRegister` plugin emulation ([ReplaceWithRegister](https://www.vim.org/scripts/script.php?script_id=2703)).
|
||||||
|
* `argtextobj.vim` plugin emulation ([argtextobj.vim](https://vim.sourceforge.io/scripts/script.php?script_id=2699)).
|
||||||
|
* `vim-textobj-entire` plugin emulation ([vim-textobj-entire](https://github.com/kana/vim-textobj-entire)).
|
||||||
|
* [VIM-434](https://youtrack.jetbrains.com/issue/VIM-434) Add `'showcmd'` support, on by default.
|
||||||
|
* Support `ls/buffers/files` commands.
|
||||||
|
|
||||||
**Changes:**
|
**Changes:**
|
||||||
* Replace `ideastatusbar` option with `ideastatusicon`. Now you can make the icon gray.
|
* Replace `ideastatusbar` option with `ideastatusicon`. Now you can make the icon gray.
|
||||||
|
|
||||||
|
**Deprecations:**
|
||||||
|
* `ideastatusbar` option is deprecated now. See `ideastatusicon`.
|
||||||
|
|
||||||
**Fixes:**
|
**Fixes:**
|
||||||
* [VIM-1008](https://youtrack.jetbrains.com/issue/VIM-1008) Correct `ci{` behavior
|
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits.
|
||||||
|
* Fix handling of counts on both operator and motion, e.g. `3d2w` deletes 6 words, instead of 32.
|
||||||
|
* Allow mapping of `<C-K>` and `<C-V>`/`<C-Q>`.
|
||||||
|
* [VIM-1899](https://youtrack.jetbrains.com/issue/VIM-1899) Add argument to `:registers` command.
|
||||||
|
* [VIM-1835](https://youtrack.jetbrains.com/issue/VIM-1835) Macros record input keystrokes instead of mapped keystrokes.
|
||||||
|
* [VIM-1900](https://youtrack.jetbrains.com/issue/VIM-1900) Ensure non-printable output for `:registers`, `:marks` and `:jumps` is encoded correctly.
|
||||||
|
* [VIM-570](https://youtrack.jetbrains.com/issue/VIM-570) Print non-ascii characters in ex panel.
|
||||||
|
* [VIM-926](https://youtrack.jetbrains.com/issue/VIM-926) Fix `<S-Space>` mapping.
|
||||||
|
* [VIM-1958](https://youtrack.jetbrains.com/issue/VIM-1958) Fix `X` command for linewise selection.
|
||||||
|
* [VIM-1911](https://youtrack.jetbrains.com/issue/VIM-1911) Lookup keys respect `IDE` handler.
|
||||||
|
* [VIM-1008](https://youtrack.jetbrains.com/issue/VIM-1008) Correct `ci{` behavior.
|
||||||
|
|
||||||
0.55, 2020-01-20
|
0.55, 2020-01-20
|
||||||
--------------
|
--------------
|
||||||
|
@@ -1,33 +1,4 @@
|
|||||||
<div>
|
[![TeamCity Build][teamcity-build-status-svg]][teamcity-build-status]
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20183&guest=1">
|
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20183)/statusIcon.svg?guest=1"/>
|
|
||||||
</a>
|
|
||||||
<span>2018.3 Tests</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20191&guest=1">
|
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20191)/statusIcon.svg?guest=1"/>
|
|
||||||
</a>
|
|
||||||
<span>2019.1 Tests</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20192&guest=1">
|
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20192)/statusIcon.svg?guest=1"/>
|
|
||||||
</a>
|
|
||||||
<span>2019.2 Tests</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20193&guest=1">
|
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20193)/statusIcon.svg?guest=1"/>
|
|
||||||
</a>
|
|
||||||
<span>2019.3 Tests</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1">
|
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1"/>
|
|
||||||
</a>
|
|
||||||
<span>2020.1 Tests</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
### Where to Start
|
### Where to Start
|
||||||
@@ -46,6 +17,8 @@ You can start by:
|
|||||||
in the issue tracker.
|
in the issue tracker.
|
||||||
- Read about the `@VimBehaviorDiffers` annotation and fix the corresponding functionality.
|
- Read about the `@VimBehaviorDiffers` annotation and fix the corresponding functionality.
|
||||||
|
|
||||||
|
Also join the brand new [](https://gitter.im/JetBrains/ideavim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for IdeaVim developers!
|
||||||
|
|
||||||
|
|
||||||
### Development Environment
|
### Development Environment
|
||||||
|
|
||||||
@@ -54,7 +27,6 @@ in the issue tracker.
|
|||||||
2. Import the project from the existing sources in IntelliJ IDEA 2018.1 or newer (Community or
|
2. Import the project from the existing sources in IntelliJ IDEA 2018.1 or newer (Community or
|
||||||
Ultimate), by selecting "File | New | Project from Existing Sources..." or selecting "Import
|
Ultimate), by selecting "File | New | Project from Existing Sources..." or selecting "Import
|
||||||
Project" from the Welcome screen.
|
Project" from the Welcome screen.
|
||||||
|
|
||||||
* In the project wizard, select "Import project from external model | Gradle".
|
* In the project wizard, select "Import project from external model | Gradle".
|
||||||
|
|
||||||
* Select your Java 8+ JDK as the Gradle JVM; leave other parameters unchanged.
|
* Select your Java 8+ JDK as the Gradle JVM; leave other parameters unchanged.
|
||||||
@@ -79,36 +51,6 @@ in the issue tracker.
|
|||||||
* You can install this file by selecting "Settings | Plugins | Install plugin
|
* You can install this file by selecting "Settings | Plugins | Install plugin
|
||||||
from disk...".
|
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
|
### Testing
|
||||||
|
|
||||||
1. Read about the `@VimBehaviorDiffers` annotation.
|
1. Read about the `@VimBehaviorDiffers` annotation.
|
||||||
@@ -122,3 +64,8 @@ For example, take a few lines from your favorite poem, or use
|
|||||||
3. Test your functionality properly.
|
3. Test your functionality properly.
|
||||||
Especially check whether your command works with:
|
Especially check whether your command works with:
|
||||||
line start, line end, file start, file end, empty line, multiple carets, dollar motion, etc.
|
line start, line end, file start, file end, empty line, multiple carets, dollar motion, etc.
|
||||||
|
|
||||||
|
<!-- Badges -->
|
||||||
|
|
||||||
|
[teamcity-build-status]: https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1
|
||||||
|
[teamcity-build-status-svg]: https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1
|
||||||
|
144
README.md
144
README.md
@@ -3,14 +3,12 @@
|
|||||||
IdeaVim
|
IdeaVim
|
||||||
===
|
===
|
||||||
|
|
||||||
<div>
|
[![Official JetBrains Project][jb-official-svg]][jb-official]
|
||||||
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub">
|
[![Downloads][plugin-downloads-svg]][plugin-repo]
|
||||||
<img src="https://jb.gg/badges/official.svg" alt="official JetBrains project"/>
|
[![Rating][plugin-rating-svg]][plugin-repo]
|
||||||
</a>
|
[![Version][plugin-version-svg]][plugin-repo]
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1">
|
[![Gitter][gitter-svg]][gitter]
|
||||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1" alt="TeamCity Build"/>
|
[![Twitter][twitter-svg]][twitter]
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
IdeaVim is a Vim emulation plugin for IDEs based on the IntelliJ Platform.
|
IdeaVim is a Vim emulation plugin for IDEs based on the IntelliJ Platform.
|
||||||
IdeaVim can be used with IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm,
|
IdeaVim can be used with IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm,
|
||||||
@@ -24,24 +22,20 @@ Resources:
|
|||||||
* [Continuous integration builds](https://teamcity.jetbrains.com/project.html?projectId=IdeaVim&guest=1)
|
* [Continuous integration builds](https://teamcity.jetbrains.com/project.html?projectId=IdeaVim&guest=1)
|
||||||
* [@IdeaVim](https://twitter.com/ideavim) in Twitter
|
* [@IdeaVim](https://twitter.com/ideavim) in Twitter
|
||||||
|
|
||||||
|
Setup
|
||||||
Installation
|
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Use the IDE's plugin manager to install the latest version of the plugin.
|
- IdeaVim can be installed via `Settings | Plugins`.
|
||||||
Start the IDE normally and enable the Vim emulation using "Tools | Vim
|
See [detailed instructions](https://www.jetbrains.com/help/idea/managing-plugins.html#).
|
||||||
Emulator" menu item. At this point you must use Vim keystrokes in all editors.
|
|
||||||
|
|
||||||
If you wish to disable the plugin, select the "Tools | Vim Emulator" menu so
|
- Use `Tools | Vim Emulator` to enable or disable emulation.
|
||||||
it is unchecked. At this point your IDE will work with its regular keyboard
|
|
||||||
shortcuts.
|
|
||||||
|
|
||||||
Keyboard shortcut conflicts between the Vim emulation and the IDE can be
|
- Use `~/.ideavimrc` file as an analog of `~/.vimrc` ([details](#Files)). XGD standard is supported as well.
|
||||||
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.
|
|
||||||
|
|
||||||
|
- 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
|
Get Early Access
|
||||||
-------------------
|
-------------------
|
||||||
@@ -49,7 +43,9 @@ Get Early Access
|
|||||||
Would you like to try new features and fixes? Join the Early Access Program and
|
Would you like to try new features and fixes? Join the Early Access Program and
|
||||||
receive EAP builds as updates!
|
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:
|
Or subscribe to EAP updates manually:
|
||||||
|
|
||||||
@@ -57,7 +53,7 @@ Or subscribe to EAP updates manually:
|
|||||||
2. Click the gear icon :gear:, select `Manage Plugin Repositories`, and add the following url:
|
2. Click the gear icon :gear:, select `Manage Plugin Repositories`, and add the following url:
|
||||||
`https://plugins.jetbrains.com/plugins/eap/ideavim`
|
`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.
|
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
|
Please note that the quality of EAP versions may at times be way below even
|
||||||
@@ -91,14 +87,16 @@ Supported:
|
|||||||
* Vim web help
|
* Vim web help
|
||||||
* Select mode
|
* Select mode
|
||||||
|
|
||||||
Emulated Vim plugins:
|
[Emulated Vim plugins](doc/emulated-plugins.md):
|
||||||
|
|
||||||
* vim-easymotion
|
* vim-easymotion
|
||||||
* vim-surround
|
* vim-surround
|
||||||
* vim-multiple-cursors
|
* vim-multiple-cursors
|
||||||
* vim-commentary
|
* vim-commentary
|
||||||
* argtextobj.vim [To Be Released]
|
* argtextobj.vim
|
||||||
* vim-textobj-entire [To Be Released]
|
* vim-textobj-entire
|
||||||
|
* ReplaceWithRegister
|
||||||
|
* vim-exchange [To Be Released]
|
||||||
|
|
||||||
Not supported (yet):
|
Not supported (yet):
|
||||||
|
|
||||||
@@ -114,19 +112,19 @@ See also:
|
|||||||
Files
|
Files
|
||||||
-----
|
-----
|
||||||
|
|
||||||
* ~/.ideavimrc
|
* `~/.ideavimrc`
|
||||||
* Your IdeaVim-specific Vim initialization commands
|
* 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
|
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
|
See [VIM-669](https://youtrack.jetbrains.com/issue/VIM-669) for proper parsing
|
||||||
of VimL files.
|
of VimL files.
|
||||||
|
|
||||||
Also note that if you have overridden the `user.home` JVM option, this
|
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
|
have `-Duser.home=/my/alternate/home` then IdeaVim will source
|
||||||
`/my/alternate/home/.ideavimrc` instead of `~/.ideavimrc`.
|
`/my/alternate/home/.ideavimrc` instead of `~/.ideavimrc`.
|
||||||
|
|
||||||
@@ -137,50 +135,25 @@ Put your settings to `$XDG_CONFIG_HOME$/ideavim/ideavimrc` file.
|
|||||||
Emulated Vim Plugins
|
Emulated Vim Plugins
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
IdeaVim extensions emulate some plugins of the original Vim. In order to use
|
See [doc/emulated-plugins.md](doc/emulated-plugins.md)
|
||||||
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`
|
|
||||||
|
|
||||||
* argtextobj [To Be Released]
|
|
||||||
* Setup: `set argtextobj`
|
|
||||||
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
|
|
||||||
* Additional text objects: `aa`, `ia`
|
|
||||||
|
|
||||||
* textobj-entire [To Be Released]
|
|
||||||
* Setup: `set textobj-entire`
|
|
||||||
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
|
|
||||||
* Additional text objects: `ae`, `ie`
|
|
||||||
|
|
||||||
|
|
||||||
Changes to the IDE
|
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
|
### Undo/Redo
|
||||||
|
|
||||||
The IdeaVim plugin uses the undo/redo functionality of the IntelliJ Platform,
|
The IdeaVim plugin uses the undo/redo functionality of the IntelliJ Platform,
|
||||||
@@ -199,21 +172,6 @@ improvement.
|
|||||||
|
|
||||||
See also [unresolved escape issues](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+Help+topic%3A+i_Esc).
|
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
|
Contributing
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@@ -231,3 +189,19 @@ License
|
|||||||
|
|
||||||
IdeaVim is licensed under the terms of the GNU Public License version 2
|
IdeaVim is licensed under the terms of the GNU Public License version 2
|
||||||
or any later version.
|
or any later version.
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Badges -->
|
||||||
|
[jb-official]: https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub
|
||||||
|
[jb-official-svg]: https://jb.gg/badges/official.svg
|
||||||
|
|
||||||
|
[plugin-repo]: https://plugins.jetbrains.com/plugin/164-ideavim
|
||||||
|
[plugin-downloads-svg]: http://img.shields.io/jetbrains/plugin/d/IdeaVIM
|
||||||
|
[plugin-rating-svg]: http://img.shields.io/jetbrains/plugin/r/rating/IdeaVIM
|
||||||
|
[plugin-version-svg]: https://img.shields.io/jetbrains/plugin/v/ideavim?label=version
|
||||||
|
|
||||||
|
[gitter-svg]: https://badges.gitter.im/JetBrains/ideavim.svg
|
||||||
|
[gitter]: https://gitter.im/JetBrains/ideavim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||||
|
|
||||||
|
[twitter]: https://twitter.com/ideavim
|
||||||
|
[twitter-svg]: https://img.shields.io/twitter/follow/ideavim?label=twitter%20%40ideavim
|
||||||
|
51
build.gradle
51
build.gradle
@@ -9,7 +9,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.jetbrains.intellij' version '0.4.16'
|
id 'org.jetbrains.intellij' version '0.4.18'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
@@ -37,11 +37,7 @@ intellij {
|
|||||||
downloadSources Boolean.valueOf(downloadIdeaSources)
|
downloadSources Boolean.valueOf(downloadIdeaSources)
|
||||||
instrumentCode Boolean.valueOf(instrumentPluginCode)
|
instrumentCode Boolean.valueOf(instrumentPluginCode)
|
||||||
intellijRepo = "https://www.jetbrains.com/intellij-repository"
|
intellijRepo = "https://www.jetbrains.com/intellij-repository"
|
||||||
if (!Boolean.valueOf(legacyNoJavaPlugin)) {
|
|
||||||
// Since 192 version of IJ java plugin should be defined separately
|
|
||||||
// Set `legacyNoJavaPlugin` to true if you are going to run tests under idea version < 192
|
|
||||||
plugins = ['java']
|
plugins = ['java']
|
||||||
}
|
|
||||||
|
|
||||||
publishPlugin {
|
publishPlugin {
|
||||||
channels publishChannels.split(',')
|
channels publishChannels.split(',')
|
||||||
@@ -74,15 +70,56 @@ tasks.register("slackEapNotification") {
|
|||||||
doLast {
|
doLast {
|
||||||
if (!slackUrl) return
|
if (!slackUrl) return
|
||||||
def post = new URL(slackUrl).openConnection()
|
def post = new URL(slackUrl).openConnection()
|
||||||
def message = "{\"text\":\"New EAP released: $version\"}"
|
def changeLog = extractChangelog()
|
||||||
|
changeLog = changeLog.replace("* ", "• ") // Replace stars with bullets
|
||||||
|
changeLog = changeLog.replace("**", "*") // Enable bold text
|
||||||
|
changeLog = changeLog.replaceAll("\\[([^]]+)]\\(([^)]+)\\)", '<$2|$1>') // Enable links
|
||||||
|
def message ="""
|
||||||
|
{
|
||||||
|
"text": "New version of IdeaVim",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"text": {
|
||||||
|
"type": "mrkdwn",
|
||||||
|
"text": "IdeaVim EAP $version has been relesed\\n$changeLog"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
post.setRequestMethod("POST")
|
post.setRequestMethod("POST")
|
||||||
post.setDoOutput(true)
|
post.setDoOutput(true)
|
||||||
post.setRequestProperty("Content-Type", "application/json")
|
post.setRequestProperty("Content-Type", "application/json")
|
||||||
post.getOutputStream().write(message.getBytes("UTF-8"))
|
post.getOutputStream().write(message.getBytes("UTF-8"))
|
||||||
def postRC = post.getResponseCode()
|
def postRC = post.getResponseCode()
|
||||||
println(postRC)
|
println(postRC)
|
||||||
if(postRC.equals(200)) {
|
if(postRC == 200) {
|
||||||
println(post.getInputStream().getText())
|
println(post.getInputStream().getText())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Very primitive changelog extraction code
|
||||||
|
def extractChangelog() {
|
||||||
|
def startLine = "_Available since $version EAP:_"
|
||||||
|
def endLine = "_To Be Released..._"
|
||||||
|
def startSaving = false
|
||||||
|
def res = new StringBuilder()
|
||||||
|
new File("./CHANGES.md").eachLine { line ->
|
||||||
|
if (startSaving) {
|
||||||
|
if (line == endLine) {
|
||||||
|
startSaving = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res.append(line).append('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (line == startLine) {
|
||||||
|
startSaving = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.toString()
|
||||||
|
}
|
68
doc/emulated-plugins.md
Normal file
68
doc/emulated-plugins.md
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
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`
|
||||||
|
|
||||||
|
## exchange [To Be Released]
|
||||||
|
|
||||||
|
* Setup: `set exchange`
|
||||||
|
* Emulates [vim-exchange](https://github.com/tommcdo/vim-exchange)
|
||||||
|
* Commands: `cx`, `cxx`, `X`, `cxc`
|
||||||
|
|
||||||
|
## textobj-entire
|
||||||
|
|
||||||
|
* Setup: `set textobj-entire`
|
||||||
|
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
|
||||||
|
* Additional text objects: `ae`, `ie`
|
||||||
|
|
@@ -65,6 +65,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
|||||||
same as ideaselection - IdeaVim ONLY
|
same as ideaselection - IdeaVim ONLY
|
||||||
|
|
||||||
'showmode' 'smd' message on the status line to show current mode
|
'showmode' 'smd' message on the status line to show current mode
|
||||||
|
'showcmd' 'sc' show (partial) command in the status bar
|
||||||
'sidescroll' 'ss' minimum number of columns to scroll horizontally
|
'sidescroll' 'ss' minimum number of columns to scroll horizontally
|
||||||
'sidescrolloff' 'siso' min. number of columns to left and right of cursor
|
'sidescrolloff' 'siso' min. number of columns to left and right of cursor
|
||||||
'smartcase' 'scs' no ignore case when pattern is uppercase
|
'smartcase' 'scs' no ignore case when pattern is uppercase
|
||||||
@@ -109,7 +110,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
|||||||
If false, IdeaVim icon won't be shown in the status bar.
|
If false, IdeaVim icon won't be shown in the status bar.
|
||||||
Works only from `~/.ideavimrc` after the IDE restart.
|
Works only from `~/.ideavimrc` after the IDE restart.
|
||||||
|
|
||||||
`ideastatusicon` `ideastatusicon` String(default "enabled") [To Be Released]
|
`ideastatusicon` `ideastatusicon` String(default "enabled")
|
||||||
|
|
||||||
Define the behavior of IdeaVim icon in the status bar.
|
Define the behavior of IdeaVim icon in the status bar.
|
||||||
|
|
||||||
@@ -118,8 +119,6 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
|||||||
- gray - use the gray version of the icon
|
- gray - use the gray version of the icon
|
||||||
- disabled - hide the icon
|
- disabled - hide the icon
|
||||||
|
|
||||||
Works only from `~/.ideavimrc` after the IDE restart.
|
|
||||||
|
|
||||||
`lookupkeys` `lookupkeys` List of strings
|
`lookupkeys` `lookupkeys` List of strings
|
||||||
|
|
||||||
List of keys that should be processed by the IDE during the active lookup (autocompletion).
|
List of keys that should be processed by the IDE during the active lookup (autocompletion).
|
||||||
|
@@ -1,16 +1,13 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
|
|
||||||
ideaVersion=201-EAP-SNAPSHOT
|
ideaVersion=2020.1
|
||||||
downloadIdeaSources=true
|
downloadIdeaSources=true
|
||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=SNAPSHOT
|
version=SNAPSHOT
|
||||||
javaVersion=1.8
|
javaVersion=1.8
|
||||||
kotlinVersion=1.3.61
|
kotlinVersion=1.3.71
|
||||||
publishUsername=username
|
publishUsername=username
|
||||||
publishToken=token
|
publishToken=token
|
||||||
publishChannels=eap
|
publishChannels=eap
|
||||||
# Since 192 version of IJ java plugin should be defined separately
|
|
||||||
# Set this value to true if you are going to run tests under idea version < 192
|
|
||||||
legacyNoJavaPlugin=false
|
|
||||||
|
|
||||||
slackUrl=
|
slackUrl=
|
||||||
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
5
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
|||||||
|
#Fri Apr 10 10:57:10 MSK 2020
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@@ -197,8 +197,8 @@
|
|||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction" mappingModes="I" keys="«C-O»"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertSingleCommandAction" mappingModes="I" keys="«C-O»"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction" mappingModes="X" keys="I"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockInsertAction" mappingModes="X" keys="I"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction" mappingModes="X" keys="A"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.VisualBlockAppendAction" mappingModes="X" keys="A"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.StartInsertDigraphAction" mappingModes="IC" keys="«C-K»"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction" mappingModes="IC" keys="«C-K»"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.StartInsertLiteralAction" mappingModes="IC" keys="«C-V»,«C-Q»"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction" mappingModes="IC" keys="«C-V»,«C-Q»"/>
|
||||||
|
|
||||||
<!-- Delete -->
|
<!-- Delete -->
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction" mappingModes="N" keys="«DEL»"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.change.delete.DeleteCharacterAction" mappingModes="N" keys="«DEL»"/>
|
||||||
@@ -260,14 +260,16 @@
|
|||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction" mappingModes="N" keys="[P,]P,[p"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorNoIndentAction" mappingModes="N" keys="[P,]P,[p"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor" mappingModes="N" keys="gp"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextAfterCursorActionMoveCursor" mappingModes="N" keys="gp"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor" mappingModes="N" keys="gP"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutTextBeforeCursorActionMoveCursor" mappingModes="N" keys="gP"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.SelectRegisterAction" mappingModes="NXO" keys='"'/>
|
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankLineAction" mappingModes="N" keys="Y"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankLineAction" mappingModes="N" keys="Y"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankMotionAction" mappingModes="N" keys="y"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankMotionAction" mappingModes="N" keys="y"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualAction" mappingModes="X" keys="y"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualAction" mappingModes="X" keys="y"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualLinesAction" mappingModes="X" keys="Y"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.YankVisualLinesAction" mappingModes="X" keys="Y"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAction" mappingModes="X" keys="p,P"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorAction" mappingModes="X" keys="P"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextNoIndentAction" mappingModes="X" keys="[p,]p,]P,[P"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorAction" mappingModes="X" keys="p"/>
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextMoveCursorAction" mappingModes="X" keys="gp,gP"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorNoIndentAction" mappingModes="X" keys="]P,[P"/>
|
||||||
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorNoIndentAction" mappingModes="X" keys="[p,]p"/>
|
||||||
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextBeforeCursorMoveCursorAction" mappingModes="X" keys="gP"/>
|
||||||
|
<vimAction implementation="com.maddyhome.idea.vim.action.copy.PutVisualTextAfterCursorMoveCursorAction" mappingModes="X" keys="gp"/>
|
||||||
|
|
||||||
<!-- File -->
|
<!-- File -->
|
||||||
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileSaveCloseAction" mappingModes="N" keys="ZQ,ZZ"/>
|
<vimAction implementation="com.maddyhome.idea.vim.action.file.FileSaveCloseAction" mappingModes="N" keys="ZQ,ZZ"/>
|
||||||
|
@@ -61,5 +61,6 @@
|
|||||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.PreviousTabHandler" names="tabp[revious],tabN[ext]"/>
|
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.PreviousTabHandler" names="tabp[revious],tabN[ext]"/>
|
||||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.TabOnlyHandler" names="tabo[nly]"/>
|
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.TabOnlyHandler" names="tabo[nly]"/>
|
||||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferListHandler" names="buffers,ls,files"/>
|
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferListHandler" names="buffers,ls,files"/>
|
||||||
|
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferHandler" names="b[uffer]"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@@ -5,5 +5,7 @@
|
|||||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.commentary.CommentaryExtension"/>
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.commentary.CommentaryExtension"/>
|
||||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.textobjentire.VimTextObjEntireExtension"/>
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.textobjentire.VimTextObjEntireExtension"/>
|
||||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.argtextobj.VimArgTextObjExtension"/>
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.argtextobj.VimArgTextObjExtension"/>
|
||||||
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.replacewithregister.ReplaceWithRegister"/>
|
||||||
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.exchange.VimExchangeExtension"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
25
resources/META-INF/includes/VimListeners.xml
Normal file
25
resources/META-INF/includes/VimListeners.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<idea-plugin>
|
||||||
|
<applicationListeners>
|
||||||
|
<listener class="com.maddyhome.idea.vim.ui.ExEntryPanel$LafListener"
|
||||||
|
topic="com.intellij.ide.ui.LafManagerListener"/>
|
||||||
|
</applicationListeners>
|
||||||
|
<projectListeners>
|
||||||
|
<listener class="com.maddyhome.idea.vim.ui.ExOutputPanel$LafListener"
|
||||||
|
topic="com.intellij.ide.ui.LafManagerListener"/>
|
||||||
|
|
||||||
|
<listener class="com.maddyhome.idea.vim.listener.VimListenerManager$VimFileEditorManagerListener"
|
||||||
|
topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/>
|
||||||
|
|
||||||
|
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimActionListener"
|
||||||
|
topic="com.intellij.openapi.actionSystem.ex.AnActionListener"/>
|
||||||
|
|
||||||
|
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimTemplateManagerListener"
|
||||||
|
topic="com.intellij.codeInsight.template.TemplateManagerListener"/>
|
||||||
|
|
||||||
|
<listener class="com.maddyhome.idea.vim.group.MarkGroup$MarkListener"
|
||||||
|
topic="com.intellij.ide.bookmarks.BookmarksListener"/>
|
||||||
|
|
||||||
|
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimFindModelListener"
|
||||||
|
topic="com.intellij.find.FindModelListener"/>
|
||||||
|
</projectListeners>
|
||||||
|
</idea-plugin>
|
@@ -3,9 +3,8 @@
|
|||||||
<id>IdeaVIM</id>
|
<id>IdeaVIM</id>
|
||||||
<change-notes><![CDATA[
|
<change-notes><![CDATA[
|
||||||
<ul>
|
<ul>
|
||||||
<li>Support argtextobj.vim plugin emulation</li>
|
<li>vim-exchange plugin emulation</li>
|
||||||
<li>Support vim-textobj-entire plugin emulation</li>
|
<li>A new floating action for reloading .ideavimrc</li>
|
||||||
<li>Support ls/buffers/files command</li>
|
|
||||||
<li>Various bug fixes</li>
|
<li>Various bug fixes</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
|
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
|
|
||||||
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
|
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
|
||||||
<!-- Check for [Version Update] tag in YouTrack as well -->
|
<!-- Check for [Version Update] tag in YouTrack as well -->
|
||||||
<idea-version since-build="183.4284.148"/>
|
<idea-version since-build="201.5985"/>
|
||||||
|
|
||||||
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform -->
|
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform -->
|
||||||
<depends>com.intellij.modules.lang</depends>
|
<depends>com.intellij.modules.lang</depends>
|
||||||
@@ -52,19 +51,25 @@
|
|||||||
<extensions defaultExtensionNs="com.intellij">
|
<extensions defaultExtensionNs="com.intellij">
|
||||||
<applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/>
|
<applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/>
|
||||||
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
|
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
|
||||||
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.StatusBarIconProvider"/>
|
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/>
|
||||||
|
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/>
|
||||||
|
|
||||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
|
||||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
|
||||||
|
|
||||||
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup"/>
|
<!-- Initialise as early as possible so that we're ready to edit quickly. This is especially important for Rider,
|
||||||
|
which (at least for 2020.1) has some long running activities that block other startup extensions. None of the
|
||||||
|
core platform activities have IDs, so we can't use "before ID". We have to use "first" -->
|
||||||
|
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup" order="first"/>
|
||||||
|
|
||||||
|
<editorFloatingToolbarProvider implementation="com.maddyhome.idea.vim.ui.ReloadFloatingToolbar"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||||
|
|
||||||
<xi:include href="/META-INF/includes/VimActions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
<xi:include href="/META-INF/includes/VimActions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||||
<xi:include href="/META-INF/includes/VimExCommands.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
<xi:include href="/META-INF/includes/VimExCommands.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||||
<xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
<xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||||
|
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||||
|
|
||||||
<actions>
|
<actions>
|
||||||
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction" text="Vim Emulator" description="Toggle the vim plugin On/off">
|
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction" text="Vim Emulator" description="Toggle the vim plugin On/off">
|
||||||
@@ -75,6 +80,20 @@
|
|||||||
<action id="VimInternalAddInlays" class="com.maddyhome.idea.vim.action.internal.AddInlaysAction" text="Add Test Inlays | IdeaVim Internal" internal="true"/>
|
<action id="VimInternalAddInlays" class="com.maddyhome.idea.vim.action.internal.AddInlaysAction" text="Add Test Inlays | IdeaVim Internal" internal="true"/>
|
||||||
|
|
||||||
<action id="VimShortcutKeyAction" class="com.maddyhome.idea.vim.action.VimShortcutKeyAction" text="Shortcuts"/>
|
<action id="VimShortcutKeyAction" class="com.maddyhome.idea.vim.action.VimShortcutKeyAction" text="Shortcuts"/>
|
||||||
<action id="VimActions" class="com.maddyhome.idea.vim.VimActions" text="Vim Actions"/>
|
<action id="VimActions" class="com.maddyhome.idea.vim.ui.VimActions" text="Vim Actions"/>
|
||||||
|
|
||||||
|
<!-- [Version Update] 202+ use-shortcut-of="ExternalSystem.ProjectRefreshAction" -->
|
||||||
|
<group id="IdeaVim.ReloadVimRc.group" class="com.maddyhome.idea.vim.ui.ReloadFloatingToolbarActionGroup">
|
||||||
|
<action id="IdeaVim.ReloadVimRc.reload" class="com.maddyhome.idea.vim.ui.ReloadVimRc">
|
||||||
|
<keyboard-shortcut first-keystroke="control shift O" keymap="$default"/>
|
||||||
|
<keyboard-shortcut first-keystroke="control shift O" keymap="Eclipse" remove="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="control shift O" keymap="NetBeans 6.5" remove="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="control shift O" keymap="Visual Studio" remove="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="meta shift O" keymap="Mac OS X" replace-all="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="meta shift O" keymap="Eclipse (Mac OS X)" replace-all="true" remove="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="meta shift O" keymap="Xcode" replace-all="true" remove="true"/>
|
||||||
|
<keyboard-shortcut first-keystroke="meta shift I" keymap="Mac OS X 10.5+" replace-all="true"/>
|
||||||
|
</action>
|
||||||
|
</group>
|
||||||
</actions>
|
</actions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
9
resources/META-INF/pluginIcon_noBorders.svg
Normal file
9
resources/META-INF/pluginIcon_noBorders.svg
Normal 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 |
BIN
resources/changes/0.58/reload_ideavimrc.png
Normal file
BIN
resources/changes/0.58/reload_ideavimrc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 194 KiB |
@@ -56,7 +56,6 @@ public class EventFacade {
|
|||||||
private static final @NotNull EventFacade ourInstance = new EventFacade();
|
private static final @NotNull EventFacade ourInstance = new EventFacade();
|
||||||
|
|
||||||
private @Nullable TypedActionHandler myOriginalTypedActionHandler;
|
private @Nullable TypedActionHandler myOriginalTypedActionHandler;
|
||||||
private Map<Project, MessageBusConnection> connections = new HashMap<>();
|
|
||||||
|
|
||||||
private EventFacade() {
|
private EventFacade() {
|
||||||
}
|
}
|
||||||
@@ -92,31 +91,6 @@ public class EventFacade {
|
|||||||
action.unregisterCustomShortcutSet(component);
|
action.unregisterCustomShortcutSet(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connectFileEditorManagerListener(@NotNull Project project, @NotNull FileEditorManagerListener listener) {
|
|
||||||
final MessageBusConnection connection = getConnection(project);
|
|
||||||
connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectAnActionListener(@NotNull Project project, @NotNull AnActionListener listener) {
|
|
||||||
final MessageBusConnection connection = getConnection(project);
|
|
||||||
connection.subscribe(AnActionListener.TOPIC, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectTemplateStartedListener(@NotNull Project project, @NotNull TemplateManagerListener listener) {
|
|
||||||
final MessageBusConnection connection = getConnection(project);
|
|
||||||
connection.subscribe(TemplateManager.TEMPLATE_STARTED_TOPIC, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectBookmarkListener(@NotNull Project project, @NotNull BookmarksListener bookmarksListener) {
|
|
||||||
final MessageBusConnection connection = getConnection(project);
|
|
||||||
connection.subscribe(BookmarksListener.TOPIC, bookmarksListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connectFindModelListener(@NotNull Project project, @NotNull FindModelListener findModelListener) {
|
|
||||||
final MessageBusConnection connection = getConnection(project);
|
|
||||||
connection.subscribe(FindManager.FIND_MODEL_TOPIC, findModelListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||||
document.addDocumentListener(listener);
|
document.addDocumentListener(listener);
|
||||||
}
|
}
|
||||||
@@ -168,30 +142,14 @@ public class EventFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void registerLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
public void registerLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
||||||
LookupManager.getInstance(project).addPropertyChangeListener(propertyChangeListener, project);
|
VimProjectService parentDisposable = VimProjectService.getInstance(project);
|
||||||
|
LookupManager.getInstance(project).addPropertyChangeListener(propertyChangeListener, parentDisposable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
public void removeLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
||||||
LookupManager.getInstance(project).removePropertyChangeListener(propertyChangeListener);
|
LookupManager.getInstance(project).removePropertyChangeListener(propertyChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableBusConnection() {
|
|
||||||
connections.values().forEach(MessageBusConnection::disconnect);
|
|
||||||
connections.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MessageBusConnection getConnection(Project project) {
|
|
||||||
if (!connections.containsKey(project)) {
|
|
||||||
final MessageBusConnection connection = project.getMessageBus().connect();
|
|
||||||
connections.put(project, connection);
|
|
||||||
Disposer.register(project, () -> {
|
|
||||||
connection.disconnect();
|
|
||||||
connections.remove(project);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return connections.get(project);
|
|
||||||
}
|
|
||||||
|
|
||||||
private @NotNull TypedAction getTypedAction() {
|
private @NotNull TypedAction getTypedAction() {
|
||||||
return EditorActionManager.getInstance().getTypedAction();
|
return EditorActionManager.getInstance().getTypedAction();
|
||||||
}
|
}
|
||||||
|
@@ -35,6 +35,8 @@ import com.intellij.openapi.project.Project;
|
|||||||
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
import com.intellij.openapi.ui.popup.JBPopupFactory;
|
||||||
import com.intellij.openapi.ui.popup.ListPopup;
|
import com.intellij.openapi.ui.popup.ListPopup;
|
||||||
import com.maddyhome.idea.vim.action.change.VimRepeater;
|
import com.maddyhome.idea.vim.action.change.VimRepeater;
|
||||||
|
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction;
|
||||||
|
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction;
|
||||||
import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction;
|
import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.*;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
||||||
@@ -48,6 +50,7 @@ import com.maddyhome.idea.vim.key.*;
|
|||||||
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||||
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||||
|
import com.maddyhome.idea.vim.ui.ShowCmd;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NonNls;
|
import org.jetbrains.annotations.NonNls;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -155,16 +158,6 @@ public class KeyHandler {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startDigraphSequence(@NotNull Editor editor) {
|
|
||||||
final CommandState editorState = CommandState.getInstance(editor);
|
|
||||||
editorState.startDigraphSequence();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startLiteralSequence(@NotNull Editor editor) {
|
|
||||||
final CommandState editorState = CommandState.getInstance(editor);
|
|
||||||
editorState.startLiteralSequence();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the main key handler for the Vim plugin. Every keystroke not handled directly by Idea is sent here for
|
* This is the main key handler for the Vim plugin. Every keystroke not handled directly by Idea is sent here for
|
||||||
* processing.
|
* processing.
|
||||||
@@ -221,7 +214,7 @@ public class KeyHandler {
|
|||||||
try {
|
try {
|
||||||
if (!allowKeyMappings || !handleKeyMapping(editor, key, context)) {
|
if (!allowKeyMappings || !handleKeyMapping(editor, key, context)) {
|
||||||
if (isCommandCountKey(chKey, editorState)) {
|
if (isCommandCountKey(chKey, editorState)) {
|
||||||
commandBuilder.addCountCharacter(chKey);
|
commandBuilder.addCountCharacter(key);
|
||||||
} else if (isDeleteCommandCountKey(key, editorState)) {
|
} else if (isDeleteCommandCountKey(key, editorState)) {
|
||||||
commandBuilder.deleteCountCharacter();
|
commandBuilder.deleteCountCharacter();
|
||||||
} else if (isEditorReset(key, editorState)) {
|
} else if (isEditorReset(key, editorState)) {
|
||||||
@@ -232,21 +225,29 @@ public class KeyHandler {
|
|||||||
else if (isExpectingCharArgument(commandBuilder)) {
|
else if (isExpectingCharArgument(commandBuilder)) {
|
||||||
handleCharArgument(key, chKey, editorState);
|
handleCharArgument(key, chKey, editorState);
|
||||||
}
|
}
|
||||||
|
else if (editorState.getSubMode() == CommandState.SubMode.REGISTER_PENDING) {
|
||||||
|
commandBuilder.addKey(key);
|
||||||
|
handleSelectRegister(editorState, chKey);
|
||||||
|
}
|
||||||
// If we are this far, then the user must be entering a command or a non-single-character argument
|
// If we are this far, then the user must be entering a command or a non-single-character argument
|
||||||
// to an entered command. Let's figure out which it is.
|
// to an entered command. Let's figure out which it is.
|
||||||
else if (!handleDigraph(editor, key, context, editorState)) {
|
else if (!handleDigraph(editor, key, context, editorState)) {
|
||||||
|
|
||||||
commandBuilder.addKey(key);
|
|
||||||
|
|
||||||
// Ask the key/action tree if this is an appropriate key at this point in the command and if so,
|
// Ask the key/action tree if this is an appropriate key at this point in the command and if so,
|
||||||
// return the node matching this keystroke
|
// return the node matching this keystroke
|
||||||
final Node node = mapOpCommand(key, commandBuilder.getChildNode(key), editorState);
|
final Node node = mapOpCommand(key, commandBuilder.getChildNode(key), editorState);
|
||||||
|
|
||||||
if (node instanceof CommandNode) {
|
if (node instanceof CommandNode) {
|
||||||
handleCommandNode(editor, context, key, (CommandNode) node, editorState);
|
handleCommandNode(editor, context, key, (CommandNode) node, editorState);
|
||||||
|
commandBuilder.addKey(key);
|
||||||
} else if (node instanceof CommandPartNode) {
|
} else if (node instanceof CommandPartNode) {
|
||||||
commandBuilder.setCurrentCommandPartNode((CommandPartNode) node);
|
commandBuilder.setCurrentCommandPartNode((CommandPartNode) node);
|
||||||
} else {
|
commandBuilder.addKey(key);
|
||||||
|
} else if (isSelectRegister(key, editorState)) {
|
||||||
|
editorState.pushModes(CommandState.Mode.COMMAND, CommandState.SubMode.REGISTER_PENDING);
|
||||||
|
commandBuilder.addKey(key);
|
||||||
|
}
|
||||||
|
else { // node == null
|
||||||
|
|
||||||
// If we are in insert/replace mode send this key in for processing
|
// If we are in insert/replace mode send this key in for processing
|
||||||
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
||||||
shouldRecord &= VimPlugin.getChange().processKey(editor, context, key);
|
shouldRecord &= VimPlugin.getChange().processKey(editor, context, key);
|
||||||
@@ -271,10 +272,11 @@ public class KeyHandler {
|
|||||||
|
|
||||||
// Do we have a fully entered command at this point? If so, let's execute it.
|
// Do we have a fully entered command at this point? If so, let's execute it.
|
||||||
if (commandBuilder.isReady()) {
|
if (commandBuilder.isReady()) {
|
||||||
executeCommand(editor, key, context, editorState);
|
executeCommand(editor, context, editorState);
|
||||||
}
|
}
|
||||||
else if (commandBuilder.isBad()) {
|
else if (commandBuilder.isBad()) {
|
||||||
editorState.resetOpPending();
|
editorState.resetOpPending();
|
||||||
|
editorState.resetRegisterPending();
|
||||||
VimPlugin.indicateError();
|
VimPlugin.indicateError();
|
||||||
reset(editor);
|
reset(editor);
|
||||||
}
|
}
|
||||||
@@ -283,6 +285,9 @@ public class KeyHandler {
|
|||||||
if (shouldRecord && editorState.isRecording()) {
|
if (shouldRecord && editorState.isRecording()) {
|
||||||
VimPlugin.getRegister().recordKeyStroke(key);
|
VimPlugin.getRegister().recordKeyStroke(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This will update immediately, if we're on the EDT (which we are)
|
||||||
|
ShowCmd.INSTANCE.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,7 +563,9 @@ public class KeyHandler {
|
|||||||
private boolean isCommandCountKey(char chKey, @NotNull CommandState editorState) {
|
private boolean isCommandCountKey(char chKey, @NotNull CommandState editorState) {
|
||||||
// Make sure to avoid handling '0' as the start of a count.
|
// Make sure to avoid handling '0' as the start of a count.
|
||||||
final CommandBuilder commandBuilder = editorState.getCommandBuilder();
|
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');
|
&& commandBuilder.isExpectingCount() && Character.isDigit(chKey) && (commandBuilder.getCount() > 0 || chKey != '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +577,29 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isEditorReset(@NotNull KeyStroke key, @NotNull CommandState editorState) {
|
private boolean isEditorReset(@NotNull KeyStroke key, @NotNull CommandState editorState) {
|
||||||
return (editorState.getMode() == CommandState.Mode.COMMAND) && StringHelper.isCloseKeyStroke(key);
|
return editorState.getMode() == CommandState.Mode.COMMAND && StringHelper.isCloseKeyStroke(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSelectRegister(@NotNull KeyStroke key, @NotNull CommandState editorState) {
|
||||||
|
if (editorState.getMode() != CommandState.Mode.COMMAND && editorState.getMode() != CommandState.Mode.VISUAL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editorState.getSubMode() == CommandState.SubMode.REGISTER_PENDING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key.getKeyChar() == '"' && !editorState.isOperatorPending() && editorState.getCommandBuilder().getExpectedArgumentType() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleSelectRegister(@NotNull CommandState commandState, char chKey) {
|
||||||
|
commandState.resetRegisterPending();
|
||||||
|
if (VimPlugin.getRegister().isValid(chKey)) {
|
||||||
|
commandState.getCommandBuilder().pushCommandPart(chKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
commandState.getCommandBuilder().setCommandState(CurrentCommandState.BAD_COMMAND);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isExpectingCharArgument(@NotNull CommandBuilder commandBuilder) {
|
private boolean isExpectingCharArgument(@NotNull CommandBuilder commandBuilder) {
|
||||||
@@ -615,10 +644,12 @@ public class KeyHandler {
|
|||||||
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
||||||
if (DigraphSequence.isDigraphStart(key)) {
|
if (DigraphSequence.isDigraphStart(key)) {
|
||||||
editorState.startDigraphSequence();
|
editorState.startDigraphSequence();
|
||||||
|
editorState.getCommandBuilder().addKey(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (DigraphSequence.isLiteralStart(key)) {
|
if (DigraphSequence.isLiteralStart(key)) {
|
||||||
editorState.startLiteralSequence();
|
editorState.startLiteralSequence();
|
||||||
|
editorState.getCommandBuilder().addKey(key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,7 +657,7 @@ public class KeyHandler {
|
|||||||
DigraphResult res = editorState.processDigraphKey(key, editor);
|
DigraphResult res = editorState.processDigraphKey(key, editor);
|
||||||
switch (res.getResult()) {
|
switch (res.getResult()) {
|
||||||
case DigraphResult.RES_HANDLED:
|
case DigraphResult.RES_HANDLED:
|
||||||
case DigraphResult.RES_BAD:
|
editorState.getCommandBuilder().addKey(key);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case DigraphResult.RES_DONE:
|
case DigraphResult.RES_DONE:
|
||||||
@@ -637,10 +668,20 @@ public class KeyHandler {
|
|||||||
if (stroke == null) {
|
if (stroke == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
editorState.getCommandBuilder().addKey(key);
|
||||||
handleKey(editor, stroke, context);
|
handleKey(editor, stroke, context);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case DigraphResult.RES_BAD:
|
||||||
|
// BAD is an error. We were expecting a valid character, and we didn't get it.
|
||||||
|
if (commandBuilder.getExpectedArgumentType() != null) {
|
||||||
|
commandBuilder.setCommandState(CurrentCommandState.BAD_COMMAND);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
case DigraphResult.RES_UNHANDLED:
|
case DigraphResult.RES_UNHANDLED:
|
||||||
|
// UNHANDLED means the key stroke made no sense in the context of a digraph, but isn't an error in the current
|
||||||
|
// state. E.g. waiting for {char} <BS> {char}. Let the key handler have a go at it.
|
||||||
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
if (commandBuilder.getExpectedArgumentType() == Argument.Type.DIGRAPH) {
|
||||||
commandBuilder.fallbackToCharacterArgument();
|
commandBuilder.fallbackToCharacterArgument();
|
||||||
handleKey(editor, key, context);
|
handleKey(editor, key, context);
|
||||||
@@ -653,7 +694,6 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void executeCommand(@NotNull Editor editor,
|
private void executeCommand(@NotNull Editor editor,
|
||||||
@NotNull KeyStroke key,
|
|
||||||
@NotNull DataContext context,
|
@NotNull DataContext context,
|
||||||
@NotNull CommandState editorState) {
|
@NotNull CommandState editorState) {
|
||||||
final Command command = editorState.getCommandBuilder().buildCommand();
|
final Command command = editorState.getCommandBuilder().buildCommand();
|
||||||
@@ -676,7 +716,7 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||||
Runnable action = new ActionRunner(editor, context, command, key);
|
Runnable action = new ActionRunner(editor, context, command);
|
||||||
EditorActionHandlerBase cmdAction = command.getAction();
|
EditorActionHandlerBase cmdAction = command.getAction();
|
||||||
String name = cmdAction.getId();
|
String name = cmdAction.getId();
|
||||||
|
|
||||||
@@ -714,7 +754,7 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final Argument.Type argumentType = action.getArgumentType();
|
final Argument.Type argumentType = action.getArgumentType();
|
||||||
startWaitingForArgument(editor, context, key.getKeyChar(), argumentType, editorState);
|
startWaitingForArgument(editor, context, key.getKeyChar(), action, argumentType, editorState);
|
||||||
partialReset(editor);
|
partialReset(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -751,6 +791,7 @@ public class KeyHandler {
|
|||||||
private void startWaitingForArgument(Editor editor,
|
private void startWaitingForArgument(Editor editor,
|
||||||
DataContext context,
|
DataContext context,
|
||||||
char key,
|
char key,
|
||||||
|
@NotNull EditorActionHandlerBase action,
|
||||||
@NotNull Argument.Type argument,
|
@NotNull Argument.Type argument,
|
||||||
CommandState editorState) {
|
CommandState editorState) {
|
||||||
final CommandBuilder commandBuilder = editorState.getCommandBuilder();
|
final CommandBuilder commandBuilder = editorState.getCommandBuilder();
|
||||||
@@ -761,6 +802,17 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
editorState.pushModes(editorState.getMode(), CommandState.SubMode.OP_PENDING);
|
editorState.pushModes(editorState.getMode(), CommandState.SubMode.OP_PENDING);
|
||||||
break;
|
break;
|
||||||
|
case DIGRAPH:
|
||||||
|
// Command actions represent the completion of a command. Showcmd relies on this - if the action represents a
|
||||||
|
// part of a command, the showcmd output is reset part way through. This means we need to special case entering
|
||||||
|
// digraph/literal input mode. We have an action that takes a digraph as an argument, and pushes it back through
|
||||||
|
// the key handler when it's complete.
|
||||||
|
if (action instanceof InsertCompletedDigraphAction) {
|
||||||
|
editorState.startDigraphSequence();
|
||||||
|
} else if (action instanceof InsertCompletedLiteralAction) {
|
||||||
|
editorState.startLiteralSequence();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case EX_STRING:
|
case EX_STRING:
|
||||||
// The current Command expects an EX_STRING argument. E.g. SearchEntry(Fwd|Rev)Action. This won't execute until
|
// The current Command expects an EX_STRING argument. E.g. SearchEntry(Fwd|Rev)Action. This won't execute until
|
||||||
// state hits READY. Start the ex input field, push CMD_LINE mode and wait for the argument.
|
// state hits READY. Start the ex input field, push CMD_LINE mode and wait for the argument.
|
||||||
@@ -884,11 +936,10 @@ public class KeyHandler {
|
|||||||
*/
|
*/
|
||||||
static class ActionRunner implements Runnable {
|
static class ActionRunner implements Runnable {
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
ActionRunner(Editor editor, DataContext context, Command cmd, KeyStroke key) {
|
ActionRunner(Editor editor, DataContext context, Command cmd) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.cmd = cmd;
|
this.cmd = cmd;
|
||||||
this.key = key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -897,6 +948,11 @@ public class KeyHandler {
|
|||||||
|
|
||||||
editorState.getCommandBuilder().setCommandState(CurrentCommandState.NEW_COMMAND);
|
editorState.getCommandBuilder().setCommandState(CurrentCommandState.NEW_COMMAND);
|
||||||
|
|
||||||
|
final Character register = cmd.getRegister();
|
||||||
|
if (register != null) {
|
||||||
|
VimPlugin.getRegister().selectRegister(register);
|
||||||
|
}
|
||||||
|
|
||||||
executeVimAction(editor, cmd.getAction(), context);
|
executeVimAction(editor, cmd.getAction(), context);
|
||||||
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
if (editorState.getMode() == CommandState.Mode.INSERT || editorState.getMode() == CommandState.Mode.REPLACE) {
|
||||||
VimPlugin.getChange().processCommand(editor, cmd);
|
VimPlugin.getChange().processCommand(editor, cmd);
|
||||||
@@ -905,10 +961,8 @@ public class KeyHandler {
|
|||||||
// Now the command has been executed let's clean up a few things.
|
// Now the command has been executed let's clean up a few things.
|
||||||
|
|
||||||
// By default, the "empty" register is used by all commands, so we want to reset whatever the last register
|
// By default, the "empty" register is used by all commands, so we want to reset whatever the last register
|
||||||
// selected by the user was to the empty register - unless we just executed the "select register" command.
|
// selected by the user was to the empty register
|
||||||
if (cmd.getType() != Command.Type.SELECT_REGISTER) {
|
|
||||||
VimPlugin.getRegister().resetRegister();
|
VimPlugin.getRegister().resetRegister();
|
||||||
}
|
|
||||||
|
|
||||||
// If, at this point, we are not in insert, replace, or visual modes, we need to restore the previous
|
// If, at this point, we are not in insert, replace, or visual modes, we need to restore the previous
|
||||||
// mode we were in. This handles commands in those modes that temporarily allow us to execute normal
|
// mode we were in. This handles commands in those modes that temporarily allow us to execute normal
|
||||||
@@ -925,7 +979,6 @@ public class KeyHandler {
|
|||||||
private final Editor editor;
|
private final Editor editor;
|
||||||
private final DataContext context;
|
private final DataContext context;
|
||||||
private final Command cmd;
|
private final Command cmd;
|
||||||
private final KeyStroke key;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TypedActionHandler origHandler;
|
private TypedActionHandler origHandler;
|
||||||
|
@@ -18,17 +18,14 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim
|
package com.maddyhome.idea.vim
|
||||||
|
|
||||||
import com.intellij.openapi.project.DumbAware
|
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.startup.StartupActivity
|
import com.intellij.openapi.startup.StartupActivity
|
||||||
import com.maddyhome.idea.vim.listener.VimListenerManager
|
import com.maddyhome.idea.vim.listener.VimListenerManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
*
|
|
||||||
* [VERSION UPDATE] 193+ Use StartupActivity.DumbAware
|
|
||||||
*/
|
*/
|
||||||
class PluginStartup : StartupActivity, DumbAware {
|
class PluginStartup : StartupActivity.DumbAware {
|
||||||
|
|
||||||
private var firstInitializationOccurred = false
|
private var firstInitializationOccurred = false
|
||||||
|
|
||||||
|
@@ -34,33 +34,6 @@ public class RegisterActions {
|
|||||||
|
|
||||||
public static final ExtensionPointName<ActionBeanClass> VIM_ACTIONS_EP =
|
public static final ExtensionPointName<ActionBeanClass> VIM_ACTIONS_EP =
|
||||||
ExtensionPointName.create("IdeaVIM.vimAction");
|
ExtensionPointName.create("IdeaVIM.vimAction");
|
||||||
private static boolean initialRegistration = false;
|
|
||||||
|
|
||||||
static {
|
|
||||||
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
|
|
||||||
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
|
|
||||||
// TODO: [VERSION UPDATE] since 191 use
|
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
|
||||||
// TODO: [VERSION UPDATE] since 201 use
|
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
|
||||||
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ActionBeanClass>() {
|
|
||||||
@Override
|
|
||||||
public void extensionAdded(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
|
||||||
// version update (or earlier).
|
|
||||||
if (!initialRegistration) return;
|
|
||||||
unregisterActions();
|
|
||||||
registerActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void extensionRemoved(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
if (!initialRegistration) return;
|
|
||||||
unregisterActions();
|
|
||||||
registerActions();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register all the key/action mappings for the plugin.
|
* Register all the key/action mappings for the plugin.
|
||||||
@@ -68,7 +41,16 @@ public class RegisterActions {
|
|||||||
public static void registerActions() {
|
public static void registerActions() {
|
||||||
registerVimCommandActions();
|
registerVimCommandActions();
|
||||||
registerEmptyShortcuts();
|
registerEmptyShortcuts();
|
||||||
initialRegistration = true;
|
registerEpListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerEpListener() {
|
||||||
|
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
|
||||||
|
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
|
||||||
|
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(() -> {
|
||||||
|
unregisterActions();
|
||||||
|
registerActions();
|
||||||
|
}, false, VimPlugin.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @Nullable EditorActionHandlerBase findAction(@NotNull String id) {
|
public static @Nullable EditorActionHandlerBase findAction(@NotNull String id) {
|
||||||
|
@@ -33,7 +33,6 @@ import org.jdom.Element
|
|||||||
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
|
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
|
||||||
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true)
|
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true)
|
||||||
])
|
])
|
||||||
// TODO: 27.01.2020 [VERSION UPDATE] 2019.3 https://www.jetbrains.org/intellij/sdk/docs/basics/plugin_structure/plugin_services.html#light-services
|
|
||||||
@Deprecated("The data from this class will be stored in vim_settings")
|
@Deprecated("The data from this class will be stored in vim_settings")
|
||||||
class VimLocalConfig : PersistentStateComponent<Element> {
|
class VimLocalConfig : PersistentStateComponent<Element> {
|
||||||
override fun getState(): Element? = null
|
override fun getState(): Element? = null
|
||||||
@@ -50,6 +49,7 @@ class VimLocalConfig : PersistentStateComponent<Element> {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
ServiceManager.getService(VimLocalConfig::class.java)
|
ServiceManager.getService(VimLocalConfig::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,14 +19,11 @@ package com.maddyhome.idea.vim;
|
|||||||
|
|
||||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||||
import com.intellij.ide.plugins.PluginManager;
|
import com.intellij.ide.plugins.PluginManager;
|
||||||
import com.intellij.ide.util.PropertiesComponent;
|
|
||||||
import com.intellij.notification.Notification;
|
import com.intellij.notification.Notification;
|
||||||
import com.intellij.notification.NotificationListener;
|
import com.intellij.notification.NotificationListener;
|
||||||
import com.intellij.openapi.Disposable;
|
import com.intellij.openapi.Disposable;
|
||||||
import com.intellij.openapi.application.Application;
|
import com.intellij.openapi.application.Application;
|
||||||
import com.intellij.openapi.application.ApplicationInfo;
|
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
import com.intellij.openapi.application.PermanentInstallationID;
|
|
||||||
import com.intellij.openapi.components.PersistentStateComponent;
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
import com.intellij.openapi.components.ServiceManager;
|
import com.intellij.openapi.components.ServiceManager;
|
||||||
import com.intellij.openapi.components.State;
|
import com.intellij.openapi.components.State;
|
||||||
@@ -40,12 +37,9 @@ import com.intellij.openapi.options.ShowSettingsUtil;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.project.ProjectManager;
|
import com.intellij.openapi.project.ProjectManager;
|
||||||
import com.intellij.openapi.ui.Messages;
|
import com.intellij.openapi.ui.Messages;
|
||||||
import com.intellij.openapi.util.JDOMUtil;
|
|
||||||
import com.intellij.openapi.util.SystemInfo;
|
import com.intellij.openapi.util.SystemInfo;
|
||||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
|
||||||
import com.intellij.openapi.wm.StatusBar;
|
import com.intellij.openapi.wm.StatusBar;
|
||||||
import com.intellij.openapi.wm.WindowManager;
|
import com.intellij.openapi.wm.WindowManager;
|
||||||
import com.intellij.util.io.HttpRequests;
|
|
||||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
|
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
|
||||||
@@ -57,18 +51,17 @@ import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
|||||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||||
import com.maddyhome.idea.vim.ui.ExEntryPanel;
|
import com.maddyhome.idea.vim.ui.ExEntryPanel;
|
||||||
|
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
|
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
|
||||||
|
import com.maddyhome.idea.vim.ui.VimRcFileState;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jdom.JDOMException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.util.List;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||||
@@ -84,7 +77,6 @@ import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
|||||||
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
|
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
|
||||||
public class VimPlugin implements PersistentStateComponent<Element>, Disposable {
|
public class VimPlugin implements PersistentStateComponent<Element>, Disposable {
|
||||||
private static final String IDEAVIM_PLUGIN_ID = "IdeaVIM";
|
private static final String IDEAVIM_PLUGIN_ID = "IdeaVIM";
|
||||||
private static final String IDEAVIM_STATISTICS_TIMESTAMP_KEY = "ideavim.statistics.timestamp";
|
|
||||||
private static final int STATE_VERSION = 6;
|
private static final int STATE_VERSION = 6;
|
||||||
|
|
||||||
private static long lastBeepTimeMillis;
|
private static long lastBeepTimeMillis;
|
||||||
@@ -114,7 +106,8 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
Application application = ApplicationManager.getApplication();
|
Application application = ApplicationManager.getApplication();
|
||||||
if (application.isUnitTestMode()) {
|
if (application.isUnitTestMode()) {
|
||||||
application.invokeAndWait(this::turnOnPlugin);
|
application.invokeAndWait(this::turnOnPlugin);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
application.invokeLater(this::turnOnPlugin);
|
application.invokeLater(this::turnOnPlugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,53 +143,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
return ServiceManager.getService(MotionGroup.class);
|
return ServiceManager.getService(MotionGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reports statistics about installed IdeaVim and enabled Vim emulation.
|
|
||||||
* <p>
|
|
||||||
* See https://github.com/go-lang-plugin-org/go-lang-idea-plugin/commit/5182ab4a1d01ad37f6786268a2fe5e908575a217
|
|
||||||
*/
|
|
||||||
public static void statisticReport() {
|
|
||||||
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
|
|
||||||
final long lastUpdate = propertiesComponent.getOrInitLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0);
|
|
||||||
final boolean outOfDate = lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
|
|
||||||
if (outOfDate && isEnabled()) {
|
|
||||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
|
||||||
try {
|
|
||||||
final String buildNumber = ApplicationInfo.getInstance().getBuild().asString();
|
|
||||||
final String version = URLEncoder.encode(getVersion(), CharsetToolkit.UTF8);
|
|
||||||
final String os = URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
|
|
||||||
final String uid = PermanentInstallationID.get();
|
|
||||||
final String url = "https://plugins.jetbrains.com/plugins/list" +
|
|
||||||
"?pluginId=" +
|
|
||||||
IDEAVIM_PLUGIN_ID +
|
|
||||||
"&build=" +
|
|
||||||
buildNumber +
|
|
||||||
"&pluginVersion=" +
|
|
||||||
version +
|
|
||||||
"&os=" +
|
|
||||||
os +
|
|
||||||
"&uuid=" +
|
|
||||||
uid;
|
|
||||||
PropertiesComponent.getInstance()
|
|
||||||
.setValue(IDEAVIM_STATISTICS_TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
|
|
||||||
HttpRequests.request(url).connect(request -> {
|
|
||||||
LOG.info("Sending statistics: " + url);
|
|
||||||
try {
|
|
||||||
JDOMUtil.load(request.getInputStream());
|
|
||||||
}
|
|
||||||
catch (JDOMException e) {
|
|
||||||
LOG.warn(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
LOG.warn(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull ChangeGroup getChange() {
|
public static @NotNull ChangeGroup getChange() {
|
||||||
return ServiceManager.getService(ChangeGroup.class);
|
return ServiceManager.getService(ChangeGroup.class);
|
||||||
}
|
}
|
||||||
@@ -288,10 +234,15 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
ideavimrcRegistered = true;
|
ideavimrcRegistered = true;
|
||||||
|
|
||||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||||
|
executeIdeaVimRc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeIdeaVimRc() {
|
||||||
final File ideaVimRc = VimScriptParser.findIdeaVimRc();
|
final File ideaVimRc = VimScriptParser.findIdeaVimRc();
|
||||||
if (ideaVimRc != null) {
|
if (ideaVimRc != null) {
|
||||||
VimScriptParser.executeFile(ideaVimRc);
|
List<String> parsedLines = VimScriptParser.executeFile(ideaVimRc);
|
||||||
}
|
VimRcFileState.INSTANCE.saveFileState(ideaVimRc.getAbsolutePath(), parsedLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,8 +250,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [VERSION UPDATE] 193+ remove suppress
|
|
||||||
@SuppressWarnings({"MissingRecentApi", "UnstableApiUsage"})
|
|
||||||
public static @NotNull String getVersion() {
|
public static @NotNull String getVersion() {
|
||||||
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
|
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
|
||||||
if (!ApplicationManager.getApplication().isInternal()) {
|
if (!ApplicationManager.getApplication().isInternal()) {
|
||||||
@@ -328,7 +277,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
getInstance().turnOnPlugin();
|
getInstance().turnOnPlugin();
|
||||||
}
|
}
|
||||||
|
|
||||||
VimStatusBar.INSTANCE.update();
|
StatusBarIconFactory.Companion.updateIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isError() {
|
public static boolean isError() {
|
||||||
@@ -392,10 +341,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
private void turnOnPlugin() {
|
private void turnOnPlugin() {
|
||||||
ApplicationManager.getApplication().invokeLater(this::updateState);
|
ApplicationManager.getApplication().invokeLater(this::updateState);
|
||||||
|
|
||||||
getEditor().turnOn();
|
|
||||||
getSearch().turnOn();
|
|
||||||
VimListenerManager.INSTANCE.turnOn();
|
|
||||||
|
|
||||||
// Register vim actions in command mode
|
// Register vim actions in command mode
|
||||||
RegisterActions.registerActions();
|
RegisterActions.registerActions();
|
||||||
|
|
||||||
@@ -407,27 +352,27 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
|||||||
|
|
||||||
// Execute ~/.ideavimrc
|
// Execute ~/.ideavimrc
|
||||||
registerIdeavimrc();
|
registerIdeavimrc();
|
||||||
|
|
||||||
|
// Turing on should be performed after all commands registration
|
||||||
|
getSearch().turnOn();
|
||||||
|
VimListenerManager.INSTANCE.turnOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void turnOffPlugin() {
|
private void turnOffPlugin() {
|
||||||
KeyHandler.getInstance().fullReset(null);
|
KeyHandler.getInstance().fullReset(null);
|
||||||
|
|
||||||
// Unregister vim actions in command mode
|
|
||||||
RegisterActions.unregisterActions();
|
|
||||||
|
|
||||||
// Unregister ex handlers
|
|
||||||
CommandParser.getInstance().unregisterHandlers();
|
|
||||||
|
|
||||||
EditorGroup editorGroup = getEditorIfCreated();
|
|
||||||
if (editorGroup != null) {
|
|
||||||
editorGroup.turnOff();
|
|
||||||
}
|
|
||||||
SearchGroup searchGroup = getSearchIfCreated();
|
SearchGroup searchGroup = getSearchIfCreated();
|
||||||
if (searchGroup != null) {
|
if (searchGroup != null) {
|
||||||
searchGroup.turnOff();
|
searchGroup.turnOff();
|
||||||
}
|
}
|
||||||
VimListenerManager.INSTANCE.turnOff();
|
VimListenerManager.INSTANCE.turnOff();
|
||||||
ExEntryPanel.fullReset();
|
ExEntryPanel.fullReset();
|
||||||
|
|
||||||
|
// Unregister vim actions in command mode
|
||||||
|
RegisterActions.unregisterActions();
|
||||||
|
|
||||||
|
// Unregister ex handlers
|
||||||
|
CommandParser.getInstance().unregisterHandlers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stateUpdated = false;
|
private boolean stateUpdated = false;
|
||||||
|
39
src/com/maddyhome/idea/vim/VimProjectService.kt
Normal file
39
src/com/maddyhome/idea/vim/VimProjectService.kt
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.intellij.openapi.Disposable
|
||||||
|
import com.intellij.openapi.components.Service
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
|
||||||
|
@Service
|
||||||
|
class VimProjectService(val project: Project) : Disposable {
|
||||||
|
override fun dispose() {}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun getInstance(project: Project): VimProjectService {
|
||||||
|
return ServiceManager.getService(project, VimProjectService::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val Project.vimDisposable
|
||||||
|
get() = VimProjectService.getInstance(this)
|
@@ -1,61 +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 com.maddyhome.idea.vim;
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext;
|
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
|
||||||
import com.intellij.openapi.editor.Editor;
|
|
||||||
import com.intellij.openapi.editor.actionSystem.ActionPlan;
|
|
||||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
|
||||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx;
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts all regular keystrokes and passes them on to the Vim key handler.
|
|
||||||
*
|
|
||||||
* IDE shortcut keys used by Vim commands are handled by {@link com.maddyhome.idea.vim.action.VimShortcutKeyAction}.
|
|
||||||
*/
|
|
||||||
public class VimTypedActionHandler implements TypedActionHandlerEx {
|
|
||||||
private static final Logger logger = Logger.getInstance(VimTypedActionHandler.class.getName());
|
|
||||||
|
|
||||||
private final @NotNull KeyHandler handler;
|
|
||||||
|
|
||||||
public VimTypedActionHandler(TypedActionHandler origHandler) {
|
|
||||||
handler = KeyHandler.getInstance();
|
|
||||||
handler.setOriginalHandler(origHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeExecute(@NotNull Editor editor, char charTyped, @NotNull DataContext context, @NotNull ActionPlan plan) {
|
|
||||||
handler.beforeHandleKey(editor, KeyStroke.getKeyStroke(charTyped), context, plan);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(final @NotNull Editor editor, final char charTyped, final @NotNull DataContext context) {
|
|
||||||
try {
|
|
||||||
handler.handleKey(editor, KeyStroke.getKeyStroke(charTyped), new EditorDataContext(editor));
|
|
||||||
}
|
|
||||||
catch (Throwable e) {
|
|
||||||
logger.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
74
src/com/maddyhome/idea/vim/VimTypedActionHandler.kt
Normal file
74
src/com/maddyhome/idea/vim/VimTypedActionHandler.kt
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.diagnostic.logger
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.actionSystem.ActionPlan
|
||||||
|
import com.intellij.openapi.editor.actionSystem.TypedActionHandler
|
||||||
|
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||||
|
import java.awt.event.KeyAdapter
|
||||||
|
import java.awt.event.KeyEvent
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts all regular keystrokes and passes them on to the Vim key handler.
|
||||||
|
*
|
||||||
|
* IDE shortcut keys used by Vim commands are handled by [com.maddyhome.idea.vim.action.VimShortcutKeyAction].
|
||||||
|
*/
|
||||||
|
class VimTypedActionHandler(origHandler: TypedActionHandler?) : TypedActionHandlerEx {
|
||||||
|
private val handler = KeyHandler.getInstance()
|
||||||
|
|
||||||
|
init {
|
||||||
|
handler.originalHandler = origHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeExecute(editor: Editor, charTyped: Char, context: DataContext, plan: ActionPlan) {
|
||||||
|
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||||
|
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||||
|
handler.beforeHandleKey(editor, keyStroke, context, plan)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, charTyped: Char, context: DataContext) {
|
||||||
|
try {
|
||||||
|
val modifiers = if (charTyped == ' ' && VimKeyListener.isSpaceShift) KeyEvent.SHIFT_DOWN_MASK else 0
|
||||||
|
val keyStroke = KeyStroke.getKeyStroke(charTyped, modifiers)
|
||||||
|
handler.handleKey(editor, keyStroke, EditorDataContext(editor))
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
logger.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val logger = logger<VimTypedActionHandler>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A nasty workaround to handle `<S-Space>` events. Probably all the key events should go trough this listener.
|
||||||
|
*/
|
||||||
|
object VimKeyListener : KeyAdapter() {
|
||||||
|
|
||||||
|
var isSpaceShift = false
|
||||||
|
|
||||||
|
override fun keyPressed(e: KeyEvent) {
|
||||||
|
isSpaceShift = e.modifiersEx and KeyEvent.SHIFT_DOWN_MASK != 0 && e.keyChar == ' '
|
||||||
|
}
|
||||||
|
}
|
@@ -20,7 +20,7 @@ package com.maddyhome.idea.vim.action
|
|||||||
|
|
||||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||||
import com.intellij.openapi.project.DumbAwareToggleAction
|
import com.intellij.openapi.project.DumbAwareToggleAction
|
||||||
import com.maddyhome.idea.vim.VimActions
|
import com.maddyhome.idea.vim.ui.VimActions
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -86,27 +86,26 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
val SMART_STEP_INPLACE_DATA = Key.findKeyByName("SMART_STEP_INPLACE_DATA")
|
val SMART_STEP_INPLACE_DATA = Key.findKeyByName("SMART_STEP_INPLACE_DATA")
|
||||||
if (SMART_STEP_INPLACE_DATA != null && editor.getUserData(SMART_STEP_INPLACE_DATA) != null) return false
|
if (SMART_STEP_INPLACE_DATA != null && editor.getUserData(SMART_STEP_INPLACE_DATA) != null) return false
|
||||||
|
|
||||||
if (aceJumpActive()) return false
|
if (aceJumpActive()) return false
|
||||||
|
|
||||||
val keyCode = keyStroke.keyCode
|
val keyCode = keyStroke.keyCode
|
||||||
if (LookupManager.getActiveLookup(editor) != null) {
|
|
||||||
return LookupKeys.isEnabledForLookup(keyStroke)
|
if (LookupManager.getActiveLookup(editor) != null && !LookupKeys.isEnabledForLookup(keyStroke)) return false
|
||||||
}
|
|
||||||
if (keyCode == KeyEvent.VK_ESCAPE) {
|
if (keyCode == KeyEvent.VK_ESCAPE) return isEnabledForEscape(editor)
|
||||||
return isEnabledForEscape(editor)
|
|
||||||
}
|
|
||||||
if (editor.inInsertMode) { // XXX: <Tab> won't be recorded in macros
|
if (editor.inInsertMode) { // XXX: <Tab> won't be recorded in macros
|
||||||
if (keyCode == KeyEvent.VK_TAB) {
|
if (keyCode == KeyEvent.VK_TAB) {
|
||||||
VimPlugin.getChange().tabAction = true
|
VimPlugin.getChange().tabAction = true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Debug watch, Python console, etc.
|
// Debug watch, Python console, etc.
|
||||||
if (NON_FILE_EDITOR_KEYS.contains(keyStroke) && !EditorHelper.isFileEditor(editor)) {
|
if (keyStroke in NON_FILE_EDITOR_KEYS && !EditorHelper.isFileEditor(editor)) return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (VIM_ONLY_EDITOR_KEYS.contains(keyStroke)) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (keyStroke in VIM_ONLY_EDITOR_KEYS) return true
|
||||||
|
|
||||||
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
|
val savedShortcutConflicts = VimPlugin.getKey().savedShortcutConflicts
|
||||||
return when (savedShortcutConflicts[keyStroke]) {
|
return when (savedShortcutConflicts[keyStroke]) {
|
||||||
ShortcutOwner.VIM -> true
|
ShortcutOwner.VIM -> true
|
||||||
@@ -191,15 +190,15 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
|
|||||||
companion object {
|
companion object {
|
||||||
@JvmField
|
@JvmField
|
||||||
val VIM_ONLY_EDITOR_KEYS: Set<KeyStroke> = ImmutableSet.builder<KeyStroke>().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)).addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0))
|
val VIM_ONLY_EDITOR_KEYS: Set<KeyStroke> = ImmutableSet.builder<KeyStroke>().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)).addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_END, 0, InputEvent.CTRL_MASK, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_END, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_UP, 0, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK))
|
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_UP, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))
|
||||||
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_DOWN, 0, InputEvent.SHIFT_MASK, InputEvent.CTRL_MASK or InputEvent.SHIFT_MASK)).build()
|
.addAll(getKeyStrokes(KeyEvent.VK_PAGE_DOWN, 0, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)).build()
|
||||||
|
|
||||||
private const val ACTION_ID = "VimShortcutKeyAction"
|
private const val ACTION_ID = "VimShortcutKeyAction"
|
||||||
|
|
||||||
|
@@ -45,6 +45,7 @@ class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
if (!editor.commandState.isDotRepeatInProgress) {
|
if (!editor.commandState.isDotRepeatInProgress) {
|
||||||
VimRepeater.Extension.argumentCaptured = argument
|
VimRepeater.Extension.argumentCaptured = argument
|
||||||
}
|
}
|
||||||
|
val saveRepeatHandler = VimRepeater.repeatHandler
|
||||||
val motion = argument.motion
|
val motion = argument.motion
|
||||||
val range = MotionGroup
|
val range = MotionGroup
|
||||||
.getMotionRange(editor, editor.caretModel.primaryCaret, context, cmd.count, cmd.rawCount, argument)
|
.getMotionRange(editor, editor.caretModel.primaryCaret, context, cmd.count, cmd.rawCount, argument)
|
||||||
@@ -52,7 +53,9 @@ class OperatorAction : VimActionHandler.SingleExecution() {
|
|||||||
VimPlugin.getMark().setChangeMarks(editor, range)
|
VimPlugin.getMark().setChangeMarks(editor, range)
|
||||||
val selectionType = SelectionType.fromCommandFlags(motion.flags)
|
val selectionType = SelectionType.fromCommandFlags(motion.flags)
|
||||||
KeyHandler.getInstance().reset(editor)
|
KeyHandler.getInstance().reset(editor)
|
||||||
return operatorFunction.apply(editor, context, selectionType)
|
val result = operatorFunction.apply(editor, context, selectionType)
|
||||||
|
VimRepeater.repeatHandler = saveRepeatHandler
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@@ -64,7 +64,7 @@ class RepeatChangeAction : VimActionHandler.SingleExecution() {
|
|||||||
}
|
}
|
||||||
state.setExecutingCommand(lastCommand)
|
state.setExecutingCommand(lastCommand)
|
||||||
|
|
||||||
KeyHandler.executeVimAction(editor, lastCommand.action, context)
|
KeyHandler.executeVimAction(editor, lastCommand.action!!, context)
|
||||||
|
|
||||||
VimRepeater.saveLastChange(lastCommand)
|
VimRepeater.saveLastChange(lastCommand)
|
||||||
}
|
}
|
||||||
|
@@ -23,14 +23,12 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.command.Command
|
import com.maddyhome.idea.vim.command.Command
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
import com.maddyhome.idea.vim.command.CommandState.SubMode
|
|
||||||
import com.maddyhome.idea.vim.command.SelectionType
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
import com.maddyhome.idea.vim.helper.enumSetOf
|
||||||
import com.maddyhome.idea.vim.helper.subMode
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,15 +44,18 @@ class DeleteVisualLinesAction : VisualOperatorActionHandler.ForEachCaret() {
|
|||||||
context: DataContext,
|
context: DataContext,
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
range: VimSelection): Boolean {
|
range: VimSelection): Boolean {
|
||||||
val mode = editor.subMode
|
|
||||||
val textRange = range.toVimTextRange(false)
|
val textRange = range.toVimTextRange(false)
|
||||||
return if (mode == SubMode.VISUAL_BLOCK) {
|
val (usedCaret, usedRange, usedType) = when (range.type) {
|
||||||
VimPlugin.getChange()
|
SelectionType.BLOCK_WISE -> Triple(editor.caretModel.primaryCaret, textRange, range.type)
|
||||||
.deleteRange(editor, editor.caretModel.primaryCaret, textRange, SelectionType.fromSubMode(mode), false)
|
SelectionType.LINE_WISE -> Triple(caret, textRange, SelectionType.LINE_WISE)
|
||||||
} else {
|
SelectionType.CHARACTER_WISE -> {
|
||||||
val lineRange = TextRange(EditorHelper.getLineStartForOffset(editor, textRange.startOffset),
|
val lineRange = TextRange(
|
||||||
EditorHelper.getLineEndForOffset(editor, textRange.endOffset) + 1)
|
EditorHelper.getLineStartForOffset(editor, textRange.startOffset),
|
||||||
VimPlugin.getChange().deleteRange(editor, caret, lineRange, SelectionType.LINE_WISE, false)
|
EditorHelper.getLineEndForOffset(editor, textRange.endOffset) + 1
|
||||||
|
)
|
||||||
|
Triple(caret, lineRange, SelectionType.LINE_WISE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return VimPlugin.getChange().deleteRange(editor, usedCaret, usedRange, usedType, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.maddyhome.idea.vim.action.change.insert
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
|
import com.maddyhome.idea.vim.command.Command
|
||||||
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
|
class InsertCompletedDigraphAction : VimActionHandler.SingleExecution() {
|
||||||
|
override val type: Command.Type = Command.Type.INSERT
|
||||||
|
override val argumentType: Argument.Type? = Argument.Type.DIGRAPH
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||||
|
// The converted digraph character has been captured as an argument, push it back through key handler
|
||||||
|
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character)
|
||||||
|
KeyHandler.getInstance().handleKey(editor, keyStroke, context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.maddyhome.idea.vim.action.change.insert
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.KeyHandler
|
||||||
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
|
import com.maddyhome.idea.vim.command.Command
|
||||||
|
import com.maddyhome.idea.vim.handler.VimActionHandler
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
|
class InsertCompletedLiteralAction : VimActionHandler.SingleExecution() {
|
||||||
|
override val type: Command.Type = Command.Type.INSERT
|
||||||
|
override val argumentType: Argument.Type? = Argument.Type.DIGRAPH
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
||||||
|
// The converted literal character has been captured as an argument, push it back through key handler
|
||||||
|
val keyStroke = KeyStroke.getKeyStroke(cmd.argument!!.character)
|
||||||
|
KeyHandler.getInstance().handleKey(editor, keyStroke, context)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@@ -29,9 +29,9 @@ import javax.swing.KeyStroke
|
|||||||
|
|
||||||
class InsertPreviousInsertExitAction : ChangeEditorActionHandler.SingleExecution(), ComplicatedKeysAction {
|
class InsertPreviousInsertExitAction : ChangeEditorActionHandler.SingleExecution(), ComplicatedKeysAction {
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_MASK or KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK or KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_2, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_AT, KeyEvent.CTRL_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_AT, KeyEvent.CTRL_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
override val type: Command.Type = Command.Type.INSERT
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
package com.maddyhome.idea.vim.action.change.insert
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
|
||||||
|
|
||||||
class StartInsertDigraphAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
|
||||||
|
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
|
||||||
KeyHandler.getInstance().startDigraphSequence(editor)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,16 +0,0 @@
|
|||||||
package com.maddyhome.idea.vim.action.change.insert
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.KeyHandler
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
|
||||||
|
|
||||||
class StartInsertLiteralAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.INSERT
|
|
||||||
|
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
|
||||||
KeyHandler.getInstance().startLiteralSequence(editor)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
@@ -26,17 +26,27 @@ import com.maddyhome.idea.vim.group.copy.PutData
|
|||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
||||||
|
|
||||||
class PutTextBeforeCursorAction : ChangeEditorActionHandler.SingleExecution() {
|
sealed class PutTextBaseAction(
|
||||||
|
private val insertTextBeforeCaret: Boolean,
|
||||||
|
private val indent: Boolean,
|
||||||
|
private val caretAfterInsertedText: Boolean
|
||||||
|
) : ChangeEditorActionHandler.SingleExecution() {
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
override fun execute(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean {
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
val lastRegister = VimPlugin.getRegister().lastRegister
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
||||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
val putData = PutData(textData, null, count, insertTextBeforeCaret, indent, caretAfterInsertedText, -1)
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
return VimPlugin.getPut().putText(editor, context, putData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PutTextAfterCursorAction : PutTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = false)
|
||||||
|
class PutTextAfterCursorActionMoveCursor : PutTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = true)
|
||||||
|
|
||||||
|
class PutTextAfterCursorNoIndentAction : PutTextBaseAction(insertTextBeforeCaret = false, indent = false, caretAfterInsertedText = false)
|
||||||
|
class PutTextBeforeCursorNoIndentAction : PutTextBaseAction(insertTextBeforeCaret = true, indent = false, caretAfterInsertedText = false)
|
||||||
|
|
||||||
|
class PutTextBeforeCursorAction : PutTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = false)
|
||||||
|
class PutTextBeforeCursorActionMoveCursor : PutTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = true)
|
||||||
|
|
@@ -1,42 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|
||||||
|
|
||||||
class PutTextAfterCursorAction : ChangeEditorActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
|
||||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = false, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|
||||||
|
|
||||||
class PutTextAfterCursorActionMoveCursor : ChangeEditorActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
|
||||||
val putData = PutData(textData, null, count, false, _indent = true, caretAfterInsertedText = true, putToLine = -1)
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|
||||||
|
|
||||||
class PutTextAfterCursorNoIndentAction : ChangeEditorActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
|
||||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = false, _indent = false, caretAfterInsertedText = false, putToLine = -1)
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|
||||||
|
|
||||||
class PutTextBeforeCursorActionMoveCursor : ChangeEditorActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
|
||||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = true, putToLine = -1)
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,42 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData.TextData
|
|
||||||
import com.maddyhome.idea.vim.handler.ChangeEditorActionHandler
|
|
||||||
|
|
||||||
class PutTextBeforeCursorNoIndentAction : ChangeEditorActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override fun execute(editor: Editor,
|
|
||||||
context: DataContext,
|
|
||||||
count: Int,
|
|
||||||
rawCount: Int,
|
|
||||||
argument: Argument?): Boolean {
|
|
||||||
val lastRegister = VimPlugin.getRegister().lastRegister
|
|
||||||
val textData = if (lastRegister != null) TextData(lastRegister.text, lastRegister.type, lastRegister.transferableData) else null
|
|
||||||
val putData = PutData(textData, null, count, insertTextBeforeCaret = true, _indent = false, caretAfterInsertedText = false, putToLine = -1)
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -33,23 +33,33 @@ import java.util.*
|
|||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
class PutVisualTextAction : VisualOperatorActionHandler.SingleExecution() {
|
sealed class PutVisualTextBaseAction(
|
||||||
|
private val insertTextBeforeCaret: Boolean,
|
||||||
|
private val indent: Boolean,
|
||||||
|
private val caretAfterInsertedText: Boolean
|
||||||
|
) : VisualOperatorActionHandler.SingleExecution() {
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
||||||
override fun executeForAllCarets(editor: Editor,
|
|
||||||
context: DataContext,
|
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
||||||
cmd: Command,
|
|
||||||
caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
|
||||||
if (caretsAndSelections.isEmpty()) return false
|
if (caretsAndSelections.isEmpty()) return false
|
||||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
||||||
VimPlugin.getRegister().resetRegister()
|
VimPlugin.getRegister().resetRegister()
|
||||||
|
|
||||||
val insertTextBeforeCaret = cmd.keys[0].keyChar == 'P'
|
|
||||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
||||||
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, _indent = true, caretAfterInsertedText = false)
|
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, indent, caretAfterInsertedText)
|
||||||
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
return VimPlugin.getPut().putText(editor, context, putData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PutVisualTextBeforeCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = false)
|
||||||
|
class PutVisualTextAfterCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = false)
|
||||||
|
|
||||||
|
class PutVisualTextBeforeCursorNoIndentAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = false, caretAfterInsertedText = false)
|
||||||
|
class PutVisualTextAfterCursorNoIndentAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = false, caretAfterInsertedText = false)
|
||||||
|
|
||||||
|
class PutVisualTextBeforeCursorMoveCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = true, indent = true, caretAfterInsertedText = true)
|
||||||
|
class PutVisualTextAfterCursorMoveCursorAction: PutVisualTextBaseAction(insertTextBeforeCaret = false, indent = true, caretAfterInsertedText = true)
|
||||||
|
@@ -1,53 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Caret
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
|
||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author vlan
|
|
||||||
*/
|
|
||||||
class PutVisualTextMoveCursorAction : VisualOperatorActionHandler.SingleExecution() {
|
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
|
||||||
|
|
||||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
|
||||||
if (caretsAndSelections.isEmpty()) return false
|
|
||||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
|
||||||
VimPlugin.getRegister().resetRegister()
|
|
||||||
|
|
||||||
val insertTextBeforeCaret = cmd.keys[1].keyChar == 'P'
|
|
||||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
|
||||||
val putData = PutData(textData, selection, cmd.count, insertTextBeforeCaret, _indent = true, caretAfterInsertedText = true)
|
|
||||||
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,53 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Caret
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
|
||||||
import com.maddyhome.idea.vim.group.visual.VimSelection
|
|
||||||
import com.maddyhome.idea.vim.handler.VisualOperatorActionHandler
|
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author vlan
|
|
||||||
*/
|
|
||||||
class PutVisualTextNoIndentAction : VisualOperatorActionHandler.SingleExecution() {
|
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_SELF_SYNCHRONIZED
|
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXIT_VISUAL)
|
|
||||||
|
|
||||||
override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map<Caret, VimSelection>): Boolean {
|
|
||||||
if (caretsAndSelections.isEmpty()) return false
|
|
||||||
val textData = VimPlugin.getRegister().lastRegister?.let { PutData.TextData(it.text, it.type, it.transferableData) }
|
|
||||||
VimPlugin.getRegister().resetRegister()
|
|
||||||
|
|
||||||
val insertBeforeCaret = cmd.keys[1].keyChar == 'P'
|
|
||||||
val selection = PutData.VisualSelection(caretsAndSelections, caretsAndSelections.values.first().type)
|
|
||||||
val putData = PutData(textData, selection, cmd.count, insertBeforeCaret, _indent = false, caretAfterInsertedText = false)
|
|
||||||
|
|
||||||
return VimPlugin.getPut().putText(editor, context, putData)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,41 +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 com.maddyhome.idea.vim.action.copy
|
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Editor
|
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
|
||||||
import com.maddyhome.idea.vim.command.Argument
|
|
||||||
import com.maddyhome.idea.vim.command.Command
|
|
||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
|
||||||
import com.maddyhome.idea.vim.handler.VimActionHandler
|
|
||||||
import com.maddyhome.idea.vim.helper.enumSetOf
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class SelectRegisterAction : VimActionHandler.SingleExecution() {
|
|
||||||
override val type: Command.Type = Command.Type.SELECT_REGISTER
|
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_EXPECT_MORE)
|
|
||||||
|
|
||||||
override val argumentType: Argument.Type = Argument.Type.CHARACTER
|
|
||||||
|
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
|
|
||||||
val argument = cmd.argument
|
|
||||||
return argument != null && VimPlugin.getRegister().selectRegister(argument.character)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -34,7 +34,7 @@ class VimEditorBackSpace : VimActionHandler.SingleExecution(), ComplicatedKeysAc
|
|||||||
private val actionName: String = "EditorBackSpace"
|
private val actionName: String = "EditorBackSpace"
|
||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ class VimEditorTab : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
|
|||||||
private val actionName: String = "EditorTab"
|
private val actionName: String = "EditorTab"
|
||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -28,8 +28,8 @@ import javax.swing.KeyStroke
|
|||||||
|
|
||||||
class FilePreviousAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
|
class FilePreviousAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_6, KeyEvent.CTRL_MASK or KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_6, KeyEvent.CTRL_DOWN_MASK or KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_CIRCUMFLEX, KeyEvent.CTRL_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_CIRCUMFLEX, KeyEvent.CTRL_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||||
|
@@ -44,10 +44,10 @@ class MotionScrollPageDownInsertModeAction : VimActionHandler.SingleExecution(),
|
|||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||||
|
@@ -44,10 +44,10 @@ class MotionScrollPageUpInsertModeAction : VimActionHandler.SingleExecution(), C
|
|||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.SHIFT_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.SHIFT_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.OTHER_READONLY
|
override val type: Command.Type = Command.Type.OTHER_READONLY
|
||||||
|
@@ -50,10 +50,10 @@ class MotionWordLeftInsertAction : MotionActionHandler.ForEachCaret(), Complicat
|
|||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||||
|
@@ -50,10 +50,10 @@ class MotionWordRightInsertAction : MotionActionHandler.ForEachCaret(), Complica
|
|||||||
override val motionType: MotionType = MotionType.EXCLUSIVE
|
override val motionType: MotionType = MotionType.EXCLUSIVE
|
||||||
|
|
||||||
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_MASK)),
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK)),
|
||||||
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.SHIFT_MASK))
|
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.SHIFT_DOWN_MASK))
|
||||||
)
|
)
|
||||||
|
|
||||||
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)
|
||||||
|
@@ -50,6 +50,6 @@ class Argument private constructor(
|
|||||||
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean = true
|
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean = true
|
||||||
|
|
||||||
override val type: Command.Type = Command.Type.UNDEFINED
|
override val type: Command.Type = Command.Type.UNDEFINED
|
||||||
}, Command.Type.UNDEFINED, EnumSet.noneOf(CommandFlags::class.java), emptyList())
|
}, Command.Type.UNDEFINED, EnumSet.noneOf(CommandFlags::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,6 @@ package com.maddyhome.idea.vim.command
|
|||||||
|
|
||||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.KeyStroke
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This represents a single Vim command to be executed (operator, motion, text object, etc.). It may optionally include
|
* This represents a single Vim command to be executed (operator, motion, text object, etc.). It may optionally include
|
||||||
@@ -28,14 +27,17 @@ import javax.swing.KeyStroke
|
|||||||
*/
|
*/
|
||||||
data class Command(
|
data class Command(
|
||||||
var rawCount: Int,
|
var rawCount: Int,
|
||||||
var action: EditorActionHandlerBase,
|
var action: EditorActionHandlerBase?,
|
||||||
val type: Type,
|
val type: Type,
|
||||||
var flags: EnumSet<CommandFlags>,
|
var flags: EnumSet<CommandFlags>
|
||||||
var keys: List<KeyStroke>
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
constructor(rawCount: Int, register: Char): this(rawCount, null, Type.SELECT_REGISTER, EnumSet.of(CommandFlags.FLAG_EXPECT_MORE)) {
|
||||||
|
this.register = register
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
action.process(this)
|
action?.process(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
var count: Int
|
var count: Int
|
||||||
@@ -45,6 +47,7 @@ data class Command(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var argument: Argument? = null
|
var argument: Argument? = null
|
||||||
|
var register: Char? = null
|
||||||
|
|
||||||
enum class Type {
|
enum class Type {
|
||||||
/**
|
/**
|
||||||
@@ -72,12 +75,12 @@ data class Command(
|
|||||||
*/
|
*/
|
||||||
COPY,
|
COPY,
|
||||||
PASTE,
|
PASTE,
|
||||||
// TODO REMOVE?
|
|
||||||
RESET,
|
|
||||||
/**
|
/**
|
||||||
* Represents commands that select the register.
|
* Represents commands that select the register.
|
||||||
*/
|
*/
|
||||||
SELECT_REGISTER,
|
SELECT_REGISTER,
|
||||||
|
// TODO REMOVE?
|
||||||
|
RESET,
|
||||||
OTHER_READONLY,
|
OTHER_READONLY,
|
||||||
OTHER_WRITABLE,
|
OTHER_WRITABLE,
|
||||||
/**
|
/**
|
||||||
@@ -88,7 +91,7 @@ data class Command(
|
|||||||
|
|
||||||
val isRead: Boolean
|
val isRead: Boolean
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
MOTION, COPY, SELECT_REGISTER, OTHER_READONLY, COMPLETION -> true
|
MOTION, COPY, OTHER_READONLY, COMPLETION -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,12 +9,13 @@ import java.util.*
|
|||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
||||||
private val commandParts = Stack<Command>()
|
private val commandParts = ArrayDeque<Command>()
|
||||||
private var keys = mutableListOf<KeyStroke>()
|
private var keyList = mutableListOf<KeyStroke>()
|
||||||
|
|
||||||
var commandState = CurrentCommandState.NEW_COMMAND
|
var commandState = CurrentCommandState.NEW_COMMAND
|
||||||
var count = 0
|
var count = 0
|
||||||
private set
|
private set
|
||||||
|
val keys: Iterable<KeyStroke> get() = keyList
|
||||||
|
|
||||||
// The argument type for the current command part's action. Kept separate to handle digraphs and characters. We first
|
// The argument type for the current command part's action. Kept separate to handle digraphs and characters. We first
|
||||||
// try to accept a digraph. If we get it, set expected argument type to character and handle the converted key. If we
|
// try to accept a digraph. If we get it, set expected argument type to character and handle the converted key. If we
|
||||||
@@ -24,7 +25,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
|
|
||||||
val isReady get() = commandState == CurrentCommandState.READY
|
val isReady get() = commandState == CurrentCommandState.READY
|
||||||
val isBad get() = commandState == CurrentCommandState.BAD_COMMAND
|
val isBad get() = commandState == CurrentCommandState.BAD_COMMAND
|
||||||
val isEmpty get() = commandParts.empty()
|
val isEmpty get() = commandParts.isEmpty()
|
||||||
val isAtDefaultState get() = isEmpty && count == 0 && expectedArgumentType == null
|
val isAtDefaultState get() = isEmpty && count == 0 && expectedArgumentType == null
|
||||||
|
|
||||||
val isExpectingCount: Boolean
|
val isExpectingCount: Boolean
|
||||||
@@ -35,14 +36,23 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun pushCommandPart(action: EditorActionHandlerBase) {
|
fun pushCommandPart(action: EditorActionHandlerBase) {
|
||||||
commandParts.push(Command(count, action, action.type, action.flags, keys))
|
commandParts.add(Command(count, action, action.type, action.flags))
|
||||||
expectedArgumentType = action.argumentType
|
expectedArgumentType = action.argumentType
|
||||||
keys = mutableListOf()
|
count = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun popCommandPart() {
|
fun pushCommandPart(register: Char) {
|
||||||
commandParts.pop()
|
// We will never execute this command, but we need to push something to correctly handle counts on either side of a
|
||||||
expectedArgumentType = if (commandParts.size > 0) commandParts.peek().action.argumentType else null
|
// select register command part. e.g. 2"a2d2w or even crazier 2"a2"a2"a2"a2"a2d2w
|
||||||
|
commandParts.add(Command(count, register))
|
||||||
|
expectedArgumentType = null
|
||||||
|
count = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun popCommandPart(): Command {
|
||||||
|
val command = commandParts.removeLast()
|
||||||
|
expectedArgumentType = if (commandParts.size > 0) commandParts.peekLast().action?.argumentType else null
|
||||||
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fallbackToCharacterArgument() {
|
fun fallbackToCharacterArgument() {
|
||||||
@@ -52,16 +62,24 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
expectedArgumentType = Argument.Type.CHARACTER
|
expectedArgumentType = Argument.Type.CHARACTER
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addKey(keyStroke: KeyStroke) {
|
fun addKey(key: KeyStroke) {
|
||||||
keys.add(keyStroke)
|
keyList.add(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCountCharacter(chKey: Char) {
|
fun addCountCharacter(key: KeyStroke) {
|
||||||
count = (count * 10) + (chKey - '0')
|
count = (count * 10) + (key.keyChar - '0')
|
||||||
|
// If count overflows and flips negative, reset to 999999999L. In Vim, count is a long, which is *usually* 32 bits,
|
||||||
|
// so will flip at 2147483648. We store count as an Int, which is also 32 bit.
|
||||||
|
// See https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/normal.c#L631
|
||||||
|
if (count < 0) {
|
||||||
|
count = 999999999
|
||||||
|
}
|
||||||
|
addKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteCountCharacter() {
|
fun deleteCountCharacter() {
|
||||||
count /= 10
|
count /= 10
|
||||||
|
keyList.removeAt(keyList.size - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCurrentCommandPartNode(newNode: CommandPartNode) {
|
fun setCurrentCommandPartNode(newNode: CommandPartNode) {
|
||||||
@@ -74,7 +92,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
|
|
||||||
fun isAwaitingCharOrDigraphArgument(): Boolean {
|
fun isAwaitingCharOrDigraphArgument(): Boolean {
|
||||||
if (commandParts.size == 0) return false
|
if (commandParts.size == 0) return false
|
||||||
val argumentType = commandParts.peek().action.argumentType
|
val argumentType = commandParts.peekLast().action?.argumentType
|
||||||
return argumentType == Argument.Type.CHARACTER || argumentType == Argument.Type.DIGRAPH
|
return argumentType == Argument.Type.CHARACTER || argumentType == Argument.Type.DIGRAPH
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,12 +106,12 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun completeCommandPart(argument: Argument) {
|
fun completeCommandPart(argument: Argument) {
|
||||||
commandParts.peek().argument = argument
|
commandParts.peekLast().argument = argument
|
||||||
commandState = CurrentCommandState.READY
|
commandState = CurrentCommandState.READY
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDuplicateOperatorKeyStroke(key: KeyStroke): Boolean {
|
fun isDuplicateOperatorKeyStroke(key: KeyStroke): Boolean {
|
||||||
val action = commandParts.peek()?.action as? DuplicableOperatorAction
|
val action = commandParts.peekLast()?.action as? DuplicableOperatorAction
|
||||||
return action?.duplicateWith == key.keyChar
|
return action?.duplicateWith == key.keyChar
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,27 +120,23 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun buildCommand(): Command {
|
fun buildCommand(): Command {
|
||||||
/* Let's go through the command stack and merge it all into one command. At this time there should never
|
|
||||||
be more than two commands on the stack - one is the actual command, and the other would be a motion
|
|
||||||
command argument needed by the first command */
|
|
||||||
var command: Command = commandParts.pop()
|
|
||||||
while (commandParts.size > 0) {
|
|
||||||
val top: Command = commandParts.pop()
|
|
||||||
top.argument = Argument(command)
|
|
||||||
command = top
|
|
||||||
}
|
|
||||||
return fixCommandCounts(command)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fixCommandCounts(command: Command): Command {
|
var command: Command = commandParts.removeFirst()
|
||||||
// If we have a command with a motion command argument, both could have their own counts. We need to adjust the
|
while (commandParts.size > 0) {
|
||||||
// counts, so the motion gets the product of both counts, and the count associated with the command gets reset.
|
val next = commandParts.removeFirst()
|
||||||
// E.g. 3c2w (change 2 words, three times) becomes c6w (change 6 words)
|
next.count = if (command.rawCount == 0 && next.rawCount == 0) 0 else command.count * next.count
|
||||||
if (command.argument?.type === Argument.Type.MOTION) {
|
|
||||||
val motion = command.argument!!.motion
|
|
||||||
motion.count = if (command.rawCount == 0 && motion.rawCount == 0) 0 else command.count * motion.count
|
|
||||||
command.count = 0
|
command.count = 0
|
||||||
|
if (command.type == Command.Type.SELECT_REGISTER) {
|
||||||
|
next.register = command.register
|
||||||
|
command.register = null
|
||||||
|
command = next
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
command.argument = Argument(next)
|
||||||
|
assert(commandParts.size == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expectedArgumentType = null
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,11 +144,11 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
|
|||||||
resetInProgressCommandPart(commandPartNode)
|
resetInProgressCommandPart(commandPartNode)
|
||||||
commandState = CurrentCommandState.NEW_COMMAND
|
commandState = CurrentCommandState.NEW_COMMAND
|
||||||
commandParts.clear()
|
commandParts.clear()
|
||||||
|
keyList.clear()
|
||||||
expectedArgumentType = null
|
expectedArgumentType = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {
|
fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {
|
||||||
keys.clear()
|
|
||||||
count = 0
|
count = 0
|
||||||
setCurrentCommandPartNode(commandPartNode)
|
setCurrentCommandPartNode(commandPartNode)
|
||||||
}
|
}
|
||||||
|
@@ -86,7 +86,7 @@ public class CommandState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Keep the compatibility with the IdeaVim-EasyMotion plugin before the stable release
|
// Keep the compatibility with the IdeaVim-EasyMotion plugin before the stable release
|
||||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.56")
|
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public MappingMode getMappingMode() {
|
public MappingMode getMappingMode() {
|
||||||
return mappingState.getMappingMode();
|
return mappingState.getMappingMode();
|
||||||
@@ -164,6 +164,12 @@ public class CommandState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resetRegisterPending() {
|
||||||
|
if (getSubMode() == SubMode.REGISTER_PENDING) {
|
||||||
|
popModes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void resetModes() {
|
private void resetModes() {
|
||||||
modeStates.clear();
|
modeStates.clear();
|
||||||
setMappingMode();
|
setMappingMode();
|
||||||
@@ -343,7 +349,7 @@ public class CommandState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SubMode {
|
public enum SubMode {
|
||||||
NONE, SINGLE_COMMAND, OP_PENDING, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
NONE, SINGLE_COMMAND, OP_PENDING, REGISTER_PENDING, VISUAL_CHARACTER, VISUAL_LINE, VISUAL_BLOCK
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ModeState {
|
private static class ModeState {
|
||||||
|
@@ -61,3 +61,7 @@ enum class SelectionType(val value: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val SelectionType.isLine get() = this == SelectionType.LINE_WISE
|
||||||
|
val SelectionType.isChar get() = this == SelectionType.CHARACTER_WISE
|
||||||
|
val SelectionType.isBlock get() = this == SelectionType.BLOCK_WISE
|
||||||
|
@@ -59,9 +59,7 @@ class IndentConfig private constructor(indentOptions: IndentOptions) {
|
|||||||
val indentOptions = if (project != null) {
|
val indentOptions = if (project != null) {
|
||||||
CodeStyle.getIndentOptions(project, editor.document)
|
CodeStyle.getIndentOptions(project, editor.document)
|
||||||
} else {
|
} else {
|
||||||
// [VERSION UPDATE] 191+
|
CodeStyle.getDefaultSettings().indentOptions
|
||||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
|
||||||
CodeStyle.getDefaultSettings().indentOptions!!
|
|
||||||
}
|
}
|
||||||
return IndentConfig(indentOptions)
|
return IndentConfig(indentOptions)
|
||||||
}
|
}
|
||||||
|
@@ -29,12 +29,14 @@ class Register {
|
|||||||
val type: SelectionType
|
val type: SelectionType
|
||||||
val keys: MutableList<KeyStroke>
|
val keys: MutableList<KeyStroke>
|
||||||
val transferableData: MutableList<out TextBlockTransferableData>
|
val transferableData: MutableList<out TextBlockTransferableData>
|
||||||
|
val rawText: String?
|
||||||
|
|
||||||
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) {
|
constructor(name: Char, type: SelectionType, keys: MutableList<KeyStroke>) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.type = type
|
this.type = type
|
||||||
this.keys = keys
|
this.keys = keys
|
||||||
this.transferableData = mutableListOf()
|
this.transferableData = mutableListOf()
|
||||||
|
this.rawText = text
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>) {
|
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>) {
|
||||||
@@ -42,6 +44,15 @@ class Register {
|
|||||||
this.type = type
|
this.type = type
|
||||||
this.keys = StringHelper.stringToKeys(text)
|
this.keys = StringHelper.stringToKeys(text)
|
||||||
this.transferableData = transferableData
|
this.transferableData = transferableData
|
||||||
|
this.rawText = text
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(name: Char, type: SelectionType, text: String, transferableData: MutableList<out TextBlockTransferableData>, rawText: String) {
|
||||||
|
this.name = name
|
||||||
|
this.type = type
|
||||||
|
this.keys = StringHelper.stringToKeys(text)
|
||||||
|
this.transferableData = transferableData
|
||||||
|
this.rawText = rawText
|
||||||
}
|
}
|
||||||
|
|
||||||
val text: String?
|
val text: String?
|
||||||
|
@@ -67,34 +67,8 @@ public class CommandParser {
|
|||||||
* Don't let anyone create one of these.
|
* Don't let anyone create one of these.
|
||||||
*/
|
*/
|
||||||
private CommandParser() {
|
private CommandParser() {
|
||||||
// IdeaVim doesn't support contribution to ex_command_ep extension point, so technically we can skip this update,
|
|
||||||
// but let's support dynamic plugins in a more classic way and reload handlers on every EP change.
|
|
||||||
// TODO: [VERSION UPDATE] since 191 use
|
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
|
||||||
// TODO: [VERSION UPDATE] since 201 use
|
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
|
||||||
//noinspection deprecation
|
|
||||||
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ExBeanClass>() {
|
|
||||||
@Override
|
|
||||||
public void extensionAdded(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
|
||||||
// version update (or earlier).
|
|
||||||
if (!initialRegistration) return;
|
|
||||||
unregisterHandlers();
|
|
||||||
registerHandlers();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void extensionRemoved(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
if (!initialRegistration) return;
|
|
||||||
unregisterHandlers();
|
|
||||||
registerHandlers();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean initialRegistration = false;
|
|
||||||
|
|
||||||
public void unregisterHandlers() {
|
public void unregisterHandlers() {
|
||||||
root.clear();
|
root.clear();
|
||||||
}
|
}
|
||||||
@@ -104,7 +78,16 @@ public class CommandParser {
|
|||||||
*/
|
*/
|
||||||
public void registerHandlers() {
|
public void registerHandlers() {
|
||||||
EX_COMMAND_EP.extensions().forEach(ExBeanClass::register);
|
EX_COMMAND_EP.extensions().forEach(ExBeanClass::register);
|
||||||
initialRegistration = true;
|
registerEpListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerEpListener() {
|
||||||
|
// IdeaVim doesn't support contribution to ex_command_ep extension point, so technically we can skip this update,
|
||||||
|
// but let's support dynamic plugins in a more classic way and reload handlers on every EP change.
|
||||||
|
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(() -> {
|
||||||
|
unregisterHandlers();
|
||||||
|
registerHandlers();
|
||||||
|
}, false, VimPlugin.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -32,9 +32,7 @@ class ExBeanClass : AbstractExtensionPointBean() {
|
|||||||
var names: String? = null
|
var names: String? = null
|
||||||
|
|
||||||
val handler: CommandHandler by lazy {
|
val handler: CommandHandler by lazy {
|
||||||
// FIXME. [VERSION UPDATE] change to instantiateClass for 193+
|
this.instantiateClass<CommandHandler>(
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
this.instantiate<CommandHandler>(
|
|
||||||
implementation ?: "", ApplicationManager.getApplication().picoContainer)
|
implementation ?: "", ApplicationManager.getApplication().picoContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.ex
|
package com.maddyhome.idea.vim.ex
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Caret
|
import com.intellij.openapi.editor.Caret
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
@@ -28,33 +27,33 @@ class ExCommand(val ranges: Ranges, val command: String, var argument: String) {
|
|||||||
|
|
||||||
fun getLine(editor: Editor): Int = ranges.getLine(editor)
|
fun getLine(editor: Editor): Int = ranges.getLine(editor)
|
||||||
|
|
||||||
fun getLine(editor: Editor, caret: Caret, context: DataContext): Int = ranges.getLine(editor, caret, context)
|
fun getLine(editor: Editor, caret: Caret): Int = ranges.getLine(editor, caret)
|
||||||
|
|
||||||
fun getCount(editor: Editor, context: DataContext?, defaultCount: Int, checkCount: Boolean): Int {
|
fun getCount(editor: Editor, defaultCount: Int, checkCount: Boolean): Int {
|
||||||
val count = if (checkCount) countArgument else -1
|
val count = if (checkCount) countArgument else -1
|
||||||
|
|
||||||
val res = ranges.getCount(editor, count)
|
val res = ranges.getCount(editor, count)
|
||||||
return if (res == -1) defaultCount else res
|
return if (res == -1) defaultCount else res
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCount(editor: Editor, caret: Caret, context: DataContext, defaultCount: Int, checkCount: Boolean): Int {
|
fun getCount(editor: Editor, caret: Caret, defaultCount: Int, checkCount: Boolean): Int {
|
||||||
val count = ranges.getCount(editor, caret, context, if (checkCount) countArgument else -1)
|
val count = ranges.getCount(editor, caret, if (checkCount) countArgument else -1)
|
||||||
return if (count == -1) defaultCount else count
|
return if (count == -1) defaultCount else count
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLineRange(editor: Editor): LineRange = ranges.getLineRange(editor, -1)
|
fun getLineRange(editor: Editor): LineRange = ranges.getLineRange(editor, -1)
|
||||||
|
|
||||||
fun getLineRange(editor: Editor, caret: Caret, context: DataContext): LineRange {
|
fun getLineRange(editor: Editor, caret: Caret): LineRange {
|
||||||
return ranges.getLineRange(editor, caret, context, -1)
|
return ranges.getLineRange(editor, caret, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTextRange(editor: Editor, context: DataContext?, checkCount: Boolean): TextRange {
|
fun getTextRange(editor: Editor, checkCount: Boolean): TextRange {
|
||||||
val count = if (checkCount) countArgument else -1
|
val count = if (checkCount) countArgument else -1
|
||||||
return ranges.getTextRange(editor, context, count)
|
return ranges.getTextRange(editor, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTextRange(editor: Editor, caret: Caret, context: DataContext, checkCount: Boolean): TextRange {
|
fun getTextRange(editor: Editor, caret: Caret, checkCount: Boolean): TextRange {
|
||||||
return ranges.getTextRange(editor, caret, context, if (checkCount) countArgument else -1)
|
return ranges.getTextRange(editor, caret, if (checkCount) countArgument else -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val countArgument: Int
|
private val countArgument: Int
|
||||||
|
95
src/com/maddyhome/idea/vim/ex/handler/BufferHandler.kt
Normal file
95
src/com/maddyhome/idea/vim/ex/handler/BufferHandler.kt
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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.ex.handler
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles buffer, buf, bu, b.
|
||||||
|
*
|
||||||
|
* @author John Weigel
|
||||||
|
*/
|
||||||
|
class BufferHandler : CommandHandler.SingleExecution() {
|
||||||
|
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
|
val arg = cmd.argument.trim()
|
||||||
|
val overrideModified = arg.startsWith('!')
|
||||||
|
val buffer = if (overrideModified) arg.replace(Regex("^!\\s*"), "") else arg
|
||||||
|
var result = true
|
||||||
|
|
||||||
|
if (buffer.isNotEmpty()) {
|
||||||
|
if (buffer.matches(Regex("^\\d+$"))) {
|
||||||
|
val bufNum = buffer.toInt() - 1
|
||||||
|
|
||||||
|
if (!VimPlugin.getFile().selectFile(bufNum, context)) {
|
||||||
|
VimPlugin.showMessage("Buffer $bufNum does not exist")
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val editors = findPartialMatch(context, buffer)
|
||||||
|
|
||||||
|
when(editors.size) {
|
||||||
|
0 -> {
|
||||||
|
VimPlugin.showMessage("No matching buffer for $buffer")
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
if (EditorHelper.hasUnsavedChanges(editor) && !overrideModified) {
|
||||||
|
VimPlugin.showMessage("No write since last change (add ! to override)")
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VimPlugin.getFile().openFile(EditorHelper.getVirtualFile(editors[0])!!.name, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
VimPlugin.showMessage("More than one match for $buffer")
|
||||||
|
result = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findPartialMatch(context: DataContext, fileName: String): List<Editor> {
|
||||||
|
val matchedFiles = mutableListOf<Editor>()
|
||||||
|
val project = PlatformDataKeys.PROJECT.getData(context) ?: return matchedFiles
|
||||||
|
|
||||||
|
for (file in FileEditorManager.getInstance(project).openFiles) {
|
||||||
|
if (file.name.contains(fileName)) {
|
||||||
|
val editor = EditorHelper.getEditor(file) ?: continue
|
||||||
|
matchedFiles.add(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedFiles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -22,7 +22,11 @@ import com.intellij.openapi.actionSystem.DataContext
|
|||||||
import com.intellij.openapi.diagnostic.Logger
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.ex.*
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
|
import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
import com.maddyhome.idea.vim.helper.MessageHelper
|
import com.maddyhome.idea.vim.helper.MessageHelper
|
||||||
import com.maddyhome.idea.vim.helper.Msg
|
import com.maddyhome.idea.vim.helper.Msg
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -54,7 +58,7 @@ class CmdFilterHandler : CommandHandler.SingleExecution() {
|
|||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
// Filter
|
// Filter
|
||||||
val range = cmd.getTextRange(editor, context, false)
|
val range = cmd.getTextRange(editor, false)
|
||||||
VimPlugin.getProcess().executeFilter(editor, range, command)
|
VimPlugin.getProcess().executeFilter(editor, range, command)
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
|
@@ -22,7 +22,10 @@ import com.intellij.openapi.actionSystem.DataContext
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.command.SelectionType
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
import com.maddyhome.idea.vim.ex.*
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.CommandParser
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
import com.maddyhome.idea.vim.group.copy.PutData
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
|
|
||||||
@@ -31,11 +34,11 @@ class CopyTextHandler : CommandHandler.SingleExecution() {
|
|||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val carets = EditorHelper.getOrderedCaretsList(editor)
|
val carets = EditorHelper.getOrderedCaretsList(editor)
|
||||||
for (caret in carets) {
|
for (caret in carets) {
|
||||||
val range = cmd.getTextRange(editor, caret, context, false)
|
val range = cmd.getTextRange(editor, caret, false)
|
||||||
val text = EditorHelper.getText(editor, range.startOffset, range.endOffset)
|
val text = EditorHelper.getText(editor, range.startOffset, range.endOffset)
|
||||||
|
|
||||||
val arg = CommandParser.getInstance().parse(cmd.argument)
|
val arg = CommandParser.getInstance().parse(cmd.argument)
|
||||||
val line = arg.ranges.getFirstLine(editor, caret, context)
|
val line = arg.ranges.getFirstLine(editor, caret)
|
||||||
|
|
||||||
val transferableData = VimPlugin.getRegister().getTransferableData(editor, range, text)
|
val transferableData = VimPlugin.getRegister().getTransferableData(editor, range, text)
|
||||||
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData)
|
val textData = PutData.TextData(text, SelectionType.LINE_WISE, transferableData)
|
||||||
|
@@ -40,7 +40,7 @@ class DeleteLinesHandler : CommandHandler.ForEachCaret() {
|
|||||||
|
|
||||||
if (!VimPlugin.getRegister().selectRegister(register)) return false
|
if (!VimPlugin.getRegister().selectRegister(register)) return false
|
||||||
|
|
||||||
val textRange = cmd.getTextRange(editor, caret, context, true)
|
val textRange = cmd.getTextRange(editor, caret, true)
|
||||||
return VimPlugin.getChange().deleteRange(editor, caret, textRange, SelectionType.LINE_WISE, false)
|
return VimPlugin.getChange().deleteRange(editor, caret, textRange, SelectionType.LINE_WISE, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,7 @@ import com.maddyhome.idea.vim.ex.flags
|
|||||||
class FileHandler : CommandHandler.SingleExecution() {
|
class FileHandler : CommandHandler.SingleExecution() {
|
||||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ARGUMENT_FORBIDDEN, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ARGUMENT_FORBIDDEN, Access.READ_ONLY)
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 0, false)
|
val count = cmd.getCount(editor, 0, false)
|
||||||
VimPlugin.getFile().displayFileInfo(editor, count > 0)
|
VimPlugin.getFile().displayFileInfo(editor, count > 0)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,7 @@ class GotoCharacterHandler : CommandHandler.ForEachCaret() {
|
|||||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
|
|
||||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, caret, context, 1, true)
|
val count = cmd.getCount(editor, caret, 1, true)
|
||||||
if (count <= 0) return false
|
if (count <= 0) return false
|
||||||
|
|
||||||
val offset = VimPlugin.getMotion().moveCaretToNthCharacter(editor, count - 1)
|
val offset = VimPlugin.getMotion().moveCaretToNthCharacter(editor, count - 1)
|
||||||
|
@@ -47,7 +47,7 @@ class GotoLineHandler : CommandHandler.ForEachCaret() {
|
|||||||
* @return True if able to perform the command, false if not
|
* @return True if able to perform the command, false if not
|
||||||
*/
|
*/
|
||||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val line = min(cmd.getLine(editor, caret, context), EditorHelper.getLineCount(editor) - 1)
|
val line = min(cmd.getLine(editor, caret), EditorHelper.getLineCount(editor) - 1)
|
||||||
|
|
||||||
if (line >= 0) {
|
if (line >= 0) {
|
||||||
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, line))
|
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, line))
|
||||||
|
@@ -23,7 +23,10 @@ import com.intellij.openapi.editor.Caret
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.ex.*
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.CommandHandlerFlags
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
|
|
||||||
class JoinLinesHandler : CommandHandler.ForEachCaret() {
|
class JoinLinesHandler : CommandHandler.ForEachCaret() {
|
||||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||||
@@ -32,7 +35,7 @@ class JoinLinesHandler : CommandHandler.ForEachCaret() {
|
|||||||
val arg = cmd.argument
|
val arg = cmd.argument
|
||||||
val spaces = arg.isEmpty() || arg[0] != '!'
|
val spaces = arg.isEmpty() || arg[0] != '!'
|
||||||
|
|
||||||
val textRange = cmd.getTextRange(editor, caret, context, true) ?: return false
|
val textRange = cmd.getTextRange(editor, caret, true)
|
||||||
|
|
||||||
return VimPlugin.getChange().deleteJoinRange(editor, caret, TextRange(textRange.startOffset,
|
return VimPlugin.getChange().deleteJoinRange(editor, caret, TextRange(textRange.startOffset,
|
||||||
textRange.endOffset - 1), spaces)
|
textRange.endOffset - 1), spaces)
|
||||||
|
@@ -24,7 +24,12 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.command.SelectionType
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
import com.maddyhome.idea.vim.ex.*
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.CommandParser
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
|
import com.maddyhome.idea.vim.ex.InvalidRangeException
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||||
import com.maddyhome.idea.vim.group.copy.PutData
|
import com.maddyhome.idea.vim.group.copy.PutData
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
@@ -48,10 +53,10 @@ class MoveTextHandler : CommandHandler.SingleExecution() {
|
|||||||
|
|
||||||
var lastRange: TextRange? = null
|
var lastRange: TextRange? = null
|
||||||
for (caret in carets) {
|
for (caret in carets) {
|
||||||
val range = cmd.getTextRange(editor, caret, context, false)
|
val range = cmd.getTextRange(editor, caret, false)
|
||||||
val lineRange = cmd.getLineRange(editor, caret, context)
|
val lineRange = cmd.getLineRange(editor, caret)
|
||||||
|
|
||||||
line = min(line, normalizeLine(editor, caret, context, command, lineRange))
|
line = min(line, normalizeLine(editor, caret, command, lineRange))
|
||||||
texts.add(EditorHelper.getText(editor, range.startOffset, range.endOffset))
|
texts.add(EditorHelper.getText(editor, range.startOffset, range.endOffset))
|
||||||
|
|
||||||
if (lastRange == null || lastRange.startOffset != range.startOffset && lastRange.endOffset != range.endOffset) {
|
if (lastRange == null || lastRange.startOffset != range.startOffset && lastRange.endOffset != range.endOffset) {
|
||||||
@@ -75,9 +80,9 @@ class MoveTextHandler : CommandHandler.SingleExecution() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Throws
|
@Throws
|
||||||
private fun normalizeLine(editor: Editor, caret: Caret, context: DataContext,
|
private fun normalizeLine(editor: Editor, caret: Caret, command: ExCommand,
|
||||||
command: ExCommand, lineRange: LineRange): Int {
|
lineRange: LineRange): Int {
|
||||||
var line = command.ranges.getFirstLine(editor, caret, context)
|
var line = command.ranges.getFirstLine(editor, caret)
|
||||||
val adj = lineRange.endLine - lineRange.startLine + 1
|
val adj = lineRange.endLine - lineRange.startLine + 1
|
||||||
if (line >= lineRange.endLine)
|
if (line >= lineRange.endLine)
|
||||||
line -= adj
|
line -= adj
|
||||||
|
@@ -29,7 +29,7 @@ class NextFileHandler : CommandHandler.SingleExecution() {
|
|||||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
|
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 1, true)
|
val count = cmd.getCount(editor, 1, true)
|
||||||
VimPlugin.getMark().saveJumpLocation(editor)
|
VimPlugin.getMark().saveJumpLocation(editor)
|
||||||
VimPlugin.getFile().selectNextFile(count, context)
|
VimPlugin.getFile().selectNextFile(count, context)
|
||||||
return true
|
return true
|
||||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
|||||||
class PreviousFileHandler : CommandHandler.SingleExecution() {
|
class PreviousFileHandler : CommandHandler.SingleExecution() {
|
||||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 1, true)
|
val count = cmd.getCount(editor, 1, true)
|
||||||
|
|
||||||
VimPlugin.getMark().saveJumpLocation(editor)
|
VimPlugin.getMark().saveJumpLocation(editor)
|
||||||
VimPlugin.getFile().selectNextFile(-count, context)
|
VimPlugin.getFile().selectNextFile(-count, context)
|
||||||
|
@@ -37,7 +37,7 @@ class RepeatHandler : CommandHandler.ForEachCaret() {
|
|||||||
if (arg == '@') arg = lastArg
|
if (arg == '@') arg = lastArg
|
||||||
lastArg = arg
|
lastArg = arg
|
||||||
|
|
||||||
val line = cmd.getLine(editor, caret, context)
|
val line = cmd.getLine(editor, caret)
|
||||||
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLine(editor, line, editor.caretModel.primaryCaret))
|
MotionGroup.moveCaret(editor, caret, VimPlugin.getMotion().moveCaretToLine(editor, line, editor.caretModel.primaryCaret))
|
||||||
|
|
||||||
if (arg == ':') {
|
if (arg == ':') {
|
||||||
|
@@ -29,7 +29,7 @@ class SelectFileHandler : CommandHandler.SingleExecution() {
|
|||||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
|
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 0, true)
|
val count = cmd.getCount(editor, 0, true)
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
val res = VimPlugin.getFile().selectFile(count - 1, context)
|
val res = VimPlugin.getFile().selectFile(count - 1, context)
|
||||||
|
@@ -36,7 +36,7 @@ class ShiftLeftHandler : CommandHandler.ForEachCaret(), ComplicatedNameExCommand
|
|||||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||||
|
|
||||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val range = cmd.getTextRange(editor, caret, context, true)
|
val range = cmd.getTextRange(editor, caret, true)
|
||||||
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
||||||
VimPlugin.getChange().indentRange(editor, caret, context,
|
VimPlugin.getChange().indentRange(editor, caret, context,
|
||||||
TextRange(range.startOffsets, endOffsets),
|
TextRange(range.startOffsets, endOffsets),
|
||||||
|
@@ -36,7 +36,7 @@ class ShiftRightHandler : CommandHandler.ForEachCaret(), ComplicatedNameExComman
|
|||||||
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
override val argFlags: CommandHandlerFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.WRITABLE)
|
||||||
|
|
||||||
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val range = cmd.getTextRange(editor, caret, context, true)
|
val range = cmd.getTextRange(editor, caret, true)
|
||||||
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
val endOffsets = range.endOffsets.map { it - 1 }.toIntArray()
|
||||||
VimPlugin.getChange().indentRange(editor, caret, context,
|
VimPlugin.getChange().indentRange(editor, caret, context,
|
||||||
TextRange(range.startOffsets, endOffsets),
|
TextRange(range.startOffsets, endOffsets),
|
||||||
|
@@ -23,7 +23,10 @@ import com.intellij.openapi.editor.Caret
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.util.text.StringUtil
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.ex.*
|
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||||
|
import com.maddyhome.idea.vim.ex.ExCommand
|
||||||
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
import com.maddyhome.idea.vim.ex.ranges.LineRange
|
||||||
import com.maddyhome.idea.vim.helper.inBlockSubMode
|
import com.maddyhome.idea.vim.helper.inBlockSubMode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -46,7 +49,7 @@ class SortHandler : CommandHandler.SingleExecution() {
|
|||||||
val lineComparator = LineComparator(ignoreCase, number, reverse)
|
val lineComparator = LineComparator(ignoreCase, number, reverse)
|
||||||
if (editor.inBlockSubMode) {
|
if (editor.inBlockSubMode) {
|
||||||
val primaryCaret = editor.caretModel.primaryCaret
|
val primaryCaret = editor.caretModel.primaryCaret
|
||||||
val range = getLineRange(editor, primaryCaret, context, cmd)
|
val range = getLineRange(editor, primaryCaret, cmd)
|
||||||
val worked = VimPlugin.getChange().sortRange(editor, range, lineComparator)
|
val worked = VimPlugin.getChange().sortRange(editor, range, lineComparator)
|
||||||
primaryCaret.moveToOffset(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, range.startLine))
|
primaryCaret.moveToOffset(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, range.startLine))
|
||||||
return worked
|
return worked
|
||||||
@@ -54,7 +57,7 @@ class SortHandler : CommandHandler.SingleExecution() {
|
|||||||
|
|
||||||
var worked = true
|
var worked = true
|
||||||
for (caret in editor.caretModel.allCarets) {
|
for (caret in editor.caretModel.allCarets) {
|
||||||
val range = getLineRange(editor, caret, context, cmd)
|
val range = getLineRange(editor, caret, cmd)
|
||||||
if (!VimPlugin.getChange().sortRange(editor, range, lineComparator)) {
|
if (!VimPlugin.getChange().sortRange(editor, range, lineComparator)) {
|
||||||
worked = false
|
worked = false
|
||||||
}
|
}
|
||||||
@@ -64,8 +67,8 @@ class SortHandler : CommandHandler.SingleExecution() {
|
|||||||
return worked
|
return worked
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLineRange(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): LineRange {
|
private fun getLineRange(editor: Editor, caret: Caret, cmd: ExCommand): LineRange {
|
||||||
val range = cmd.getLineRange(editor, caret, context)
|
val range = cmd.getLineRange(editor, caret)
|
||||||
|
|
||||||
// Something like "30,20sort" gets converted to "20,30sort"
|
// Something like "30,20sort" gets converted to "20,30sort"
|
||||||
val normalizedRange = if (range.endLine < range.startLine) LineRange(range.endLine, range.startLine) else range
|
val normalizedRange = if (range.endLine < range.startLine) LineRange(range.endLine, range.startLine) else range
|
||||||
|
@@ -30,7 +30,7 @@ class SubstituteHandler : CommandHandler.SingleExecution() {
|
|||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
var result = true
|
var result = true
|
||||||
for (caret in editor.caretModel.allCarets) {
|
for (caret in editor.caretModel.allCarets) {
|
||||||
val lineRange = cmd.getLineRange(editor, caret, context)
|
val lineRange = cmd.getLineRange(editor, caret)
|
||||||
if (!VimPlugin.getSearch().searchAndReplace(editor, caret, lineRange, cmd.command, cmd.argument)) {
|
if (!VimPlugin.getSearch().searchAndReplace(editor, caret, lineRange, cmd.command, cmd.argument)) {
|
||||||
result = false
|
result = false
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
|||||||
class WriteNextFileHandler : CommandHandler.SingleExecution() {
|
class WriteNextFileHandler : CommandHandler.SingleExecution() {
|
||||||
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_IS_COUNT, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 1, true)
|
val count = cmd.getCount(editor, 1, true)
|
||||||
|
|
||||||
VimPlugin.getFile().saveFile(context)
|
VimPlugin.getFile().saveFile(context)
|
||||||
VimPlugin.getMark().saveJumpLocation(editor)
|
VimPlugin.getMark().saveJumpLocation(editor)
|
||||||
|
@@ -28,7 +28,7 @@ import com.maddyhome.idea.vim.ex.flags
|
|||||||
class WritePreviousFileHandler : CommandHandler.SingleExecution() {
|
class WritePreviousFileHandler : CommandHandler.SingleExecution() {
|
||||||
override val argFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
override val argFlags = flags(RangeFlag.RANGE_OPTIONAL, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
val count = cmd.getCount(editor, context, 1, true)
|
val count = cmd.getCount(editor, 1, true)
|
||||||
|
|
||||||
VimPlugin.getFile().saveFile(context)
|
VimPlugin.getFile().saveFile(context)
|
||||||
VimPlugin.getMark().saveJumpLocation(editor)
|
VimPlugin.getMark().saveJumpLocation(editor)
|
||||||
|
@@ -45,7 +45,7 @@ class YankLinesHandler : CommandHandler.SingleExecution() {
|
|||||||
val starts = ArrayList<Int>(caretModel.caretCount)
|
val starts = ArrayList<Int>(caretModel.caretCount)
|
||||||
val ends = ArrayList<Int>(caretModel.caretCount)
|
val ends = ArrayList<Int>(caretModel.caretCount)
|
||||||
for (caret in caretModel.allCarets) {
|
for (caret in caretModel.allCarets) {
|
||||||
val range = cmd.getTextRange(editor, caret, context, true)
|
val range = cmd.getTextRange(editor, caret, true)
|
||||||
starts.add(range.startOffset)
|
starts.add(range.startOffset)
|
||||||
ends.add(range.endOffset)
|
ends.add(range.endOffset)
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.maddyhome.idea.vim.ex.ranges
|
package com.maddyhome.idea.vim.ex.ranges
|
||||||
|
|
||||||
import com.intellij.openapi.actionSystem.DataContext
|
|
||||||
import com.intellij.openapi.editor.Caret
|
import com.intellij.openapi.editor.Caret
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
@@ -59,13 +58,13 @@ class Ranges {
|
|||||||
return endLine
|
return endLine
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLine(editor: Editor, caret: Caret, context: DataContext): Int {
|
fun getLine(editor: Editor, caret: Caret): Int {
|
||||||
processRange(editor, caret, context)
|
processRange(editor, caret)
|
||||||
return endLine
|
return endLine
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFirstLine(editor: Editor, caret: Caret, context: DataContext): Int {
|
fun getFirstLine(editor: Editor, caret: Caret): Int {
|
||||||
processRange(editor, caret, context)
|
processRange(editor, caret)
|
||||||
return startLine
|
return startLine
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +78,8 @@ class Ranges {
|
|||||||
*/
|
*/
|
||||||
fun getCount(editor: Editor, count: Int): Int = if (count == -1) getLine(editor) else count
|
fun getCount(editor: Editor, count: Int): Int = if (count == -1) getLine(editor) else count
|
||||||
|
|
||||||
fun getCount(editor: Editor, caret: Caret, context: DataContext, count: Int): Int {
|
fun getCount(editor: Editor, caret: Caret, count: Int): Int {
|
||||||
return if (count == -1) getLine(editor, caret, context) else count
|
return if (count == -1) getLine(editor, caret) else count
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,8 +104,8 @@ class Ranges {
|
|||||||
return LineRange(start, end)
|
return LineRange(start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLineRange(editor: Editor, caret: Caret, context: DataContext, count: Int): LineRange {
|
fun getLineRange(editor: Editor, caret: Caret, count: Int): LineRange {
|
||||||
processRange(editor, caret, context)
|
processRange(editor, caret)
|
||||||
return if (count == -1) LineRange(startLine, endLine) else LineRange(endLine, endLine + count - 1)
|
return if (count == -1) LineRange(startLine, endLine) else LineRange(endLine, endLine + count - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,15 +119,15 @@ class Ranges {
|
|||||||
* @param count The count given at the end of the command or -1 if no such count
|
* @param count The count given at the end of the command or -1 if no such count
|
||||||
* @return The text range
|
* @return The text range
|
||||||
*/
|
*/
|
||||||
fun getTextRange(editor: Editor, context: DataContext?, count: Int): TextRange {
|
fun getTextRange(editor: Editor, count: Int): TextRange {
|
||||||
val lr = getLineRange(editor, count)
|
val lr = getLineRange(editor, count)
|
||||||
val start = EditorHelper.getLineStartOffset(editor, lr.startLine)
|
val start = EditorHelper.getLineStartOffset(editor, lr.startLine)
|
||||||
val end = EditorHelper.getLineEndOffset(editor, lr.endLine, true) + 1
|
val end = EditorHelper.getLineEndOffset(editor, lr.endLine, true) + 1
|
||||||
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTextRange(editor: Editor, caret: Caret, context: DataContext, count: Int): TextRange {
|
fun getTextRange(editor: Editor, caret: Caret, count: Int): TextRange {
|
||||||
val lineRange = getLineRange(editor, caret, context, count)
|
val lineRange = getLineRange(editor, caret, count)
|
||||||
val start = EditorHelper.getLineStartOffset(editor, lineRange.startLine)
|
val start = EditorHelper.getLineStartOffset(editor, lineRange.startLine)
|
||||||
val end = EditorHelper.getLineEndOffset(editor, lineRange.endLine, true) + 1
|
val end = EditorHelper.getLineEndOffset(editor, lineRange.endLine, true) + 1
|
||||||
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
return TextRange(start, min(end, EditorHelper.getFileSize(editor)))
|
||||||
@@ -165,7 +164,7 @@ class Ranges {
|
|||||||
done = true
|
done = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processRange(editor: Editor, caret: Caret, context: DataContext) {
|
private fun processRange(editor: Editor, caret: Caret) {
|
||||||
startLine = if (defaultLine == -1) caret.logicalPosition.line else defaultLine
|
startLine = if (defaultLine == -1) caret.logicalPosition.line else defaultLine
|
||||||
endLine = startLine
|
endLine = startLine
|
||||||
var lastZero = false
|
var lastZero = false
|
||||||
|
@@ -1,188 +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 com.maddyhome.idea.vim.ex.vimscript;
|
|
||||||
|
|
||||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
|
||||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
|
||||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
|
||||||
import com.maddyhome.idea.vim.ex.ExException;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author vlan
|
|
||||||
*/
|
|
||||||
public class VimScriptParser {
|
|
||||||
public static final String VIMRC_FILE_NAME = "ideavimrc";
|
|
||||||
public static final String[] HOME_VIMRC_PATHS = {"." + VIMRC_FILE_NAME, "_" + VIMRC_FILE_NAME};
|
|
||||||
public static final String XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME;
|
|
||||||
public static final int BUFSIZE = 4096;
|
|
||||||
private static final Pattern EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *");
|
|
||||||
private static final Pattern DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"");
|
|
||||||
private static final Pattern SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'");
|
|
||||||
private static final Pattern REFERENCE_EXPR = Pattern.compile("([A-Za-z_][A-Za-z_0-9]*)");
|
|
||||||
private static final Pattern DEC_NUMBER = Pattern.compile("(\\d+)");
|
|
||||||
|
|
||||||
private VimScriptParser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @Nullable File findIdeaVimRc() {
|
|
||||||
final String homeDirName = System.getProperty("user.home");
|
|
||||||
// Check whether file exists in home dir
|
|
||||||
if (homeDirName != null) {
|
|
||||||
for (String fileName : HOME_VIMRC_PATHS) {
|
|
||||||
final File file = new File(homeDirName, fileName);
|
|
||||||
if (file.exists()) {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check in XDG config directory
|
|
||||||
final String xdgConfigHomeProperty = System.getenv("XDG_CONFIG_HOME");
|
|
||||||
File xdgConfig = null;
|
|
||||||
if (xdgConfigHomeProperty == null || Objects.equals(xdgConfigHomeProperty, "")) {
|
|
||||||
if (homeDirName != null) {
|
|
||||||
xdgConfig = Paths.get(homeDirName, ".config", XDG_VIMRC_PATH).toFile();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
xdgConfig = new File(xdgConfigHomeProperty, XDG_VIMRC_PATH);
|
|
||||||
}
|
|
||||||
if (xdgConfig != null && xdgConfig.exists()) {
|
|
||||||
return xdgConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @Nullable File findOrCreateIdeaVimRc() {
|
|
||||||
final File found = findIdeaVimRc();
|
|
||||||
if (found != null) return found;
|
|
||||||
|
|
||||||
final String homeDirName = System.getProperty("user.home");
|
|
||||||
if (homeDirName != null) {
|
|
||||||
for (String fileName : HOME_VIMRC_PATHS) {
|
|
||||||
try {
|
|
||||||
final File file = new File(homeDirName, fileName);
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
file.createNewFile();
|
|
||||||
return file;
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
// Try to create one of two files
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void executeFile(@NotNull File file) {
|
|
||||||
final String data;
|
|
||||||
try {
|
|
||||||
data = readFile(file);
|
|
||||||
}
|
|
||||||
catch (IOException ignored) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
executeText(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void executeText(@NotNull String text) {
|
|
||||||
for (String line : EOL_SPLIT_PATTERN.split(text)) {
|
|
||||||
// TODO: Build a proper parse tree for a VimL file instead of ignoring potentially nested lines (VIM-669)
|
|
||||||
if (line.startsWith(" ") || line.startsWith("\t")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (line.startsWith(":")) {
|
|
||||||
line = line.substring(1);
|
|
||||||
}
|
|
||||||
final CommandParser commandParser = CommandParser.getInstance();
|
|
||||||
try {
|
|
||||||
final ExCommand command = commandParser.parse(line);
|
|
||||||
final CommandHandler commandHandler = commandParser.getCommandHandler(command);
|
|
||||||
if (commandHandler instanceof VimScriptCommandHandler) {
|
|
||||||
final VimScriptCommandHandler handler = (VimScriptCommandHandler)commandHandler;
|
|
||||||
handler.execute(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ExException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
|
|
||||||
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
|
|
||||||
// support mapleader, VIM-650. See also VIM-669.
|
|
||||||
Matcher m;
|
|
||||||
m = DOUBLE_QUOTED_STRING.matcher(expression);
|
|
||||||
if (m.matches()) {
|
|
||||||
return m.group(1);
|
|
||||||
}
|
|
||||||
m = SINGLE_QUOTED_STRING.matcher(expression);
|
|
||||||
if (m.matches()) {
|
|
||||||
return m.group(1);
|
|
||||||
}
|
|
||||||
m = REFERENCE_EXPR.matcher(expression);
|
|
||||||
if (m.matches()) {
|
|
||||||
final String name = m.group(1);
|
|
||||||
final Object value = globals.get(name);
|
|
||||||
if (value != null) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new ExException(String.format("Undefined variable: %s", name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m = DEC_NUMBER.matcher(expression);
|
|
||||||
if (m.matches()) {
|
|
||||||
return Integer.parseInt(m.group(1));
|
|
||||||
}
|
|
||||||
throw new ExException(String.format("Invalid expression: %s", expression));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static @NotNull String expressionToString(@NotNull Object value) throws ExException {
|
|
||||||
// TODO: Return meaningful value representations
|
|
||||||
if (value instanceof String) {
|
|
||||||
return (String)value;
|
|
||||||
} else if (value instanceof Integer) {
|
|
||||||
return value.toString();
|
|
||||||
}
|
|
||||||
throw new ExException(String.format("Cannot convert '%s' to string", value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NotNull String readFile(@NotNull File file) throws IOException {
|
|
||||||
final BufferedReader reader = new BufferedReader(new FileReader(file));
|
|
||||||
final StringBuilder builder = new StringBuilder();
|
|
||||||
final char[] buffer = new char[BUFSIZE];
|
|
||||||
int n;
|
|
||||||
while ((n = reader.read(buffer)) > 0) {
|
|
||||||
builder.append(buffer, 0, n);
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
}
|
|
167
src/com/maddyhome/idea/vim/ex/vimscript/VimScriptParser.kt
Normal file
167
src/com/maddyhome/idea/vim/ex/vimscript/VimScriptParser.kt
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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.ex.vimscript
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.ex.CommandParser
|
||||||
|
import com.maddyhome.idea.vim.ex.ExException
|
||||||
|
import com.maddyhome.idea.vim.ui.VimRcFileState
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.regex.Matcher
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author vlan
|
||||||
|
*/
|
||||||
|
object VimScriptParser {
|
||||||
|
const val VIMRC_FILE_NAME = "ideavimrc"
|
||||||
|
val HOME_VIMRC_PATHS = arrayOf(".$VIMRC_FILE_NAME", "_$VIMRC_FILE_NAME")
|
||||||
|
val XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME
|
||||||
|
private val DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"")
|
||||||
|
private val SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'")
|
||||||
|
private val REFERENCE_EXPR = Pattern.compile("([A-Za-z_][A-Za-z_0-9]*)")
|
||||||
|
private val DEC_NUMBER = Pattern.compile("(\\d+)")
|
||||||
|
|
||||||
|
// This is a pattern used in ideavimrc parsing for a long time. It removes all trailing/leading spaced and blank lines
|
||||||
|
private val EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *")
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun findIdeaVimRc(): File? {
|
||||||
|
val homeDirName = System.getProperty("user.home")
|
||||||
|
// Check whether file exists in home dir
|
||||||
|
if (homeDirName != null) {
|
||||||
|
for (fileName in HOME_VIMRC_PATHS) {
|
||||||
|
val file = File(homeDirName, fileName)
|
||||||
|
if (file.exists()) {
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check in XDG config directory
|
||||||
|
val xdgConfigHomeProperty = System.getenv("XDG_CONFIG_HOME")
|
||||||
|
val xdgConfig = if (xdgConfigHomeProperty == null || xdgConfigHomeProperty == "") {
|
||||||
|
if (homeDirName != null) Paths.get(homeDirName, ".config", XDG_VIMRC_PATH).toFile() else null
|
||||||
|
} else {
|
||||||
|
File(xdgConfigHomeProperty, XDG_VIMRC_PATH)
|
||||||
|
}
|
||||||
|
return if (xdgConfig != null && xdgConfig.exists()) xdgConfig else null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findOrCreateIdeaVimRc(): File? {
|
||||||
|
val found = findIdeaVimRc()
|
||||||
|
if (found != null) return found
|
||||||
|
|
||||||
|
val homeDirName = System.getProperty("user.home")
|
||||||
|
if (homeDirName != null) {
|
||||||
|
for (fileName in HOME_VIMRC_PATHS) {
|
||||||
|
try {
|
||||||
|
val file = File(homeDirName, fileName)
|
||||||
|
file.createNewFile()
|
||||||
|
VimRcFileState.filePath = file.absolutePath
|
||||||
|
return file
|
||||||
|
} catch (ignored: IOException) {
|
||||||
|
// Try to create one of two files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun executeFile(file: File): List<String> {
|
||||||
|
val data = try {
|
||||||
|
readFile(file)
|
||||||
|
} catch (ignored: IOException) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
executeText(data)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
fun executeText(text: List<String>) {
|
||||||
|
for (line in text) {
|
||||||
|
// TODO: Build a proper parse tree for a VimL file instead of ignoring potentially nested lines (VIM-669)
|
||||||
|
if (line.startsWith(" ") || line.startsWith("\t")) continue
|
||||||
|
|
||||||
|
val lineToExecute = if (line.startsWith(":")) line.substring(1) else line
|
||||||
|
val commandParser = CommandParser.getInstance()
|
||||||
|
try {
|
||||||
|
val command = commandParser.parse(lineToExecute)
|
||||||
|
val commandHandler = commandParser.getCommandHandler(command)
|
||||||
|
if (commandHandler is VimScriptCommandHandler) {
|
||||||
|
commandHandler.execute(command)
|
||||||
|
}
|
||||||
|
} catch (ignored: ExException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ExException::class)
|
||||||
|
fun evaluate(expression: String, globals: Map<String?, Any?>): Any {
|
||||||
|
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
|
||||||
|
// support mapleader, VIM-650. See also VIM-669.
|
||||||
|
var m: Matcher = DOUBLE_QUOTED_STRING.matcher(expression)
|
||||||
|
if (m.matches()) return m.group(1)
|
||||||
|
|
||||||
|
m = SINGLE_QUOTED_STRING.matcher(expression)
|
||||||
|
if (m.matches()) return m.group(1)
|
||||||
|
|
||||||
|
m = REFERENCE_EXPR.matcher(expression)
|
||||||
|
if (m.matches()) {
|
||||||
|
val name = m.group(1)
|
||||||
|
val value = globals[name]
|
||||||
|
return value ?: throw ExException("Undefined variable: $name")
|
||||||
|
}
|
||||||
|
|
||||||
|
m = DEC_NUMBER.matcher(expression)
|
||||||
|
if (m.matches()) return m.group(1).toInt()
|
||||||
|
|
||||||
|
throw ExException("Invalid expression: $expression")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(ExException::class)
|
||||||
|
fun expressionToString(value: Any): String {
|
||||||
|
// TODO: Return meaningful value representations
|
||||||
|
return when (value) {
|
||||||
|
is String -> value
|
||||||
|
is Int -> value.toString()
|
||||||
|
else -> throw ExException("Cannot convert '$value' to string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun readFile(file: File): List<String> {
|
||||||
|
val lines = ArrayList<String>()
|
||||||
|
file.forEachLine { line -> lineProcessor(line, lines) }
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readText(data: CharSequence): List<String> {
|
||||||
|
val lines = ArrayList<String>()
|
||||||
|
EOL_SPLIT_PATTERN.split(data).forEach { line -> lineProcessor(line, lines) }
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lineProcessor(line: String, lines: ArrayList<String>) {
|
||||||
|
val trimmedLine = line.trim()
|
||||||
|
if (trimmedLine.isBlank()) return
|
||||||
|
lines += trimmedLine
|
||||||
|
}
|
||||||
|
}
|
@@ -23,6 +23,7 @@ import com.maddyhome.idea.vim.KeyHandler
|
|||||||
import com.maddyhome.idea.vim.VimPlugin
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
import com.maddyhome.idea.vim.action.change.VimRepeater
|
import com.maddyhome.idea.vim.action.change.VimRepeater
|
||||||
import com.maddyhome.idea.vim.command.MappingMode
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||||
import com.maddyhome.idea.vim.helper.StringHelper
|
import com.maddyhome.idea.vim.helper.StringHelper
|
||||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||||
@@ -45,13 +46,13 @@ import javax.swing.KeyStroke
|
|||||||
object VimExtensionFacade {
|
object VimExtensionFacade {
|
||||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@ScheduledForRemoval(inVersion = "0.57")
|
@ScheduledForRemoval(inVersion = "0.58")
|
||||||
@Deprecated("Only for EasyMotion support")
|
@Deprecated("Only for EasyMotion support")
|
||||||
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>, extensionHandler: VimExtensionHandler, recursive: Boolean) {
|
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>, extensionHandler: VimExtensionHandler, recursive: Boolean) {
|
||||||
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), extensionHandler, recursive)
|
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), extensionHandler, recursive)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ScheduledForRemoval(inVersion = "0.57")
|
@ScheduledForRemoval(inVersion = "0.58")
|
||||||
@Deprecated("Only for EasyMotion support")
|
@Deprecated("Only for EasyMotion support")
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
||||||
@@ -186,4 +187,10 @@ object VimExtensionFacade {
|
|||||||
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||||
VimPlugin.getRegister().setKeys(register, keys ?: emptyList())
|
VimPlugin.getRegister().setKeys(register, keys ?: emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Set the current contents of the given register */
|
||||||
|
@JvmStatic
|
||||||
|
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||||
|
VimPlugin.getRegister().setKeys(register, keys ?: emptyList(), type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,97 +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 com.maddyhome.idea.vim.extension;
|
|
||||||
|
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
|
||||||
import com.intellij.openapi.extensions.ExtensionPointListener;
|
|
||||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
|
||||||
import com.maddyhome.idea.vim.key.MappingOwner;
|
|
||||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
|
||||||
import com.maddyhome.idea.vim.option.ToggleOption;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO [VERSION UPDATE] this file cannot be converted to kt before 192 because of nullabilities problems in
|
|
||||||
* [ExtensionPointListener]. (In previous versions of IJ pluginDescriptor was nullable)
|
|
||||||
*/
|
|
||||||
public class VimExtensionRegistrar {
|
|
||||||
|
|
||||||
private static final Set<String> registeredExtensions = new HashSet<>();
|
|
||||||
private static boolean extensionRegistered = false;
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
|
|
||||||
|
|
||||||
public static void registerExtensions() {
|
|
||||||
if (extensionRegistered) return;
|
|
||||||
extensionRegistered = true;
|
|
||||||
|
|
||||||
// TODO: [VERSION UPDATE] since 191 use
|
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
|
||||||
//noinspection deprecation
|
|
||||||
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(new ExtensionPointListener<VimExtension>() {
|
|
||||||
@Override
|
|
||||||
public void extensionAdded(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
registerExtension(extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void extensionRemoved(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
|
||||||
unregisterExtension(extension);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized void registerExtension(@NotNull VimExtension extension) {
|
|
||||||
String name = extension.getName();
|
|
||||||
|
|
||||||
if (registeredExtensions.contains(name)) return;
|
|
||||||
|
|
||||||
registeredExtensions.add(name);
|
|
||||||
ToggleOption option = new ToggleOption(name, name, false);
|
|
||||||
option.addOptionChangeListener((oldValue, newValue) -> {
|
|
||||||
for (VimExtension extensionInListener : VimExtension.EP_NAME.getExtensionList()) {
|
|
||||||
if (name.equals(extensionInListener.getName())) {
|
|
||||||
if (OptionsManager.INSTANCE.isSet(name)) {
|
|
||||||
extensionInListener.init();
|
|
||||||
logger.info("IdeaVim extension '" + name + "' initialized");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
extensionInListener.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
OptionsManager.INSTANCE.addOption(option);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static synchronized void unregisterExtension(@NotNull VimExtension extension) {
|
|
||||||
String name = extension.getName();
|
|
||||||
|
|
||||||
if (!registeredExtensions.contains(name)) return;
|
|
||||||
|
|
||||||
registeredExtensions.remove(name);
|
|
||||||
extension.dispose();
|
|
||||||
OptionsManager.INSTANCE.removeOption(name);
|
|
||||||
MappingOwner.Plugin.Companion.remove(name);
|
|
||||||
logger.info("IdeaVim extension '" + name + "' disposed");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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.extension
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.logger
|
||||||
|
import com.intellij.openapi.extensions.ExtensionPointListener
|
||||||
|
import com.intellij.openapi.extensions.PluginDescriptor
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.key.MappingOwner.Plugin.Companion.remove
|
||||||
|
import com.maddyhome.idea.vim.option.OptionsManager.addOption
|
||||||
|
import com.maddyhome.idea.vim.option.OptionsManager.isSet
|
||||||
|
import com.maddyhome.idea.vim.option.OptionsManager.removeOption
|
||||||
|
import com.maddyhome.idea.vim.option.ToggleOption
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object VimExtensionRegistrar {
|
||||||
|
private val registeredExtensions: MutableSet<String> = HashSet()
|
||||||
|
private var extensionRegistered = false
|
||||||
|
private val logger = logger<VimExtensionRegistrar>()
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun registerExtensions() {
|
||||||
|
if (extensionRegistered) return
|
||||||
|
extensionRegistered = true
|
||||||
|
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(object : ExtensionPointListener<VimExtension> {
|
||||||
|
override fun extensionAdded(extension: VimExtension, pluginDescriptor: PluginDescriptor) {
|
||||||
|
registerExtension(extension)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun extensionRemoved(extension: VimExtension, pluginDescriptor: PluginDescriptor) {
|
||||||
|
unregisterExtension(extension)
|
||||||
|
}
|
||||||
|
}, true, VimPlugin.getInstance())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun registerExtension(extension: VimExtension) {
|
||||||
|
val name = extension.name
|
||||||
|
if (name in registeredExtensions) return
|
||||||
|
|
||||||
|
registeredExtensions.add(name)
|
||||||
|
val option = ToggleOption(name, name, false)
|
||||||
|
option.addOptionChangeListener { _, _ ->
|
||||||
|
for (extensionInListener in VimExtension.EP_NAME.extensionList) {
|
||||||
|
if (name != extensionInListener.name) continue
|
||||||
|
if (isSet(name)) {
|
||||||
|
extensionInListener.init()
|
||||||
|
logger.info("IdeaVim extension '$name' initialized")
|
||||||
|
} else {
|
||||||
|
extensionInListener.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun unregisterExtension(extension: VimExtension) {
|
||||||
|
val name = extension.name
|
||||||
|
if (name !in registeredExtensions) return
|
||||||
|
registeredExtensions.remove(name)
|
||||||
|
extension.dispose()
|
||||||
|
removeOption(name)
|
||||||
|
remove(name)
|
||||||
|
logger.info("IdeaVim extension '$name' disposed")
|
||||||
|
}
|
||||||
|
}
|
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
|
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public abstract class VimNonDisposableExtension implements VimExtension {
|
public abstract class VimNonDisposableExtension implements VimExtension {
|
||||||
@Override
|
@Override
|
||||||
|
@@ -7,6 +7,7 @@ import com.intellij.openapi.editor.Editor;
|
|||||||
import com.maddyhome.idea.vim.VimPlugin;
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
import com.maddyhome.idea.vim.command.*;
|
import com.maddyhome.idea.vim.command.*;
|
||||||
import com.maddyhome.idea.vim.common.TextRange;
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
|
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtension;
|
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
||||||
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
|
||||||
@@ -46,6 +47,117 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pairs of brackets that delimit different types of argument lists.
|
||||||
|
*/
|
||||||
|
static private class BracketPairs {
|
||||||
|
// NOTE: brackets must match by the position, and ordered by rank (highest to lowest).
|
||||||
|
@NotNull private final String openBrackets;
|
||||||
|
@NotNull private final String closeBrackets;
|
||||||
|
|
||||||
|
static class ParseError extends Exception {
|
||||||
|
public ParseError(@NotNull String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ParseState {
|
||||||
|
OPEN,
|
||||||
|
COLON,
|
||||||
|
CLOSE,
|
||||||
|
COMMA,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs @ref BracketPair from a string of bracket pairs with the same syntax
|
||||||
|
* as VIM's @c matchpairs option: "(:),{:},[:]"
|
||||||
|
*
|
||||||
|
* @param bracketPairs comma-separated list of colon-separated bracket pairs.
|
||||||
|
* @throws ParseError if a syntax error is detected.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
static BracketPairs fromBracketPairList(@NotNull final String bracketPairs) throws ParseError {
|
||||||
|
StringBuilder openBrackets = new StringBuilder();
|
||||||
|
StringBuilder closeBrackets = new StringBuilder();
|
||||||
|
ParseState state = ParseState.OPEN;
|
||||||
|
for (char ch : bracketPairs.toCharArray()) {
|
||||||
|
switch (state) {
|
||||||
|
case OPEN:
|
||||||
|
openBrackets.append(ch);
|
||||||
|
state = ParseState.COLON;
|
||||||
|
break;
|
||||||
|
case COLON:
|
||||||
|
if (ch == ':') {
|
||||||
|
state = ParseState.CLOSE;
|
||||||
|
} else {
|
||||||
|
throw new ParseError("expecting ':', but got '" + ch + "' instead");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CLOSE:
|
||||||
|
final char lastOpenBracket = openBrackets.charAt(openBrackets.length() - 1);
|
||||||
|
if (lastOpenBracket == ch) {
|
||||||
|
throw new ParseError("open and close brackets must be different");
|
||||||
|
}
|
||||||
|
closeBrackets.append(ch);
|
||||||
|
state = ParseState.COMMA;
|
||||||
|
break;
|
||||||
|
case COMMA:
|
||||||
|
if (ch == ',') {
|
||||||
|
state = ParseState.OPEN;
|
||||||
|
} else {
|
||||||
|
throw new ParseError("expecting ',', but got '" + ch + "' instead");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state != ParseState.COMMA) {
|
||||||
|
throw new ParseError("list of pairs is incomplete");
|
||||||
|
}
|
||||||
|
return new BracketPairs(openBrackets.toString(), closeBrackets.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
BracketPairs(@NotNull final String openBrackets, @NotNull final String closeBrackets) {
|
||||||
|
assert openBrackets.length() == closeBrackets.length();
|
||||||
|
this.openBrackets = openBrackets;
|
||||||
|
this.closeBrackets = closeBrackets;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getBracketPrio(char ch) {
|
||||||
|
return Math.max(openBrackets.toString().indexOf(ch), closeBrackets.indexOf(ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
char matchingBracket(char ch) {
|
||||||
|
int idx = closeBrackets.indexOf(ch);
|
||||||
|
if (idx != -1) {
|
||||||
|
return openBrackets.charAt(idx);
|
||||||
|
} else {
|
||||||
|
assert isOpenBracket(ch);
|
||||||
|
idx = openBrackets.toString().indexOf(ch);
|
||||||
|
return closeBrackets.charAt(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCloseBracket(final int ch) {
|
||||||
|
return closeBrackets.indexOf(ch) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isOpenBracket(final int ch) {
|
||||||
|
return openBrackets.toString().indexOf(ch) != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final BracketPairs DEFAULT_BRACKET_PAIRS = new BracketPairs("(", ")");
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static String bracketPairsVariable() {
|
||||||
|
final VimScriptGlobalEnvironment env = VimScriptGlobalEnvironment.getInstance();
|
||||||
|
final Object value = env.getVariables().get("g:argtextobj_pairs");
|
||||||
|
if (value instanceof String) {
|
||||||
|
return (String) value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A text object for an argument to a function definition or a call.
|
* A text object for an argument to a function definition or a call.
|
||||||
*/
|
*/
|
||||||
@@ -66,7 +178,18 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context, int count, int rawCount, @Nullable Argument argument) {
|
public @Nullable TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context, int count, int rawCount, @Nullable Argument argument) {
|
||||||
final ArgBoundsFinder finder = new ArgBoundsFinder(editor.getDocument());
|
BracketPairs bracketPairs = DEFAULT_BRACKET_PAIRS;
|
||||||
|
final String bracketPairsVar = bracketPairsVariable();
|
||||||
|
if (bracketPairsVar != null) {
|
||||||
|
try {
|
||||||
|
bracketPairs = BracketPairs.fromBracketPairList(bracketPairsVar);
|
||||||
|
} catch (BracketPairs.ParseError parseError) {
|
||||||
|
VimPlugin.showMessage("argtextobj: Invalid value of g:argtextobj_pairs -- " + parseError.getMessage());
|
||||||
|
VimPlugin.indicateError();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ArgBoundsFinder finder = new ArgBoundsFinder(editor.getDocument(), bracketPairs);
|
||||||
int pos = caret.getOffset();
|
int pos = caret.getOffset();
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
@@ -114,9 +237,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
|
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||||
emptyList()
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,8 +247,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
* position
|
* position
|
||||||
*/
|
*/
|
||||||
private static class ArgBoundsFinder {
|
private static class ArgBoundsFinder {
|
||||||
private final CharSequence text;
|
@NotNull private final CharSequence text;
|
||||||
private final Document document;
|
@NotNull private final Document document;
|
||||||
|
@NotNull private final BracketPairs brackets;
|
||||||
private int leftBound = Integer.MAX_VALUE;
|
private int leftBound = Integer.MAX_VALUE;
|
||||||
private int rightBound = Integer.MIN_VALUE;
|
private int rightBound = Integer.MIN_VALUE;
|
||||||
private int leftBracket;
|
private int leftBracket;
|
||||||
@@ -135,18 +257,13 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
private String error = null;
|
private String error = null;
|
||||||
private static final String QUOTES = "\"'";
|
private static final String QUOTES = "\"'";
|
||||||
|
|
||||||
// NOTE: brackets must match by the position, and ordered by rank.
|
|
||||||
// NOTE2: The original implementation worked for ], } and > as well, but because of some broken cases this feature
|
|
||||||
// was removed.
|
|
||||||
private static final String OPEN_BRACKETS = "("; // "[{(<"
|
|
||||||
private static final String CLOSE_BRACKETS = ")"; // "]})>"
|
|
||||||
|
|
||||||
private static final int MAX_SEARCH_LINES = 10;
|
private static final int MAX_SEARCH_LINES = 10;
|
||||||
private static final int MAX_SEARCH_OFFSET = MAX_SEARCH_LINES * 80;
|
private static final int MAX_SEARCH_OFFSET = MAX_SEARCH_LINES * 80;
|
||||||
|
|
||||||
ArgBoundsFinder(@NotNull Document document) {
|
ArgBoundsFinder(@NotNull Document document, @NotNull BracketPairs bracketPairs) {
|
||||||
this.text = document.getImmutableCharSequence();
|
this.text = document.getImmutableCharSequence();
|
||||||
this.document = document;
|
this.document = document;
|
||||||
|
this.brackets = bracketPairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,7 +284,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
rightBound = Math.max(position, rightBound);
|
rightBound = Math.max(position, rightBound);
|
||||||
getOutOfQuotedText();
|
getOutOfQuotedText();
|
||||||
if (rightBound == leftBound) {
|
if (rightBound == leftBound) {
|
||||||
if (isCloseBracket(getCharAt(rightBound))) {
|
if (brackets.isCloseBracket(getCharAt(rightBound))) {
|
||||||
--leftBound;
|
--leftBound;
|
||||||
} else {
|
} else {
|
||||||
++rightBound;
|
++rightBound;
|
||||||
@@ -196,14 +313,15 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
findRightBound();
|
findRightBound();
|
||||||
nextRight = rightBound + 1;
|
nextRight = rightBound + 1;
|
||||||
//
|
//
|
||||||
// If reached text boundaries or there is nothing between delimiters.
|
// If reached text boundaries
|
||||||
//
|
//
|
||||||
if (nextLeft < leftLimit || nextRight > rightLimit || (rightBound - leftBound) == 1) {
|
if (nextLeft < leftLimit || nextRight > rightLimit) {
|
||||||
error = "not an argument";
|
error = "not an argument";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bothBrackets = getCharAt(leftBound) != ',' && getCharAt(rightBound) != ',';
|
bothBrackets = getCharAt(leftBound) != ',' && getCharAt(rightBound) != ',';
|
||||||
if (bothBrackets && isIdentPreceding()) {
|
final boolean nonEmptyArg = (rightBound - leftBound) > 1;
|
||||||
|
if (bothBrackets && nonEmptyArg && isIdentPreceding()) {
|
||||||
// Looking at a pair of brackets preceded by an
|
// Looking at a pair of brackets preceded by an
|
||||||
// identifier -- single argument function call.
|
// identifier -- single argument function call.
|
||||||
break;
|
break;
|
||||||
@@ -257,7 +375,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
private boolean isIdentPreceding() {
|
private boolean isIdentPreceding() {
|
||||||
int i = leftBound - 1;
|
int i = leftBound - 1;
|
||||||
final int idEnd = i;
|
final int idEnd = i;
|
||||||
while (i > 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
|
while (i >= 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
return (idEnd - i) > 0 && Character.isJavaIdentifierStart(getCharAt(i + 1));
|
return (idEnd - i) > 0 && Character.isJavaIdentifierStart(getCharAt(i + 1));
|
||||||
@@ -296,8 +414,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
if (ch == ',') {
|
if (ch == ',') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isOpenBracket(ch)) {
|
if (brackets.isOpenBracket(ch)) {
|
||||||
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.FORWARD);
|
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.forward(brackets));
|
||||||
} else {
|
} else {
|
||||||
if (isQuoteChar(ch)) {
|
if (isQuoteChar(ch)) {
|
||||||
rightBound = skipQuotedTextForward(rightBound, rightBracket);
|
rightBound = skipQuotedTextForward(rightBound, rightBracket);
|
||||||
@@ -307,33 +425,14 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static char matchingBracket(char ch) {
|
|
||||||
int idx = CLOSE_BRACKETS.indexOf(ch);
|
|
||||||
if (idx != -1) {
|
|
||||||
return OPEN_BRACKETS.charAt(idx);
|
|
||||||
} else {
|
|
||||||
assert isOpenBracket(ch);
|
|
||||||
idx = OPEN_BRACKETS.indexOf(ch);
|
|
||||||
return CLOSE_BRACKETS.charAt(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isCloseBracket(final int ch) {
|
|
||||||
return CLOSE_BRACKETS.indexOf(ch) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isOpenBracket(final int ch) {
|
|
||||||
return OPEN_BRACKETS.indexOf(ch) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findLeftBound() {
|
private void findLeftBound() {
|
||||||
while (leftBound > leftBracket) {
|
while (leftBound > leftBracket) {
|
||||||
final char ch = getCharAt(leftBound);
|
final char ch = getCharAt(leftBound);
|
||||||
if (ch == ',') {
|
if (ch == ',') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isCloseBracket(ch)) {
|
if (brackets.isCloseBracket(ch)) {
|
||||||
leftBound = skipSexp(leftBound, leftBracket, SexpDirection.BACKWARD);
|
leftBound = skipSexp(leftBound, leftBracket, SexpDirection.backward(brackets));
|
||||||
} else {
|
} else {
|
||||||
if (isQuoteChar(ch)) {
|
if (isQuoteChar(ch)) {
|
||||||
leftBound = skipQuotedTextBackward(leftBound, leftBracket);
|
leftBound = skipQuotedTextBackward(leftBound, leftBracket);
|
||||||
@@ -365,7 +464,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
while (i <= end) {
|
while (i <= end) {
|
||||||
final char ch = getCharAt(i);
|
final char ch = getCharAt(i);
|
||||||
if (ch == quoteChar && !backSlash) {
|
if (ch == quoteChar && !backSlash) {
|
||||||
// Found matching quote and it's not escaped.
|
// Found a matching quote, and it's not escaped.
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
backSlash = ch == '\\' && !backSlash;
|
backSlash = ch == '\\' && !backSlash;
|
||||||
@@ -386,7 +485,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
// NOTE: doesn't handle cases like \\"str", but they make no
|
// NOTE: doesn't handle cases like \\"str", but they make no
|
||||||
// sense anyway.
|
// sense anyway.
|
||||||
if (ch == quoteChar && prevChar != '\\') {
|
if (ch == quoteChar && prevChar != '\\') {
|
||||||
// Found matching quote and it's not escaped.
|
// Found a matching quote, and it's not escaped.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
--i;
|
--i;
|
||||||
@@ -424,7 +523,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
abstract int skipQuotedText(int pos, int end, ArgBoundsFinder self);
|
abstract int skipQuotedText(int pos, int end, ArgBoundsFinder self);
|
||||||
|
|
||||||
static final SexpDirection FORWARD = new SexpDirection() {
|
static SexpDirection forward(BracketPairs brackets) {
|
||||||
|
return new SexpDirection() {
|
||||||
@Override
|
@Override
|
||||||
int delta() {
|
int delta() {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -432,12 +532,12 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isOpenBracket(char ch) {
|
boolean isOpenBracket(char ch) {
|
||||||
return ArgBoundsFinder.isOpenBracket(ch);
|
return brackets.isOpenBracket(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isCloseBracket(char ch) {
|
boolean isCloseBracket(char ch) {
|
||||||
return ArgBoundsFinder.isCloseBracket(ch);
|
return brackets.isCloseBracket(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -445,7 +545,10 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
return self.skipQuotedTextForward(pos, end);
|
return self.skipQuotedTextForward(pos, end);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
static final SexpDirection BACKWARD = new SexpDirection() {
|
}
|
||||||
|
|
||||||
|
static SexpDirection backward(BracketPairs brackets) {
|
||||||
|
return new SexpDirection() {
|
||||||
@Override
|
@Override
|
||||||
int delta() {
|
int delta() {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -453,12 +556,12 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isOpenBracket(char ch) {
|
boolean isOpenBracket(char ch) {
|
||||||
return ArgBoundsFinder.isCloseBracket(ch);
|
return brackets.isCloseBracket(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean isCloseBracket(char ch) {
|
boolean isCloseBracket(char ch) {
|
||||||
return ArgBoundsFinder.isOpenBracket(ch);
|
return brackets.isOpenBracket(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -467,6 +570,7 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skip over an S-expression considering priorities when unbalanced.
|
* Skip over an S-expression considering priorities when unbalanced.
|
||||||
@@ -489,11 +593,11 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
bracketStack.push(ch);
|
bracketStack.push(ch);
|
||||||
} else {
|
} else {
|
||||||
if (dir.isCloseBracket(ch)) {
|
if (dir.isCloseBracket(ch)) {
|
||||||
if (bracketStack.lastElement() == matchingBracket(ch)) {
|
if (bracketStack.lastElement() == brackets.matchingBracket(ch)) {
|
||||||
bracketStack.pop();
|
bracketStack.pop();
|
||||||
} else {
|
} else {
|
||||||
//noinspection StatementWithEmptyBody
|
//noinspection StatementWithEmptyBody
|
||||||
if (getBracketPrio(ch) < getBracketPrio(bracketStack.lastElement())) {
|
if (brackets.getBracketPrio(ch) < brackets.getBracketPrio(bracketStack.lastElement())) {
|
||||||
// (<...) -> (...)
|
// (<...) -> (...)
|
||||||
bracketStack.pop();
|
bracketStack.pop();
|
||||||
// Retry the same character again for cases like (...<<...).
|
// Retry the same character again for cases like (...<<...).
|
||||||
@@ -518,13 +622,6 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return rank of a bracket.
|
|
||||||
*/
|
|
||||||
static int getBracketPrio(char ch) {
|
|
||||||
return Math.max(OPEN_BRACKETS.indexOf(ch), CLOSE_BRACKETS.indexOf(ch));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a pair of brackets surrounding (leftBracket..rightBracket) block.
|
* Find a pair of brackets surrounding (leftBracket..rightBracket) block.
|
||||||
*
|
*
|
||||||
@@ -535,8 +632,8 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
boolean findOuterBrackets(final int start, final int end) {
|
boolean findOuterBrackets(final int start, final int end) {
|
||||||
boolean hasNewBracket = findPrevOpenBracket(start) && findNextCloseBracket(end);
|
boolean hasNewBracket = findPrevOpenBracket(start) && findNextCloseBracket(end);
|
||||||
while (hasNewBracket) {
|
while (hasNewBracket) {
|
||||||
final int leftPrio = getBracketPrio(getCharAt(leftBracket));
|
final int leftPrio = brackets.getBracketPrio(getCharAt(leftBracket));
|
||||||
final int rightPrio = getBracketPrio(getCharAt(rightBracket));
|
final int rightPrio = brackets.getBracketPrio(getCharAt(rightBracket));
|
||||||
if (leftPrio == rightPrio) {
|
if (leftPrio == rightPrio) {
|
||||||
// matching brackets
|
// matching brackets
|
||||||
return true;
|
return true;
|
||||||
@@ -569,9 +666,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
*/
|
*/
|
||||||
private boolean findPrevOpenBracket(final int start) {
|
private boolean findPrevOpenBracket(final int start) {
|
||||||
char ch;
|
char ch;
|
||||||
while (!isOpenBracket(ch = getCharAt(leftBracket))) {
|
while (!brackets.isOpenBracket(ch = getCharAt(leftBracket))) {
|
||||||
if (isCloseBracket(ch)) {
|
if (brackets.isCloseBracket(ch)) {
|
||||||
leftBracket = skipSexp(leftBracket, start, SexpDirection.BACKWARD);
|
leftBracket = skipSexp(leftBracket, start, SexpDirection.backward(brackets));
|
||||||
} else {
|
} else {
|
||||||
if (isQuoteChar(ch)) {
|
if (isQuoteChar(ch)) {
|
||||||
leftBracket = skipQuotedTextBackward(leftBracket, start);
|
leftBracket = skipQuotedTextBackward(leftBracket, start);
|
||||||
@@ -594,9 +691,9 @@ public class VimArgTextObjExtension implements VimExtension {
|
|||||||
*/
|
*/
|
||||||
private boolean findNextCloseBracket(final int end) {
|
private boolean findNextCloseBracket(final int end) {
|
||||||
char ch;
|
char ch;
|
||||||
while (!isCloseBracket(ch = getCharAt(rightBracket))) {
|
while (!brackets.isCloseBracket(ch = getCharAt(rightBracket))) {
|
||||||
if (isOpenBracket(ch)) {
|
if (brackets.isOpenBracket(ch)) {
|
||||||
rightBracket = skipSexp(rightBracket, end, SexpDirection.FORWARD);
|
rightBracket = skipSexp(rightBracket, end, SexpDirection.forward(brackets));
|
||||||
} else {
|
} else {
|
||||||
if (isQuoteChar(ch)) {
|
if (isQuoteChar(ch)) {
|
||||||
rightBracket = skipQuotedTextForward(rightBracket, end);
|
rightBracket = skipQuotedTextForward(rightBracket, end);
|
||||||
|
@@ -0,0 +1,349 @@
|
|||||||
|
/*
|
||||||
|
* 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.extension.exchange
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.application.runWriteAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.editor.LogicalPosition
|
||||||
|
import com.intellij.openapi.editor.colors.EditorColors
|
||||||
|
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||||
|
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||||
|
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||||
|
import com.intellij.openapi.util.Key
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.command.CommandState
|
||||||
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
|
import com.maddyhome.idea.vim.common.Mark
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.common.VimMark
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||||
|
import com.maddyhome.idea.vim.group.MarkGroup
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
|
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||||
|
import com.maddyhome.idea.vim.helper.StringHelper.stringToKeys
|
||||||
|
import com.maddyhome.idea.vim.helper.subMode
|
||||||
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This emulation misses:
|
||||||
|
* - `:ExchangeClear` command
|
||||||
|
* - `g:exchange_no_mappings` variable
|
||||||
|
* - `g:exchange_indent` variable (?)
|
||||||
|
* - Default mappings should not be applied if there is a mapping defined in `~/.ideavimrc`.
|
||||||
|
* This functionality requires rewriting of IdeaVim initialization, so that plugins would be
|
||||||
|
* loaded after `~/.ideavimrc` is executed (as vim works). But the `if no bindings` can be added even now.
|
||||||
|
* It just won't work if the binding is defined after `set exchange`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class VimExchangeExtension: VimExtension {
|
||||||
|
override fun getName() = "exchange"
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_CMD), owner, ExchangeHandler(false), false)
|
||||||
|
putExtensionHandlerMapping(MappingMode.X, parseKeys(EXCHANGE_CMD), owner, VExchangeHandler(), false)
|
||||||
|
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_CLEAR_CMD), owner, ExchangeClearHandler(), false)
|
||||||
|
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_LINE_CMD), owner, ExchangeHandler(true), false)
|
||||||
|
|
||||||
|
putKeyMapping(MappingMode.N, parseKeys("cx"), owner, parseKeys(EXCHANGE_CMD), true)
|
||||||
|
putKeyMapping(MappingMode.X, parseKeys("X"), owner, parseKeys(EXCHANGE_CMD), true)
|
||||||
|
putKeyMapping(MappingMode.N, parseKeys("cxc"), owner, parseKeys(EXCHANGE_CLEAR_CMD), true)
|
||||||
|
putKeyMapping(MappingMode.N, parseKeys("cxx"), owner, parseKeys(EXCHANGE_LINE_CMD), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||||
|
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||||
|
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||||
|
|
||||||
|
val EXCHANGE_KEY = Key<Exchange>("exchange");
|
||||||
|
|
||||||
|
class Exchange(val type: CommandState.SubMode, val start: Mark, val end: Mark, val text: String) {
|
||||||
|
private var myHighlighter: RangeHighlighter? = null
|
||||||
|
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||||
|
myHighlighter = highlighter
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearExchange(editor: Editor) {
|
||||||
|
editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
|
||||||
|
editor.markupModel.removeHighlighter(it)
|
||||||
|
}
|
||||||
|
editor.putUserData(EXCHANGE_KEY, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExchangeHandler(private val isLine: Boolean) : VimExtensionHandler {
|
||||||
|
|
||||||
|
override fun isRepeatable() = true
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
setOperatorFunction(Operator(false))
|
||||||
|
executeNormal(parseKeys(if(isLine) "g@_" else "g@"), editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ExchangeClearHandler: VimExtensionHandler {
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
clearExchange(editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VExchangeHandler: VimExtensionHandler {
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
runWriteAction {
|
||||||
|
val subMode = editor.subMode
|
||||||
|
// Leave visual mode to create selection marks
|
||||||
|
executeNormal(parseKeys("<Esc>"), editor)
|
||||||
|
Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Operator(private val isVisual: Boolean): OperatorFunction {
|
||||||
|
fun Editor.getMarkOffset(mark: Mark) = EditorHelper.getOffset(this, mark.logicalLine, mark.col)
|
||||||
|
fun CommandState.SubMode.getString() = when(this) {
|
||||||
|
CommandState.SubMode.VISUAL_CHARACTER -> "v"
|
||||||
|
CommandState.SubMode.VISUAL_LINE -> "V"
|
||||||
|
CommandState.SubMode.VISUAL_BLOCK -> "\\<C-V>"
|
||||||
|
else -> throw Error("Invalid SubMode: $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
|
||||||
|
fun highlightExchange(ex: Exchange): RangeHighlighter {
|
||||||
|
val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
|
||||||
|
val hlArea = when(ex.type) {
|
||||||
|
CommandState.SubMode.VISUAL_LINE -> HighlighterTargetArea.LINES_IN_RANGE
|
||||||
|
// TODO: handle other modes
|
||||||
|
else -> HighlighterTargetArea.EXACT_RANGE
|
||||||
|
}
|
||||||
|
return editor.markupModel.addRangeHighlighter(
|
||||||
|
editor.getMarkOffset(ex.start),
|
||||||
|
(editor.getMarkOffset(ex.end) + 1).coerceAtMost(EditorHelper.getFileSize(editor, true)),
|
||||||
|
HighlighterLayer.SELECTION - 1,
|
||||||
|
attributes,
|
||||||
|
hlArea
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val currentExchange = getExchange(editor, isVisual, selectionType)
|
||||||
|
val exchange1 = editor.getUserData(EXCHANGE_KEY)
|
||||||
|
if (exchange1 == null) {
|
||||||
|
val highlighter = highlightExchange(currentExchange)
|
||||||
|
currentExchange.setHighlighter(highlighter)
|
||||||
|
editor.putUserData(EXCHANGE_KEY, currentExchange)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
val cmp = compareExchanges(exchange1, currentExchange)
|
||||||
|
var reverse = false
|
||||||
|
var expand = false
|
||||||
|
val (ex1, ex2) = when(cmp) {
|
||||||
|
ExchangeCompareResult.OVERLAP -> return false
|
||||||
|
ExchangeCompareResult.OUTER -> {
|
||||||
|
reverse = true
|
||||||
|
expand = true
|
||||||
|
Pair(currentExchange, exchange1)
|
||||||
|
}
|
||||||
|
ExchangeCompareResult.INNER -> {
|
||||||
|
expand = true
|
||||||
|
Pair(exchange1, currentExchange)
|
||||||
|
}
|
||||||
|
ExchangeCompareResult.GT -> {
|
||||||
|
reverse = true
|
||||||
|
Pair(currentExchange, exchange1)
|
||||||
|
}
|
||||||
|
ExchangeCompareResult.LT -> {
|
||||||
|
Pair(exchange1, currentExchange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exchange(editor, ex1, ex2, reverse, expand)
|
||||||
|
clearExchange(editor)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun exchange(editor: Editor, ex1: Exchange, ex2: Exchange, reverse: Boolean, expand: Boolean) {
|
||||||
|
fun pasteExchange(sourceExchange: Exchange, targetExchange: Exchange) {
|
||||||
|
VimPlugin.getMark().setChangeMarks(editor, TextRange(editor.getMarkOffset(targetExchange.start), editor.getMarkOffset(targetExchange.end)+1))
|
||||||
|
// do this instead of direct text manipulation to set change marks
|
||||||
|
setRegister('z', stringToKeys(sourceExchange.text), SelectionType.fromSubMode(sourceExchange.type))
|
||||||
|
executeNormal(stringToKeys("""`[${targetExchange.type.getString()}`]"zp"""), editor)
|
||||||
|
}
|
||||||
|
fun fixCursor(ex1: Exchange, ex2: Exchange, reverse: Boolean) {
|
||||||
|
val primaryCaret = editor.caretModel.primaryCaret
|
||||||
|
if(reverse) {
|
||||||
|
primaryCaret.moveToOffset(editor.getMarkOffset(ex1.start))
|
||||||
|
} else {
|
||||||
|
if (ex1.start.logicalLine == ex2.start.logicalLine) {
|
||||||
|
val horizontalOffset = ex1.end.col - ex2.end.col
|
||||||
|
primaryCaret.moveToLogicalPosition(LogicalPosition(ex1.start.logicalLine, ex1.start.col - horizontalOffset))
|
||||||
|
} else if(ex1.end.logicalLine - ex1.start.logicalLine != ex2.end.logicalLine - ex2.start.logicalLine) {
|
||||||
|
val verticalOffset = ex1.end.logicalLine - ex2.end.logicalLine
|
||||||
|
primaryCaret.moveToLogicalPosition(LogicalPosition(ex1.start.logicalLine - verticalOffset, ex1.start.col))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val zRegText = getRegister('z')
|
||||||
|
val unnRegText = getRegister('"')
|
||||||
|
val startRegText = getRegister('*')
|
||||||
|
val plusRegText = getRegister('+')
|
||||||
|
runWriteAction {
|
||||||
|
// TODO handle:
|
||||||
|
// " Compare using =~ because "'==' != 0" returns 0
|
||||||
|
// let indent = s:get_setting('exchange_indent', 1) !~ 0 && a:x.type ==# 'V' && a:y.type ==# 'V'
|
||||||
|
pasteExchange(ex1, ex2)
|
||||||
|
if (!expand) {
|
||||||
|
pasteExchange(ex2, ex1)
|
||||||
|
}
|
||||||
|
// TODO: handle: if ident
|
||||||
|
if (!expand) {
|
||||||
|
fixCursor(ex1, ex2, reverse)
|
||||||
|
}
|
||||||
|
setRegister('z', zRegText)
|
||||||
|
setRegister('"', unnRegText)
|
||||||
|
setRegister('*', startRegText)
|
||||||
|
setRegister('+', plusRegText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult {
|
||||||
|
fun intersects(x: Exchange, y: Exchange) =
|
||||||
|
x.end.logicalLine < y.start.logicalLine ||
|
||||||
|
x.start.logicalLine > y.end.logicalLine ||
|
||||||
|
x.end.col < y.start.col ||
|
||||||
|
x.start.col > y.end.col
|
||||||
|
|
||||||
|
fun comparePos(x: Mark, y: Mark): Int =
|
||||||
|
if (x.logicalLine == y.logicalLine) {
|
||||||
|
x.col - y.col
|
||||||
|
} else {
|
||||||
|
x.logicalLine - y.logicalLine
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (x.type == CommandState.SubMode.VISUAL_BLOCK && y.type == CommandState.SubMode.VISUAL_BLOCK) {
|
||||||
|
when {
|
||||||
|
intersects(x, y) -> {
|
||||||
|
ExchangeCompareResult.OVERLAP
|
||||||
|
}
|
||||||
|
x.start.col <= y.start.col -> {
|
||||||
|
ExchangeCompareResult.LT
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
ExchangeCompareResult.GT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (comparePos(x.start, y.start) <=0 && comparePos(x.end, y.end) >=0) {
|
||||||
|
ExchangeCompareResult.OUTER
|
||||||
|
} else if (comparePos(y.start, x.start) <=0 && comparePos(y.end, x.end) >=0) {
|
||||||
|
ExchangeCompareResult.INNER
|
||||||
|
} else if (comparePos(x.start, y.end) <=0 && comparePos(y.start, x.end) <=0 ||
|
||||||
|
comparePos(y.start, x.end) <=0 && comparePos(x.start, y.end) <=0
|
||||||
|
) {
|
||||||
|
ExchangeCompareResult.OVERLAP
|
||||||
|
} else {
|
||||||
|
val cmp = comparePos(x.start, y.start)
|
||||||
|
when {
|
||||||
|
cmp == 0 -> ExchangeCompareResult.OVERLAP
|
||||||
|
cmp < 0 -> ExchangeCompareResult.LT
|
||||||
|
else -> ExchangeCompareResult.GT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ExchangeCompareResult {
|
||||||
|
OVERLAP,
|
||||||
|
OUTER,
|
||||||
|
INNER,
|
||||||
|
LT,
|
||||||
|
GT,
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
|
||||||
|
fun getEndCol(selectionEnd: Mark, type: CommandState.SubMode): Int {
|
||||||
|
return if (type == CommandState.SubMode.VISUAL_LINE) {
|
||||||
|
EditorHelper.getLineLength(editor, selectionEnd.logicalLine)
|
||||||
|
} else {
|
||||||
|
selectionEnd.col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: improve KeyStroke list to sting conversion
|
||||||
|
fun getRegisterText(reg: Char): String = getRegister(reg)?.map { it.keyChar }?.joinToString("") ?: ""
|
||||||
|
fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
|
||||||
|
val (startMark, endMark) =
|
||||||
|
if (isVisual) {
|
||||||
|
Pair(MarkGroup.MARK_VISUAL_START, MarkGroup.MARK_VISUAL_END)
|
||||||
|
} else {
|
||||||
|
Pair(MarkGroup.MARK_CHANGE_START, MarkGroup.MARK_CHANGE_END)
|
||||||
|
}
|
||||||
|
val marks = VimPlugin.getMark()
|
||||||
|
return Pair(marks.getMark(editor, startMark)!!, marks.getMark(editor, endMark)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
val unnRegText = getRegister('"')
|
||||||
|
val starRegText = getRegister('*')
|
||||||
|
val plusRegText = getRegister('+')
|
||||||
|
|
||||||
|
var (selectionStart, selectionEnd) = getMarks(isVisual)
|
||||||
|
if (isVisual) {
|
||||||
|
executeNormal(parseKeys("gvy"), editor)
|
||||||
|
// TODO: handle
|
||||||
|
//if &selection ==# 'exclusive' && start != end
|
||||||
|
// let end.column -= len(matchstr(@@, '\_.$'))
|
||||||
|
} else {
|
||||||
|
selectionEnd = selectionEnd.let {
|
||||||
|
VimMark.create(
|
||||||
|
it.key,
|
||||||
|
it.logicalLine,
|
||||||
|
getEndCol(it, selectionType.toSubMode()),
|
||||||
|
it.filename,
|
||||||
|
it.protocol
|
||||||
|
)!!
|
||||||
|
}
|
||||||
|
when (selectionType) {
|
||||||
|
SelectionType.LINE_WISE -> executeNormal(stringToKeys("`[V`]y"), editor)
|
||||||
|
SelectionType.BLOCK_WISE -> executeNormal(stringToKeys("""`[<C-V>`]y"""), editor)
|
||||||
|
SelectionType.CHARACTER_WISE -> executeNormal(stringToKeys("`[v`]y"), editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = getRegisterText('"')
|
||||||
|
|
||||||
|
setRegister('"', unnRegText)
|
||||||
|
setRegister('*', starRegText)
|
||||||
|
setRegister('+', plusRegText)
|
||||||
|
|
||||||
|
return Exchange(
|
||||||
|
selectionType.toSubMode(),
|
||||||
|
selectionStart,
|
||||||
|
selectionEnd,
|
||||||
|
text
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* 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.extension.replacewithregister
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.editor.Caret
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.command.CommandState
|
||||||
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
|
import com.maddyhome.idea.vim.command.SelectionType
|
||||||
|
import com.maddyhome.idea.vim.command.isLine
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||||
|
import com.maddyhome.idea.vim.group.MotionGroup
|
||||||
|
import com.maddyhome.idea.vim.group.RegisterGroup
|
||||||
|
import com.maddyhome.idea.vim.group.copy.PutData
|
||||||
|
import com.maddyhome.idea.vim.group.visual.VimSelection
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||||
|
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||||
|
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||||
|
import com.maddyhome.idea.vim.helper.subMode
|
||||||
|
import com.maddyhome.idea.vim.helper.vimForEachCaret
|
||||||
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
|
|
||||||
|
|
||||||
|
class ReplaceWithRegister : VimExtension {
|
||||||
|
override fun getName(): String = "ReplaceWithRegister"
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.N, parseKeys(RWR_OPERATOR), owner, RwrMotion(), false)
|
||||||
|
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.N, parseKeys(RWR_LINE), owner, RwrLine(), false)
|
||||||
|
VimExtensionFacade.putExtensionHandlerMapping(MappingMode.X, parseKeys(RWR_VISUAL), owner, RwrVisual(), false)
|
||||||
|
|
||||||
|
putKeyMapping(MappingMode.N, parseKeys("gr"), owner, parseKeys(RWR_OPERATOR), true)
|
||||||
|
putKeyMapping(MappingMode.N, parseKeys("grr"), owner, parseKeys(RWR_LINE), true)
|
||||||
|
putKeyMapping(MappingMode.X, parseKeys("gr"), owner, parseKeys(RWR_VISUAL), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RwrVisual : VimExtensionHandler {
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
val caretsAndSelections = mutableMapOf<Caret, VimSelection>()
|
||||||
|
val typeInEditor = SelectionType.fromSubMode(editor.subMode)
|
||||||
|
editor.vimForEachCaret { caret ->
|
||||||
|
val selectionStart = caret.selectionStart
|
||||||
|
val selectionEnd = caret.selectionEnd
|
||||||
|
|
||||||
|
caretsAndSelections += caret to VimSelection.create(selectionStart, selectionEnd - 1, typeInEditor, editor)
|
||||||
|
}
|
||||||
|
doReplace(editor, PutData.VisualSelection(caretsAndSelections, typeInEditor))
|
||||||
|
editor.exitVisualMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RwrMotion : VimExtensionHandler {
|
||||||
|
override fun isRepeatable(): Boolean = true
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
setOperatorFunction(Operator())
|
||||||
|
executeNormal(parseKeys("g@"), editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RwrLine : VimExtensionHandler {
|
||||||
|
override fun isRepeatable(): Boolean = true
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext) {
|
||||||
|
val caretsAndSelections = mutableMapOf<Caret, VimSelection>()
|
||||||
|
editor.vimForEachCaret { caret ->
|
||||||
|
val logicalLine = caret.logicalPosition.line
|
||||||
|
val lineStart = editor.document.getLineStartOffset(logicalLine)
|
||||||
|
val lineEnd = editor.document.getLineEndOffset(logicalLine)
|
||||||
|
|
||||||
|
caretsAndSelections += caret to VimSelection.create(lineStart, lineEnd, SelectionType.LINE_WISE, editor)
|
||||||
|
}
|
||||||
|
|
||||||
|
val visualSelection = PutData.VisualSelection(caretsAndSelections, SelectionType.LINE_WISE)
|
||||||
|
doReplace(editor, visualSelection)
|
||||||
|
|
||||||
|
editor.vimForEachCaret { caret ->
|
||||||
|
val vimStart = caretsAndSelections[caret]?.vimStart
|
||||||
|
if (vimStart != null) {
|
||||||
|
MotionGroup.moveCaret(editor, caret, vimStart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Operator : OperatorFunction {
|
||||||
|
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
|
||||||
|
val range = getRange(editor) ?: return false
|
||||||
|
val visualSelection = PutData.VisualSelection(mapOf(editor.caretModel.primaryCaret to VimSelection.create(range.startOffset, range.endOffset - 1, selectionType, editor)), selectionType)
|
||||||
|
doReplace(editor, visualSelection)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRange(editor: Editor): TextRange? = when (CommandState.getInstance(editor).mode) {
|
||||||
|
CommandState.Mode.COMMAND -> VimPlugin.getMark().getChangeMarks(editor)
|
||||||
|
CommandState.Mode.VISUAL -> editor.caretModel.primaryCaret.run { TextRange(selectionStart, selectionEnd) }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
|
||||||
|
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
|
||||||
|
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
|
||||||
|
|
||||||
|
private fun doReplace(editor: Editor, visualSelection: PutData.VisualSelection) {
|
||||||
|
val savedRegister = VimPlugin.getRegister().lastRegister ?: return
|
||||||
|
|
||||||
|
var usedType = savedRegister.type
|
||||||
|
var usedText = savedRegister.text
|
||||||
|
if (usedType.isLine && usedText?.endsWith('\n') == true) {
|
||||||
|
// Code from original plugin implementation. Correct text for linewise selected text
|
||||||
|
usedText = usedText.dropLast(1)
|
||||||
|
usedType = SelectionType.CHARACTER_WISE
|
||||||
|
}
|
||||||
|
|
||||||
|
val textData = PutData.TextData(usedText, usedType, savedRegister.transferableData)
|
||||||
|
|
||||||
|
val putData = PutData(textData, visualSelection, 1, insertTextBeforeCaret = true, _indent = true, caretAfterInsertedText = false, putToLine = -1)
|
||||||
|
VimPlugin.getPut().putText(editor, EditorDataContext(editor), putData)
|
||||||
|
|
||||||
|
VimPlugin.getRegister().saveRegister(savedRegister.name, savedRegister)
|
||||||
|
VimPlugin.getRegister().saveRegister(RegisterGroup.DEFAULT_REGISTER, savedRegister)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -144,8 +144,7 @@ public class VimTextObjEntireExtension implements VimExtension {
|
|||||||
} else {
|
} else {
|
||||||
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
textObjectHandler, Command.Type.MOTION,
|
textObjectHandler, Command.Type.MOTION,
|
||||||
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE),
|
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||||
emptyList())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,7 +32,10 @@ import com.intellij.openapi.editor.*;
|
|||||||
import com.intellij.openapi.editor.actionSystem.ActionPlan;
|
import com.intellij.openapi.editor.actionSystem.ActionPlan;
|
||||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
||||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx;
|
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx;
|
||||||
import com.intellij.openapi.editor.event.*;
|
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||||
|
import com.intellij.openapi.editor.event.DocumentListener;
|
||||||
|
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
||||||
|
import com.intellij.openapi.editor.event.EditorMouseListener;
|
||||||
import com.intellij.openapi.editor.ex.EditorEx;
|
import com.intellij.openapi.editor.ex.EditorEx;
|
||||||
import com.intellij.openapi.editor.impl.TextRangeInterval;
|
import com.intellij.openapi.editor.impl.TextRangeInterval;
|
||||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||||
@@ -84,6 +87,8 @@ public class ChangeGroup {
|
|||||||
private static final String VIM_MOTION_WORD_END_RIGHT = "VimMotionWordEndRightAction";
|
private static final String VIM_MOTION_WORD_END_RIGHT = "VimMotionWordEndRightAction";
|
||||||
private static final String VIM_MOTION_BIG_WORD_END_RIGHT = "VimMotionBigWordEndRightAction";
|
private static final String VIM_MOTION_BIG_WORD_END_RIGHT = "VimMotionBigWordEndRightAction";
|
||||||
private static final String VIM_MOTION_CAMEL_END_RIGHT = "VimMotionCamelEndRightAction";
|
private static final String VIM_MOTION_CAMEL_END_RIGHT = "VimMotionCamelEndRightAction";
|
||||||
|
private static final ImmutableSet<String> wordMotions =
|
||||||
|
ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT);
|
||||||
|
|
||||||
private @Nullable Command lastInsert;
|
private @Nullable Command lastInsert;
|
||||||
|
|
||||||
@@ -431,18 +436,12 @@ public class ChangeGroup {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
public void editorCreated(Editor editor) {
|
||||||
final Editor editor = event.getEditor();
|
|
||||||
EventFacade.getInstance().addEditorMouseListener(editor, listener);
|
EventFacade.getInstance().addEditorMouseListener(editor, listener);
|
||||||
UserDataManager.setVimChangeGroup(editor, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void editorReleased(@NotNull EditorFactoryEvent event) {
|
public void editorReleased(Editor editor) {
|
||||||
final Editor editor = event.getEditor();
|
|
||||||
if (UserDataManager.getVimChangeGroup(editor)) {
|
|
||||||
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
|
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
|
||||||
UserDataManager.setVimChangeGroup(editor, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1223,13 +1222,12 @@ public class ChangeGroup {
|
|||||||
boolean bigWord = id.equals(VIM_MOTION_BIG_WORD_RIGHT);
|
boolean bigWord = id.equals(VIM_MOTION_BIG_WORD_RIGHT);
|
||||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||||
final int offset = caret.getOffset();
|
final int offset = caret.getOffset();
|
||||||
if (EditorHelper.getFileSize(editor) > 0) {
|
int fileSize = EditorHelper.getFileSize(editor);
|
||||||
|
if (fileSize > 0) {
|
||||||
final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), bigWord);
|
final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), bigWord);
|
||||||
if (charType != CharacterHelper.CharacterType.WHITESPACE) {
|
if (charType != CharacterHelper.CharacterType.WHITESPACE) {
|
||||||
final boolean lastWordChar = offset > EditorHelper.getFileSize(editor) ||
|
final boolean lastWordChar = offset >= fileSize - 1 ||
|
||||||
CharacterHelper.charType(chars.charAt(offset + 1), bigWord) != charType;
|
CharacterHelper.charType(chars.charAt(offset + 1), bigWord) != charType;
|
||||||
final ImmutableSet<String> wordMotions =
|
|
||||||
ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT);
|
|
||||||
if (wordMotions.contains(id) && lastWordChar && motion.getCount() == 1) {
|
if (wordMotions.contains(id) && lastWordChar && motion.getCount() == 1) {
|
||||||
final boolean res = deleteCharacter(editor, caret, 1, true);
|
final boolean res = deleteCharacter(editor, caret, 1, true);
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -1258,7 +1256,7 @@ public class ChangeGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (kludge) {
|
if (kludge) {
|
||||||
int size = EditorHelper.getFileSize(editor);
|
int size = fileSize;
|
||||||
int cnt = count * motion.getCount();
|
int cnt = count * motion.getCount();
|
||||||
int pos1 = SearchHelper.findNextWordEnd(chars, offset, size, cnt, bigWord, false);
|
int pos1 = SearchHelper.findNextWordEnd(chars, offset, size, cnt, bigWord, false);
|
||||||
int pos2 = SearchHelper.findNextWordEnd(chars, pos1, size, -cnt, bigWord, false);
|
int pos2 = SearchHelper.findNextWordEnd(chars, pos1, size, -cnt, bigWord, false);
|
||||||
@@ -1751,7 +1749,7 @@ public class ChangeGroup {
|
|||||||
if (type != null) {
|
if (type != null) {
|
||||||
final int start = range.getStartOffset();
|
final int start = range.getStartOffset();
|
||||||
VimPlugin.getMark().setMark(editor, MarkGroup.MARK_CHANGE_POS, start);
|
VimPlugin.getMark().setMark(editor, MarkGroup.MARK_CHANGE_POS, start);
|
||||||
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, start));
|
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, start+1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -60,7 +60,6 @@ public class DigraphGroup {
|
|||||||
|
|
||||||
final String digraph = keys.get(ch);
|
final String digraph = keys.get(ch);
|
||||||
final String digraphText = digraph == null ? "" : ", Digr " + digraph;
|
final String digraphText = digraph == null ? "" : ", Digr " + digraph;
|
||||||
final String hexText = (ch > 0xff) ? String.format("Hex %04x", (int) ch) : String.format("Hex %02x", (int) ch);
|
|
||||||
|
|
||||||
if (ch < 0x100) {
|
if (ch < 0x100) {
|
||||||
VimPlugin.showMessage(String.format("<%s> %d, Hex %02x, Oct %03o%s",
|
VimPlugin.showMessage(String.format("<%s> %d, Hex %02x, Oct %03o%s",
|
||||||
|
@@ -52,12 +52,10 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
@State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
||||||
public class EditorGroup implements PersistentStateComponent<Element> {
|
public class EditorGroup implements PersistentStateComponent<Element> {
|
||||||
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
|
||||||
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
|
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
|
||||||
public static final String EDITOR_STORE_ELEMENT = "editor";
|
public static final String EDITOR_STORE_ELEMENT = "editor";
|
||||||
|
|
||||||
private boolean isBlockCursor = false;
|
private boolean isBlockCursor = false;
|
||||||
private boolean isAnimatedScrolling = false;
|
|
||||||
private boolean isRefrainFromScrolling = false;
|
private boolean isRefrainFromScrolling = false;
|
||||||
private Boolean isKeyRepeat = null;
|
private Boolean isKeyRepeat = null;
|
||||||
|
|
||||||
@@ -69,18 +67,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void turnOn() {
|
|
||||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
|
||||||
VimPlugin.getEditor().editorCreated(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void turnOff() {
|
|
||||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
|
||||||
VimPlugin.getEditor().editorDeinit(editor, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initLineNumbers(final @NotNull Editor editor) {
|
private void initLineNumbers(final @NotNull Editor editor) {
|
||||||
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
|
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
|
||||||
return;
|
return;
|
||||||
@@ -244,7 +230,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
|||||||
|
|
||||||
public void editorCreated(@NotNull Editor editor) {
|
public void editorCreated(@NotNull Editor editor) {
|
||||||
isBlockCursor = editor.getSettings().isBlockCursor();
|
isBlockCursor = editor.getSettings().isBlockCursor();
|
||||||
isAnimatedScrolling = editor.getSettings().isAnimatedScrolling();
|
|
||||||
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
|
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
|
||||||
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
||||||
VimPlugin.getKey().registerRequiredShortcutKeys(editor);
|
VimPlugin.getKey().registerRequiredShortcutKeys(editor);
|
||||||
@@ -258,7 +243,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
|||||||
KeyHandler.getInstance().reset(editor);
|
KeyHandler.getInstance().reset(editor);
|
||||||
}
|
}
|
||||||
editor.getSettings().setBlockCursor(!CommandStateHelper.inInsertMode(editor));
|
editor.getSettings().setBlockCursor(!CommandStateHelper.inInsertMode(editor));
|
||||||
editor.getSettings().setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
|
||||||
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +251,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
|||||||
UserDataManager.unInitializeEditor(editor);
|
UserDataManager.unInitializeEditor(editor);
|
||||||
VimPlugin.getKey().unregisterShortcutKeys(editor);
|
VimPlugin.getKey().unregisterShortcutKeys(editor);
|
||||||
editor.getSettings().setBlockCursor(isBlockCursor);
|
editor.getSettings().setBlockCursor(isBlockCursor);
|
||||||
editor.getSettings().setAnimatedScrolling(isAnimatedScrolling);
|
|
||||||
editor.getSettings().setRefrainFromScrolling(isRefrainFromScrolling);
|
editor.getSettings().setRefrainFromScrolling(isRefrainFromScrolling);
|
||||||
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user