mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
119 Commits
0.55.1-EAP
...
0.56
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0a4683d908 | ||
![]() |
4c280b0193 | ||
![]() |
e88a3deafd | ||
![]() |
bd172b3300 | ||
![]() |
95c7a13cb5 | ||
![]() |
b1ddf03385 | ||
![]() |
a83c326736 | ||
![]() |
b1acb56247 | ||
![]() |
caa4731a13 | ||
![]() |
5b0ece7a91 | ||
![]() |
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 | ||
![]() |
2f5946640e | ||
![]() |
7cdb7dc308 | ||
![]() |
c0038d0373 | ||
![]() |
2820decb5e | ||
![]() |
c64f368e6a | ||
![]() |
b7c8e84f5e | ||
![]() |
5acf6c9158 | ||
![]() |
a8197b0c84 | ||
![]() |
2e03062c24 | ||
![]() |
7fb60a185b | ||
![]() |
0327ea972b | ||
![]() |
561cc77ecc | ||
![]() |
a1ab4acd14 | ||
![]() |
d4939803da | ||
![]() |
730ce3aca9 | ||
![]() |
1893dc6afd | ||
![]() |
6ec39314ee | ||
![]() |
c87528939b | ||
![]() |
e56646105d | ||
![]() |
b8a40d93f7 | ||
![]() |
4bfc025248 | ||
![]() |
36f6027b0e | ||
![]() |
e032377e68 | ||
![]() |
929eee4a12 | ||
![]() |
61ce50264a | ||
![]() |
48927b1207 | ||
![]() |
0820893dc6 | ||
![]() |
dd6079cfa6 | ||
![]() |
3d7d75bae4 | ||
![]() |
6da4d0ce5e | ||
![]() |
4994d70b1a | ||
![]() |
c873081dc3 | ||
![]() |
070237f77f | ||
![]() |
eb01b25f35 | ||
![]() |
c0c9cfaf86 | ||
![]() |
304f860eb2 | ||
![]() |
2f18b25593 | ||
![]() |
14c8b6a248 | ||
![]() |
adaa683e58 | ||
![]() |
9b71215cde | ||
![]() |
8be572f976 | ||
![]() |
4f43bcffb9 | ||
![]() |
29e4dc5fb5 | ||
![]() |
0dc95cb13c | ||
![]() |
5ee0a93675 | ||
![]() |
767b3c4a39 | ||
![]() |
bb948a463c | ||
![]() |
e4e9a03d0a | ||
![]() |
50ba386f59 | ||
![]() |
79d0565c2d | ||
![]() |
bcc9b0a7b1 | ||
![]() |
2c8f4940b9 | ||
![]() |
41876cf8fd | ||
![]() |
f6fd0b52f0 | ||
![]() |
843faa7cc6 | ||
![]() |
a8af2c3242 | ||
![]() |
e5bfad974e | ||
![]() |
59d87e0c94 | ||
![]() |
50c2d04503 | ||
![]() |
480de62686 | ||
![]() |
955b501058 | ||
![]() |
d985527624 | ||
![]() |
afbe7f0e69 | ||
![]() |
94e65ddce6 | ||
![]() |
cb9f144255 | ||
![]() |
ac84624faa | ||
![]() |
c2196785e7 | ||
![]() |
30097fbae6 | ||
![]() |
c295dd5c62 | ||
![]() |
373fef2824 | ||
![]() |
cfc255bf2b | ||
![]() |
ea7e58535b | ||
![]() |
9fad4a74ed | ||
![]() |
ff209d0120 | ||
![]() |
ea2fe618b5 |
16
AUTHORS.md
16
AUTHORS.md
@@ -271,6 +271,22 @@ Contributors:
|
|||||||
[![icon][github]](https://github.com/igrekster)
|
[![icon][github]](https://github.com/igrekster)
|
||||||
|
|
||||||
igrekster
|
igrekster
|
||||||
|
* [![icon][mail]](mailto:lokomot476@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/fan-tom)
|
||||||
|
|
||||||
|
Alexey Gerasimov
|
||||||
|
* [![icon][mail]](mailto:a.grison+github@gmail.com)
|
||||||
|
[![icon][github]](https://github.com/agrison)
|
||||||
|
|
||||||
|
Alexandre Grison
|
||||||
|
* [![icon][mail]](mailto:angel@knight-industries.com)
|
||||||
|
[![icon][github]](https://github.com/angelbot)
|
||||||
|
|
||||||
|
John Weigel
|
||||||
|
* [![icon][mail]](mailto:kevinz@weghst.com)
|
||||||
|
[![icon][github]](https://github.com/kevin70)
|
||||||
|
|
||||||
|
kk
|
||||||
|
|
||||||
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.
|
||||||
|
38
CHANGES.md
38
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
|
||||||
-------------------
|
-------------------
|
||||||
@@ -16,18 +22,34 @@ It is important to distinguish EAP from traditional pre-release software.
|
|||||||
Please note that the quality of EAP versions may at times be way below even
|
Please note that the quality of EAP versions may at times be way below even
|
||||||
usual beta standards.
|
usual beta standards.
|
||||||
|
|
||||||
To Be Released
|
0.56, 2020-04-09
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
_Available since 0.55.1 EAP:_
|
**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:**
|
||||||
|
* Replace `ideastatusbar` option with `ideastatusicon`. Now you can make the icon gray.
|
||||||
|
|
||||||
|
**Deprecations:**
|
||||||
|
* `ideastatusbar` option is deprecated now. See `ideastatusicon`.
|
||||||
|
|
||||||
**Fixes:**
|
**Fixes:**
|
||||||
* [VIM-1284](https://youtrack.jetbrains.com/issue/VIM-1284) Fix mapping of digits
|
* [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
|
* 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>`
|
* 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-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-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-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
|
||||||
--------------
|
--------------
|
||||||
|
@@ -16,6 +16,18 @@
|
|||||||
</a>
|
</a>
|
||||||
<span>2019.2 Tests</span>
|
<span>2019.2 Tests</span>
|
||||||
</div>
|
</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
|
||||||
|
31
README.md
31
README.md
@@ -1,4 +1,4 @@
|
|||||||
<img src="resources/META-INF/pluginIcon.svg" width="80" height="80" alt="icon" align="left"/>
|
<img src="resources/META-INF/pluginIcon.svg" width="80" height="80" alt="icon" align="left"/>
|
||||||
|
|
||||||
IdeaVim
|
IdeaVim
|
||||||
===
|
===
|
||||||
@@ -7,8 +7,8 @@ IdeaVim
|
|||||||
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub">
|
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub">
|
||||||
<img src="https://jb.gg/badges/official.svg" alt="official JetBrains project"/>
|
<img src="https://jb.gg/badges/official.svg" alt="official JetBrains project"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20191&guest=1">
|
<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_TestsForIntelliJ20191)/statusIcon.svg?guest=1"/>
|
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1" alt="TeamCity Build"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -97,6 +97,9 @@ Emulated Vim plugins:
|
|||||||
* vim-surround
|
* vim-surround
|
||||||
* vim-multiple-cursors
|
* vim-multiple-cursors
|
||||||
* vim-commentary
|
* vim-commentary
|
||||||
|
* argtextobj.vim
|
||||||
|
* vim-textobj-entire
|
||||||
|
* ReplaceWithRegister
|
||||||
|
|
||||||
Not supported (yet):
|
Not supported (yet):
|
||||||
|
|
||||||
@@ -165,6 +168,28 @@ Available extensions:
|
|||||||
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
|
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
|
||||||
* Commands: `gcc`, `gc + motion`, `v_gc`
|
* Commands: `gcc`, `gc + motion`, `v_gc`
|
||||||
|
|
||||||
|
* ReplaceWithRegister
|
||||||
|
* Setup: `set ReplaceWithRegister`
|
||||||
|
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
|
||||||
|
* Commands: `gr`, `grr`
|
||||||
|
|
||||||
|
* argtextobj
|
||||||
|
* Setup:
|
||||||
|
* `set argtextobj`
|
||||||
|
* By default, only the arguments inside parenthesis are considered. To extend the functionality
|
||||||
|
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
|
||||||
|
list of colon-separated pairs (same as VIM's `matchpairs` option), like
|
||||||
|
`let g:argtextobj_pairs="(:),{:},<:>"`. The order of pairs matters when
|
||||||
|
handling symbols that can also be operators: `func(x << 5, 20) >> 17`. To handle
|
||||||
|
this syntax parenthesis, must come before angle brackets in the list.
|
||||||
|
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
|
||||||
|
* Additional text objects: `aa`, `ia`
|
||||||
|
|
||||||
|
* textobj-entire
|
||||||
|
* Setup: `set textobj-entire`
|
||||||
|
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
|
||||||
|
* Additional text objects: `ae`, `ie`
|
||||||
|
|
||||||
|
|
||||||
Changes to the IDE
|
Changes to the IDE
|
||||||
------------------
|
------------------
|
||||||
|
49
build.gradle
49
build.gradle
@@ -9,7 +9,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.jetbrains.intellij' version '0.4.9'
|
id 'org.jetbrains.intellij' version '0.4.16'
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
@@ -56,7 +56,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||||
compileOnly "org.jetbrains:annotations:17.0.0"
|
compileOnly "org.jetbrains:annotations:19.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
compileKotlin {
|
||||||
@@ -74,15 +74,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": "Danny Torrence left a 1 star review for your property.",
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"text": {
|
||||||
|
"type": "mrkdwn",
|
||||||
|
"text": "New EAP released: $version\\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()
|
||||||
|
}
|
@@ -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
|
||||||
@@ -104,10 +105,22 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
|||||||
See wiki/`ideajoin` examples
|
See wiki/`ideajoin` examples
|
||||||
|
|
||||||
`ideastatusbar` `ideastatusbar` Boolean (default true)
|
`ideastatusbar` `ideastatusbar` Boolean (default true)
|
||||||
|
DEPRECATED. Please use `ideastatusicon`
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
|
Define the behavior of IdeaVim icon in the status bar.
|
||||||
|
|
||||||
|
Use one of the following values:
|
||||||
|
- enabled - icon is shown in the status bar
|
||||||
|
- gray - use the gray version of 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).
|
||||||
|
@@ -5,7 +5,7 @@ downloadIdeaSources=true
|
|||||||
instrumentPluginCode=true
|
instrumentPluginCode=true
|
||||||
version=SNAPSHOT
|
version=SNAPSHOT
|
||||||
javaVersion=1.8
|
javaVersion=1.8
|
||||||
kotlinVersion=1.3.61
|
kotlinVersion=1.3.70
|
||||||
publishUsername=username
|
publishUsername=username
|
||||||
publishToken=token
|
publishToken=token
|
||||||
publishChannels=eap
|
publishChannels=eap
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
#Fri Nov 22 14:42:01 MSK 2019
|
#Fri Mar 20 11:41:45 MSK 2020
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-all.zip
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
22
gradlew
vendored
22
gradlew
vendored
@@ -1,5 +1,21 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
@@ -28,7 +44,7 @@ APP_NAME="Gradle"
|
|||||||
APP_BASE_NAME=`basename "$0"`
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
@@ -109,8 +125,8 @@ if $darwin; then
|
|||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
18
gradlew.bat
vendored
18
gradlew.bat
vendored
@@ -1,3 +1,19 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
|
|||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
@@ -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"/>
|
||||||
|
@@ -60,5 +60,6 @@
|
|||||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.NextTabHandler" names="tabn[ext]"/>
|
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.NextTabHandler" names="tabn[ext]"/>
|
||||||
<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"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@@ -3,5 +3,8 @@
|
|||||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.surround.VimSurroundExtension"/>
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.surround.VimSurroundExtension"/>
|
||||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.multiplecursors.VimMultipleCursorsExtension"/>
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.multiplecursors.VimMultipleCursorsExtension"/>
|
||||||
<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.argtextobj.VimArgTextObjExtension"/>
|
||||||
|
<vimExtension implementation="com.maddyhome.idea.vim.extension.replacewithregister.ReplaceWithRegister"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@@ -3,9 +3,12 @@
|
|||||||
<id>IdeaVIM</id>
|
<id>IdeaVIM</id>
|
||||||
<change-notes><![CDATA[
|
<change-notes><![CDATA[
|
||||||
<ul>
|
<ul>
|
||||||
<li>Support dot command for Surround and Commentary extensions</li>
|
<li>Support ReplaceWithRegister plugin emulation</li>
|
||||||
<li>Support XDG settings standard</li>
|
<li>Support argtextobj.vim plugin emulation</li>
|
||||||
<li>Add option to remove the status bar icon</li>
|
<li>Support vim-textobj-entire plugin emulation</li>
|
||||||
|
<li>Support showcmd command</li>
|
||||||
|
<li>Support ls/buffers/files command</li>
|
||||||
|
<li>Control the icon in the status bar using an `ideastatusicon` option</li>
|
||||||
<li>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>
|
||||||
@@ -34,25 +37,16 @@
|
|||||||
<component>
|
<component>
|
||||||
<implementation-class>com.maddyhome.idea.vim.DynamicLoaderStopper</implementation-class>
|
<implementation-class>com.maddyhome.idea.vim.DynamicLoaderStopper</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
<component>
|
|
||||||
<implementation-class>com.maddyhome.idea.vim.VimPlugin</implementation-class>
|
|
||||||
</component>
|
|
||||||
<component>
|
|
||||||
<implementation-class>com.maddyhome.idea.vim.VimLocalConfig</implementation-class>
|
|
||||||
</component>
|
|
||||||
</application-components>
|
</application-components>
|
||||||
<project-components>
|
|
||||||
<component>
|
|
||||||
<implementation-class>com.maddyhome.idea.vim.VimProjectComponent</implementation-class>
|
|
||||||
</component>
|
|
||||||
</project-components>
|
|
||||||
|
|
||||||
<extensionPoints>
|
<extensionPoints>
|
||||||
<extensionPoint name="vimExtension" interface="com.maddyhome.idea.vim.extension.VimExtension"/>
|
<extensionPoint name="vimExtension" interface="com.maddyhome.idea.vim.extension.VimExtension" dynamic="true"/>
|
||||||
|
|
||||||
|
<!-- For internal use only -->
|
||||||
<extensionPoint name="vimExCommand" beanClass="com.maddyhome.idea.vim.ex.ExBeanClass" dynamic="true">
|
<extensionPoint name="vimExCommand" beanClass="com.maddyhome.idea.vim.ex.ExBeanClass" dynamic="true">
|
||||||
<with attribute="implementation" implements="com.maddyhome.idea.vim.ex.CommandHandler"/>
|
<with attribute="implementation" implements="com.maddyhome.idea.vim.ex.CommandHandler"/>
|
||||||
</extensionPoint>
|
</extensionPoint>
|
||||||
|
<!-- For internal use only -->
|
||||||
<extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true">
|
<extensionPoint name="vimAction" beanClass="com.maddyhome.idea.vim.handler.ActionBeanClass" dynamic="true">
|
||||||
<with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/>
|
<with attribute="implementation" implements="com.maddyhome.idea.vim.handler.EditorActionHandlerBase"/>
|
||||||
</extensionPoint>
|
</extensionPoint>
|
||||||
@@ -62,6 +56,12 @@
|
|||||||
<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"/>
|
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.StatusBarIconProvider"/>
|
||||||
|
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidget"/>
|
||||||
|
|
||||||
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
|
||||||
|
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
|
||||||
|
|
||||||
|
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup"/>
|
||||||
</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/*)"/>
|
||||||
|
@@ -53,16 +53,15 @@ import java.util.Map;
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
public class EventFacade {
|
public class EventFacade {
|
||||||
@NotNull private static final EventFacade ourInstance = new EventFacade();
|
private static final @NotNull EventFacade ourInstance = new EventFacade();
|
||||||
|
|
||||||
@Nullable private TypedActionHandler myOriginalTypedActionHandler;
|
private @Nullable TypedActionHandler myOriginalTypedActionHandler;
|
||||||
private Map<Project, MessageBusConnection> connections = new HashMap<>();
|
private Map<Project, MessageBusConnection> connections = new HashMap<>();
|
||||||
|
|
||||||
private EventFacade() {
|
private EventFacade() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull EventFacade getInstance() {
|
||||||
public static EventFacade getInstance() {
|
|
||||||
return ourInstance;
|
return ourInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,8 +192,7 @@ public class EventFacade {
|
|||||||
return connections.get(project);
|
return connections.get(project);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private @NotNull TypedAction getTypedAction() {
|
||||||
private 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;
|
||||||
@@ -56,8 +59,10 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -74,8 +79,7 @@ public class KeyHandler {
|
|||||||
*
|
*
|
||||||
* @return A reference to the singleton
|
* @return A reference to the singleton
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public static @NotNull KeyHandler getInstance() {
|
||||||
public static KeyHandler getInstance() {
|
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new KeyHandler();
|
instance = new KeyHandler();
|
||||||
}
|
}
|
||||||
@@ -154,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.
|
||||||
@@ -220,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)) {
|
||||||
@@ -231,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);
|
||||||
@@ -270,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);
|
||||||
}
|
}
|
||||||
@@ -282,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -306,7 +312,7 @@ public class KeyHandler {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleEditorReset(@NotNull Editor editor, @NotNull KeyStroke key, @NotNull final DataContext context, @NotNull CommandState editorState) {
|
private void handleEditorReset(@NotNull Editor editor, @NotNull KeyStroke key, final @NotNull DataContext context, @NotNull CommandState editorState) {
|
||||||
if (editorState.getCommandBuilder().isAtDefaultState()) {
|
if (editorState.getCommandBuilder().isAtDefaultState()) {
|
||||||
RegisterGroup register = VimPlugin.getRegister();
|
RegisterGroup register = VimPlugin.getRegister();
|
||||||
if (register.getCurrentRegister() == register.getDefaultRegister()) {
|
if (register.getCurrentRegister() == register.getDefaultRegister()) {
|
||||||
@@ -321,9 +327,9 @@ public class KeyHandler {
|
|||||||
ChangeGroup.resetCaret(editor, false);
|
ChangeGroup.resetCaret(editor, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleKeyMapping(@NotNull final Editor editor,
|
private boolean handleKeyMapping(final @NotNull Editor editor,
|
||||||
@NotNull final KeyStroke key,
|
final @NotNull KeyStroke key,
|
||||||
@NotNull final DataContext context) {
|
final @NotNull DataContext context) {
|
||||||
|
|
||||||
final CommandState commandState = CommandState.getInstance(editor);
|
final CommandState commandState = CommandState.getInstance(editor);
|
||||||
final MappingState mappingState = commandState.getMappingState();
|
final MappingState mappingState = commandState.getMappingState();
|
||||||
@@ -430,10 +436,8 @@ public class KeyHandler {
|
|||||||
|
|
||||||
final EditorDataContext currentContext = new EditorDataContext(editor);
|
final EditorDataContext currentContext = new EditorDataContext(editor);
|
||||||
|
|
||||||
final List<KeyStroke> toKeys = mappingInfo.getToKeys();
|
if (mappingInfo instanceof ToKeysMappingInfo) {
|
||||||
final VimExtensionHandler extensionHandler = mappingInfo.getExtensionHandler();
|
final List<KeyStroke> toKeys = ((ToKeysMappingInfo)mappingInfo).getToKeys();
|
||||||
|
|
||||||
if (toKeys != null) {
|
|
||||||
final boolean fromIsPrefix = isPrefix(mappingInfo.getFromKeys(), toKeys);
|
final boolean fromIsPrefix = isPrefix(mappingInfo.getFromKeys(), toKeys);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
for (KeyStroke keyStroke : toKeys) {
|
for (KeyStroke keyStroke : toKeys) {
|
||||||
@@ -442,7 +446,8 @@ public class KeyHandler {
|
|||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (extensionHandler != null) {
|
else if (mappingInfo instanceof ToHandlerMappingInfo) {
|
||||||
|
final VimExtensionHandler extensionHandler = ((ToHandlerMappingInfo)mappingInfo).getExtensionHandler();
|
||||||
final CommandProcessor processor = CommandProcessor.getInstance();
|
final CommandProcessor processor = CommandProcessor.getInstance();
|
||||||
|
|
||||||
// Cache isOperatorPending in case the extension changes the mode while moving the caret
|
// Cache isOperatorPending in case the extension changes the mode while moving the caret
|
||||||
@@ -570,7 +575,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 +642,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 +655,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 +666,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 +692,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 +714,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 +752,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 +789,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 +800,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.
|
||||||
@@ -810,8 +860,7 @@ public class KeyHandler {
|
|||||||
editorState.getCommandBuilder().resetAll(getKeyRoot(editorState.getMappingState().getMappingMode()));
|
editorState.getCommandBuilder().resetAll(getKeyRoot(editorState.getMappingState().getMappingMode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private @NotNull CommandPartNode getKeyRoot(MappingMode mappingMode) {
|
||||||
private CommandPartNode getKeyRoot(MappingMode mappingMode) {
|
|
||||||
return VimPlugin.getKey().getKeyRoot(mappingMode);
|
return VimPlugin.getKey().getKeyRoot(mappingMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,7 +874,10 @@ public class KeyHandler {
|
|||||||
VimPlugin.clearError();
|
VimPlugin.clearError();
|
||||||
CommandState.getInstance(editor).reset();
|
CommandState.getInstance(editor).reset();
|
||||||
reset(editor);
|
reset(editor);
|
||||||
VimPlugin.getRegister().resetRegister();
|
RegisterGroup registerGroup = VimPlugin.getRegisterIfCreated();
|
||||||
|
if (registerGroup != null) {
|
||||||
|
registerGroup.resetRegister();
|
||||||
|
}
|
||||||
if (editor != null) {
|
if (editor != null) {
|
||||||
VisualGroupKt.updateCaretState(editor);
|
VisualGroupKt.updateCaretState(editor);
|
||||||
editor.getSelectionModel().removeSelection();
|
editor.getSelectionModel().removeSelection();
|
||||||
@@ -833,9 +885,8 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This method is copied from com.intellij.openapi.editor.actionSystem.EditorAction.getProjectAwareDataContext
|
// This method is copied from com.intellij.openapi.editor.actionSystem.EditorAction.getProjectAwareDataContext
|
||||||
@NotNull
|
private static @NotNull DataContext getProjectAwareDataContext(final @NotNull Editor editor,
|
||||||
private static DataContext getProjectAwareDataContext(@NotNull final Editor editor,
|
final @NotNull DataContext original) {
|
||||||
@NotNull final DataContext original) {
|
|
||||||
if (PROJECT.getData(original) == editor.getProject()) {
|
if (PROJECT.getData(original) == editor.getProject()) {
|
||||||
return new DialogAwareDataContext(original);
|
return new DialogAwareDataContext(original);
|
||||||
}
|
}
|
||||||
@@ -853,7 +904,7 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext
|
// This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext
|
||||||
private final static class DialogAwareDataContext implements DataContext {
|
private static final class DialogAwareDataContext implements DataContext {
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
private static final DataKey[] keys = {PROJECT, PROJECT_FILE_DIRECTORY, EDITOR, VIRTUAL_FILE, PSI_FILE};
|
private static final DataKey[] keys = {PROJECT, PROJECT_FILE_DIRECTORY, EDITOR, VIRTUAL_FILE, PSI_FILE};
|
||||||
private final Map<String, Object> values = new HashMap<>();
|
private final Map<String, Object> values = new HashMap<>();
|
||||||
@@ -865,9 +916,8 @@ public class KeyHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public Object getData(@NotNull @NonNls String dataId) {
|
public @Nullable Object getData(@NotNull @NonNls String dataId) {
|
||||||
if (values.containsKey(dataId)) {
|
if (values.containsKey(dataId)) {
|
||||||
return values.get(dataId);
|
return values.get(dataId);
|
||||||
}
|
}
|
||||||
@@ -884,11 +934,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 +946,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 +959,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 +977,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;
|
||||||
|
48
src/com/maddyhome/idea/vim/PluginStartup.kt
Normal file
48
src/com/maddyhome/idea/vim/PluginStartup.kt
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.project.DumbAware
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.startup.StartupActivity
|
||||||
|
import com.maddyhome.idea.vim.listener.VimListenerManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Alex Plate
|
||||||
|
*
|
||||||
|
* [VERSION UPDATE] 193+ Use StartupActivity.DumbAware
|
||||||
|
*/
|
||||||
|
class PluginStartup : StartupActivity, DumbAware {
|
||||||
|
|
||||||
|
private var firstInitializationOccurred = false
|
||||||
|
|
||||||
|
override fun runActivity(project: Project) {
|
||||||
|
if (firstInitializationOccurred && VimPlugin.isEnabled()) {
|
||||||
|
// This code should be executed on every project open
|
||||||
|
// Project listeners are self-disposable, so there is no need to unregister them on project close
|
||||||
|
VimListenerManager.ProjectListeners.add(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstInitializationOccurred) return
|
||||||
|
firstInitializationOccurred = true
|
||||||
|
|
||||||
|
// This code should be executed once
|
||||||
|
VimPlugin.getInstance().initialize()
|
||||||
|
}
|
||||||
|
}
|
@@ -23,6 +23,7 @@ import com.intellij.openapi.extensions.PluginDescriptor;
|
|||||||
import com.maddyhome.idea.vim.group.KeyGroup;
|
import com.maddyhome.idea.vim.group.KeyGroup;
|
||||||
import com.maddyhome.idea.vim.handler.ActionBeanClass;
|
import com.maddyhome.idea.vim.handler.ActionBeanClass;
|
||||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
|
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
|
||||||
|
import com.maddyhome.idea.vim.key.MappingOwner;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ public class RegisterActions {
|
|||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
||||||
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ActionBeanClass>() {
|
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ActionBeanClass>() {
|
||||||
@Override
|
@Override
|
||||||
public void extensionAdded(@NotNull ActionBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
|
public void extensionAdded(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
||||||
// version update (or earlier).
|
// version update (or earlier).
|
||||||
if (!initialRegistration) return;
|
if (!initialRegistration) return;
|
||||||
@@ -53,7 +54,7 @@ public class RegisterActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void extensionRemoved(@NotNull ActionBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
|
public void extensionRemoved(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||||
if (!initialRegistration) return;
|
if (!initialRegistration) return;
|
||||||
unregisterActions();
|
unregisterActions();
|
||||||
registerActions();
|
registerActions();
|
||||||
@@ -64,27 +65,28 @@ public class RegisterActions {
|
|||||||
/**
|
/**
|
||||||
* Register all the key/action mappings for the plugin.
|
* Register all the key/action mappings for the plugin.
|
||||||
*/
|
*/
|
||||||
static void registerActions() {
|
public static void registerActions() {
|
||||||
registerVimCommandActions();
|
registerVimCommandActions();
|
||||||
registerEmptyShortcuts();
|
registerEmptyShortcuts();
|
||||||
initialRegistration = true;
|
initialRegistration = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public static @Nullable EditorActionHandlerBase findAction(@NotNull String id) {
|
||||||
public static EditorActionHandlerBase findAction(@NotNull String id) {
|
|
||||||
return VIM_ACTIONS_EP.extensions().filter(vimActionBean -> vimActionBean.getActionId().equals(id)).findFirst()
|
return VIM_ACTIONS_EP.extensions().filter(vimActionBean -> vimActionBean.getActionId().equals(id)).findFirst()
|
||||||
.map(ActionBeanClass::getAction).orElse(null);
|
.map(ActionBeanClass::getAction).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull EditorActionHandlerBase findActionOrDie(@NotNull String id) {
|
||||||
public static EditorActionHandlerBase findActionOrDie(@NotNull String id) {
|
|
||||||
EditorActionHandlerBase action = findAction(id);
|
EditorActionHandlerBase action = findAction(id);
|
||||||
if (action == null) throw new RuntimeException("Action " + id + " is not registered");
|
if (action == null) throw new RuntimeException("Action " + id + " is not registered");
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void unregisterActions() {
|
public static void unregisterActions() {
|
||||||
VimPlugin.getKey().unregisterCommandActions();
|
KeyGroup keyGroup = VimPlugin.getKeyIfCreated();
|
||||||
|
if (keyGroup != null) {
|
||||||
|
keyGroup.unregisterCommandActions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerVimCommandActions() {
|
private static void registerVimCommandActions() {
|
||||||
@@ -97,6 +99,6 @@ public class RegisterActions {
|
|||||||
|
|
||||||
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
|
// The {char1} <BS> {char2} shortcut is handled directly by KeyHandler#handleKey, so doesn't have an action. But we
|
||||||
// still need to register the shortcut, to make sure the editor doesn't swallow it.
|
// still need to register the shortcut, to make sure the editor doesn't swallow it.
|
||||||
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0));
|
parser.registerShortcutWithoutAction(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), MappingOwner.IdeaVim.INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -50,6 +50,7 @@ import com.intellij.ui.awt.RelativePoint
|
|||||||
import com.intellij.util.Consumer
|
import com.intellij.util.Consumer
|
||||||
import com.intellij.util.text.VersionComparatorUtil
|
import com.intellij.util.text.VersionComparatorUtil
|
||||||
import com.maddyhome.idea.vim.group.NotificationService
|
import com.maddyhome.idea.vim.group.NotificationService
|
||||||
|
import com.maddyhome.idea.vim.option.IdeaStatusIcon
|
||||||
import com.maddyhome.idea.vim.option.OptionsManager
|
import com.maddyhome.idea.vim.option.OptionsManager
|
||||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable
|
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable
|
||||||
import icons.VimIcons
|
import icons.VimIcons
|
||||||
@@ -59,11 +60,20 @@ import javax.swing.Icon
|
|||||||
import javax.swing.SwingConstants
|
import javax.swing.SwingConstants
|
||||||
|
|
||||||
private class StatusBarIconProvider : StatusBarWidgetProvider {
|
private class StatusBarIconProvider : StatusBarWidgetProvider {
|
||||||
override fun getWidget(project: Project): VimStatusBar? = if (OptionsManager.ideastatusbar.isSet) VimStatusBar else null
|
override fun getWidget(project: Project): VimStatusBar? {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
if (!OptionsManager.ideastatusbar.isSet) return null
|
||||||
|
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.disabled) return null
|
||||||
|
return VimStatusBar
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
||||||
|
|
||||||
|
init {
|
||||||
|
OptionsManager.ideastatusicon.addOptionChangeListener { _, _ -> this.update() }
|
||||||
|
}
|
||||||
|
|
||||||
private var statusBar: StatusBar? = null
|
private var statusBar: StatusBar? = null
|
||||||
|
|
||||||
override fun ID(): String = "IdeaVim-Icon"
|
override fun ID(): String = "IdeaVim-Icon"
|
||||||
@@ -76,7 +86,10 @@ object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
|||||||
|
|
||||||
override fun getTooltipText() = "IdeaVim"
|
override fun getTooltipText() = "IdeaVim"
|
||||||
|
|
||||||
override fun getIcon(): Icon = if (VimPlugin.isEnabled()) VimIcons.IDEAVIM else VimIcons.IDEAVIM_DISABLED
|
override fun getIcon(): Icon {
|
||||||
|
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.gray) return VimIcons.IDEAVIM_DISABLED
|
||||||
|
return if (VimPlugin.isEnabled()) VimIcons.IDEAVIM else VimIcons.IDEAVIM_DISABLED
|
||||||
|
}
|
||||||
|
|
||||||
override fun getClickConsumer() = Consumer<MouseEvent> { event ->
|
override fun getClickConsumer() = Consumer<MouseEvent> { event ->
|
||||||
val component = event.component
|
val component = event.component
|
||||||
|
@@ -20,9 +20,9 @@ package com.maddyhome.idea.vim
|
|||||||
|
|
||||||
import com.intellij.openapi.components.PersistentStateComponent
|
import com.intellij.openapi.components.PersistentStateComponent
|
||||||
import com.intellij.openapi.components.RoamingType
|
import com.intellij.openapi.components.RoamingType
|
||||||
|
import com.intellij.openapi.components.ServiceManager
|
||||||
import com.intellij.openapi.components.State
|
import com.intellij.openapi.components.State
|
||||||
import com.intellij.openapi.components.Storage
|
import com.intellij.openapi.components.Storage
|
||||||
import com.maddyhome.idea.vim.VimPlugin.STATE_VERSION
|
|
||||||
import org.jdom.Element
|
import org.jdom.Element
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,29 +30,28 @@ import org.jdom.Element
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@State(name = "VimLocalSettings", storages = [
|
@State(name = "VimLocalSettings", storages = [
|
||||||
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)
|
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
|
// 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")
|
||||||
class VimLocalConfig : PersistentStateComponent<Element> {
|
class VimLocalConfig : PersistentStateComponent<Element> {
|
||||||
override fun getState(): Element {
|
override fun getState(): Element? = null
|
||||||
val element = Element("ideavim-local")
|
|
||||||
|
|
||||||
val state = Element("state")
|
|
||||||
state.setAttribute("version", STATE_VERSION.toString())
|
|
||||||
element.addContent(state)
|
|
||||||
|
|
||||||
VimPlugin.getMark().saveData(element)
|
|
||||||
VimPlugin.getRegister().saveData(element)
|
|
||||||
VimPlugin.getSearch().saveData(element)
|
|
||||||
VimPlugin.getHistory().saveData(element)
|
|
||||||
return element
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun loadState(state: Element) {
|
override fun loadState(state: Element) {
|
||||||
|
// This is initialization of state from the legacy configuration structure.
|
||||||
|
// This code should be performed only once on settings migration.
|
||||||
|
// After the migration is done, the file with settings gets removed and this method won't be called again.
|
||||||
VimPlugin.getMark().readData(state)
|
VimPlugin.getMark().readData(state)
|
||||||
VimPlugin.getRegister().readData(state)
|
VimPlugin.getRegister().readData(state)
|
||||||
VimPlugin.getSearch().readData(state)
|
VimPlugin.getSearch().readData(state)
|
||||||
VimPlugin.getHistory().readData(state)
|
VimPlugin.getHistory().readData(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun initialize() {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
ServiceManager.getService(VimLocalConfig::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,10 +23,14 @@ 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.ApplicationInfo;
|
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.application.PermanentInstallationID;
|
||||||
import com.intellij.openapi.components.*;
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.ServiceManager;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.extensions.PluginId;
|
import com.intellij.openapi.extensions.PluginId;
|
||||||
import com.intellij.openapi.keymap.Keymap;
|
import com.intellij.openapi.keymap.Keymap;
|
||||||
@@ -66,6 +70,9 @@ import java.io.IOException;
|
|||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||||
|
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This plugin attempts to emulate the key binding and general functionality of Vim and gVim. See the supplied
|
* This plugin attempts to emulate the key binding and general functionality of Vim and gVim. See the supplied
|
||||||
* documentation for a complete list of supported and unsupported Vim emulation. The code base contains some debugging
|
* documentation for a complete list of supported and unsupported Vim emulation. The code base contains some debugging
|
||||||
@@ -75,11 +82,10 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* Registers and marks are shared across open projects so you can copy and paste between files of different projects.
|
* Registers and marks are shared across open projects so you can copy and paste between files of different projects.
|
||||||
*/
|
*/
|
||||||
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
|
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
|
||||||
public class VimPlugin implements BaseComponent, PersistentStateComponent<Element>, Disposable {
|
public class VimPlugin implements PersistentStateComponent<Element>, Disposable {
|
||||||
private static final String IDEAVIM_COMPONENT_NAME = "VimPlugin";
|
|
||||||
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 String IDEAVIM_STATISTICS_TIMESTAMP_KEY = "ideavim.statistics.timestamp";
|
||||||
public static final int STATE_VERSION = 5;
|
private static final int STATE_VERSION = 6;
|
||||||
|
|
||||||
private static long lastBeepTimeMillis;
|
private static long lastBeepTimeMillis;
|
||||||
|
|
||||||
@@ -94,21 +100,25 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
|
|
||||||
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
||||||
|
|
||||||
@NotNull
|
private final @NotNull VimState state = new VimState();
|
||||||
@Override
|
|
||||||
public String getComponentName() {
|
|
||||||
return IDEAVIM_COMPONENT_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
public void initialize() {
|
||||||
@NotNull private final VimState state = new VimState();
|
|
||||||
|
|
||||||
// [VERSION UPDATE] 193+ replace with com.intellij.openapi.components.PersistentStateComponent.initializeComponent
|
|
||||||
@Override
|
|
||||||
public void initComponent() {
|
|
||||||
LOG.debug("initComponent");
|
LOG.debug("initComponent");
|
||||||
|
|
||||||
if (enabled) turnOnPlugin();
|
// Initialize a legacy local config.
|
||||||
|
if (previousStateVersion == 5) {
|
||||||
|
//noinspection deprecation
|
||||||
|
VimLocalConfig.Companion.initialize();
|
||||||
|
}
|
||||||
|
if (enabled) {
|
||||||
|
Application application = ApplicationManager.getApplication();
|
||||||
|
if (application.isUnitTestMode()) {
|
||||||
|
application.invokeAndWait(this::turnOnPlugin);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
application.invokeLater(this::turnOnPlugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG.debug("done");
|
LOG.debug("done");
|
||||||
}
|
}
|
||||||
@@ -123,23 +133,21 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
/**
|
/**
|
||||||
* @return NotificationService as applicationService if project is null and projectService otherwise
|
* @return NotificationService as applicationService if project is null and projectService otherwise
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public static @NotNull NotificationService getNotifications(@Nullable Project project) {
|
||||||
public static NotificationService getNotifications(@Nullable Project project) {
|
|
||||||
if (project == null) {
|
if (project == null) {
|
||||||
return ServiceManager.getService(NotificationService.class);
|
return ServiceManager.getService(NotificationService.class);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return ServiceManager.getService(project, NotificationService.class);
|
return ServiceManager.getService(project, NotificationService.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull VimState getVimState() {
|
||||||
public static VimState getVimState() {
|
|
||||||
return getInstance().state;
|
return getInstance().state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull MotionGroup getMotion() {
|
||||||
public static MotionGroup getMotion() {
|
|
||||||
return ServiceManager.getService(MotionGroup.class);
|
return ServiceManager.getService(MotionGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,18 +159,17 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
public static void statisticReport() {
|
public static void statisticReport() {
|
||||||
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
|
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
|
||||||
final long lastUpdate = propertiesComponent.getOrInitLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0);
|
final long lastUpdate = propertiesComponent.getOrInitLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0);
|
||||||
final boolean outOfDate =
|
final boolean outOfDate = lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
|
||||||
lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
|
|
||||||
if (outOfDate && isEnabled()) {
|
if (outOfDate && isEnabled()) {
|
||||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||||
try {
|
try {
|
||||||
final String buildNumber = ApplicationInfo.getInstance().getBuild().asString();
|
final String buildNumber = ApplicationInfo.getInstance().getBuild().asString();
|
||||||
final String version = URLEncoder.encode(getVersion(), CharsetToolkit.UTF8);
|
final String version = URLEncoder.encode(getVersion(), CharsetToolkit.UTF8);
|
||||||
final String os =
|
final String os = URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
|
||||||
URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
|
|
||||||
final String uid = PermanentInstallationID.get();
|
final String uid = PermanentInstallationID.get();
|
||||||
final String url = "https://plugins.jetbrains.com/plugins/list" +
|
final String url = "https://plugins.jetbrains.com/plugins/list" +
|
||||||
"?pluginId=" + IDEAVIM_PLUGIN_ID +
|
"?pluginId=" +
|
||||||
|
IDEAVIM_PLUGIN_ID +
|
||||||
"&build=" +
|
"&build=" +
|
||||||
buildNumber +
|
buildNumber +
|
||||||
"&pluginVersion=" +
|
"&pluginVersion=" +
|
||||||
@@ -191,106 +198,87 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull ChangeGroup getChange() {
|
||||||
public static ChangeGroup getChange() {
|
|
||||||
return ServiceManager.getService(ChangeGroup.class);
|
return ServiceManager.getService(ChangeGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull CommandGroup getCommand() {
|
||||||
public static CommandGroup getCommand() {
|
|
||||||
return ServiceManager.getService(CommandGroup.class);
|
return ServiceManager.getService(CommandGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull MarkGroup getMark() {
|
||||||
public static MarkGroup getMark() {
|
|
||||||
return ServiceManager.getService(MarkGroup.class);
|
return ServiceManager.getService(MarkGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull RegisterGroup getRegister() {
|
||||||
public static RegisterGroup getRegister() {
|
|
||||||
return ServiceManager.getService(RegisterGroup.class);
|
return ServiceManager.getService(RegisterGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @Nullable RegisterGroup getRegisterIfCreated() {
|
||||||
public static FileGroup getFile() {
|
return ServiceManager.getServiceIfCreated(RegisterGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull FileGroup getFile() {
|
||||||
return ServiceManager.getService(FileGroup.class);
|
return ServiceManager.getService(FileGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull SearchGroup getSearch() {
|
||||||
public static SearchGroup getSearch() {
|
|
||||||
return ServiceManager.getService(SearchGroup.class);
|
return ServiceManager.getService(SearchGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @Nullable SearchGroup getSearchIfCreated() {
|
||||||
public static ProcessGroup getProcess() {
|
return ServiceManager.getServiceIfCreated(SearchGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ProcessGroup getProcess() {
|
||||||
return ServiceManager.getService(ProcessGroup.class);
|
return ServiceManager.getService(ProcessGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull MacroGroup getMacro() {
|
||||||
public static MacroGroup getMacro() {
|
|
||||||
return ServiceManager.getService(MacroGroup.class);
|
return ServiceManager.getService(MacroGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull DigraphGroup getDigraph() {
|
||||||
public static DigraphGroup getDigraph() {
|
|
||||||
return ServiceManager.getService(DigraphGroup.class);
|
return ServiceManager.getService(DigraphGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull HistoryGroup getHistory() {
|
||||||
public static HistoryGroup getHistory() {
|
|
||||||
return ServiceManager.getService(HistoryGroup.class);
|
return ServiceManager.getService(HistoryGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull KeyGroup getKey() {
|
||||||
public static KeyGroup getKey() {
|
|
||||||
return ServiceManager.getService(KeyGroup.class);
|
return ServiceManager.getService(KeyGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @Nullable KeyGroup getKeyIfCreated() {
|
||||||
public static WindowGroup getWindow() {
|
return ServiceManager.getServiceIfCreated(KeyGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull WindowGroup getWindow() {
|
||||||
return ServiceManager.getService(WindowGroup.class);
|
return ServiceManager.getService(WindowGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull EditorGroup getEditor() {
|
||||||
public static EditorGroup getEditor() {
|
|
||||||
return ServiceManager.getService(EditorGroup.class);
|
return ServiceManager.getService(EditorGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @Nullable EditorGroup getEditorIfCreated() {
|
||||||
public static VisualMotionGroup getVisualMotion() {
|
return ServiceManager.getServiceIfCreated(EditorGroup.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull VisualMotionGroup getVisualMotion() {
|
||||||
return ServiceManager.getService(VisualMotionGroup.class);
|
return ServiceManager.getService(VisualMotionGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull YankGroup getYank() {
|
||||||
public static YankGroup getYank() {
|
|
||||||
return ServiceManager.getService(YankGroup.class);
|
return ServiceManager.getService(YankGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull PutGroup getPut() {
|
||||||
public static PutGroup getPut() {
|
|
||||||
return ServiceManager.getService(PutGroup.class);
|
return ServiceManager.getService(PutGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static @NotNull NotificationService getNotifications() {
|
||||||
public Element getState() {
|
|
||||||
LOG.debug("Saving state");
|
|
||||||
|
|
||||||
final Element element = new Element("ideavim");
|
|
||||||
// Save whether the plugin is enabled or not
|
|
||||||
final Element state = new Element("state");
|
|
||||||
state.setAttribute("version", Integer.toString(STATE_VERSION));
|
|
||||||
state.setAttribute("enabled", Boolean.toString(enabled));
|
|
||||||
element.addContent(state);
|
|
||||||
|
|
||||||
getKey().saveData(element);
|
|
||||||
getEditor().saveData(element);
|
|
||||||
this.state.saveData(element);
|
|
||||||
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static NotificationService getNotifications() {
|
|
||||||
return getNotifications(null);
|
return getNotifications(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,15 +296,13 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull PluginId getPluginId() {
|
||||||
public static PluginId getPluginId() {
|
|
||||||
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [VERSION UPDATE] 193+ remove suppress
|
// [VERSION UPDATE] 193+ remove suppress
|
||||||
@SuppressWarnings({"MissingRecentApi", "UnstableApiUsage"})
|
@SuppressWarnings({"MissingRecentApi", "UnstableApiUsage"})
|
||||||
@NotNull
|
public static @NotNull String getVersion() {
|
||||||
public static String getVersion() {
|
|
||||||
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
|
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
|
||||||
if (!ApplicationManager.getApplication().isInternal()) {
|
if (!ApplicationManager.getApplication().isInternal()) {
|
||||||
return plugin != null ? plugin.getVersion() : "SNAPSHOT";
|
return plugin != null ? plugin.getVersion() : "SNAPSHOT";
|
||||||
@@ -400,18 +386,13 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull VimPlugin getInstance() {
|
||||||
private static VimPlugin getInstance() {
|
return ServiceManager.getService(VimPlugin.class);
|
||||||
return ApplicationManager.getApplication().getComponent(VimPlugin.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
@@ -423,21 +404,32 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
|
|
||||||
// Execute ~/.ideavimrc
|
// Execute ~/.ideavimrc
|
||||||
registerIdeavimrc();
|
registerIdeavimrc();
|
||||||
|
|
||||||
|
// Turing on should be performed after all commands registration
|
||||||
|
getEditor().turnOn();
|
||||||
|
getSearch().turnOn();
|
||||||
|
VimListenerManager.INSTANCE.turnOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void turnOffPlugin() {
|
private void turnOffPlugin() {
|
||||||
KeyHandler.getInstance().fullReset(null);
|
KeyHandler.getInstance().fullReset(null);
|
||||||
|
|
||||||
|
EditorGroup editorGroup = getEditorIfCreated();
|
||||||
|
if (editorGroup != null) {
|
||||||
|
editorGroup.turnOff();
|
||||||
|
}
|
||||||
|
SearchGroup searchGroup = getSearchIfCreated();
|
||||||
|
if (searchGroup != null) {
|
||||||
|
searchGroup.turnOff();
|
||||||
|
}
|
||||||
|
VimListenerManager.INSTANCE.turnOff();
|
||||||
|
ExEntryPanel.fullReset();
|
||||||
|
|
||||||
// Unregister vim actions in command mode
|
// Unregister vim actions in command mode
|
||||||
RegisterActions.unregisterActions();
|
RegisterActions.unregisterActions();
|
||||||
|
|
||||||
// Unregister ex handlers
|
// Unregister ex handlers
|
||||||
CommandParser.getInstance().unregisterHandlers();
|
CommandParser.getInstance().unregisterHandlers();
|
||||||
|
|
||||||
getEditor().turnOff();
|
|
||||||
getSearch().turnOff();
|
|
||||||
VimListenerManager.INSTANCE.turnOff();
|
|
||||||
ExEntryPanel.fullReset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean stateUpdated = false;
|
private boolean stateUpdated = false;
|
||||||
@@ -485,7 +477,7 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadState(@NotNull final Element element) {
|
public void loadState(final @NotNull Element element) {
|
||||||
LOG.debug("Loading state");
|
LOG.debug("Loading state");
|
||||||
|
|
||||||
// Restore whether the plugin is enabled or not
|
// Restore whether the plugin is enabled or not
|
||||||
@@ -500,6 +492,27 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
previousKeyMap = state.getAttributeValue("keymap");
|
previousKeyMap = state.getAttributeValue("keymap");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
legacyStateLoading(element);
|
||||||
|
this.state.readData(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Element getState() {
|
||||||
|
LOG.debug("Saving state");
|
||||||
|
|
||||||
|
final Element element = new Element("ideavim");
|
||||||
|
// Save whether the plugin is enabled or not
|
||||||
|
final Element state = new Element("state");
|
||||||
|
state.setAttribute("version", Integer.toString(STATE_VERSION));
|
||||||
|
state.setAttribute("enabled", Boolean.toString(enabled));
|
||||||
|
element.addContent(state);
|
||||||
|
|
||||||
|
this.state.saveData(element);
|
||||||
|
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void legacyStateLoading(@NotNull Element element) {
|
||||||
if (previousStateVersion > 0 && previousStateVersion < 5) {
|
if (previousStateVersion > 0 && previousStateVersion < 5) {
|
||||||
// Migrate settings from 4 to 5 version
|
// Migrate settings from 4 to 5 version
|
||||||
getMark().readData(element);
|
getMark().readData(element);
|
||||||
@@ -507,8 +520,11 @@ public class VimPlugin implements BaseComponent, PersistentStateComponent<Elemen
|
|||||||
getSearch().readData(element);
|
getSearch().readData(element);
|
||||||
getHistory().readData(element);
|
getHistory().readData(element);
|
||||||
}
|
}
|
||||||
getKey().readData(element);
|
if (element.getChild(SHORTCUT_CONFLICTS_ELEMENT) != null) {
|
||||||
getEditor().readData(element);
|
getKey().readData(element);
|
||||||
this.state.readData(element);
|
}
|
||||||
|
if (element.getChild(EDITOR_STORE_ELEMENT) != null) {
|
||||||
|
getEditor().readData(element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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());
|
|
||||||
|
|
||||||
@NotNull private final 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(@NotNull final Editor editor, final char charTyped, @NotNull final 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 == ' '
|
||||||
|
}
|
||||||
|
}
|
@@ -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"
|
||||||
|
|
||||||
|
@@ -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 PutTextAfterCursorAction : 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 = false, _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 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 PutTextBeforeCursorAction : 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 = 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)
|
||||||
}
|
}
|
||||||
|
@@ -60,15 +60,14 @@ public class CommandState {
|
|||||||
*
|
*
|
||||||
* This field is reset after the command has been executed.
|
* This field is reset after the command has been executed.
|
||||||
*/
|
*/
|
||||||
@Nullable private Command executingCommand;
|
private @Nullable Command executingCommand;
|
||||||
|
|
||||||
private CommandState() {
|
private CommandState() {
|
||||||
pushModes(defaultModeState.getMode(), defaultModeState.getSubMode());
|
pushModes(defaultModeState.getMode(), defaultModeState.getSubMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> new")
|
@Contract("null -> new")
|
||||||
@NotNull
|
public static @NotNull CommandState getInstance(@Nullable Editor editor) {
|
||||||
public static CommandState getInstance(@Nullable Editor editor) {
|
|
||||||
if (editor == null) {
|
if (editor == null) {
|
||||||
return new CommandState();
|
return new CommandState();
|
||||||
}
|
}
|
||||||
@@ -82,8 +81,7 @@ public class CommandState {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull CommandPartNode getKeyRootNode(MappingMode mappingMode) {
|
||||||
private static CommandPartNode getKeyRootNode(MappingMode mappingMode) {
|
|
||||||
return VimPlugin.getKey().getKeyRoot(mappingMode);
|
return VimPlugin.getKey().getKeyRoot(mappingMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,13 +104,11 @@ public class CommandState {
|
|||||||
return commandBuilder;
|
return commandBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull MappingState getMappingState() {
|
||||||
public MappingState getMappingState() {
|
|
||||||
return mappingState;
|
return mappingState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable Command getExecutingCommand() {
|
||||||
public Command getExecutingCommand() {
|
|
||||||
return executingCommand;
|
return executingCommand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,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();
|
||||||
@@ -202,13 +204,11 @@ public class CommandState {
|
|||||||
throw new IllegalArgumentException("Unexpected mode: " + mode);
|
throw new IllegalArgumentException("Unexpected mode: " + mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull Mode getMode() {
|
||||||
public Mode getMode() {
|
|
||||||
return currentModeState().getMode();
|
return currentModeState().getMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull SubMode getSubMode() {
|
||||||
public SubMode getSubMode() {
|
|
||||||
return currentModeState().getSubMode();
|
return currentModeState().getSubMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,8 +294,7 @@ public class CommandState {
|
|||||||
VimPlugin.showMode(msg.toString());
|
VimPlugin.showMode(msg.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private @NotNull String getStatusString(int pos) {
|
||||||
private String getStatusString(int pos) {
|
|
||||||
ModeState modeState;
|
ModeState modeState;
|
||||||
if (pos >= 0 && pos < modeStates.size()) {
|
if (pos >= 0 && pos < modeStates.size()) {
|
||||||
modeState = modeStates.get(pos);
|
modeState = modeStates.get(pos);
|
||||||
@@ -350,12 +349,12 @@ 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 {
|
||||||
@NotNull private final Mode myMode;
|
private final @NotNull Mode myMode;
|
||||||
@NotNull private final SubMode mySubMode;
|
private final @NotNull SubMode mySubMode;
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
public ModeState(@NotNull Mode mode, @NotNull SubMode subMode) {
|
public ModeState(@NotNull Mode mode, @NotNull SubMode subMode) {
|
||||||
@@ -363,13 +362,11 @@ public class CommandState {
|
|||||||
this.mySubMode = subMode;
|
this.mySubMode = subMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull Mode getMode() {
|
||||||
public Mode getMode() {
|
|
||||||
return myMode;
|
return myMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull SubMode getSubMode() {
|
||||||
public SubMode getSubMode() {
|
|
||||||
return mySubMode;
|
return mySubMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,4 +60,8 @@ enum class SelectionType(val value: Int) {
|
|||||||
else -> CHARACTER_WISE
|
else -> CHARACTER_WISE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val SelectionType.isLine get() = this == SelectionType.LINE_WISE
|
||||||
|
val SelectionType.isChar get() = this == SelectionType.CHARACTER_WISE
|
||||||
|
val SelectionType.isBlock get() = this == SelectionType.BLOCK_WISE
|
||||||
|
@@ -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?
|
||||||
|
@@ -59,7 +59,7 @@ public class CommandParser {
|
|||||||
*
|
*
|
||||||
* @return The singleton instance
|
* @return The singleton instance
|
||||||
*/
|
*/
|
||||||
public synchronized static CommandParser getInstance() {
|
public static synchronized CommandParser getInstance() {
|
||||||
return CommandParserHolder.INSTANCE;
|
return CommandParserHolder.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ public class CommandParser {
|
|||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ExBeanClass>() {
|
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ExBeanClass>() {
|
||||||
@Override
|
@Override
|
||||||
public void extensionAdded(@NotNull ExBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
|
public void extensionAdded(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
||||||
// version update (or earlier).
|
// version update (or earlier).
|
||||||
if (!initialRegistration) return;
|
if (!initialRegistration) return;
|
||||||
@@ -85,7 +85,7 @@ public class CommandParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void extensionRemoved(@NotNull ExBeanClass extension, @NotNull PluginDescriptor pluginDescriptor) {
|
public void extensionRemoved(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||||
if (!initialRegistration) return;
|
if (!initialRegistration) return;
|
||||||
unregisterHandlers();
|
unregisterHandlers();
|
||||||
registerHandlers();
|
registerHandlers();
|
||||||
@@ -215,8 +215,7 @@ public class CommandParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable CommandHandler getCommandHandler(@NotNull ExCommand command) {
|
||||||
public CommandHandler getCommandHandler(@NotNull ExCommand command) {
|
|
||||||
final String cmd = command.getCommand();
|
final String cmd = command.getCommand();
|
||||||
// If there is no command, just a range, use the 'goto line' handler
|
// If there is no command, just a range, use the 'goto line' handler
|
||||||
if (cmd.length() == 0) {
|
if (cmd.length() == 0) {
|
||||||
@@ -241,8 +240,7 @@ public class CommandParser {
|
|||||||
* @return The parse result
|
* @return The parse result
|
||||||
* @throws ExException if the text is syntactically incorrect
|
* @throws ExException if the text is syntactically incorrect
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull ExCommand parse(@NotNull String cmd) throws ExException {
|
||||||
public ExCommand parse(@NotNull String cmd) throws ExException {
|
|
||||||
// This is a complicated state machine that should probably be rewritten
|
// This is a complicated state machine that should probably be rewritten
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("processing `" + cmd + "'");
|
logger.debug("processing `" + cmd + "'");
|
||||||
@@ -622,7 +620,7 @@ public class CommandParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull private final CommandNode root = new CommandNode();
|
private final @NotNull CommandNode root = new CommandNode();
|
||||||
|
|
||||||
private enum State {
|
private enum State {
|
||||||
START,
|
START,
|
||||||
|
@@ -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
|
||||||
|
148
src/com/maddyhome/idea/vim/ex/handler/BufferListHandler.kt
Normal file
148
src/com/maddyhome/idea/vim/ex/handler/BufferListHandler.kt
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
* 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.Document
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
|
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.ExOutputModel
|
||||||
|
import com.maddyhome.idea.vim.ex.flags
|
||||||
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles buffers, files, ls command. Supports +, =, a, %, # filters.
|
||||||
|
*
|
||||||
|
* @author John Weigel
|
||||||
|
*/
|
||||||
|
class BufferListHandler : CommandHandler.SingleExecution() {
|
||||||
|
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val FILE_NAME_PAD = 30
|
||||||
|
val SUPPORTED_FILTERS = setOf('+', '=', 'a', '%', '#')
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||||
|
val arg = cmd.argument.trim()
|
||||||
|
val filter = pruneUnsupportedFilters(arg)
|
||||||
|
val bufferList = getBufferList(context, filter)
|
||||||
|
|
||||||
|
ExOutputModel.getInstance(editor).output(bufferList.joinToString(separator = "\n"))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pruneUnsupportedFilters(filter: String) = filter.filter { it in SUPPORTED_FILTERS }
|
||||||
|
|
||||||
|
private fun getBufferList(context: DataContext, filter: String): List<String> {
|
||||||
|
val bufferList = mutableListOf<String>()
|
||||||
|
val project = PlatformDataKeys.PROJECT.getData(context) ?: return emptyList()
|
||||||
|
|
||||||
|
val fem = FileEditorManager.getInstance(project)
|
||||||
|
val openFiles = fem.openFiles
|
||||||
|
val bufNumPad = openFiles.size.toString().length
|
||||||
|
val currentFile = fem.selectedFiles[0]
|
||||||
|
val previousFile = VimPlugin.getFile().getPreviousTab(context)
|
||||||
|
val virtualFileDisplayMap = buildVirtualFileDisplayMap(project)
|
||||||
|
|
||||||
|
var index = 1
|
||||||
|
for ((file, displayFileName) in virtualFileDisplayMap) {
|
||||||
|
val editor = EditorHelper.getEditor(file) ?: continue
|
||||||
|
|
||||||
|
val bufStatus = getBufferStatus(editor, file, currentFile, previousFile)
|
||||||
|
|
||||||
|
if (bufStatusMatchesFilter(filter, bufStatus)) {
|
||||||
|
val lineNum = editor.caretModel.currentCaret.logicalPosition.line + 1
|
||||||
|
val lineNumPad = if (displayFileName.length < FILE_NAME_PAD) (FILE_NAME_PAD - displayFileName.length).toString() else ""
|
||||||
|
|
||||||
|
bufferList.add(String.format(
|
||||||
|
" %${bufNumPad}s %s %s%${lineNumPad}s line: %d", index, bufStatus, displayFileName, "", lineNum)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufferList
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildVirtualFileDisplayMap(project: Project): Map<VirtualFile, String> {
|
||||||
|
val openFiles = FileEditorManager.getInstance(project).openFiles
|
||||||
|
val basePath = if (project.basePath != null) project.basePath + File.separator else ""
|
||||||
|
val filePaths = mutableMapOf<VirtualFile, String>()
|
||||||
|
|
||||||
|
for (file in openFiles) {
|
||||||
|
val filePath = file.path
|
||||||
|
|
||||||
|
// If the file is under the project path, then remove the project base path from the file.
|
||||||
|
val displayFilePath = if (basePath.isNotEmpty() && filePath.startsWith(basePath)) {
|
||||||
|
filePath.replace(basePath, "")
|
||||||
|
} else {
|
||||||
|
// File is not under the project base path so add the full path.
|
||||||
|
filePath
|
||||||
|
}
|
||||||
|
|
||||||
|
filePaths[file] = '"' + displayFilePath + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
return filePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bufStatusMatchesFilter(filter: String, bufStatus: String) = filter.all { it in bufStatus }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBufferStatus(editor: Editor, file: VirtualFile, currentFile: VirtualFile, previousFile: VirtualFile?): String {
|
||||||
|
val bufStatus = StringBuilder()
|
||||||
|
|
||||||
|
when(file) {
|
||||||
|
currentFile -> bufStatus.append("%a ")
|
||||||
|
previousFile -> bufStatus.append("# ")
|
||||||
|
else -> bufStatus.append(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.isWritable) {
|
||||||
|
bufStatus.setCharAt(2, '=')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDocumentDirty(editor.document)) {
|
||||||
|
bufStatus.setCharAt(3, '+')
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufStatus.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isDocumentDirty(document: Document): Boolean {
|
||||||
|
var line = 0
|
||||||
|
|
||||||
|
while (line < document.lineCount) {
|
||||||
|
if (document.isLineModified(line)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
line++
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
@@ -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)
|
||||||
|
@@ -34,6 +34,7 @@ import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.EXPR
|
|||||||
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.SCRIPT
|
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.SCRIPT
|
||||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
|
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
|
||||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||||
|
import com.maddyhome.idea.vim.key.MappingOwner
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
@@ -75,8 +76,7 @@ class MapHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, Co
|
|||||||
throw ExException("Unsupported map argument: $unsupportedArgument")
|
throw ExException("Unsupported map argument: $unsupportedArgument")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, arguments.toKeys, null,
|
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, MappingOwner.IdeaVim, arguments.toKeys, commandInfo.isRecursive)
|
||||||
commandInfo.isRecursive)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -33,13 +33,11 @@ public class VimScriptGlobalEnvironment {
|
|||||||
|
|
||||||
private VimScriptGlobalEnvironment() {}
|
private VimScriptGlobalEnvironment() {}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull VimScriptGlobalEnvironment getInstance() {
|
||||||
public static VimScriptGlobalEnvironment getInstance() {
|
|
||||||
return ourInstance;
|
return ourInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull Map<String, Object> getVariables() {
|
||||||
public Map<String, Object> getVariables() {
|
|
||||||
return myVariables;
|
return myVariables;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,8 +52,7 @@ public class VimScriptParser {
|
|||||||
private VimScriptParser() {
|
private VimScriptParser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public static @Nullable File findIdeaVimRc() {
|
||||||
public static File findIdeaVimRc() {
|
|
||||||
final String homeDirName = System.getProperty("user.home");
|
final String homeDirName = System.getProperty("user.home");
|
||||||
// Check whether file exists in home dir
|
// Check whether file exists in home dir
|
||||||
if (homeDirName != null) {
|
if (homeDirName != null) {
|
||||||
@@ -82,8 +81,7 @@ public class VimScriptParser {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public static @Nullable File findOrCreateIdeaVimRc() {
|
||||||
public static File findOrCreateIdeaVimRc() {
|
|
||||||
final File found = findIdeaVimRc();
|
final File found = findIdeaVimRc();
|
||||||
if (found != null) return found;
|
if (found != null) return found;
|
||||||
|
|
||||||
@@ -137,8 +135,7 @@ public class VimScriptParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
|
||||||
public static 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
|
// 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.
|
// support mapleader, VIM-650. See also VIM-669.
|
||||||
Matcher m;
|
Matcher m;
|
||||||
@@ -168,8 +165,7 @@ public class VimScriptParser {
|
|||||||
throw new ExException(String.format("Invalid expression: %s", expression));
|
throw new ExException(String.format("Invalid expression: %s", expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public static @NotNull String expressionToString(@NotNull Object value) throws ExException {
|
||||||
public static String expressionToString(@NotNull Object value) throws ExException {
|
|
||||||
// TODO: Return meaningful value representations
|
// TODO: Return meaningful value representations
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
return (String)value;
|
return (String)value;
|
||||||
@@ -179,8 +175,7 @@ public class VimScriptParser {
|
|||||||
throw new ExException(String.format("Cannot convert '%s' to string", value));
|
throw new ExException(String.format("Cannot convert '%s' to string", value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull String readFile(@NotNull File file) throws IOException {
|
||||||
private static String readFile(@NotNull File file) throws IOException {
|
|
||||||
final BufferedReader reader = new BufferedReader(new FileReader(file));
|
final BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
final char[] buffer = new char[BUFSIZE];
|
final char[] buffer = new char[BUFSIZE];
|
||||||
|
@@ -19,6 +19,8 @@
|
|||||||
package com.maddyhome.idea.vim.extension;
|
package com.maddyhome.idea.vim.extension;
|
||||||
|
|
||||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
|
import com.maddyhome.idea.vim.key.MappingOwner;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +32,13 @@ public interface VimExtension {
|
|||||||
@NotNull
|
@NotNull
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
|
default MappingOwner getOwner() {
|
||||||
|
return MappingOwner.Plugin.Companion.get(getName());
|
||||||
|
}
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void dispose();
|
default void dispose() {
|
||||||
|
VimPlugin.getKey().removeKeyMapping(getOwner());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -27,9 +27,11 @@ 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
|
||||||
import com.maddyhome.idea.vim.helper.commandState
|
import com.maddyhome.idea.vim.helper.commandState
|
||||||
|
import com.maddyhome.idea.vim.key.MappingOwner
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||||
import com.maddyhome.idea.vim.ui.ExEntryPanel
|
import com.maddyhome.idea.vim.ui.ExEntryPanel
|
||||||
import com.maddyhome.idea.vim.ui.ModalEntry
|
import com.maddyhome.idea.vim.ui.ModalEntry
|
||||||
|
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval
|
||||||
import java.awt.event.KeyEvent
|
import java.awt.event.KeyEvent
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
@@ -41,18 +43,34 @@ import javax.swing.KeyStroke
|
|||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
object VimExtensionFacade {
|
object VimExtensionFacade {
|
||||||
|
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||||
|
@JvmStatic
|
||||||
|
@ScheduledForRemoval(inVersion = "0.57")
|
||||||
|
@Deprecated("Only for EasyMotion support")
|
||||||
|
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>, extensionHandler: VimExtensionHandler, recursive: Boolean) {
|
||||||
|
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), extensionHandler, recursive)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ScheduledForRemoval(inVersion = "0.57")
|
||||||
|
@Deprecated("Only for EasyMotion support")
|
||||||
|
@JvmStatic
|
||||||
|
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
||||||
|
toKeys: List<KeyStroke>, recursive: Boolean) {
|
||||||
|
VimPlugin.getKey().putKeyMapping(modes, fromKeys, MappingOwner.Plugin.get("easymotion"), toKeys, recursive)
|
||||||
|
}
|
||||||
|
|
||||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
fun putExtensionHandlerMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
||||||
extensionHandler: VimExtensionHandler, recursive: Boolean) {
|
pluginOwner: MappingOwner, extensionHandler: VimExtensionHandler, recursive: Boolean) {
|
||||||
VimPlugin.getKey().putKeyMapping(modes, fromKeys, null, extensionHandler, recursive)
|
VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, extensionHandler, recursive)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The 'map' command for mapping keys to other keys. */
|
/** The 'map' command for mapping keys to other keys. */
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
||||||
toKeys: List<KeyStroke>, recursive: Boolean) {
|
pluginOwner: MappingOwner, toKeys: List<KeyStroke>, recursive: Boolean) {
|
||||||
VimPlugin.getKey().putKeyMapping(modes, fromKeys, toKeys, null, recursive)
|
VimPlugin.getKey().putKeyMapping(modes, fromKeys, pluginOwner, toKeys, recursive)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
|
/** Sets the value of 'operatorfunc' to be used as the operator function in 'g@'. */
|
||||||
@@ -78,7 +96,8 @@ object VimExtensionFacade {
|
|||||||
fun inputKeyStroke(editor: Editor): KeyStroke {
|
fun inputKeyStroke(editor: Editor): KeyStroke {
|
||||||
if (editor.commandState.isDotRepeatInProgress) {
|
if (editor.commandState.isDotRepeatInProgress) {
|
||||||
val input = VimRepeater.Extension.consumeKeystroke()
|
val input = VimRepeater.Extension.consumeKeystroke()
|
||||||
return input ?: throw RuntimeException("Not enough keystrokes saved: ${VimRepeater.Extension.lastExtensionHandler}")
|
return input
|
||||||
|
?: throw RuntimeException("Not enough keystrokes saved: ${VimRepeater.Extension.lastExtensionHandler}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) {
|
val key: KeyStroke? = if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
@@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.extension;
|
|||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.extensions.ExtensionPointListener;
|
import com.intellij.openapi.extensions.ExtensionPointListener;
|
||||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
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.OptionsManager;
|
||||||
import com.maddyhome.idea.vim.option.ToggleOption;
|
import com.maddyhome.idea.vim.option.ToggleOption;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -34,24 +35,32 @@ import java.util.Set;
|
|||||||
*/
|
*/
|
||||||
public class VimExtensionRegistrar {
|
public class VimExtensionRegistrar {
|
||||||
|
|
||||||
private static Set<String> registeredExtensions = new HashSet<>();
|
private static final Set<String> registeredExtensions = new HashSet<>();
|
||||||
private static boolean extensionRegistered = false;
|
private static boolean extensionRegistered = false;
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
|
||||||
|
|
||||||
public static void registerExtensions() {
|
public static void registerExtensions() {
|
||||||
if (extensionRegistered) return;
|
if (extensionRegistered) return;
|
||||||
extensionRegistered = true;
|
extensionRegistered = true;
|
||||||
|
|
||||||
// TODO: [VERSION UPDATE] since 191 use
|
// TODO: [VERSION UPDATE] since 191 use
|
||||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
||||||
|
//noinspection deprecation
|
||||||
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(new ExtensionPointListener<VimExtension>() {
|
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(new ExtensionPointListener<VimExtension>() {
|
||||||
@Override
|
@Override
|
||||||
public void extensionAdded(@NotNull VimExtension extension, @NotNull PluginDescriptor pluginDescriptor) {
|
public void extensionAdded(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
||||||
registerExtension(extension);
|
registerExtension(extension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void extensionRemoved(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
||||||
|
unregisterExtension(extension);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized private static void registerExtension(@NotNull VimExtension extension) {
|
private static synchronized void registerExtension(@NotNull VimExtension extension) {
|
||||||
String name = extension.getName();
|
String name = extension.getName();
|
||||||
|
|
||||||
if (registeredExtensions.contains(name)) return;
|
if (registeredExtensions.contains(name)) return;
|
||||||
@@ -74,5 +83,15 @@ public class VimExtensionRegistrar {
|
|||||||
OptionsManager.INSTANCE.addOption(option);
|
OptionsManager.INSTANCE.addOption(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,29 +18,18 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.extension;
|
package com.maddyhome.idea.vim.extension;
|
||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
public abstract class VimNonDisposableExtension implements VimExtension {
|
|
||||||
private boolean myInitialized = false;
|
|
||||||
|
|
||||||
|
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
|
||||||
|
@Deprecated
|
||||||
|
public abstract class VimNonDisposableExtension implements VimExtension {
|
||||||
@Override
|
@Override
|
||||||
public final void init() {
|
public final void init() {
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
initOnce();
|
||||||
initOnce();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!myInitialized) {
|
|
||||||
myInitialized = true;
|
|
||||||
initOnce();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void dispose() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void initOnce();
|
protected abstract void initOnce();
|
||||||
|
@@ -0,0 +1,711 @@
|
|||||||
|
package com.maddyhome.idea.vim.extension.argtextobj;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext;
|
||||||
|
import com.intellij.openapi.editor.Caret;
|
||||||
|
import com.intellij.openapi.editor.Document;
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin;
|
||||||
|
import com.maddyhome.idea.vim.command.*;
|
||||||
|
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.VimExtensionHandler;
|
||||||
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
|
||||||
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||||
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||||
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
||||||
|
import static com.maddyhome.idea.vim.group.visual.VisualGroupKt.vimSetSelection;
|
||||||
|
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author igrekster
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class VimArgTextObjExtension implements VimExtension {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String getName() {
|
||||||
|
return "argtextobj";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
|
||||||
|
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>InnerArgument"), getOwner(), new VimArgTextObjExtension.ArgumentHandler(true), false);
|
||||||
|
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>OuterArgument"), getOwner(), new VimArgTextObjExtension.ArgumentHandler(false), false);
|
||||||
|
|
||||||
|
putKeyMapping(MappingMode.XO, parseKeys("ia"), getOwner(), parseKeys("<Plug>InnerArgument"), true);
|
||||||
|
putKeyMapping(MappingMode.XO, parseKeys("aa"), getOwner(), parseKeys("<Plug>OuterArgument"), true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
static class ArgumentHandler implements VimExtensionHandler {
|
||||||
|
final boolean isInner;
|
||||||
|
|
||||||
|
ArgumentHandler(boolean isInner) {
|
||||||
|
super();
|
||||||
|
this.isInner = isInner;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ArgumentTextObjectHandler extends TextObjectActionHandler {
|
||||||
|
private final boolean isInner;
|
||||||
|
|
||||||
|
ArgumentTextObjectHandler(boolean isInner) {
|
||||||
|
this.isInner = isInner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context, int count, int rawCount, @Nullable Argument argument) {
|
||||||
|
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();
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (!finder.findBoundsAt(pos)) {
|
||||||
|
VimPlugin.showMessage(finder.errorMessage());
|
||||||
|
VimPlugin.indicateError();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (i + 1 < count) {
|
||||||
|
finder.extendTillNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = finder.getRightBound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInner) {
|
||||||
|
finder.adjustForInner();
|
||||||
|
} else {
|
||||||
|
finder.adjustForOuter();
|
||||||
|
}
|
||||||
|
return new TextRange(finder.getLeftBound(), finder.getRightBound());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Editor editor, @NotNull DataContext context) {
|
||||||
|
|
||||||
|
@NotNull CommandState commandState = CommandState.getInstance(editor);
|
||||||
|
int count = Math.max(1, commandState.getCommandBuilder().getCount());
|
||||||
|
|
||||||
|
final ArgumentTextObjectHandler textObjectHandler = new ArgumentTextObjectHandler(isInner);
|
||||||
|
if (!commandState.isOperatorPending()) {
|
||||||
|
editor.getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
|
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0, null);
|
||||||
|
if (range != null) {
|
||||||
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
|
if (commandState.getMode() == CommandState.Mode.VISUAL) {
|
||||||
|
vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||||
|
} else {
|
||||||
|
caret.moveToOffset(range.getStartOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
|
textObjectHandler, Command.Type.MOTION, EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to find argument boundaries starting at the specified
|
||||||
|
* position
|
||||||
|
*/
|
||||||
|
private static class ArgBoundsFinder {
|
||||||
|
@NotNull private final CharSequence text;
|
||||||
|
@NotNull private final Document document;
|
||||||
|
@NotNull private final BracketPairs brackets;
|
||||||
|
private int leftBound = Integer.MAX_VALUE;
|
||||||
|
private int rightBound = Integer.MIN_VALUE;
|
||||||
|
private int leftBracket;
|
||||||
|
private int rightBracket;
|
||||||
|
private String error = null;
|
||||||
|
private static final String QUOTES = "\"'";
|
||||||
|
|
||||||
|
private static final int MAX_SEARCH_LINES = 10;
|
||||||
|
private static final int MAX_SEARCH_OFFSET = MAX_SEARCH_LINES * 80;
|
||||||
|
|
||||||
|
ArgBoundsFinder(@NotNull Document document, @NotNull BracketPairs bracketPairs) {
|
||||||
|
this.text = document.getImmutableCharSequence();
|
||||||
|
this.document = document;
|
||||||
|
this.brackets = bracketPairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds left and right boundaries of an argument at the specified
|
||||||
|
* position. If successful @ref getLeftBound() will point to the left
|
||||||
|
* argument delimiter and @ref getRightBound() will point to the right
|
||||||
|
* argument delimiter. Use @ref adjustForInner or @ref adjustForOuter to
|
||||||
|
* fix the boundaries based on the type of text object.
|
||||||
|
*
|
||||||
|
* @param position starting position.
|
||||||
|
*/
|
||||||
|
boolean findBoundsAt(int position) throws IllegalStateException {
|
||||||
|
if (text.length() == 0) {
|
||||||
|
error = "empty document";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
leftBound = Math.min(position, leftBound);
|
||||||
|
rightBound = Math.max(position, rightBound);
|
||||||
|
getOutOfQuotedText();
|
||||||
|
if (rightBound == leftBound) {
|
||||||
|
if (brackets.isCloseBracket(getCharAt(rightBound))) {
|
||||||
|
--leftBound;
|
||||||
|
} else {
|
||||||
|
++rightBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int nextLeft = leftBound;
|
||||||
|
int nextRight = rightBound;
|
||||||
|
final int leftLimit = leftLimit(position);
|
||||||
|
final int rightLimit = rightLimit(position);
|
||||||
|
//
|
||||||
|
// Try to extend the bounds until one of the bounds is a comma.
|
||||||
|
// This handles cases like: fun(a, (30 + <cursor>x) * 20, c)
|
||||||
|
//
|
||||||
|
boolean bothBrackets;
|
||||||
|
do {
|
||||||
|
leftBracket = nextLeft;
|
||||||
|
rightBracket = nextRight;
|
||||||
|
if (!findOuterBrackets(leftLimit, rightLimit)) {
|
||||||
|
error = "not inside argument list";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
leftBound = nextLeft;
|
||||||
|
findLeftBound();
|
||||||
|
nextLeft = leftBound - 1;
|
||||||
|
rightBound = nextRight;
|
||||||
|
findRightBound();
|
||||||
|
nextRight = rightBound + 1;
|
||||||
|
//
|
||||||
|
// If reached text boundaries
|
||||||
|
//
|
||||||
|
if (nextLeft < leftLimit || nextRight > rightLimit) {
|
||||||
|
error = "not an argument";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bothBrackets = getCharAt(leftBound) != ',' && getCharAt(rightBound) != ',';
|
||||||
|
final boolean nonEmptyArg = (rightBound - leftBound) > 1;
|
||||||
|
if (bothBrackets && nonEmptyArg && isIdentPreceding()) {
|
||||||
|
// Looking at a pair of brackets preceded by an
|
||||||
|
// identifier -- single argument function call.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (leftBound > leftLimit && rightBound < rightLimit && bothBrackets);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip left delimiter character and any following whitespace.
|
||||||
|
*/
|
||||||
|
void adjustForInner() {
|
||||||
|
++leftBound;
|
||||||
|
while (leftBound < rightBound && Character.isWhitespace(getCharAt(leftBound))) {
|
||||||
|
++leftBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclude left bound character for the first argument, include the
|
||||||
|
* right bound character and any following whitespace.
|
||||||
|
*/
|
||||||
|
void adjustForOuter() {
|
||||||
|
if (getCharAt(leftBound) != ',') {
|
||||||
|
++leftBound;
|
||||||
|
extendTillNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the right bound to the beginning of the next argument (if any).
|
||||||
|
*/
|
||||||
|
void extendTillNext() {
|
||||||
|
if (rightBound + 1 < rightBracket && getCharAt(rightBound) == ',') {
|
||||||
|
++rightBound;
|
||||||
|
while (rightBound + 1 < rightBracket && Character.isWhitespace(getCharAt(rightBound))) {
|
||||||
|
++rightBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLeftBound() {
|
||||||
|
return leftBound;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRightBound() {
|
||||||
|
return rightBound;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isIdentPreceding() {
|
||||||
|
int i = leftBound - 1;
|
||||||
|
final int idEnd = i;
|
||||||
|
while (i >= 0 && Character.isJavaIdentifierPart(getCharAt(i))) {
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
return (idEnd - i) > 0 && Character.isJavaIdentifierStart(getCharAt(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if current position is inside a quoted string and adjusts
|
||||||
|
* left and right bounds to the boundaries of the string.
|
||||||
|
*
|
||||||
|
* NOTE: Does not support line continuations for quoted string ('\' at the end of line).
|
||||||
|
*/
|
||||||
|
private void getOutOfQuotedText() {
|
||||||
|
// TODO this method should use IdeaVim methods to determine if the current position is in the string
|
||||||
|
final int lineNo = document.getLineNumber(leftBound);
|
||||||
|
final int lineStartOffset = document.getLineStartOffset(lineNo);
|
||||||
|
final int lineEndOffset = document.getLineEndOffset(lineNo);
|
||||||
|
int i = lineStartOffset;
|
||||||
|
while (i <= leftBound) {
|
||||||
|
if (isQuote(i)) {
|
||||||
|
final int endOfQuotedText = skipQuotedTextForward(i, lineEndOffset);
|
||||||
|
if (endOfQuotedText >= leftBound) {
|
||||||
|
leftBound = i - 1;
|
||||||
|
rightBound = endOfQuotedText + 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i = endOfQuotedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findRightBound() {
|
||||||
|
while (rightBound < rightBracket) {
|
||||||
|
final char ch = getCharAt(rightBound);
|
||||||
|
if (ch == ',') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (brackets.isOpenBracket(ch)) {
|
||||||
|
rightBound = skipSexp(rightBound, rightBracket, SexpDirection.forward(brackets));
|
||||||
|
} else {
|
||||||
|
if (isQuoteChar(ch)) {
|
||||||
|
rightBound = skipQuotedTextForward(rightBound, rightBracket);
|
||||||
|
}
|
||||||
|
++rightBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findLeftBound() {
|
||||||
|
while (leftBound > leftBracket) {
|
||||||
|
final char ch = getCharAt(leftBound);
|
||||||
|
if (ch == ',') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (brackets.isCloseBracket(ch)) {
|
||||||
|
leftBound = skipSexp(leftBound, leftBracket, SexpDirection.backward(brackets));
|
||||||
|
} else {
|
||||||
|
if (isQuoteChar(ch)) {
|
||||||
|
leftBound = skipQuotedTextBackward(leftBound, leftBracket);
|
||||||
|
}
|
||||||
|
--leftBound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isQuote(final int i) {
|
||||||
|
return QUOTES.indexOf(getCharAt(i)) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isQuoteChar(final int ch) {
|
||||||
|
return QUOTES.indexOf(ch) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char getCharAt(int logicalOffset) {
|
||||||
|
assert logicalOffset < text.length();
|
||||||
|
return text.charAt(logicalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int skipQuotedTextForward(final int start, final int end) {
|
||||||
|
assert start < end;
|
||||||
|
final char quoteChar = getCharAt(start);
|
||||||
|
boolean backSlash = false;
|
||||||
|
int i = start + 1;
|
||||||
|
|
||||||
|
while (i <= end) {
|
||||||
|
final char ch = getCharAt(i);
|
||||||
|
if (ch == quoteChar && !backSlash) {
|
||||||
|
// Found a matching quote, and it's not escaped.
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
backSlash = ch == '\\' && !backSlash;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int skipQuotedTextBackward(final int start, final int end) {
|
||||||
|
assert start > end;
|
||||||
|
final char quoteChar = getCharAt(start);
|
||||||
|
int i = start - 1;
|
||||||
|
|
||||||
|
while (i > end) {
|
||||||
|
final char ch = getCharAt(i);
|
||||||
|
final char prevChar = getCharAt(i - 1);
|
||||||
|
// NOTE: doesn't handle cases like \\"str", but they make no
|
||||||
|
// sense anyway.
|
||||||
|
if (ch == quoteChar && prevChar != '\\') {
|
||||||
|
// Found a matching quote, and it's not escaped.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int leftLimit(final int pos) {
|
||||||
|
final int offsetLimit = Math.max(pos - MAX_SEARCH_OFFSET, 0);
|
||||||
|
final int lineNo = document.getLineNumber(pos);
|
||||||
|
final int lineOffsetLimit = document.getLineStartOffset(Math.max(0, lineNo - MAX_SEARCH_LINES));
|
||||||
|
return Math.max(offsetLimit, lineOffsetLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int rightLimit(final int pos) {
|
||||||
|
final int offsetLimit = Math.min(pos + MAX_SEARCH_OFFSET, text.length());
|
||||||
|
final int lineNo = document.getLineNumber(pos);
|
||||||
|
final int lineOffsetLimit = document.getLineEndOffset(Math.min(document.getLineCount() - 1, lineNo + MAX_SEARCH_LINES));
|
||||||
|
return Math.min(offsetLimit, lineOffsetLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
String errorMessage() {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to parametrise S-expression traversal direction.
|
||||||
|
*/
|
||||||
|
abstract static class SexpDirection {
|
||||||
|
abstract int delta();
|
||||||
|
|
||||||
|
abstract boolean isOpenBracket(char ch);
|
||||||
|
|
||||||
|
abstract boolean isCloseBracket(char ch);
|
||||||
|
|
||||||
|
abstract int skipQuotedText(int pos, int end, ArgBoundsFinder self);
|
||||||
|
|
||||||
|
static SexpDirection forward(BracketPairs brackets) {
|
||||||
|
return new SexpDirection() {
|
||||||
|
@Override
|
||||||
|
int delta() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isOpenBracket(char ch) {
|
||||||
|
return brackets.isOpenBracket(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isCloseBracket(char ch) {
|
||||||
|
return brackets.isCloseBracket(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||||
|
return self.skipQuotedTextForward(pos, end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SexpDirection backward(BracketPairs brackets) {
|
||||||
|
return new SexpDirection() {
|
||||||
|
@Override
|
||||||
|
int delta() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isOpenBracket(char ch) {
|
||||||
|
return brackets.isCloseBracket(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isCloseBracket(char ch) {
|
||||||
|
return brackets.isOpenBracket(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int skipQuotedText(int pos, int end, ArgBoundsFinder self) {
|
||||||
|
return self.skipQuotedTextBackward(pos, end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skip over an S-expression considering priorities when unbalanced.
|
||||||
|
*
|
||||||
|
* @param start position of the starting bracket.
|
||||||
|
* @param end maximum position
|
||||||
|
* @param dir direction instance
|
||||||
|
* @return position after the S-expression, or the next to the start position if
|
||||||
|
* unbalanced.
|
||||||
|
*/
|
||||||
|
private int skipSexp(final int start, final int end, SexpDirection dir) {
|
||||||
|
char lastChar = getCharAt(start);
|
||||||
|
assert dir.isOpenBracket(lastChar);
|
||||||
|
Stack<Character> bracketStack = new Stack<>();
|
||||||
|
bracketStack.push(lastChar);
|
||||||
|
int i = start + dir.delta();
|
||||||
|
while (!bracketStack.empty() && i != end) {
|
||||||
|
final char ch = getCharAt(i);
|
||||||
|
if (dir.isOpenBracket(ch)) {
|
||||||
|
bracketStack.push(ch);
|
||||||
|
} else {
|
||||||
|
if (dir.isCloseBracket(ch)) {
|
||||||
|
if (bracketStack.lastElement() == brackets.matchingBracket(ch)) {
|
||||||
|
bracketStack.pop();
|
||||||
|
} else {
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
if (brackets.getBracketPrio(ch) < brackets.getBracketPrio(bracketStack.lastElement())) {
|
||||||
|
// (<...) -> (...)
|
||||||
|
bracketStack.pop();
|
||||||
|
// Retry the same character again for cases like (...<<...).
|
||||||
|
continue;
|
||||||
|
} else { // Unbalanced brackets -- check ranking.
|
||||||
|
// Ignore lower-priority closing brackets.
|
||||||
|
// (...> -> (....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isQuoteChar(ch)) {
|
||||||
|
i = dir.skipQuotedText(i, end, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += dir.delta();
|
||||||
|
}
|
||||||
|
if (bracketStack.empty()) {
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
return start + dir.delta();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a pair of brackets surrounding (leftBracket..rightBracket) block.
|
||||||
|
*
|
||||||
|
* @param start minimum position to look for
|
||||||
|
* @param end maximum position
|
||||||
|
* @return true if found
|
||||||
|
*/
|
||||||
|
boolean findOuterBrackets(final int start, final int end) {
|
||||||
|
boolean hasNewBracket = findPrevOpenBracket(start) && findNextCloseBracket(end);
|
||||||
|
while (hasNewBracket) {
|
||||||
|
final int leftPrio = brackets.getBracketPrio(getCharAt(leftBracket));
|
||||||
|
final int rightPrio = brackets.getBracketPrio(getCharAt(rightBracket));
|
||||||
|
if (leftPrio == rightPrio) {
|
||||||
|
// matching brackets
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (leftPrio < rightPrio) {
|
||||||
|
if (rightBracket + 1 < end) {
|
||||||
|
++rightBracket;
|
||||||
|
hasNewBracket = findNextCloseBracket(end);
|
||||||
|
} else {
|
||||||
|
hasNewBracket = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (leftBracket > 1) {
|
||||||
|
--leftBracket;
|
||||||
|
hasNewBracket = findPrevOpenBracket(start);
|
||||||
|
} else {
|
||||||
|
hasNewBracket = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds unmatched open bracket starting at @a leftBracket.
|
||||||
|
*
|
||||||
|
* @param start minimum position.
|
||||||
|
* @return true if found
|
||||||
|
*/
|
||||||
|
private boolean findPrevOpenBracket(final int start) {
|
||||||
|
char ch;
|
||||||
|
while (!brackets.isOpenBracket(ch = getCharAt(leftBracket))) {
|
||||||
|
if (brackets.isCloseBracket(ch)) {
|
||||||
|
leftBracket = skipSexp(leftBracket, start, SexpDirection.backward(brackets));
|
||||||
|
} else {
|
||||||
|
if (isQuoteChar(ch)) {
|
||||||
|
leftBracket = skipQuotedTextBackward(leftBracket, start);
|
||||||
|
} else {
|
||||||
|
if (leftBracket == start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--leftBracket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds unmatched close bracket starting at @a rightBracket.
|
||||||
|
*
|
||||||
|
* @param end maximum position.
|
||||||
|
* @return true if found
|
||||||
|
*/
|
||||||
|
private boolean findNextCloseBracket(final int end) {
|
||||||
|
char ch;
|
||||||
|
while (!brackets.isCloseBracket(ch = getCharAt(rightBracket))) {
|
||||||
|
if (brackets.isOpenBracket(ch)) {
|
||||||
|
rightBracket = skipSexp(rightBracket, end, SexpDirection.forward(brackets));
|
||||||
|
} else {
|
||||||
|
if (isQuoteChar(ch)) {
|
||||||
|
rightBracket = skipQuotedTextForward(rightBracket, end);
|
||||||
|
}
|
||||||
|
++rightBracket;
|
||||||
|
}
|
||||||
|
if (rightBracket >= end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -33,8 +33,8 @@ import com.maddyhome.idea.vim.command.CommandState;
|
|||||||
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.command.SelectionType;
|
||||||
import com.maddyhome.idea.vim.common.TextRange;
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
|
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.extension.VimNonDisposableExtension;
|
|
||||||
import com.maddyhome.idea.vim.key.OperatorFunction;
|
import com.maddyhome.idea.vim.key.OperatorFunction;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -45,23 +45,22 @@ import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
|||||||
/**
|
/**
|
||||||
* @author dhleong
|
* @author dhleong
|
||||||
*/
|
*/
|
||||||
public class CommentaryExtension extends VimNonDisposableExtension {
|
public class CommentaryExtension implements VimExtension {
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public @NotNull String getName() {
|
||||||
return "commentary";
|
return "commentary";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initOnce() {
|
public void init() {
|
||||||
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentMotion)"), new CommentMotionHandler(), false);
|
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentMotion)"), getOwner(), new CommentMotionHandler(), false);
|
||||||
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentLine)"), new CommentLineHandler(), false);
|
putExtensionHandlerMapping(MappingMode.N, parseKeys("<Plug>(CommentLine)"), getOwner(), new CommentLineHandler(), false);
|
||||||
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>(CommentMotionV)"), new CommentMotionVHandler(), false);
|
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>(CommentMotionV)"), getOwner(), new CommentMotionVHandler(), false);
|
||||||
|
|
||||||
putKeyMapping(MappingMode.N, parseKeys("gc"), parseKeys("<Plug>(CommentMotion)"), true);
|
putKeyMapping(MappingMode.N, parseKeys("gc"), getOwner(), parseKeys("<Plug>(CommentMotion)"), true);
|
||||||
putKeyMapping(MappingMode.N, parseKeys("gcc"), parseKeys("<Plug>(CommentLine)"), true);
|
putKeyMapping(MappingMode.N, parseKeys("gcc"), getOwner(), parseKeys("<Plug>(CommentLine)"), true);
|
||||||
putKeyMapping(MappingMode.XO, parseKeys("gc"), parseKeys("<Plug>(CommentMotionV)"), true);
|
putKeyMapping(MappingMode.XO, parseKeys("gc"), getOwner(), parseKeys("<Plug>(CommentMotionV)"), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class CommentMotionHandler implements VimExtensionHandler {
|
private static class CommentMotionHandler implements VimExtensionHandler {
|
||||||
@@ -135,8 +134,7 @@ public class CommentaryExtension extends VimNonDisposableExtension {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable TextRange getCommentRange(@NotNull Editor editor) {
|
||||||
private TextRange getCommentRange(@NotNull Editor editor) {
|
|
||||||
final CommandState.Mode mode = CommandState.getInstance(editor).getMode();
|
final CommandState.Mode mode = CommandState.getInstance(editor).getMode();
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case COMMAND:
|
case COMMAND:
|
||||||
|
@@ -27,10 +27,10 @@ import com.maddyhome.idea.vim.VimPlugin
|
|||||||
import com.maddyhome.idea.vim.command.CommandFlags
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
import com.maddyhome.idea.vim.command.MappingMode
|
import com.maddyhome.idea.vim.command.MappingMode
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||||
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension
|
|
||||||
import com.maddyhome.idea.vim.group.MotionGroup
|
import com.maddyhome.idea.vim.group.MotionGroup
|
||||||
import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
import com.maddyhome.idea.vim.group.visual.vimSetSelection
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
@@ -56,21 +56,21 @@ private const val ALL_OCCURRENCES = "<Plug>AllOccurrences"
|
|||||||
*
|
*
|
||||||
* See https://github.com/terryma/vim-multiple-cursors
|
* See https://github.com/terryma/vim-multiple-cursors
|
||||||
* */
|
* */
|
||||||
class VimMultipleCursorsExtension : VimNonDisposableExtension() {
|
class VimMultipleCursorsExtension : VimExtension {
|
||||||
override fun getName() = "multiple-cursors"
|
override fun getName() = "multiple-cursors"
|
||||||
|
|
||||||
override fun initOnce() {
|
override fun init() {
|
||||||
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), NextOccurrenceHandler(), false)
|
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), owner, NextOccurrenceHandler(), false)
|
||||||
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_OCCURRENCE), NextOccurrenceHandler(whole = false), false)
|
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_OCCURRENCE), owner, NextOccurrenceHandler(whole = false), false)
|
||||||
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), AllOccurrencesHandler(), false)
|
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), owner, AllOccurrencesHandler(), false )
|
||||||
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_OCCURRENCES), AllOccurrencesHandler(whole = false), false)
|
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_OCCURRENCES), owner, AllOccurrencesHandler(whole = false), false )
|
||||||
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), SkipOccurrenceHandler(), false)
|
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), owner, SkipOccurrenceHandler(), false )
|
||||||
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), RemoveOccurrenceHandler(), false)
|
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), owner, RemoveOccurrenceHandler(), false )
|
||||||
|
|
||||||
putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), parseKeys(NEXT_WHOLE_OCCURRENCE), true)
|
putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), owner, parseKeys(NEXT_WHOLE_OCCURRENCE), true)
|
||||||
putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), parseKeys(NEXT_OCCURRENCE), true)
|
putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), owner, parseKeys(NEXT_OCCURRENCE), true)
|
||||||
putKeyMapping(MappingMode.X, parseKeys("<A-x>"), parseKeys(SKIP_OCCURRENCE), true)
|
putKeyMapping(MappingMode.X, parseKeys("<A-x>"), owner, parseKeys(SKIP_OCCURRENCE), true)
|
||||||
putKeyMapping(MappingMode.X, parseKeys("<A-p>"), parseKeys(REMOVE_OCCURRENCE), true)
|
putKeyMapping(MappingMode.X, parseKeys("<A-p>"), owner, parseKeys(REMOVE_OCCURRENCE), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class WriteActionHandler : VimExtensionHandler {
|
abstract class WriteActionHandler : VimExtensionHandler {
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.CommandState
|
|||||||
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.command.SelectionType
|
||||||
import com.maddyhome.idea.vim.common.TextRange
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.inputKeyStroke
|
||||||
@@ -34,7 +35,6 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
|||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
||||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||||
import com.maddyhome.idea.vim.extension.VimNonDisposableExtension
|
|
||||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||||
import com.maddyhome.idea.vim.helper.StringHelper
|
import com.maddyhome.idea.vim.helper.StringHelper
|
||||||
import com.maddyhome.idea.vim.helper.mode
|
import com.maddyhome.idea.vim.helper.mode
|
||||||
@@ -51,19 +51,19 @@ import javax.swing.KeyStroke
|
|||||||
* @author dhleong
|
* @author dhleong
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
class VimSurroundExtension : VimNonDisposableExtension() {
|
class VimSurroundExtension : VimExtension {
|
||||||
override fun getName() = "surround"
|
override fun getName() = "surround"
|
||||||
|
|
||||||
override fun initOnce() {
|
override fun init() {
|
||||||
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>YSurround"), YSurroundHandler(), false)
|
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>YSurround"), owner, YSurroundHandler(), false)
|
||||||
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>CSurround"), CSurroundHandler(), false)
|
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>CSurround"), owner, CSurroundHandler(), false)
|
||||||
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>DSurround"), DSurroundHandler(), false)
|
putExtensionHandlerMapping(MappingMode.N, StringHelper.parseKeys("<Plug>DSurround"), owner, DSurroundHandler(), false)
|
||||||
putExtensionHandlerMapping(MappingMode.XO, StringHelper.parseKeys("<Plug>VSurround"), VSurroundHandler(), false)
|
putExtensionHandlerMapping(MappingMode.XO, StringHelper.parseKeys("<Plug>VSurround"), owner, VSurroundHandler(), false)
|
||||||
|
|
||||||
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ys"), StringHelper.parseKeys("<Plug>YSurround"), true)
|
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ys"), owner, StringHelper.parseKeys("<Plug>YSurround"), true)
|
||||||
putKeyMapping(MappingMode.N, StringHelper.parseKeys("cs"), StringHelper.parseKeys("<Plug>CSurround"), true)
|
putKeyMapping(MappingMode.N, StringHelper.parseKeys("cs"), owner, StringHelper.parseKeys("<Plug>CSurround"), true)
|
||||||
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ds"), StringHelper.parseKeys("<Plug>DSurround"), true)
|
putKeyMapping(MappingMode.N, StringHelper.parseKeys("ds"), owner, StringHelper.parseKeys("<Plug>DSurround"), true)
|
||||||
putKeyMapping(MappingMode.XO, StringHelper.parseKeys("S"), StringHelper.parseKeys("<Plug>VSurround"), true)
|
putKeyMapping(MappingMode.XO, StringHelper.parseKeys("S"), owner, StringHelper.parseKeys("<Plug>VSurround"), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class YSurroundHandler : VimExtensionHandler {
|
private class YSurroundHandler : VimExtensionHandler {
|
||||||
|
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* 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.textobjentire;
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext;
|
||||||
|
import com.intellij.openapi.editor.Caret;
|
||||||
|
import com.intellij.openapi.editor.Editor;
|
||||||
|
import com.maddyhome.idea.vim.command.*;
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange;
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtension;
|
||||||
|
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
|
||||||
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
|
||||||
|
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
|
||||||
|
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
|
||||||
|
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
|
||||||
|
import static com.maddyhome.idea.vim.group.visual.VisualGroupKt.vimSetSelection;
|
||||||
|
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port of vim-entire:
|
||||||
|
* https://github.com/kana/vim-textobj-entire
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* vim-textobj-entire provides two text objects:
|
||||||
|
* <ul>
|
||||||
|
* <li>ae targets the entire content of the current buffer.</li>
|
||||||
|
* <li>ie is similar to ae, but ie does not include leading and trailing empty lines. ie is handy for some situations. For example,</li>
|
||||||
|
* <ul>
|
||||||
|
* <li>Paste some text into a new buffer (<C-w>n"*P) -- note that the initial empty line is left as the last line.</li>
|
||||||
|
* <li>Edit the text (:%s/foo/bar/g etc)</li>
|
||||||
|
* <li>Then copy the resulting text to another application ("*yie)</li>
|
||||||
|
* </ul>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* See also the reference manual for more details at:
|
||||||
|
* https://github.com/kana/vim-textobj-entire/blob/master/doc/textobj-entire.txt
|
||||||
|
*
|
||||||
|
* @author Alexandre Grison (@agrison)
|
||||||
|
*/
|
||||||
|
public class VimTextObjEntireExtension implements VimExtension {
|
||||||
|
@Override
|
||||||
|
public @NotNull
|
||||||
|
String getName() {
|
||||||
|
return "textobj-entire";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
|
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>textobj-entire-a"), getOwner(),
|
||||||
|
new VimTextObjEntireExtension.EntireHandler(false), false);
|
||||||
|
putExtensionHandlerMapping(MappingMode.XO, parseKeys("<Plug>textobj-entire-i"), getOwner(),
|
||||||
|
new VimTextObjEntireExtension.EntireHandler(true), false);
|
||||||
|
|
||||||
|
putKeyMapping(MappingMode.XO, parseKeys("ae"), getOwner(), parseKeys("<Plug>textobj-entire-a"), true);
|
||||||
|
putKeyMapping(MappingMode.XO, parseKeys("ie"), getOwner(), parseKeys("<Plug>textobj-entire-i"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EntireHandler implements VimExtensionHandler {
|
||||||
|
final boolean ignoreLeadingAndTrailing;
|
||||||
|
|
||||||
|
EntireHandler(boolean ignoreLeadingAndTrailing) {
|
||||||
|
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class EntireTextObjectHandler extends TextObjectActionHandler {
|
||||||
|
final boolean ignoreLeadingAndTrailing;
|
||||||
|
|
||||||
|
EntireTextObjectHandler(boolean ignoreLeadingAndTrailing) {
|
||||||
|
this.ignoreLeadingAndTrailing = ignoreLeadingAndTrailing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable
|
||||||
|
TextRange getRange(@NotNull Editor editor, @NotNull Caret caret, @NotNull DataContext context,
|
||||||
|
int count, int rawCount, @Nullable Argument argument) {
|
||||||
|
int start = 0, end = editor.getDocument().getTextLength();
|
||||||
|
|
||||||
|
// for the `ie` text object we don't want leading an trailing spaces
|
||||||
|
// so we have to scan the document text to find the correct start & end
|
||||||
|
if (ignoreLeadingAndTrailing) {
|
||||||
|
String content = editor.getDocument().getText();
|
||||||
|
for (int i = 0; i < content.length(); ++i) {
|
||||||
|
if (!Character.isWhitespace(content.charAt(i))) {
|
||||||
|
start = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = content.length() - 1; i >= start; --i) {
|
||||||
|
if (!Character.isWhitespace(content.charAt(i))) {
|
||||||
|
end = i + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextRange(start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(@NotNull Editor editor, @NotNull DataContext context) {
|
||||||
|
@NotNull CommandState commandState = CommandState.getInstance(editor);
|
||||||
|
int count = Math.max(1, commandState.getCommandBuilder().getCount());
|
||||||
|
|
||||||
|
final EntireTextObjectHandler textObjectHandler = new EntireTextObjectHandler(ignoreLeadingAndTrailing);
|
||||||
|
if (!commandState.isOperatorPending()) {
|
||||||
|
editor.getCaretModel().runForEachCaret((Caret caret) -> {
|
||||||
|
final TextRange range = textObjectHandler.getRange(editor, caret, context, count, 0, null);
|
||||||
|
if (range != null) {
|
||||||
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
|
if (commandState.getMode() == CommandState.Mode.VISUAL) {
|
||||||
|
vimSetSelection(caret, range.getStartOffset(), range.getEndOffset() - 1, true);
|
||||||
|
} else {
|
||||||
|
caret.moveToOffset(range.getStartOffset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
commandState.getCommandBuilder().completeCommandPart(new Argument(new Command(count,
|
||||||
|
textObjectHandler, Command.Type.MOTION,
|
||||||
|
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -84,8 +84,10 @@ 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);
|
||||||
|
|
||||||
@Nullable private Command lastInsert;
|
private @Nullable Command lastInsert;
|
||||||
|
|
||||||
private void setInsertRepeat(int lines, int column, boolean append) {
|
private void setInsertRepeat(int lines, int column, boolean append) {
|
||||||
repeatLines = lines;
|
repeatLines = lines;
|
||||||
@@ -154,7 +156,7 @@ public class ChangeGroup {
|
|||||||
*
|
*
|
||||||
* @param editor The editor to insert into
|
* @param editor The editor to insert into
|
||||||
*/
|
*/
|
||||||
public void insertNewLineAbove(@NotNull final Editor editor, @NotNull DataContext context) {
|
public void insertNewLineAbove(final @NotNull Editor editor, @NotNull DataContext context) {
|
||||||
if (editor.isOneLineMode()) return;
|
if (editor.isOneLineMode()) return;
|
||||||
|
|
||||||
Set<Caret> firstLiners = new HashSet<>();
|
Set<Caret> firstLiners = new HashSet<>();
|
||||||
@@ -214,7 +216,7 @@ public class ChangeGroup {
|
|||||||
* @param editor The editor to insert into
|
* @param editor The editor to insert into
|
||||||
* @param context The data context
|
* @param context The data context
|
||||||
*/
|
*/
|
||||||
public void insertNewLineBelow(@NotNull final Editor editor, @NotNull final DataContext context) {
|
public void insertNewLineBelow(final @NotNull Editor editor, final @NotNull DataContext context) {
|
||||||
if (editor.isOneLineMode()) return;
|
if (editor.isOneLineMode()) return;
|
||||||
|
|
||||||
for (Caret caret : editor.getCaretModel().getAllCarets()) {
|
for (Caret caret : editor.getCaretModel().getAllCarets()) {
|
||||||
@@ -308,7 +310,7 @@ public class ChangeGroup {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable private DocumentListener documentListener;
|
private @Nullable DocumentListener documentListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the cursor is currently after the start of the current insert this deletes all the newly inserted text.
|
* If the cursor is currently after the start of the current insert this deletes all the newly inserted text.
|
||||||
@@ -421,7 +423,7 @@ public class ChangeGroup {
|
|||||||
// Workaround for VIM-1546. Another solution is highly appreciated.
|
// Workaround for VIM-1546. Another solution is highly appreciated.
|
||||||
public boolean tabAction = false;
|
public boolean tabAction = false;
|
||||||
|
|
||||||
@NotNull private final EditorMouseListener listener = new EditorMouseListener() {
|
private final @NotNull EditorMouseListener listener = new EditorMouseListener() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(@NotNull EditorMouseEvent event) {
|
public void mouseClicked(@NotNull EditorMouseEvent event) {
|
||||||
Editor editor = event.getEditor();
|
Editor editor = event.getEditor();
|
||||||
@@ -737,9 +739,9 @@ public class ChangeGroup {
|
|||||||
* @param key The user entered keystroke
|
* @param key The user entered keystroke
|
||||||
* @param plan the current action plan draft
|
* @param plan the current action plan draft
|
||||||
*/
|
*/
|
||||||
public void beforeProcessKey(@NotNull final Editor editor,
|
public void beforeProcessKey(final @NotNull Editor editor,
|
||||||
@NotNull final DataContext context,
|
final @NotNull DataContext context,
|
||||||
@NotNull final KeyStroke key,
|
final @NotNull KeyStroke key,
|
||||||
@NotNull ActionPlan plan) {
|
@NotNull ActionPlan plan) {
|
||||||
|
|
||||||
final TypedActionHandler originalHandler = KeyHandler.getInstance().getOriginalHandler();
|
final TypedActionHandler originalHandler = KeyHandler.getInstance().getOriginalHandler();
|
||||||
@@ -805,9 +807,9 @@ public class ChangeGroup {
|
|||||||
* @param key The user entered keystroke
|
* @param key The user entered keystroke
|
||||||
* @return true if this was a regular character, false if not
|
* @return true if this was a regular character, false if not
|
||||||
*/
|
*/
|
||||||
public boolean processKey(@NotNull final Editor editor,
|
public boolean processKey(final @NotNull Editor editor,
|
||||||
@NotNull final DataContext context,
|
final @NotNull DataContext context,
|
||||||
@NotNull final KeyStroke key) {
|
final @NotNull KeyStroke key) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("processKey(" + key + ")");
|
logger.debug("processKey(" + key + ")");
|
||||||
}
|
}
|
||||||
@@ -824,9 +826,9 @@ public class ChangeGroup {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean processKeyInSelectMode(@NotNull final Editor editor,
|
public boolean processKeyInSelectMode(final @NotNull Editor editor,
|
||||||
@NotNull final DataContext context,
|
final @NotNull DataContext context,
|
||||||
@NotNull final KeyStroke key) {
|
final @NotNull KeyStroke key) {
|
||||||
boolean res;
|
boolean res;
|
||||||
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
try (VimListenerSuppressor.Locked ignored = SelectionVimListenerSuppressor.INSTANCE.lock()) {
|
||||||
res = processKey(editor, context, key);
|
res = processKey(editor, context, key);
|
||||||
@@ -1061,14 +1063,13 @@ public class ChangeGroup {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable Pair<TextRange, SelectionType> getDeleteRangeAndType(@NotNull Editor editor,
|
||||||
public Pair<TextRange, SelectionType> getDeleteRangeAndType(@NotNull Editor editor,
|
@NotNull Caret caret,
|
||||||
@NotNull Caret caret,
|
@NotNull DataContext context,
|
||||||
@NotNull DataContext context,
|
int count,
|
||||||
int count,
|
int rawCount,
|
||||||
int rawCount,
|
final @NotNull Argument argument,
|
||||||
@NotNull final Argument argument,
|
boolean isChange) {
|
||||||
boolean isChange) {
|
|
||||||
final TextRange range = MotionGroup.getMotionRange(editor, caret, context, count, rawCount, argument);
|
final TextRange range = MotionGroup.getMotionRange(editor, caret, context, count, rawCount, argument);
|
||||||
if (range == null) return null;
|
if (range == null) return null;
|
||||||
|
|
||||||
@@ -1163,7 +1164,8 @@ public class ChangeGroup {
|
|||||||
final LogicalPosition lp =
|
final LogicalPosition lp =
|
||||||
editor.offsetToLogicalPosition(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, caret));
|
editor.offsetToLogicalPosition(VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, caret));
|
||||||
|
|
||||||
if (editor.getDocument().getText().isEmpty()) {
|
// Please don't use `getDocument().getText().isEmpty()`
|
||||||
|
if (editor.getDocument().getTextLength() == 0) {
|
||||||
insertBeforeCursor(editor, context);
|
insertBeforeCursor(editor, context);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1223,13 +1225,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 +1259,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);
|
||||||
@@ -1456,7 +1457,8 @@ public class ChangeGroup {
|
|||||||
boolean res = deleteRange(editor, caret, range, type, true);
|
boolean res = deleteRange(editor, caret, range, type, true);
|
||||||
if (res) {
|
if (res) {
|
||||||
if (type == SelectionType.LINE_WISE) {
|
if (type == SelectionType.LINE_WISE) {
|
||||||
if (editor.getDocument().getText().isEmpty()) {
|
// Please don't use `getDocument().getText().isEmpty()`
|
||||||
|
if (editor.getDocument().getTextLength() == 0) {
|
||||||
insertBeforeCursor(editor, context);
|
insertBeforeCursor(editor, context);
|
||||||
}
|
}
|
||||||
else if (after) {
|
else if (after) {
|
||||||
@@ -1729,8 +1731,8 @@ public class ChangeGroup {
|
|||||||
* @param type The type of deletion
|
* @param type The type of deletion
|
||||||
* @return true if able to delete the text, false if not
|
* @return true if able to delete the text, false if not
|
||||||
*/
|
*/
|
||||||
private boolean deleteText(@NotNull final Editor editor,
|
private boolean deleteText(final @NotNull Editor editor,
|
||||||
@NotNull final TextRange range,
|
final @NotNull TextRange range,
|
||||||
@Nullable SelectionType type) {
|
@Nullable SelectionType type) {
|
||||||
// Fix for https://youtrack.jetbrains.net/issue/VIM-35
|
// Fix for https://youtrack.jetbrains.net/issue/VIM-35
|
||||||
if (!range.normalize(EditorHelper.getFileSize(editor, true))) {
|
if (!range.normalize(EditorHelper.getFileSize(editor, true))) {
|
||||||
@@ -1838,7 +1840,7 @@ public class ChangeGroup {
|
|||||||
*
|
*
|
||||||
* @return true
|
* @return true
|
||||||
*/
|
*/
|
||||||
public boolean changeNumberVisualMode(@NotNull final Editor editor,
|
public boolean changeNumberVisualMode(final @NotNull Editor editor,
|
||||||
@NotNull Caret caret,
|
@NotNull Caret caret,
|
||||||
@NotNull TextRange selectedRange,
|
@NotNull TextRange selectedRange,
|
||||||
final int count,
|
final int count,
|
||||||
@@ -1882,7 +1884,7 @@ public class ChangeGroup {
|
|||||||
private int repeatCharsCount;
|
private int repeatCharsCount;
|
||||||
private List<Object> lastStrokes;
|
private List<Object> lastStrokes;
|
||||||
|
|
||||||
public boolean changeNumber(@NotNull final Editor editor, @NotNull Caret caret, final int count) {
|
public boolean changeNumber(final @NotNull Editor editor, @NotNull Caret caret, final int count) {
|
||||||
final BoundListOption nf = OptionsManager.INSTANCE.getNrformats();
|
final BoundListOption nf = OptionsManager.INSTANCE.getNrformats();
|
||||||
final boolean alpha = nf.contains("alpha");
|
final boolean alpha = nf.contains("alpha");
|
||||||
final boolean hex = nf.contains("hex");
|
final boolean hex = nf.contains("hex");
|
||||||
@@ -1911,13 +1913,12 @@ public class ChangeGroup {
|
|||||||
private boolean lastLower = true;
|
private boolean lastLower = true;
|
||||||
private Document document;
|
private Document document;
|
||||||
|
|
||||||
@Nullable
|
public @Nullable String changeNumberInRange(final @NotNull Editor editor,
|
||||||
public String changeNumberInRange(@NotNull final Editor editor,
|
@NotNull TextRange range,
|
||||||
@NotNull TextRange range,
|
final int count,
|
||||||
final int count,
|
boolean alpha,
|
||||||
boolean alpha,
|
boolean hex,
|
||||||
boolean hex,
|
boolean octal) {
|
||||||
boolean octal) {
|
|
||||||
String text = EditorHelper.getText(editor, range);
|
String text = EditorHelper.getText(editor, range);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("found range " + range);
|
logger.debug("found range " + range);
|
||||||
@@ -2035,8 +2036,7 @@ public class ChangeGroup {
|
|||||||
oldOffset = e.getOffset() + newFragmentLength;
|
oldOffset = e.getOffset() + newFragmentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private @NotNull List<EditorActionHandlerBase> getAdjustCaretActions(@NotNull DocumentEvent e) {
|
||||||
private List<EditorActionHandlerBase> getAdjustCaretActions(@NotNull DocumentEvent e) {
|
|
||||||
final int delta = e.getOffset() - oldOffset;
|
final int delta = e.getOffset() - oldOffset;
|
||||||
if (oldOffset >= 0 && delta != 0) {
|
if (oldOffset >= 0 && delta != 0) {
|
||||||
final List<EditorActionHandlerBase> positionCaretActions = new ArrayList<>();
|
final List<EditorActionHandlerBase> positionCaretActions = new ArrayList<>();
|
||||||
|
@@ -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",
|
||||||
@@ -1749,8 +1748,8 @@ public class DigraphGroup {
|
|||||||
'f', 't', '\ufb05', // LATIN SMALL LIGATURE FT
|
'f', 't', '\ufb05', // LATIN SMALL LIGATURE FT
|
||||||
's', 't', '\ufb06', // LATIN SMALL LIGATURE ST
|
's', 't', '\ufb06', // LATIN SMALL LIGATURE ST
|
||||||
};
|
};
|
||||||
@NotNull private final HashMap<String, Character> digraphs = new HashMap<>(defaultDigraphs.length);
|
private final @NotNull HashMap<String, Character> digraphs = new HashMap<>(defaultDigraphs.length);
|
||||||
@NotNull private final TreeMap<Character, String> keys = new TreeMap<>();
|
private final @NotNull TreeMap<Character, String> keys = new TreeMap<>();
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
|
private static final Logger logger = Logger.getInstance(DigraphGroup.class.getName());
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,9 @@ package com.maddyhome.idea.vim.group;
|
|||||||
import com.intellij.find.EditorSearchSession;
|
import com.intellij.find.EditorSearchSession;
|
||||||
import com.intellij.openapi.actionSystem.AnAction;
|
import com.intellij.openapi.actionSystem.AnAction;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.editor.*;
|
import com.intellij.openapi.editor.*;
|
||||||
import com.intellij.openapi.editor.colors.ColorKey;
|
import com.intellij.openapi.editor.colors.ColorKey;
|
||||||
import com.intellij.openapi.editor.colors.EditorColors;
|
import com.intellij.openapi.editor.colors.EditorColors;
|
||||||
@@ -47,9 +50,11 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
public class EditorGroup {
|
@State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
||||||
|
public class EditorGroup implements PersistentStateComponent<Element> {
|
||||||
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
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";
|
||||||
|
|
||||||
private boolean isBlockCursor = false;
|
private boolean isBlockCursor = false;
|
||||||
private boolean isAnimatedScrolling = false;
|
private boolean isAnimatedScrolling = false;
|
||||||
@@ -76,7 +81,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initLineNumbers(@NotNull final Editor editor) {
|
private void initLineNumbers(final @NotNull Editor editor) {
|
||||||
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
|
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -107,13 +112,13 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean supportsVimLineNumbers(@NotNull final Editor editor) {
|
private static boolean supportsVimLineNumbers(final @NotNull Editor editor) {
|
||||||
// We only support line numbers in editors that are file based, and that aren't for diffs, which control their
|
// We only support line numbers in editors that are file based, and that aren't for diffs, which control their
|
||||||
// own line numbers, often using EditorGutterComponentEx#setLineNumberConvertor
|
// own line numbers, often using EditorGutterComponentEx#setLineNumberConvertor
|
||||||
return EditorHelper.isFileEditor(editor) && !EditorHelper.isDiffEditor(editor);
|
return EditorHelper.isFileEditor(editor) && !EditorHelper.isDiffEditor(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateLineNumbers(@NotNull final Editor editor, final boolean requiresRepaint) {
|
private static void updateLineNumbers(final @NotNull Editor editor, final boolean requiresRepaint) {
|
||||||
final boolean relativeNumber = OptionsManager.INSTANCE.getRelativenumber().isSet();
|
final boolean relativeNumber = OptionsManager.INSTANCE.getRelativenumber().isSet();
|
||||||
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
|
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
|
||||||
|
|
||||||
@@ -142,7 +147,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldShowBuiltinLineNumbers(@NotNull final Editor editor, boolean number, boolean relativeNumber) {
|
private static boolean shouldShowBuiltinLineNumbers(final @NotNull Editor editor, boolean number, boolean relativeNumber) {
|
||||||
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
|
final boolean initialState = UserDataManager.getVimLineNumbersInitialState(editor);
|
||||||
|
|
||||||
// Builtin relative line numbers requires EditorGutterComponentEx#setLineNumberConvertor. If we don't have that,
|
// Builtin relative line numbers requires EditorGutterComponentEx#setLineNumberConvertor. If we don't have that,
|
||||||
@@ -155,15 +160,15 @@ public class EditorGroup {
|
|||||||
return (initialState || number) && !relativeNumber;
|
return (initialState || number) && !relativeNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setBuiltinLineNumbers(@NotNull final Editor editor, boolean show) {
|
private static void setBuiltinLineNumbers(final @NotNull Editor editor, boolean show) {
|
||||||
editor.getSettings().setLineNumbersShown(show);
|
editor.getSettings().setLineNumbersShown(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasRelativeLineNumbersInstalled(@NotNull final Editor editor) {
|
private static boolean hasRelativeLineNumbersInstalled(final @NotNull Editor editor) {
|
||||||
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
|
return UserDataManager.getVimHasRelativeLineNumbersInstalled(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void installRelativeLineNumbers(@NotNull final Editor editor) {
|
private static void installRelativeLineNumbers(final @NotNull Editor editor) {
|
||||||
if (!hasRelativeLineNumbersInstalled(editor)) {
|
if (!hasRelativeLineNumbersInstalled(editor)) {
|
||||||
final EditorGutter gutter = editor.getGutter();
|
final EditorGutter gutter = editor.getGutter();
|
||||||
if (gutter instanceof EditorGutterComponentEx) {
|
if (gutter instanceof EditorGutterComponentEx) {
|
||||||
@@ -176,7 +181,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removeRelativeLineNumbers(@NotNull final Editor editor) {
|
private static void removeRelativeLineNumbers(final @NotNull Editor editor) {
|
||||||
if (hasRelativeLineNumbersInstalled(editor)) {
|
if (hasRelativeLineNumbersInstalled(editor)) {
|
||||||
final EditorGutter gutter = editor.getGutter();
|
final EditorGutter gutter = editor.getGutter();
|
||||||
if (gutter instanceof EditorGutterComponentEx) {
|
if (gutter instanceof EditorGutterComponentEx) {
|
||||||
@@ -190,7 +195,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void repaintRelativeLineNumbers(@NotNull final Editor editor) {
|
private static void repaintRelativeLineNumbers(final @NotNull Editor editor) {
|
||||||
final EditorGutter gutter = editor.getGutter();
|
final EditorGutter gutter = editor.getGutter();
|
||||||
final EditorGutterComponentEx gutterComponent = gutter instanceof EditorGutterComponentEx ? (EditorGutterComponentEx) gutter : null;
|
final EditorGutterComponentEx gutterComponent = gutter instanceof EditorGutterComponentEx ? (EditorGutterComponentEx) gutter : null;
|
||||||
if (gutterComponent != null) {
|
if (gutterComponent != null) {
|
||||||
@@ -210,7 +215,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void readData(@NotNull Element element) {
|
public void readData(@NotNull Element element) {
|
||||||
final Element editor = element.getChild("editor");
|
final Element editor = element.getChild(EDITOR_STORE_ELEMENT);
|
||||||
if (editor != null) {
|
if (editor != null) {
|
||||||
final Element keyRepeat = editor.getChild("key-repeat");
|
final Element keyRepeat = editor.getChild("key-repeat");
|
||||||
if (keyRepeat != null) {
|
if (keyRepeat != null) {
|
||||||
@@ -222,8 +227,7 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable Boolean isKeyRepeat() {
|
||||||
public Boolean isKeyRepeat() {
|
|
||||||
return isKeyRepeat;
|
return isKeyRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +279,19 @@ public class EditorGroup {
|
|||||||
VimPlugin.getNotifications(project).notifyAboutIdeaJoin();
|
VimPlugin.getNotifications(project).notifyAboutIdeaJoin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Element getState() {
|
||||||
|
Element element = new Element("editor");
|
||||||
|
saveData(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadState(@NotNull Element state) {
|
||||||
|
readData(state);
|
||||||
|
}
|
||||||
|
|
||||||
public static class NumberChangeListener implements OptionChangeListener<Boolean> {
|
public static class NumberChangeListener implements OptionChangeListener<Boolean> {
|
||||||
public static NumberChangeListener INSTANCE = new NumberChangeListener();
|
public static NumberChangeListener INSTANCE = new NumberChangeListener();
|
||||||
|
|
||||||
@@ -293,11 +310,10 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class RelativeLineNumberConverter implements TIntFunction {
|
private static class RelativeLineNumberConverter implements TIntFunction {
|
||||||
@NotNull
|
private final @NotNull Editor editor;
|
||||||
private final Editor editor;
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
RelativeLineNumberConverter(@NotNull final Editor editor) {
|
RelativeLineNumberConverter(final @NotNull Editor editor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,17 +338,15 @@ public class EditorGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class RelativeLineNumberGutterProvider implements TextAnnotationGutterProvider {
|
private static class RelativeLineNumberGutterProvider implements TextAnnotationGutterProvider {
|
||||||
@NotNull
|
private final @NotNull Editor editor;
|
||||||
private final Editor editor;
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
RelativeLineNumberGutterProvider(@NotNull final Editor editor) {
|
RelativeLineNumberGutterProvider(final @NotNull Editor editor) {
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public String getLineText(int line, @NotNull Editor editor) {
|
public @Nullable String getLineText(int line, @NotNull Editor editor) {
|
||||||
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
|
final boolean number = OptionsManager.INSTANCE.getNumber().isSet();
|
||||||
if (number && isCaretLine(line, editor)) {
|
if (number && isCaretLine(line, editor)) {
|
||||||
return lineNumberToString(line + 1, editor, true);
|
return lineNumberToString(line + 1, editor, true);
|
||||||
@@ -360,9 +374,8 @@ public class EditorGroup {
|
|||||||
: StringsKt.padStart(Integer.toString(lineNumber), digitsCount, ' ');
|
: StringsKt.padStart(Integer.toString(lineNumber), digitsCount, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public String getToolTip(int line, Editor editor) {
|
public @Nullable String getToolTip(int line, Editor editor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,15 +384,13 @@ public class EditorGroup {
|
|||||||
return isCaretLine(line, editor) ? EditorFontType.BOLD: null;
|
return isCaretLine(line, editor) ? EditorFontType.BOLD: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public ColorKey getColor(int line, Editor editor) {
|
public @Nullable ColorKey getColor(int line, Editor editor) {
|
||||||
return isCaretLine(line, editor) ? EditorColors.LINE_NUMBER_ON_CARET_ROW_COLOR : EditorColors.LINE_NUMBERS_COLOR;
|
return isCaretLine(line, editor) ? EditorColors.LINE_NUMBER_ON_CARET_ROW_COLOR : EditorColors.LINE_NUMBERS_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
@Override
|
||||||
public Color getBgColor(int line, Editor editor) {
|
public @Nullable Color getBgColor(int line, Editor editor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -118,8 +118,7 @@ public class FileGroup {
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable VirtualFile findFile(@NotNull VirtualFile root, @NotNull String filename) {
|
||||||
private VirtualFile findFile(@NotNull VirtualFile root, @NotNull String filename) {
|
|
||||||
VirtualFile res = root.findFileByRelativePath(filename);
|
VirtualFile res = root.findFileByRelativePath(filename);
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
return res;
|
return res;
|
||||||
@@ -223,6 +222,20 @@ public class FileGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the previous tab.
|
||||||
|
*/
|
||||||
|
public @Nullable VirtualFile getPreviousTab(@NotNull DataContext context) {
|
||||||
|
Project project = PlatformDataKeys.PROJECT.getData(context);
|
||||||
|
if (project == null) return null;
|
||||||
|
FileEditorManager fem = FileEditorManager.getInstance(project); // API change - don't merge
|
||||||
|
VirtualFile vf = lastSelections.get(fem);
|
||||||
|
if (vf != null && vf.isValid()) {
|
||||||
|
return vf;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Editor selectEditor(Project project, @NotNull VirtualFile file) {
|
Editor selectEditor(Project project, @NotNull VirtualFile file) {
|
||||||
FileEditorManager fMgr = FileEditorManager.getInstance(project);
|
FileEditorManager fMgr = FileEditorManager.getInstance(project);
|
||||||
@@ -382,10 +395,10 @@ public class FileGroup {
|
|||||||
VimPlugin.showMessage(msg.toString());
|
VimPlugin.showMessage(msg.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull private static final String disposableKey = "VimFileGroupDisposable";
|
private static final @NotNull String disposableKey = "VimFileGroupDisposable";
|
||||||
|
|
||||||
@NotNull private static final HashMap<FileEditorManager, VirtualFile> lastSelections = new HashMap<>();
|
private static final @NotNull HashMap<FileEditorManager, VirtualFile> lastSelections = new HashMap<>();
|
||||||
@NotNull private static final Logger logger = Logger.getInstance(FileGroup.class.getName());
|
private static final @NotNull Logger logger = Logger.getInstance(FileGroup.class.getName());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
|
* This method listens for editor tab changes so any insert/replace modes that need to be reset can be.
|
||||||
|
@@ -18,19 +18,27 @@
|
|||||||
|
|
||||||
package com.maddyhome.idea.vim.group;
|
package com.maddyhome.idea.vim.group;
|
||||||
|
|
||||||
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.RoamingType;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||||
import com.maddyhome.idea.vim.option.NumberOption;
|
import com.maddyhome.idea.vim.option.NumberOption;
|
||||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class HistoryGroup {
|
@State(name = "VimHistorySettings", storages = {
|
||||||
|
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
|
||||||
|
})
|
||||||
|
public class HistoryGroup implements PersistentStateComponent<Element> {
|
||||||
public static final String SEARCH = "search";
|
public static final String SEARCH = "search";
|
||||||
public static final String COMMAND = "cmd";
|
public static final String COMMAND = "cmd";
|
||||||
public static final String EXPRESSION = "expr";
|
public static final String EXPRESSION = "expr";
|
||||||
@@ -45,8 +53,7 @@ public class HistoryGroup {
|
|||||||
block.addEntry(text);
|
block.addEntry(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<HistoryEntry> getEntries(String key, int first, int last) {
|
||||||
public List<HistoryEntry> getEntries(String key, int first, int last) {
|
|
||||||
HistoryBlock block = blocks(key);
|
HistoryBlock block = blocks(key);
|
||||||
|
|
||||||
List<HistoryEntry> entries = block.getEntries();
|
List<HistoryEntry> entries = block.getEntries();
|
||||||
@@ -167,6 +174,19 @@ public class HistoryGroup {
|
|||||||
return opt.value();
|
return opt.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Element getState() {
|
||||||
|
Element element = new Element("history");
|
||||||
|
saveData(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadState(@NotNull Element state) {
|
||||||
|
readData(state);
|
||||||
|
}
|
||||||
|
|
||||||
private static class HistoryBlock {
|
private static class HistoryBlock {
|
||||||
public void addEntry(@NotNull String text) {
|
public void addEntry(@NotNull String text) {
|
||||||
for (int i = 0; i < entries.size(); i++) {
|
for (int i = 0; i < entries.size(); i++) {
|
||||||
@@ -184,12 +204,11 @@ public class HistoryGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<HistoryEntry> getEntries() {
|
||||||
public List<HistoryEntry> getEntries() {
|
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull private final List<HistoryEntry> entries = new ArrayList<>();
|
private final @NotNull List<HistoryEntry> entries = new ArrayList<>();
|
||||||
private int counter;
|
private int counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,16 +222,15 @@ public class HistoryGroup {
|
|||||||
return number;
|
return number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull String getEntry() {
|
||||||
public String getEntry() {
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int number;
|
private final int number;
|
||||||
@NotNull private final String entry;
|
private final @NotNull String entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull private final Map<String, HistoryBlock> histories = new HashMap<>();
|
private final @NotNull Map<String, HistoryBlock> histories = new HashMap<>();
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(HistoryGroup.class.getName());
|
private static final Logger logger = Logger.getInstance(HistoryGroup.class.getName());
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,9 @@ import com.intellij.openapi.actionSystem.*;
|
|||||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
||||||
import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
||||||
import com.intellij.openapi.application.ApplicationManager;
|
import com.intellij.openapi.application.ApplicationManager;
|
||||||
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
import com.intellij.openapi.editor.EditorFactory;
|
import com.intellij.openapi.editor.EditorFactory;
|
||||||
@@ -41,6 +44,7 @@ import com.maddyhome.idea.vim.handler.ActionBeanClass;
|
|||||||
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
|
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
|
||||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||||
import com.maddyhome.idea.vim.key.*;
|
import com.maddyhome.idea.vim.key.*;
|
||||||
|
import kotlin.Pair;
|
||||||
import kotlin.text.StringsKt;
|
import kotlin.text.StringsKt;
|
||||||
import org.jdom.Element;
|
import org.jdom.Element;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -54,27 +58,30 @@ import java.util.*;
|
|||||||
|
|
||||||
import static com.maddyhome.idea.vim.helper.StringHelper.toKeyNotation;
|
import static com.maddyhome.idea.vim.helper.StringHelper.toKeyNotation;
|
||||||
import static java.util.stream.Collectors.joining;
|
import static java.util.stream.Collectors.joining;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author vlan
|
* @author vlan
|
||||||
*/
|
*/
|
||||||
public class KeyGroup {
|
@State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
|
||||||
private static final String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
|
public class KeyGroup implements PersistentStateComponent<Element> {
|
||||||
|
public static final String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
|
||||||
private static final String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
|
private static final String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
|
||||||
private static final String OWNER_ATTRIBUTE = "owner";
|
private static final String OWNER_ATTRIBUTE = "owner";
|
||||||
private static final String TEXT_ELEMENT = "text";
|
private static final String TEXT_ELEMENT = "text";
|
||||||
|
|
||||||
private static final Logger logger = Logger.getInstance(KeyGroup.class);
|
private static final Logger logger = Logger.getInstance(KeyGroup.class);
|
||||||
|
|
||||||
@NotNull private final Map<KeyStroke, ShortcutOwner> shortcutConflicts = new LinkedHashMap<>();
|
private final @NotNull Map<KeyStroke, ShortcutOwner> shortcutConflicts = new LinkedHashMap<>();
|
||||||
@NotNull private final Set<KeyStroke> requiredShortcutKeys = new HashSet<>(300);
|
private final @NotNull Set<RequiredShortcut> requiredShortcutKeys = new HashSet<>(300);
|
||||||
@NotNull private final Map<MappingMode, CommandPartNode> keyRoots = new EnumMap<>(MappingMode.class);
|
private final @NotNull Map<MappingMode, CommandPartNode> keyRoots = new EnumMap<>(MappingMode.class);
|
||||||
@NotNull private final Map<MappingMode, KeyMapping> keyMappings = new EnumMap<>(MappingMode.class);
|
private final @NotNull Map<MappingMode, KeyMapping> keyMappings = new EnumMap<>(MappingMode.class);
|
||||||
@Nullable private OperatorFunction operatorFunction = null;
|
private @Nullable OperatorFunction operatorFunction = null;
|
||||||
|
|
||||||
void registerRequiredShortcutKeys(@NotNull Editor editor) {
|
void registerRequiredShortcutKeys(@NotNull Editor editor) {
|
||||||
EventFacade.getInstance().registerCustomShortcutSet(VimShortcutKeyAction.getInstance(),
|
EventFacade.getInstance()
|
||||||
toShortcutSet(requiredShortcutKeys), editor.getComponent());
|
.registerCustomShortcutSet(VimShortcutKeyAction.getInstance(), toShortcutSet(requiredShortcutKeys),
|
||||||
|
editor.getComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerShortcutsForLookup(@NotNull LookupImpl lookup) {
|
public void registerShortcutsForLookup(@NotNull LookupImpl lookup) {
|
||||||
@@ -88,44 +95,75 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean showKeyMappings(@NotNull Set<MappingMode> modes, @NotNull Editor editor) {
|
public boolean showKeyMappings(@NotNull Set<MappingMode> modes, @NotNull Editor editor) {
|
||||||
final List<MappingInfo> rows = getKeyMappingRows(modes);
|
List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = getKeyMappingRows(modes);
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
for (MappingInfo row : rows) {
|
for (Pair<EnumSet<MappingMode>, MappingInfo> row : rows) {
|
||||||
builder.append(StringsKt.padEnd(getModesStringCode(row.getMappingModes()), 2, ' '));
|
MappingInfo mappingInfo = row.getSecond();
|
||||||
|
builder.append(StringsKt.padEnd(getModesStringCode(row.getFirst()), 2, ' '));
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
builder.append(StringsKt.padEnd(toKeyNotation(row.getFromKeys()), 11, ' '));
|
builder.append(StringsKt.padEnd(toKeyNotation(mappingInfo.getFromKeys()), 11, ' '));
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
builder.append(row.isRecursive() ? " " : "*");
|
builder.append(mappingInfo.isRecursive() ? " " : "*");
|
||||||
builder.append(" ");
|
builder.append(" ");
|
||||||
final List<KeyStroke> toKeys = row.getToKeys();
|
if (mappingInfo instanceof ToKeysMappingInfo) {
|
||||||
final VimExtensionHandler extensionHandler = row.getExtensionHandler();
|
List<KeyStroke> toKeys = ((ToKeysMappingInfo)mappingInfo).getToKeys();
|
||||||
if (toKeys != null) {
|
|
||||||
builder.append(toKeyNotation(toKeys));
|
builder.append(toKeyNotation(toKeys));
|
||||||
}
|
}
|
||||||
else if (extensionHandler != null) {
|
else if (mappingInfo instanceof ToHandlerMappingInfo) {
|
||||||
|
final VimExtensionHandler extensionHandler = ((ToHandlerMappingInfo)mappingInfo).getExtensionHandler();
|
||||||
builder.append("call ");
|
builder.append("call ");
|
||||||
builder.append(extensionHandler.getClass().getCanonicalName());
|
builder.append(extensionHandler.getClass().getCanonicalName());
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
builder.append("<Unknown>");
|
|
||||||
}
|
|
||||||
builder.append("\n");
|
builder.append("\n");
|
||||||
}
|
}
|
||||||
ExOutputModel.getInstance(editor).output(builder.toString());
|
ExOutputModel.getInstance(editor).output(builder.toString());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putKeyMapping(@NotNull Set<MappingMode> modes, @NotNull List<KeyStroke> fromKeys,
|
public void removeKeyMapping(@NotNull MappingOwner owner) {
|
||||||
@Nullable List<KeyStroke> toKeys, @Nullable VimExtensionHandler extensionHandler,
|
Arrays.stream(MappingMode.values()).map(this::getKeyMapping).forEach(o -> o.delete(owner));
|
||||||
|
unregisterKeyMapping(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putKeyMapping(@NotNull Set<MappingMode> modes,
|
||||||
|
@NotNull List<KeyStroke> fromKeys,
|
||||||
|
@NotNull MappingOwner owner,
|
||||||
|
@NotNull VimExtensionHandler extensionHandler,
|
||||||
boolean recursive) {
|
boolean recursive) {
|
||||||
for (MappingMode mode : modes) {
|
modes.stream().map(this::getKeyMapping).forEach(o -> o.put(fromKeys, owner, extensionHandler, recursive));
|
||||||
final KeyMapping mapping = getKeyMapping(mode);
|
registerKeyMapping(fromKeys, owner);
|
||||||
mapping.put(EnumSet.of(mode), fromKeys, toKeys, extensionHandler, recursive);
|
}
|
||||||
|
|
||||||
|
public void putKeyMapping(@NotNull Set<MappingMode> modes,
|
||||||
|
@NotNull List<KeyStroke> fromKeys,
|
||||||
|
@NotNull MappingOwner owner,
|
||||||
|
@NotNull List<KeyStroke> toKeys,
|
||||||
|
boolean recursive) {
|
||||||
|
modes.stream().map(this::getKeyMapping).forEach(o -> o.put(fromKeys, toKeys, owner, recursive));
|
||||||
|
registerKeyMapping(fromKeys, owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Pair<List<KeyStroke>, MappingInfo>> getKeyMappingByOwner(@NotNull MappingOwner owner) {
|
||||||
|
return Arrays.stream(MappingMode.values()).map(this::getKeyMapping).flatMap(o -> o.getByOwner(owner).stream())
|
||||||
|
.collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unregisterKeyMapping(MappingOwner owner) {
|
||||||
|
final int oldSize = requiredShortcutKeys.size();
|
||||||
|
requiredShortcutKeys.removeIf(requiredShortcut -> requiredShortcut.getOwner().equals(owner));
|
||||||
|
if (requiredShortcutKeys.size() != oldSize) {
|
||||||
|
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||||
|
unregisterShortcutKeys(editor);
|
||||||
|
registerRequiredShortcutKeys(editor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerKeyMapping(@NotNull List<KeyStroke> fromKeys, MappingOwner owner) {
|
||||||
final int oldSize = requiredShortcutKeys.size();
|
final int oldSize = requiredShortcutKeys.size();
|
||||||
for (KeyStroke key : fromKeys) {
|
for (KeyStroke key : fromKeys) {
|
||||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
||||||
requiredShortcutKeys.add(key);
|
requiredShortcutKeys.add(new RequiredShortcut(key, owner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (requiredShortcutKeys.size() != oldSize) {
|
if (requiredShortcutKeys.size() != oldSize) {
|
||||||
@@ -136,8 +174,7 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable OperatorFunction getOperatorFunction() {
|
||||||
public OperatorFunction getOperatorFunction() {
|
|
||||||
return operatorFunction;
|
return operatorFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,8 +224,7 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<AnAction> getKeymapConflicts(@NotNull KeyStroke keyStroke) {
|
||||||
public List<AnAction> getKeymapConflicts(@NotNull KeyStroke keyStroke) {
|
|
||||||
final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
|
final KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
|
||||||
final Keymap keymap = keymapManager.getActiveKeymap();
|
final Keymap keymap = keymapManager.getActiveKeymap();
|
||||||
final KeyboardShortcut shortcut = new KeyboardShortcut(keyStroke, null);
|
final KeyboardShortcut shortcut = new KeyboardShortcut(keyStroke, null);
|
||||||
@@ -203,12 +239,12 @@ public class KeyGroup {
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull Map<KeyStroke, ShortcutOwner> getShortcutConflicts() {
|
||||||
public Map<KeyStroke, ShortcutOwner> getShortcutConflicts() {
|
final Set<RequiredShortcut> requiredShortcutKeys = this.requiredShortcutKeys;
|
||||||
final Set<KeyStroke> requiredShortcutKeys = this.requiredShortcutKeys;
|
|
||||||
final Map<KeyStroke, ShortcutOwner> savedConflicts = getSavedShortcutConflicts();
|
final Map<KeyStroke, ShortcutOwner> savedConflicts = getSavedShortcutConflicts();
|
||||||
final Map<KeyStroke, ShortcutOwner> results = new HashMap<>();
|
final Map<KeyStroke, ShortcutOwner> results = new HashMap<>();
|
||||||
for (KeyStroke keyStroke : requiredShortcutKeys) {
|
for (RequiredShortcut requiredShortcut : requiredShortcutKeys) {
|
||||||
|
KeyStroke keyStroke = requiredShortcut.getKeyStroke();
|
||||||
if (!VimShortcutKeyAction.VIM_ONLY_EDITOR_KEYS.contains(keyStroke)) {
|
if (!VimShortcutKeyAction.VIM_ONLY_EDITOR_KEYS.contains(keyStroke)) {
|
||||||
final List<AnAction> conflicts = getKeymapConflicts(keyStroke);
|
final List<AnAction> conflicts = getKeymapConflicts(keyStroke);
|
||||||
if (!conflicts.isEmpty()) {
|
if (!conflicts.isEmpty()) {
|
||||||
@@ -220,13 +256,11 @@ public class KeyGroup {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull Map<KeyStroke, ShortcutOwner> getSavedShortcutConflicts() {
|
||||||
public Map<KeyStroke, ShortcutOwner> getSavedShortcutConflicts() {
|
|
||||||
return shortcutConflicts;
|
return shortcutConflicts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull KeyMapping getKeyMapping(@NotNull MappingMode mode) {
|
||||||
public KeyMapping getKeyMapping(@NotNull MappingMode mode) {
|
|
||||||
KeyMapping mapping = keyMappings.get(mode);
|
KeyMapping mapping = keyMappings.get(mode);
|
||||||
if (mapping == null) {
|
if (mapping == null) {
|
||||||
mapping = new KeyMapping();
|
mapping = new KeyMapping();
|
||||||
@@ -245,8 +279,7 @@ public class KeyGroup {
|
|||||||
* @param mappingMode The mapping mode
|
* @param mappingMode The mapping mode
|
||||||
* @return The key mapping tree root
|
* @return The key mapping tree root
|
||||||
*/
|
*/
|
||||||
@NotNull
|
public @NotNull CommandPartNode getKeyRoot(@NotNull MappingMode mappingMode) {
|
||||||
public CommandPartNode getKeyRoot(@NotNull MappingMode mappingMode) {
|
|
||||||
return keyRoots.computeIfAbsent(mappingMode, (key) -> new RootNode());
|
return keyRoots.computeIfAbsent(mappingMode, (key) -> new RootNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,10 +290,11 @@ public class KeyGroup {
|
|||||||
* Digraphs are handled directly by KeyHandler#handleKey instead of via an action, but we need to still make sure the
|
* Digraphs are handled directly by KeyHandler#handleKey instead of via an action, but we need to still make sure the
|
||||||
* shortcuts are registered, or the key handler won't see them
|
* shortcuts are registered, or the key handler won't see them
|
||||||
* </p>
|
* </p>
|
||||||
|
*
|
||||||
* @param keyStroke The shortcut to register
|
* @param keyStroke The shortcut to register
|
||||||
*/
|
*/
|
||||||
public void registerShortcutWithoutAction(KeyStroke keyStroke) {
|
public void registerShortcutWithoutAction(KeyStroke keyStroke, MappingOwner owner) {
|
||||||
registerRequiredShortcut(Collections.singletonList(keyStroke));
|
registerRequiredShortcut(Collections.singletonList(keyStroke), owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterCommandActions() {
|
public void unregisterCommandActions() {
|
||||||
@@ -274,8 +308,9 @@ public class KeyGroup {
|
|||||||
|
|
||||||
if (!VimPlugin.getPluginId().equals(actionHolder.getPluginId())) {
|
if (!VimPlugin.getPluginId().equals(actionHolder.getPluginId())) {
|
||||||
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
|
logger.error("IdeaVim doesn't accept contributions to `vimActions` extension points. " +
|
||||||
"Please create a plugin using `VimExtension`. " +
|
"Please create a plugin using `VimExtension`. " +
|
||||||
"Plugin to blame: " + actionHolder.getPluginId());
|
"Plugin to blame: " +
|
||||||
|
actionHolder.getPluginId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +319,8 @@ public class KeyGroup {
|
|||||||
final EditorActionHandlerBase action = actionHolder.getAction();
|
final EditorActionHandlerBase action = actionHolder.getAction();
|
||||||
if (action instanceof ComplicatedKeysAction) {
|
if (action instanceof ComplicatedKeysAction) {
|
||||||
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
|
actionKeys = ((ComplicatedKeysAction)action).getKeyStrokesSet();
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
|
throw new RuntimeException("Cannot register action: " + action.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,7 +341,7 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (List<KeyStroke> keyStrokes : actionKeys) {
|
for (List<KeyStroke> keyStrokes : actionKeys) {
|
||||||
registerRequiredShortcut(keyStrokes);
|
registerRequiredShortcut(keyStrokes, MappingOwner.IdeaVim.INSTANCE);
|
||||||
|
|
||||||
for (MappingMode mappingMode : actionModes) {
|
for (MappingMode mappingMode : actionModes) {
|
||||||
Node node = getKeyRoot(mappingMode);
|
Node node = getKeyRoot(mappingMode);
|
||||||
@@ -322,15 +358,17 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys) {
|
private void registerRequiredShortcut(@NotNull List<KeyStroke> keys, MappingOwner owner) {
|
||||||
for (KeyStroke key : keys) {
|
for (KeyStroke key : keys) {
|
||||||
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
if (key.getKeyChar() == KeyEvent.CHAR_UNDEFINED) {
|
||||||
requiredShortcutKeys.add(key);
|
requiredShortcutKeys.add(new RequiredShortcut(key, owner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCommand(@NotNull Set<MappingMode> mappingModes, EditorActionHandlerBase action, List<KeyStroke> keys) {
|
private void checkCommand(@NotNull Set<MappingMode> mappingModes,
|
||||||
|
EditorActionHandlerBase action,
|
||||||
|
List<KeyStroke> keys) {
|
||||||
for (MappingMode mappingMode : mappingModes) {
|
for (MappingMode mappingMode : mappingModes) {
|
||||||
checkIdentity(mappingMode, action.getId(), keys);
|
checkIdentity(mappingMode, action.getId(), keys);
|
||||||
}
|
}
|
||||||
@@ -340,8 +378,8 @@ public class KeyGroup {
|
|||||||
private void checkIdentity(MappingMode mappingMode, String actName, List<KeyStroke> keys) {
|
private void checkIdentity(MappingMode mappingMode, String actName, List<KeyStroke> keys) {
|
||||||
Set<List<KeyStroke>> keySets = identityChecker.computeIfAbsent(mappingMode, k -> new HashSet<>());
|
Set<List<KeyStroke>> keySets = identityChecker.computeIfAbsent(mappingMode, k -> new HashSet<>());
|
||||||
if (keySets.contains(keys)) {
|
if (keySets.contains(keys)) {
|
||||||
throw new RuntimeException("This keymap already exists: " + mappingMode + " keys: " +
|
throw new RuntimeException(
|
||||||
keys + " action:" + actName);
|
"This keymap already exists: " + mappingMode + " keys: " + keys + " action:" + actName);
|
||||||
}
|
}
|
||||||
keySets.add(keys);
|
keySets.add(keys);
|
||||||
}
|
}
|
||||||
@@ -356,7 +394,9 @@ public class KeyGroup {
|
|||||||
if (!prefix.get(i).equals(keys.get(i))) break;
|
if (!prefix.get(i).equals(keys.get(i))) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> actionExceptions = Arrays.asList("VimInsertDeletePreviousWordAction", "VimInsertAfterCursorAction", "VimInsertBeforeCursorAction", "VimFilterVisualLinesAction", "VimAutoIndentMotionAction");
|
List<String> actionExceptions = Arrays
|
||||||
|
.asList("VimInsertDeletePreviousWordAction", "VimInsertAfterCursorAction", "VimInsertBeforeCursorAction",
|
||||||
|
"VimFilterVisualLinesAction", "VimAutoIndentMotionAction");
|
||||||
if (i == shortOne && !actionExceptions.contains(action.getId()) && !actionExceptions.contains(entry.getValue())) {
|
if (i == shortOne && !actionExceptions.contains(action.getId()) && !actionExceptions.contains(entry.getValue())) {
|
||||||
throw new RuntimeException("Prefix found! " +
|
throw new RuntimeException("Prefix found! " +
|
||||||
keys +
|
keys +
|
||||||
@@ -371,47 +411,45 @@ public class KeyGroup {
|
|||||||
prefixes.put(keys, action.getId());
|
prefixes.put(keys, action.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable private Map<MappingMode, Set<List<KeyStroke>>> identityChecker;
|
private @Nullable Map<MappingMode, Set<List<KeyStroke>>> identityChecker;
|
||||||
@Nullable private Map<List<KeyStroke>, String> prefixes;
|
private @Nullable Map<List<KeyStroke>, String> prefixes;
|
||||||
|
|
||||||
@NotNull
|
private @NotNull Node addMNode(@NotNull CommandPartNode base,
|
||||||
private Node addMNode(@NotNull CommandPartNode base,
|
ActionBeanClass actionHolder,
|
||||||
ActionBeanClass actionHolder,
|
@NotNull KeyStroke key,
|
||||||
@NotNull KeyStroke key,
|
boolean isLastInSequence) {
|
||||||
boolean isLastInSequence) {
|
|
||||||
Node existing = base.get(key);
|
Node existing = base.get(key);
|
||||||
if (existing != null) return existing;
|
if (existing != null) return existing;
|
||||||
|
|
||||||
Node newNode;
|
Node newNode;
|
||||||
if (isLastInSequence) {
|
if (isLastInSequence) {
|
||||||
newNode = new CommandNode(actionHolder);
|
newNode = new CommandNode(actionHolder);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
newNode = new CommandPartNode();
|
newNode = new CommandPartNode();
|
||||||
}
|
}
|
||||||
base.put(key, newNode);
|
base.put(key, newNode);
|
||||||
return newNode;
|
return newNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull ShortcutSet toShortcutSet(@NotNull Collection<RequiredShortcut> requiredShortcuts) {
|
||||||
private static ShortcutSet toShortcutSet(@NotNull Collection<KeyStroke> keyStrokes) {
|
final List<Shortcut> shortcuts = new ArrayList<>();
|
||||||
final List<com.intellij.openapi.actionSystem.Shortcut> shortcuts = new ArrayList<>();
|
for (RequiredShortcut key : requiredShortcuts) {
|
||||||
for (KeyStroke key : keyStrokes) {
|
shortcuts.add(new KeyboardShortcut(key.getKeyStroke(), null));
|
||||||
shortcuts.add(new KeyboardShortcut(key, null));
|
|
||||||
}
|
}
|
||||||
return new CustomShortcutSet(shortcuts.toArray(new com.intellij.openapi.actionSystem.Shortcut[0]));
|
return new CustomShortcutSet(shortcuts.toArray(new Shortcut[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull List<Pair<EnumSet<MappingMode>, MappingInfo>> getKeyMappingRows(@NotNull Set<MappingMode> modes) {
|
||||||
private static List<MappingInfo> getKeyMappingRows(@NotNull Set<MappingMode> modes) {
|
final Map<ImmutableList<KeyStroke>, EnumSet<MappingMode>> actualModes = new HashMap<>();
|
||||||
final Map<ImmutableList<KeyStroke>, Set<MappingMode>> actualModes = new HashMap<>();
|
|
||||||
for (MappingMode mode : modes) {
|
for (MappingMode mode : modes) {
|
||||||
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
||||||
for (List<KeyStroke> fromKeys : mapping) {
|
for (List<KeyStroke> fromKeys : mapping) {
|
||||||
final ImmutableList<KeyStroke> key = ImmutableList.copyOf(fromKeys);
|
final ImmutableList<KeyStroke> key = ImmutableList.copyOf(fromKeys);
|
||||||
final Set<MappingMode> value = actualModes.get(key);
|
final EnumSet<MappingMode> value = actualModes.get(key);
|
||||||
final Set<MappingMode> newValue;
|
final EnumSet<MappingMode> newValue;
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
newValue = new HashSet<>(value);
|
newValue = value.clone();
|
||||||
newValue.add(mode);
|
newValue.add(mode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -420,26 +458,24 @@ public class KeyGroup {
|
|||||||
actualModes.put(key, newValue);
|
actualModes.put(key, newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final List<MappingInfo> rows = new ArrayList<>();
|
final List<Pair<EnumSet<MappingMode>, MappingInfo>> rows = new ArrayList<>();
|
||||||
for (Map.Entry<ImmutableList<KeyStroke>, Set<MappingMode>> entry : actualModes.entrySet()) {
|
for (Map.Entry<ImmutableList<KeyStroke>, EnumSet<MappingMode>> entry : actualModes.entrySet()) {
|
||||||
final ArrayList<KeyStroke> fromKeys = new ArrayList<>(entry.getKey());
|
final ArrayList<KeyStroke> fromKeys = new ArrayList<>(entry.getKey());
|
||||||
final Set<MappingMode> mappingModes = entry.getValue();
|
final EnumSet<MappingMode> mappingModes = entry.getValue();
|
||||||
if (!mappingModes.isEmpty()) {
|
if (!mappingModes.isEmpty()) {
|
||||||
final MappingMode mode = mappingModes.iterator().next();
|
final MappingMode mode = mappingModes.iterator().next();
|
||||||
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
final KeyMapping mapping = VimPlugin.getKey().getKeyMapping(mode);
|
||||||
final MappingInfo mappingInfo = mapping.get(fromKeys);
|
final MappingInfo mappingInfo = mapping.get(fromKeys);
|
||||||
if (mappingInfo != null) {
|
if (mappingInfo != null) {
|
||||||
rows.add(new MappingInfo(mappingModes, mappingInfo.getFromKeys(), mappingInfo.getToKeys(),
|
rows.add(new Pair<>(mappingModes, mappingInfo));
|
||||||
mappingInfo.getExtensionHandler(), mappingInfo.isRecursive()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(rows);
|
rows.sort(Comparator.comparing(Pair<EnumSet<MappingMode>, MappingInfo>::getSecond));
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull String getModesStringCode(@NotNull Set<MappingMode> modes) {
|
||||||
private static String getModesStringCode(@NotNull Set<MappingMode> modes) {
|
|
||||||
if (modes.equals(MappingMode.NVO)) {
|
if (modes.equals(MappingMode.NVO)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -453,16 +489,14 @@ public class KeyGroup {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||||
public List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
|
||||||
final List<AnAction> results = new ArrayList<>();
|
final List<AnAction> results = new ArrayList<>();
|
||||||
results.addAll(getLocalActions(component, keyStroke));
|
results.addAll(getLocalActions(component, keyStroke));
|
||||||
results.addAll(getKeymapActions(keyStroke));
|
results.addAll(getKeymapActions(keyStroke));
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||||
private static List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
|
||||||
final List<AnAction> results = new ArrayList<>();
|
final List<AnAction> results = new ArrayList<>();
|
||||||
final KeyboardShortcut keyStrokeShortcut = new KeyboardShortcut(keyStroke, null);
|
final KeyboardShortcut keyStrokeShortcut = new KeyboardShortcut(keyStroke, null);
|
||||||
for (Component c = component; c != null; c = c.getParent()) {
|
for (Component c = component; c != null; c = c.getParent()) {
|
||||||
@@ -484,8 +518,7 @@ public class KeyGroup {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private static @NotNull List<AnAction> getKeymapActions(@NotNull KeyStroke keyStroke) {
|
||||||
private static List<AnAction> getKeymapActions(@NotNull KeyStroke keyStroke) {
|
|
||||||
final List<AnAction> results = new ArrayList<>();
|
final List<AnAction> results = new ArrayList<>();
|
||||||
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||||
for (String id : keymap.getActionIds(keyStroke)) {
|
for (String id : keymap.getActionIds(keyStroke)) {
|
||||||
@@ -496,4 +529,17 @@ public class KeyGroup {
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Element getState() {
|
||||||
|
Element element = new Element("key");
|
||||||
|
saveData(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadState(@NotNull Element state) {
|
||||||
|
readData(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -90,8 +90,8 @@ public class MacroGroup {
|
|||||||
* @param cnt count
|
* @param cnt count
|
||||||
* @param total total
|
* @param total total
|
||||||
*/
|
*/
|
||||||
public void playbackKeys(@NotNull final Editor editor, @NotNull final DataContext context, @Nullable final Project project,
|
public void playbackKeys(final @NotNull Editor editor, final @NotNull DataContext context, final @Nullable Project project,
|
||||||
@NotNull final List<KeyStroke> keys, final int pos, final int cnt, final int total) {
|
final @NotNull List<KeyStroke> keys, final int pos, final int cnt, final int total) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("playbackKeys " + pos);
|
logger.debug("playbackKeys " + pos);
|
||||||
}
|
}
|
||||||
@@ -138,8 +138,7 @@ public class MacroGroup {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private @NotNull KeyEvent createKeyEvent(@NotNull KeyStroke stroke, Component component) {
|
||||||
private KeyEvent createKeyEvent(@NotNull KeyStroke stroke, Component component) {
|
|
||||||
return new KeyEvent(component,
|
return new KeyEvent(component,
|
||||||
stroke.getKeyChar() == KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_TYPED,
|
stroke.getKeyChar() == KeyEvent.CHAR_UNDEFINED ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_TYPED,
|
||||||
System.currentTimeMillis(), stroke.getModifiers(), stroke.getKeyCode(), stroke.getKeyChar());
|
System.currentTimeMillis(), stroke.getModifiers(), stroke.getKeyCode(), stroke.getKeyChar());
|
||||||
|
@@ -21,6 +21,10 @@ package com.maddyhome.idea.vim.group;
|
|||||||
import com.intellij.ide.bookmarks.Bookmark;
|
import com.intellij.ide.bookmarks.Bookmark;
|
||||||
import com.intellij.ide.bookmarks.BookmarkManager;
|
import com.intellij.ide.bookmarks.BookmarkManager;
|
||||||
import com.intellij.ide.bookmarks.BookmarksListener;
|
import com.intellij.ide.bookmarks.BookmarksListener;
|
||||||
|
import com.intellij.openapi.components.PersistentStateComponent;
|
||||||
|
import com.intellij.openapi.components.RoamingType;
|
||||||
|
import com.intellij.openapi.components.State;
|
||||||
|
import com.intellij.openapi.components.Storage;
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
import com.intellij.openapi.editor.Document;
|
import com.intellij.openapi.editor.Document;
|
||||||
import com.intellij.openapi.editor.Editor;
|
import com.intellij.openapi.editor.Editor;
|
||||||
@@ -53,7 +57,10 @@ import java.util.stream.Collectors;
|
|||||||
/**
|
/**
|
||||||
* This class contains all the mark related functionality
|
* This class contains all the mark related functionality
|
||||||
*/
|
*/
|
||||||
public class MarkGroup {
|
@State(name = "VimMarksSettings", storages = {
|
||||||
|
@Storage(value = "$APP_CONFIG$/vim_settings.xml", roamingType = RoamingType.DISABLED)
|
||||||
|
})
|
||||||
|
public class MarkGroup implements PersistentStateComponent<Element> {
|
||||||
public static final char MARK_VISUAL_START = '<';
|
public static final char MARK_VISUAL_START = '<';
|
||||||
public static final char MARK_VISUAL_END = '>';
|
public static final char MARK_VISUAL_END = '>';
|
||||||
public static final char MARK_CHANGE_START = '[';
|
public static final char MARK_CHANGE_START = '[';
|
||||||
@@ -83,8 +90,7 @@ public class MarkGroup {
|
|||||||
* @param ch The desired mark
|
* @param ch The desired mark
|
||||||
* @return The requested mark if set, null if not set
|
* @return The requested mark if set, null if not set
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable Mark getMark(@NotNull Editor editor, char ch) {
|
||||||
public Mark getMark(@NotNull Editor editor, char ch) {
|
|
||||||
Mark mark = null;
|
Mark mark = null;
|
||||||
if (ch == '`') ch = '\'';
|
if (ch == '`') ch = '\'';
|
||||||
|
|
||||||
@@ -135,8 +141,7 @@ public class MarkGroup {
|
|||||||
* @param count Postive for next jump (Ctrl-I), negative for previous jump (Ctrl-O).
|
* @param count Postive for next jump (Ctrl-I), negative for previous jump (Ctrl-O).
|
||||||
* @return The jump or null if out of range.
|
* @return The jump or null if out of range.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable Jump getJump(int count) {
|
||||||
public Jump getJump(int count) {
|
|
||||||
int index = jumps.size() - 1 - (jumpSpot - count);
|
int index = jumps.size() - 1 - (jumpSpot - count);
|
||||||
if (index < 0 || index >= jumps.size()) {
|
if (index < 0 || index >= jumps.size()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -154,8 +159,7 @@ public class MarkGroup {
|
|||||||
* @param ch The mark to get
|
* @param ch The mark to get
|
||||||
* @return The mark in the current file, if set, null if no such mark
|
* @return The mark in the current file, if set, null if no such mark
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public @Nullable Mark getFileMark(@NotNull Editor editor, char ch) {
|
||||||
public Mark getFileMark(@NotNull Editor editor, char ch) {
|
|
||||||
if (ch == '`') ch = '\'';
|
if (ch == '`') ch = '\'';
|
||||||
final HashMap fmarks = getFileMarks(editor.getDocument());
|
final HashMap fmarks = getFileMarks(editor.getDocument());
|
||||||
if (fmarks == null) {
|
if (fmarks == null) {
|
||||||
@@ -228,8 +232,7 @@ public class MarkGroup {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Bookmark createOrGetSystemMark(char ch, int line, @NotNull Editor editor) {
|
||||||
private Bookmark createOrGetSystemMark(char ch, int line, @NotNull Editor editor) {
|
|
||||||
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return null;
|
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return null;
|
||||||
final Project project = editor.getProject();
|
final Project project = editor.getProject();
|
||||||
if (project == null) return null;
|
if (project == null) return null;
|
||||||
@@ -259,18 +262,15 @@ public class MarkGroup {
|
|||||||
setMark(editor, MARK_CHANGE_END, range.getEndOffset());
|
setMark(editor, MARK_CHANGE_END, range.getEndOffset());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getChangeMarks(@NotNull Editor editor) {
|
||||||
public TextRange getChangeMarks(@NotNull Editor editor) {
|
|
||||||
return getMarksRange(editor, MARK_CHANGE_START, MARK_CHANGE_END);
|
return getMarksRange(editor, MARK_CHANGE_START, MARK_CHANGE_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getVisualSelectionMarks(@NotNull Editor editor) {
|
||||||
public TextRange getVisualSelectionMarks(@NotNull Editor editor) {
|
|
||||||
return getMarksRange(editor, MARK_VISUAL_START, MARK_VISUAL_END);
|
return getMarksRange(editor, MARK_VISUAL_START, MARK_VISUAL_END);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable TextRange getMarksRange(@NotNull Editor editor, char startMark, char endMark) {
|
||||||
private TextRange getMarksRange(@NotNull Editor editor, char startMark, char endMark) {
|
|
||||||
final Mark start = getMark(editor, startMark);
|
final Mark start = getMark(editor, startMark);
|
||||||
final Mark end = getMark(editor, endMark);
|
final Mark end = getMark(editor, endMark);
|
||||||
if (start != null && end != null) {
|
if (start != null && end != null) {
|
||||||
@@ -338,8 +338,7 @@ public class MarkGroup {
|
|||||||
mark.clear();
|
mark.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<Mark> getMarks(@NotNull Editor editor) {
|
||||||
public List<Mark> getMarks(@NotNull Editor editor) {
|
|
||||||
HashSet<Mark> res = new HashSet<>();
|
HashSet<Mark> res = new HashSet<>();
|
||||||
|
|
||||||
final FileMarks<Character, Mark> marks = getFileMarks(editor.getDocument());
|
final FileMarks<Character, Mark> marks = getFileMarks(editor.getDocument());
|
||||||
@@ -355,8 +354,7 @@ public class MarkGroup {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull List<Jump> getJumps() {
|
||||||
public List<Jump> getJumps() {
|
|
||||||
return jumps;
|
return jumps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,8 +369,7 @@ public class MarkGroup {
|
|||||||
* @return The map of marks. The keys are <code>Character</code>s of the mark names, the values are
|
* @return The map of marks. The keys are <code>Character</code>s of the mark names, the values are
|
||||||
* <code>Mark</code>s.
|
* <code>Mark</code>s.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
private @Nullable FileMarks<Character, Mark> getFileMarks(final @NotNull Document doc) {
|
||||||
private FileMarks<Character, Mark> getFileMarks(@NotNull final Document doc) {
|
|
||||||
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
|
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
|
||||||
if (vf == null) {
|
if (vf == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -381,8 +378,7 @@ public class MarkGroup {
|
|||||||
return getFileMarks(vf.getPath());
|
return getFileMarks(vf.getPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable HashMap<Character, Mark> getAllFileMarks(final @NotNull Document doc) {
|
||||||
private HashMap<Character, Mark> getAllFileMarks(@NotNull final Document doc) {
|
|
||||||
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
|
VirtualFile vf = FileDocumentManager.getInstance().getFile(doc);
|
||||||
if (vf == null) {
|
if (vf == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -659,6 +655,19 @@ public class MarkGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Element getState() {
|
||||||
|
Element element = new Element("marks");
|
||||||
|
saveData(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void loadState(@NotNull Element state) {
|
||||||
|
readData(state);
|
||||||
|
}
|
||||||
|
|
||||||
private static class FileMarks<K, V> extends HashMap<K, V> {
|
private static class FileMarks<K, V> extends HashMap<K, V> {
|
||||||
public void setTimestamp(Date timestamp) {
|
public void setTimestamp(Date timestamp) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
@@ -723,8 +732,7 @@ public class MarkGroup {
|
|||||||
// TODO - update jumps
|
// TODO - update jumps
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Editor getAnEditor(@NotNull Document doc) {
|
||||||
private Editor getAnEditor(@NotNull Document doc) {
|
|
||||||
Editor[] editors = EditorFactory.getInstance().getEditors(doc);
|
Editor[] editors = EditorFactory.getInstance().getEditors(doc);
|
||||||
|
|
||||||
if (editors.length > 0) {
|
if (editors.length > 0) {
|
||||||
@@ -785,9 +793,9 @@ public class MarkGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull private final HashMap<String, FileMarks<Character, Mark>> fileMarks = new HashMap<>();
|
private final @NotNull HashMap<String, FileMarks<Character, Mark>> fileMarks = new HashMap<>();
|
||||||
@NotNull private final HashMap<Character, Mark> globalMarks = new HashMap<>();
|
private final @NotNull HashMap<Character, Mark> globalMarks = new HashMap<>();
|
||||||
@NotNull private final List<Jump> jumps = new ArrayList<>();
|
private final @NotNull List<Jump> jumps = new ArrayList<>();
|
||||||
private int jumpSpot = -1;
|
private int jumpSpot = -1;
|
||||||
|
|
||||||
private static final int SAVE_MARK_COUNT = 20;
|
private static final int SAVE_MARK_COUNT = 20;
|
||||||
|
@@ -102,8 +102,7 @@ public class MotionGroup {
|
|||||||
* @param argument Any argument needed by the motion
|
* @param argument Any argument needed by the motion
|
||||||
* @return The motion's range
|
* @return The motion's range
|
||||||
*/
|
*/
|
||||||
@Nullable
|
public static @Nullable TextRange getMotionRange(@NotNull Editor editor,
|
||||||
public static TextRange getMotionRange(@NotNull Editor editor,
|
|
||||||
@NotNull Caret caret,
|
@NotNull Caret caret,
|
||||||
DataContext context,
|
DataContext context,
|
||||||
int count,
|
int count,
|
||||||
@@ -239,32 +238,27 @@ public class MotionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getBlockQuoteRange(@NotNull Editor editor, @NotNull Caret caret, char quote, boolean isOuter) {
|
||||||
public TextRange getBlockQuoteRange(@NotNull Editor editor, @NotNull Caret caret, char quote, boolean isOuter) {
|
|
||||||
return SearchHelper.findBlockQuoteInLineRange(editor, caret, quote, isOuter);
|
return SearchHelper.findBlockQuoteInLineRange(editor, caret, quote, isOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getBlockRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter, char type) {
|
||||||
public TextRange getBlockRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter, char type) {
|
|
||||||
return SearchHelper.findBlockRange(editor, caret, type, count, isOuter);
|
return SearchHelper.findBlockRange(editor, caret, type, count, isOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||||
public TextRange getBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
|
||||||
return SearchHelper.findBlockTagRange(editor, caret, count, isOuter);
|
return SearchHelper.findBlockTagRange(editor, caret, count, isOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull TextRange getSentenceRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||||
public TextRange getSentenceRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
|
||||||
return SearchHelper.findSentenceRange(editor, caret, count, isOuter);
|
return SearchHelper.findSentenceRange(editor, caret, count, isOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public @Nullable TextRange getParagraphRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||||
public TextRange getParagraphRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
|
||||||
return SearchHelper.findParagraphRange(editor, caret, count, isOuter);
|
return SearchHelper.findParagraphRange(editor, caret, count, isOuter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getScrollScreenTargetCaretVisualLine(@NotNull final Editor editor, int rawCount, boolean down) {
|
private static int getScrollScreenTargetCaretVisualLine(final @NotNull Editor editor, int rawCount, boolean down) {
|
||||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||||
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
|
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
|
||||||
final int scrollOption = getScrollOption(rawCount);
|
final int scrollOption = getScrollOption(rawCount);
|
||||||
@@ -296,7 +290,7 @@ public class MotionGroup {
|
|||||||
return rawCount;
|
return rawCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getNormalizedScrollOffset(@NotNull final Editor editor) {
|
private static int getNormalizedScrollOffset(final @NotNull Editor editor) {
|
||||||
int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value();
|
int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value();
|
||||||
return EditorHelper.normalizeScrollOffset(editor, scrollOffset);
|
return EditorHelper.normalizeScrollOffset(editor, scrollOffset);
|
||||||
}
|
}
|
||||||
@@ -328,8 +322,7 @@ public class MotionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) {
|
||||||
private Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) {
|
|
||||||
final VirtualFile virtualFile = markToVirtualFile(mark);
|
final VirtualFile virtualFile = markToVirtualFile(mark);
|
||||||
if (virtualFile != null) {
|
if (virtualFile != null) {
|
||||||
return selectEditor(editor, virtualFile);
|
return selectEditor(editor, virtualFile);
|
||||||
@@ -339,15 +332,13 @@ public class MotionGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable VirtualFile markToVirtualFile(@NotNull Mark mark) {
|
||||||
private VirtualFile markToVirtualFile(@NotNull Mark mark) {
|
|
||||||
String protocol = mark.getProtocol();
|
String protocol = mark.getProtocol();
|
||||||
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
|
VirtualFileSystem fileSystem = VirtualFileManager.getInstance().getFileSystem(protocol);
|
||||||
return fileSystem.findFileByPath(mark.getFilename());
|
return fileSystem.findFileByPath(mark.getFilename());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull VirtualFile file) {
|
||||||
private Editor selectEditor(@NotNull Editor editor, @NotNull VirtualFile file) {
|
|
||||||
return VimPlugin.getFile().selectEditor(editor.getProject(), file);
|
return VimPlugin.getFile().selectEditor(editor.getProject(), file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -778,12 +769,11 @@ public class MotionGroup {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public @NotNull TextRange getWordRange(@NotNull Editor editor,
|
||||||
public TextRange getWordRange(@NotNull Editor editor,
|
@NotNull Caret caret,
|
||||||
@NotNull Caret caret,
|
int count,
|
||||||
int count,
|
boolean isOuter,
|
||||||
boolean isOuter,
|
boolean isBig) {
|
||||||
boolean isBig) {
|
|
||||||
int dir = 1;
|
int dir = 1;
|
||||||
boolean selection = false;
|
boolean selection = false;
|
||||||
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
||||||
@@ -1035,13 +1025,13 @@ public class MotionGroup {
|
|||||||
public int moveCaretHorizontal(@NotNull Editor editor, @NotNull Caret caret, int count, boolean allowPastEnd) {
|
public int moveCaretHorizontal(@NotNull Editor editor, @NotNull Caret caret, int count, boolean allowPastEnd) {
|
||||||
int oldOffset = caret.getOffset();
|
int oldOffset = caret.getOffset();
|
||||||
int diff = 0;
|
int diff = 0;
|
||||||
String text = editor.getDocument().getText();
|
CharSequence text = editor.getDocument().getCharsSequence();
|
||||||
int sign = (int)Math.signum(count);
|
int sign = (int)Math.signum(count);
|
||||||
for (Integer pointer : new IntProgression(0, count - sign, sign)) {
|
for (Integer pointer : new IntProgression(0, count - sign, sign)) {
|
||||||
int textPointer = oldOffset + pointer;
|
int textPointer = oldOffset + pointer;
|
||||||
if (textPointer < text.length() && textPointer >= 0) {
|
if (textPointer < text.length() && textPointer >= 0) {
|
||||||
// Actual char size can differ from 1 if unicode characters are used (like 🐔)
|
// Actual char size can differ from 1 if unicode characters are used (like 🐔)
|
||||||
diff += Character.charCount(text.codePointAt(textPointer));
|
diff += Character.charCount(Character.codePointAt(text, textPointer));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
diff += 1;
|
diff += 1;
|
||||||
@@ -1108,7 +1098,7 @@ public class MotionGroup {
|
|||||||
return editor.logicalPositionToOffset(newPos);
|
return editor.logicalPositionToOffset(newPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean scrollScreen(@NotNull final Editor editor, int rawCount, boolean down) {
|
public boolean scrollScreen(final @NotNull Editor editor, int rawCount, boolean down) {
|
||||||
final CaretModel caretModel = editor.getCaretModel();
|
final CaretModel caretModel = editor.getCaretModel();
|
||||||
final int currentLogicalLine = caretModel.getLogicalPosition().line;
|
final int currentLogicalLine = caretModel.getLogicalPosition().line;
|
||||||
|
|
||||||
|
@@ -33,6 +33,7 @@ import com.intellij.openapi.actionSystem.KeyboardShortcut
|
|||||||
import com.intellij.openapi.keymap.Keymap
|
import com.intellij.openapi.keymap.Keymap
|
||||||
import com.intellij.openapi.keymap.KeymapUtil
|
import com.intellij.openapi.keymap.KeymapUtil
|
||||||
import com.intellij.openapi.options.ShowSettingsUtil
|
import com.intellij.openapi.options.ShowSettingsUtil
|
||||||
|
import com.intellij.openapi.project.DumbAwareAction
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.ui.Messages
|
import com.intellij.openapi.ui.Messages
|
||||||
import com.intellij.openapi.util.SystemInfo
|
import com.intellij.openapi.util.SystemInfo
|
||||||
@@ -48,6 +49,9 @@ import javax.swing.event.HyperlinkEvent
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Alex Plate
|
* @author Alex Plate
|
||||||
|
*
|
||||||
|
* This service is can be used as application level and as project level service.
|
||||||
|
* If project is null, this means that this is an application level service and notification will be shown for all projects
|
||||||
*/
|
*/
|
||||||
class NotificationService(private val project: Project?) {
|
class NotificationService(private val project: Project?) {
|
||||||
// This constructor is used to create an applicationService
|
// This constructor is used to create an applicationService
|
||||||
@@ -143,7 +147,7 @@ class NotificationService(private val project: Project?) {
|
|||||||
NotificationType.INFORMATION).notify(project)
|
NotificationType.INFORMATION).notify(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
class OpenIdeaVimRcAction(private val notification: Notification?) : AnAction("Open ~/.ideavimrc") {
|
class OpenIdeaVimRcAction(private val notification: Notification?) : DumbAwareAction("Open ~/.ideavimrc") {
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val eventProject = e.project
|
val eventProject = e.project
|
||||||
if (eventProject != null) {
|
if (eventProject != null) {
|
||||||
@@ -191,10 +195,11 @@ class NotificationService(private val project: Project?) {
|
|||||||
const val IDEAVIM_NOTIFICATION_ID = "ideavim"
|
const val IDEAVIM_NOTIFICATION_ID = "ideavim"
|
||||||
const val IDEAVIM_NOTIFICATION_TITLE = "IdeaVim"
|
const val IDEAVIM_NOTIFICATION_TITLE = "IdeaVim"
|
||||||
const val ideajoinExamplesUrl = "https://github.com/JetBrains/ideavim/wiki/%60ideajoin%60-examples"
|
const val ideajoinExamplesUrl = "https://github.com/JetBrains/ideavim/wiki/%60ideajoin%60-examples"
|
||||||
const val selectModeUrl = "https://vimhelp.org/visual.txt.html#Select-mode"
|
|
||||||
|
|
||||||
private fun createIdeaVimRcManually(message: String, project: Project?) {
|
private fun createIdeaVimRcManually(message: String, project: Project?) {
|
||||||
val notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, message, NotificationType.WARNING)
|
val notification = Notification(IDEAVIM_NOTIFICATION_ID, IDEAVIM_NOTIFICATION_TITLE, message, NotificationType.WARNING)
|
||||||
|
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||||
|
// [VERSION UPDATE] 193+ com.intellij.ide.actions.RevealFileAction.openDirectory
|
||||||
var actionName = if (SystemInfo.isMac) "Reveal Home in Finder" else "Show Home in " + ShowFilePathAction.getFileManagerName()
|
var actionName = if (SystemInfo.isMac) "Reveal Home in Finder" else "Show Home in " + ShowFilePathAction.getFileManagerName()
|
||||||
if (!File(System.getProperty("user.home")).exists()) {
|
if (!File(System.getProperty("user.home")).exists()) {
|
||||||
actionName = ""
|
actionName = ""
|
||||||
@@ -202,6 +207,8 @@ class NotificationService(private val project: Project?) {
|
|||||||
notification.addAction(object : AnAction(actionName) {
|
notification.addAction(object : AnAction(actionName) {
|
||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val homeDir = File(System.getProperty("user.home"))
|
val homeDir = File(System.getProperty("user.home"))
|
||||||
|
@Suppress("DEPRECATION", "UnstableApiUsage")
|
||||||
|
// [VERSION UPDATE] 193+ com.intellij.ide.actions.RevealFileAction.openDirectory
|
||||||
ShowFilePathAction.openDirectory(homeDir)
|
ShowFilePathAction.openDirectory(homeDir)
|
||||||
notification.expire()
|
notification.expire()
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user