mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
86 Commits
0.57
...
0.57.1-EAP
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0adde753f4 | ||
![]() |
5f28a22666 | ||
![]() |
a975b53894 | ||
![]() |
98aee5d0ab | ||
![]() |
f57af8bf9e | ||
![]() |
c6c3b6643e | ||
![]() |
af94079b92 | ||
![]() |
7203cc5cb3 | ||
![]() |
028423cf58 | ||
![]() |
2ead6af96a | ||
![]() |
bf853e3c0c | ||
![]() |
c85f41e65b | ||
![]() |
2759bed1b2 | ||
![]() |
89c2a8ec9b | ||
![]() |
aa2c1257ac | ||
![]() |
f9fa15b7ac | ||
![]() |
93a9be41bc | ||
![]() |
ecd2f2032c | ||
![]() |
de5ce5f635 | ||
![]() |
2eb6fd6819 | ||
![]() |
22ea4e7ffa | ||
![]() |
3d98f3035f | ||
![]() |
ec1d6ac477 | ||
![]() |
0dc236cb5b | ||
![]() |
98349a49fd | ||
![]() |
ab8be2cada | ||
![]() |
b8c22d0928 | ||
![]() |
c6cf77e4b8 | ||
![]() |
6c0511a898 | ||
![]() |
366c862bcf | ||
![]() |
03493e2390 | ||
![]() |
8f9c71dd55 | ||
![]() |
11beb1e331 | ||
![]() |
01b4dc233a | ||
![]() |
9f1e80e969 | ||
![]() |
7e319e11c6 | ||
![]() |
d11bf1c4d2 | ||
![]() |
3e2f18b757 | ||
![]() |
61677aa811 | ||
![]() |
fb04e835ef | ||
![]() |
bb133922d6 | ||
![]() |
44dd5ef872 | ||
![]() |
bcc8e1c055 | ||
![]() |
71117ed335 | ||
![]() |
de07fb3b74 | ||
![]() |
e31d5a4dcf | ||
![]() |
e14aae761d | ||
![]() |
47db2a247c | ||
![]() |
e449bb9692 | ||
![]() |
b8fc72b6a7 | ||
![]() |
64c01c1bd1 | ||
![]() |
0a0e3df42b | ||
![]() |
949c69a7e9 | ||
![]() |
69caf7a604 | ||
![]() |
23860ad5f9 | ||
![]() |
ace5234d8d | ||
![]() |
4654f821a9 | ||
![]() |
927e0e7865 | ||
![]() |
d47c9735b5 | ||
![]() |
6100433636 | ||
![]() |
43f79e8183 | ||
![]() |
f58fda0c87 | ||
![]() |
64b49e37d7 | ||
![]() |
e44418d410 | ||
![]() |
ca8d05ff13 | ||
![]() |
626871e34d | ||
![]() |
9bc2ec7d8a | ||
![]() |
7de08e08d0 | ||
![]() |
a4cd94847e | ||
![]() |
a0a7386b51 | ||
![]() |
535a72000f | ||
![]() |
60531b9cd2 | ||
![]() |
8f86ad696d | ||
![]() |
c9bda98a6a | ||
![]() |
9ea08da133 | ||
![]() |
5762ec0518 | ||
![]() |
7db74460fa | ||
![]() |
2f148255f7 | ||
![]() |
cb00b8b335 | ||
![]() |
559b56c8a2 | ||
![]() |
158cea51db | ||
![]() |
33d34f35e9 | ||
![]() |
1f4f40fd7c | ||
![]() |
7c908b247e | ||
![]() |
41c822fde1 | ||
![]() |
2a6569742d |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -4,7 +4,10 @@
|
||||
/.idea/
|
||||
!/.idea/scopes
|
||||
!/.idea/copyright
|
||||
!/.idea/icon.png
|
||||
|
||||
/build/
|
||||
/out/
|
||||
/tmp/
|
||||
/tmp/
|
||||
|
||||
*.DS_Store
|
BIN
.idea/icon.png
generated
Normal file
BIN
.idea/icon.png
generated
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
@@ -291,6 +291,14 @@ Contributors:
|
||||
[![icon][github]](https://github.com/runforprogram)
|
||||
|
||||
runforprogram
|
||||
* [![icon][mail]](mailto:valery.isaev@jetbrains.com)
|
||||
[![icon][github]](https://github.com/valis)
|
||||
|
||||
valis
|
||||
* [![icon][mail]](mailto:pmikulski@voleon.com)
|
||||
[![icon][github]](https://github.com/pmnoxx)
|
||||
|
||||
Piotr Mikulski
|
||||
|
||||
If you are a contributor and your name is not listed here, feel free to
|
||||
contact the maintainers.
|
||||
|
35
CHANGES.md
35
CHANGES.md
@@ -22,6 +22,41 @@ It is important to distinguish EAP from traditional pre-release software.
|
||||
Please note that the quality of EAP versions may at times be way below even
|
||||
usual beta standards.
|
||||
|
||||
To Be Released
|
||||
-------------
|
||||
|
||||
_Available since 0.57.1 EAP:_
|
||||
|
||||
**Features:**
|
||||
* `exchange` plugin emulation ([vim-exchange](https://github.com/tommcdo/vim-exchange)).
|
||||
* `~/.ideavimrc` file can be reloaded using the new floating action.
|
||||
|
||||
* <details>
|
||||
<summary><strong>Click to see details</strong></summary>
|
||||
<img src="resources/changes/0.58/reload_ideavimrc.png" alt="IdeaVimRc reload"/>
|
||||
</details>
|
||||
|
||||
* Add `:buffer` command.
|
||||
|
||||
**Changes:**
|
||||
* Support IntelliJ's smooth scrolling. Use "Enable smooth scrolling" checkbox in _Preferences | Editor | General_ to disable.
|
||||
|
||||
**Fixes:**
|
||||
* [VIM-1994](https://youtrack.jetbrains.com/issue/VIM-1994) Correct paste after `y}P` command.
|
||||
* [VIM-1924](https://youtrack.jetbrains.com/issue/VIM-1924) Select next occurrence doesn't become block selection.
|
||||
|
||||
**Merged PRs:**
|
||||
* [233](https://github.com/JetBrains/ideavim/pull/233) by [valis](https://github.com/valis): [VIM-1994] Correct paste after `y}P` command.
|
||||
* [224](https://github.com/JetBrains/ideavim/pull/224) by [pmnoxx](https://github.com/pmnoxx): Populate intelij navigation history together with ideavim jumplist.
|
||||
* [227](https://github.com/JetBrains/ideavim/pull/227) by [angelbot](https://github.com/angelbot): Add support for buffer command.
|
||||
* [230](https://github.com/JetBrains/ideavim/pull/230) by [fan-tom](https://github.com/fan-tom): VIM-1924.
|
||||
* [231](https://github.com/JetBrains/ideavim/pull/231) by [citizenmatt](https://github.com/citizenmatt): Support smooth scrolling.
|
||||
|
||||
_To Be Released..._
|
||||
|
||||
...
|
||||
|
||||
|
||||
0.57, 2020-04-28
|
||||
-------------
|
||||
|
||||
|
@@ -1,33 +1,4 @@
|
||||
<div>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20183&guest=1">
|
||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20183)/statusIcon.svg?guest=1"/>
|
||||
</a>
|
||||
<span>2018.3 Tests</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20191&guest=1">
|
||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20191)/statusIcon.svg?guest=1"/>
|
||||
</a>
|
||||
<span>2019.1 Tests</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20192&guest=1">
|
||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20192)/statusIcon.svg?guest=1"/>
|
||||
</a>
|
||||
<span>2019.2 Tests</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20193&guest=1">
|
||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20193)/statusIcon.svg?guest=1"/>
|
||||
</a>
|
||||
<span>2019.3 Tests</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1">
|
||||
<img src="https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1"/>
|
||||
</a>
|
||||
<span>2020.1 Tests</span>
|
||||
</div>
|
||||
[![TeamCity Build][teamcity-build-status-svg]][teamcity-build-status]
|
||||
|
||||
|
||||
### Where to Start
|
||||
@@ -46,6 +17,8 @@ You can start by:
|
||||
in the issue tracker.
|
||||
- Read about the `@VimBehaviorDiffers` annotation and fix the corresponding functionality.
|
||||
|
||||
Also join the brand new [](https://gitter.im/JetBrains/ideavim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) for IdeaVim developers!
|
||||
|
||||
|
||||
### Development Environment
|
||||
|
||||
@@ -54,11 +27,10 @@ in the issue tracker.
|
||||
2. Import the project from the existing sources in IntelliJ IDEA 2018.1 or newer (Community or
|
||||
Ultimate), by selecting "File | New | Project from Existing Sources..." or selecting "Import
|
||||
Project" from the Welcome screen.
|
||||
|
||||
* In the project wizard, select "Import project from external model | Gradle".
|
||||
|
||||
* Select your Java 8+ JDK as the Gradle JVM; leave other parameters unchanged.
|
||||
|
||||
* In the project wizard, select "Import project from external model | Gradle".
|
||||
|
||||
* Select your Java 8+ JDK as the Gradle JVM; leave other parameters unchanged.
|
||||
|
||||
3. Run your IdeaVim plugin within IntelliJ via a Gradle task:
|
||||
|
||||
* Select the "View | Tool Windows | Gradle" tool window.
|
||||
@@ -78,7 +50,7 @@ in the issue tracker.
|
||||
|
||||
* You can install this file by selecting "Settings | Plugins | Install plugin
|
||||
from disk...".
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
1. Read about the `@VimBehaviorDiffers` annotation.
|
||||
@@ -92,3 +64,8 @@ For example, take a few lines from your favorite poem, or use
|
||||
3. Test your functionality properly.
|
||||
Especially check whether your command works with:
|
||||
line start, line end, file start, file end, empty line, multiple carets, dollar motion, etc.
|
||||
|
||||
<!-- Badges -->
|
||||
|
||||
[teamcity-build-status]: https://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ20201&guest=1
|
||||
[teamcity-build-status-svg]: https://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ20201)/statusIcon.svg?guest=1
|
||||
|
32
README.md
32
README.md
@@ -3,14 +3,12 @@
|
||||
IdeaVim
|
||||
===
|
||||
|
||||
<div>
|
||||
<a href="https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub">
|
||||
<img src="https://jb.gg/badges/official.svg" alt="official JetBrains project"/>
|
||||
</a>
|
||||
<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" alt="TeamCity Build"/>
|
||||
</a>
|
||||
</div>
|
||||
[![Official JetBrains Project][jb-official-svg]][jb-official]
|
||||
[![Downloads][plugin-downloads-svg]][plugin-repo]
|
||||
[![Rating][plugin-rating-svg]][plugin-repo]
|
||||
[![Version][plugin-version-svg]][plugin-repo]
|
||||
[![Gitter][gitter-svg]][gitter]
|
||||
[![Twitter][twitter-svg]][twitter]
|
||||
|
||||
IdeaVim is a Vim emulation plugin for IDEs based on the IntelliJ Platform.
|
||||
IdeaVim can be used with IntelliJ IDEA, PyCharm, CLion, PhpStorm, WebStorm,
|
||||
@@ -24,7 +22,6 @@ Resources:
|
||||
* [Continuous integration builds](https://teamcity.jetbrains.com/project.html?projectId=IdeaVim&guest=1)
|
||||
* [@IdeaVim](https://twitter.com/ideavim) in Twitter
|
||||
|
||||
|
||||
Setup
|
||||
------------
|
||||
|
||||
@@ -99,6 +96,7 @@ Supported:
|
||||
* argtextobj.vim
|
||||
* vim-textobj-entire
|
||||
* ReplaceWithRegister
|
||||
* vim-exchange [To Be Released]
|
||||
|
||||
Not supported (yet):
|
||||
|
||||
@@ -191,3 +189,19 @@ License
|
||||
|
||||
IdeaVim is licensed under the terms of the GNU Public License version 2
|
||||
or any later version.
|
||||
|
||||
|
||||
<!-- Badges -->
|
||||
[jb-official]: https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub
|
||||
[jb-official-svg]: https://jb.gg/badges/official.svg
|
||||
|
||||
[plugin-repo]: https://plugins.jetbrains.com/plugin/164-ideavim
|
||||
[plugin-downloads-svg]: http://img.shields.io/jetbrains/plugin/d/IdeaVIM
|
||||
[plugin-rating-svg]: http://img.shields.io/jetbrains/plugin/r/rating/IdeaVIM
|
||||
[plugin-version-svg]: https://img.shields.io/jetbrains/plugin/v/ideavim?label=version
|
||||
|
||||
[gitter-svg]: https://badges.gitter.im/JetBrains/ideavim.svg
|
||||
[gitter]: https://gitter.im/JetBrains/ideavim?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
[twitter]: https://twitter.com/ideavim
|
||||
[twitter-svg]: https://img.shields.io/twitter/follow/ideavim?label=twitter%20%40ideavim
|
||||
|
@@ -37,11 +37,7 @@ intellij {
|
||||
downloadSources Boolean.valueOf(downloadIdeaSources)
|
||||
instrumentCode Boolean.valueOf(instrumentPluginCode)
|
||||
intellijRepo = "https://www.jetbrains.com/intellij-repository"
|
||||
if (!Boolean.valueOf(legacyNoJavaPlugin)) {
|
||||
// Since 192 version of IJ java plugin should be defined separately
|
||||
// Set `legacyNoJavaPlugin` to true if you are going to run tests under idea version < 192
|
||||
plugins = ['java']
|
||||
}
|
||||
plugins = ['java']
|
||||
|
||||
publishPlugin {
|
||||
channels publishChannels.split(',')
|
||||
|
@@ -53,6 +53,12 @@ Available extensions:
|
||||
this syntax parenthesis, must come before angle brackets in the list.
|
||||
* Emulates [argtextobj.vim](https://www.vim.org/scripts/script.php?script_id=2699)
|
||||
* Additional text objects: `aa`, `ia`
|
||||
|
||||
## exchange [To Be Released]
|
||||
|
||||
* Setup: `set exchange`
|
||||
* Emulates [vim-exchange](https://github.com/tommcdo/vim-exchange)
|
||||
* Commands: `cx`, `cxx`, `X`, `cxc`
|
||||
|
||||
## textobj-entire
|
||||
|
||||
|
@@ -118,8 +118,6 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
|
||||
- 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
|
||||
|
||||
|
@@ -9,8 +9,5 @@ kotlinVersion=1.3.71
|
||||
publishUsername=username
|
||||
publishToken=token
|
||||
publishChannels=eap
|
||||
# Since 192 version of IJ java plugin should be defined separately
|
||||
# Set this value to true if you are going to run tests under idea version < 192
|
||||
legacyNoJavaPlugin=false
|
||||
|
||||
slackUrl=
|
||||
|
@@ -61,5 +61,6 @@
|
||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.PreviousTabHandler" names="tabp[revious],tabN[ext]"/>
|
||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.TabOnlyHandler" names="tabo[nly]"/>
|
||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferListHandler" names="buffers,ls,files"/>
|
||||
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.BufferHandler" names="b[uffer]"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
@@ -6,5 +6,6 @@
|
||||
<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"/>
|
||||
<vimExtension implementation="com.maddyhome.idea.vim.extension.exchange.VimExchangeExtension"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
25
resources/META-INF/includes/VimListeners.xml
Normal file
25
resources/META-INF/includes/VimListeners.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<idea-plugin>
|
||||
<applicationListeners>
|
||||
<listener class="com.maddyhome.idea.vim.ui.ExEntryPanel$LafListener"
|
||||
topic="com.intellij.ide.ui.LafManagerListener"/>
|
||||
</applicationListeners>
|
||||
<projectListeners>
|
||||
<listener class="com.maddyhome.idea.vim.ui.ExOutputPanel$LafListener"
|
||||
topic="com.intellij.ide.ui.LafManagerListener"/>
|
||||
|
||||
<listener class="com.maddyhome.idea.vim.listener.VimListenerManager$VimFileEditorManagerListener"
|
||||
topic="com.intellij.openapi.fileEditor.FileEditorManagerListener"/>
|
||||
|
||||
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimActionListener"
|
||||
topic="com.intellij.openapi.actionSystem.ex.AnActionListener"/>
|
||||
|
||||
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimTemplateManagerListener"
|
||||
topic="com.intellij.codeInsight.template.TemplateManagerListener"/>
|
||||
|
||||
<listener class="com.maddyhome.idea.vim.group.MarkGroup$MarkListener"
|
||||
topic="com.intellij.ide.bookmarks.BookmarksListener"/>
|
||||
|
||||
<listener class="com.maddyhome.idea.vim.listener.IdeaSpecifics$VimFindModelListener"
|
||||
topic="com.intellij.find.FindModelListener"/>
|
||||
</projectListeners>
|
||||
</idea-plugin>
|
@@ -3,8 +3,8 @@
|
||||
<id>IdeaVIM</id>
|
||||
<change-notes><![CDATA[
|
||||
<ul>
|
||||
<li>Fix mappings for uppercase letters</li>
|
||||
<li>Fix yank/paste with number registers</li>
|
||||
<li>vim-exchange plugin emulation</li>
|
||||
<li>A new floating action for reloading .ideavimrc</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
|
||||
<!-- Check for [Version Update] tag in YouTrack as well -->
|
||||
<idea-version since-build="183.4284.148"/>
|
||||
<idea-version since-build="201.5985"/>
|
||||
|
||||
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform -->
|
||||
<depends>com.intellij.modules.lang</depends>
|
||||
@@ -51,20 +51,25 @@
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<applicationConfigurable groupId="editor" instance="com.maddyhome.idea.vim.ui.VimEmulationConfigurable"/>
|
||||
<projectService serviceImplementation="com.maddyhome.idea.vim.group.NotificationService"/>
|
||||
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.StatusBarIconProvider"/>
|
||||
<statusBarWidgetProvider implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidget"/>
|
||||
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/>
|
||||
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/>
|
||||
|
||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimLocalConfig"/>
|
||||
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
|
||||
|
||||
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup"/>
|
||||
<!-- Initialise as early as possible so that we're ready to edit quickly. This is especially important for Rider,
|
||||
which (at least for 2020.1) has some long running activities that block other startup extensions. None of the
|
||||
core platform activities have IDs, so we can't use "before ID". We have to use "first" -->
|
||||
<postStartupActivity implementation="com.maddyhome.idea.vim.PluginStartup" order="first"/>
|
||||
|
||||
<editorFloatingToolbarProvider implementation="com.maddyhome.idea.vim.ui.ReloadFloatingToolbar"/>
|
||||
</extensions>
|
||||
|
||||
<xi:include href="/META-INF/includes/ApplicationServices.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<xi:include href="/META-INF/includes/VimActions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/includes/VimExCommands.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
<actions>
|
||||
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction" text="Vim Emulator" description="Toggle the vim plugin On/off">
|
||||
@@ -75,6 +80,20 @@
|
||||
<action id="VimInternalAddInlays" class="com.maddyhome.idea.vim.action.internal.AddInlaysAction" text="Add Test Inlays | IdeaVim Internal" internal="true"/>
|
||||
|
||||
<action id="VimShortcutKeyAction" class="com.maddyhome.idea.vim.action.VimShortcutKeyAction" text="Shortcuts"/>
|
||||
<action id="VimActions" class="com.maddyhome.idea.vim.VimActions" text="Vim Actions"/>
|
||||
<action id="VimActions" class="com.maddyhome.idea.vim.ui.VimActions" text="Vim Actions"/>
|
||||
|
||||
<!-- [Version Update] 202+ use-shortcut-of="ExternalSystem.ProjectRefreshAction" -->
|
||||
<group id="IdeaVim.ReloadVimRc.group" class="com.maddyhome.idea.vim.ui.ReloadFloatingToolbarActionGroup">
|
||||
<action id="IdeaVim.ReloadVimRc.reload" class="com.maddyhome.idea.vim.ui.ReloadVimRc">
|
||||
<keyboard-shortcut first-keystroke="control shift O" keymap="$default"/>
|
||||
<keyboard-shortcut first-keystroke="control shift O" keymap="Eclipse" remove="true"/>
|
||||
<keyboard-shortcut first-keystroke="control shift O" keymap="NetBeans 6.5" remove="true"/>
|
||||
<keyboard-shortcut first-keystroke="control shift O" keymap="Visual Studio" remove="true"/>
|
||||
<keyboard-shortcut first-keystroke="meta shift O" keymap="Mac OS X" replace-all="true"/>
|
||||
<keyboard-shortcut first-keystroke="meta shift O" keymap="Eclipse (Mac OS X)" replace-all="true" remove="true"/>
|
||||
<keyboard-shortcut first-keystroke="meta shift O" keymap="Xcode" replace-all="true" remove="true"/>
|
||||
<keyboard-shortcut first-keystroke="meta shift I" keymap="Mac OS X 10.5+" replace-all="true"/>
|
||||
</action>
|
||||
</group>
|
||||
</actions>
|
||||
</idea-plugin>
|
||||
|
BIN
resources/changes/0.58/reload_ideavimrc.png
Normal file
BIN
resources/changes/0.58/reload_ideavimrc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 194 KiB |
@@ -56,7 +56,6 @@ public class EventFacade {
|
||||
private static final @NotNull EventFacade ourInstance = new EventFacade();
|
||||
|
||||
private @Nullable TypedActionHandler myOriginalTypedActionHandler;
|
||||
private Map<Project, MessageBusConnection> connections = new HashMap<>();
|
||||
|
||||
private EventFacade() {
|
||||
}
|
||||
@@ -92,31 +91,6 @@ public class EventFacade {
|
||||
action.unregisterCustomShortcutSet(component);
|
||||
}
|
||||
|
||||
public void connectFileEditorManagerListener(@NotNull Project project, @NotNull FileEditorManagerListener listener) {
|
||||
final MessageBusConnection connection = getConnection(project);
|
||||
connection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, listener);
|
||||
}
|
||||
|
||||
public void connectAnActionListener(@NotNull Project project, @NotNull AnActionListener listener) {
|
||||
final MessageBusConnection connection = getConnection(project);
|
||||
connection.subscribe(AnActionListener.TOPIC, listener);
|
||||
}
|
||||
|
||||
public void connectTemplateStartedListener(@NotNull Project project, @NotNull TemplateManagerListener listener) {
|
||||
final MessageBusConnection connection = getConnection(project);
|
||||
connection.subscribe(TemplateManager.TEMPLATE_STARTED_TOPIC, listener);
|
||||
}
|
||||
|
||||
public void connectBookmarkListener(@NotNull Project project, @NotNull BookmarksListener bookmarksListener) {
|
||||
final MessageBusConnection connection = getConnection(project);
|
||||
connection.subscribe(BookmarksListener.TOPIC, bookmarksListener);
|
||||
}
|
||||
|
||||
public void connectFindModelListener(@NotNull Project project, @NotNull FindModelListener findModelListener) {
|
||||
final MessageBusConnection connection = getConnection(project);
|
||||
connection.subscribe(FindManager.FIND_MODEL_TOPIC, findModelListener);
|
||||
}
|
||||
|
||||
public void addDocumentListener(@NotNull Document document, @NotNull DocumentListener listener) {
|
||||
document.addDocumentListener(listener);
|
||||
}
|
||||
@@ -168,30 +142,14 @@ public class EventFacade {
|
||||
}
|
||||
|
||||
public void registerLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
||||
LookupManager.getInstance(project).addPropertyChangeListener(propertyChangeListener, project);
|
||||
VimProjectService parentDisposable = VimProjectService.getInstance(project);
|
||||
LookupManager.getInstance(project).addPropertyChangeListener(propertyChangeListener, parentDisposable);
|
||||
}
|
||||
|
||||
public void removeLookupListener(@NotNull Project project, @NotNull PropertyChangeListener propertyChangeListener) {
|
||||
LookupManager.getInstance(project).removePropertyChangeListener(propertyChangeListener);
|
||||
}
|
||||
|
||||
public void disableBusConnection() {
|
||||
connections.values().forEach(MessageBusConnection::disconnect);
|
||||
connections.clear();
|
||||
}
|
||||
|
||||
private MessageBusConnection getConnection(Project project) {
|
||||
if (!connections.containsKey(project)) {
|
||||
final MessageBusConnection connection = project.getMessageBus().connect();
|
||||
connections.put(project, connection);
|
||||
Disposer.register(project, () -> {
|
||||
connection.disconnect();
|
||||
connections.remove(project);
|
||||
});
|
||||
}
|
||||
return connections.get(project);
|
||||
}
|
||||
|
||||
private @NotNull TypedAction getTypedAction() {
|
||||
return EditorActionManager.getInstance().getTypedAction();
|
||||
}
|
||||
|
@@ -18,17 +18,14 @@
|
||||
|
||||
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 {
|
||||
class PluginStartup : StartupActivity.DumbAware {
|
||||
|
||||
private var firstInitializationOccurred = false
|
||||
|
||||
|
@@ -34,33 +34,6 @@ public class RegisterActions {
|
||||
|
||||
public static final ExtensionPointName<ActionBeanClass> VIM_ACTIONS_EP =
|
||||
ExtensionPointName.create("IdeaVIM.vimAction");
|
||||
private static boolean initialRegistration = false;
|
||||
|
||||
static {
|
||||
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
|
||||
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
|
||||
// TODO: [VERSION UPDATE] since 191 use
|
||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
||||
// TODO: [VERSION UPDATE] since 201 use
|
||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
||||
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ActionBeanClass>() {
|
||||
@Override
|
||||
public void extensionAdded(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
||||
// version update (or earlier).
|
||||
if (!initialRegistration) return;
|
||||
unregisterActions();
|
||||
registerActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extensionRemoved(@NotNull ActionBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||
if (!initialRegistration) return;
|
||||
unregisterActions();
|
||||
registerActions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all the key/action mappings for the plugin.
|
||||
@@ -68,7 +41,16 @@ public class RegisterActions {
|
||||
public static void registerActions() {
|
||||
registerVimCommandActions();
|
||||
registerEmptyShortcuts();
|
||||
initialRegistration = true;
|
||||
registerEpListener();
|
||||
}
|
||||
|
||||
private static void registerEpListener() {
|
||||
// IdeaVim doesn't support contribution to VIM_ACTIONS_EP extension point, so technically we can skip this update,
|
||||
// but let's support dynamic plugins in a more classic way and reload actions on every EP change.
|
||||
VIM_ACTIONS_EP.getPoint(null).addExtensionPointListener(() -> {
|
||||
unregisterActions();
|
||||
registerActions();
|
||||
}, false, VimPlugin.getInstance());
|
||||
}
|
||||
|
||||
public static @Nullable EditorActionHandlerBase findAction(@NotNull String id) {
|
||||
|
@@ -33,7 +33,6 @@ import org.jdom.Element
|
||||
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
|
||||
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true)
|
||||
])
|
||||
// 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> {
|
||||
override fun getState(): Element? = null
|
||||
|
@@ -19,14 +19,11 @@ package com.maddyhome.idea.vim;
|
||||
|
||||
import com.intellij.ide.plugins.IdeaPluginDescriptor;
|
||||
import com.intellij.ide.plugins.PluginManager;
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationInfo;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PermanentInstallationID;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
@@ -40,12 +37,9 @@ import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.util.io.HttpRequests;
|
||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionRegistrar;
|
||||
@@ -57,18 +51,17 @@ import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager;
|
||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||
import com.maddyhome.idea.vim.ui.ExEntryPanel;
|
||||
import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
|
||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
|
||||
import com.maddyhome.idea.vim.ui.VimRcFileState;
|
||||
import org.jdom.Element;
|
||||
import org.jdom.JDOMException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.List;
|
||||
|
||||
import static com.maddyhome.idea.vim.group.EditorGroup.EDITOR_STORE_ELEMENT;
|
||||
import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||
@@ -84,7 +77,6 @@ import static com.maddyhome.idea.vim.group.KeyGroup.SHORTCUT_CONFLICTS_ELEMENT;
|
||||
@State(name = "VimSettings", storages = {@Storage("$APP_CONFIG$/vim_settings.xml")})
|
||||
public class VimPlugin implements PersistentStateComponent<Element>, Disposable {
|
||||
private static final String IDEAVIM_PLUGIN_ID = "IdeaVIM";
|
||||
private static final String IDEAVIM_STATISTICS_TIMESTAMP_KEY = "ideavim.statistics.timestamp";
|
||||
private static final int STATE_VERSION = 6;
|
||||
|
||||
private static long lastBeepTimeMillis;
|
||||
@@ -151,53 +143,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return ServiceManager.getService(MotionGroup.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports statistics about installed IdeaVim and enabled Vim emulation.
|
||||
* <p>
|
||||
* See https://github.com/go-lang-plugin-org/go-lang-idea-plugin/commit/5182ab4a1d01ad37f6786268a2fe5e908575a217
|
||||
*/
|
||||
public static void statisticReport() {
|
||||
final PropertiesComponent propertiesComponent = PropertiesComponent.getInstance();
|
||||
final long lastUpdate = propertiesComponent.getOrInitLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0);
|
||||
final boolean outOfDate = lastUpdate == 0 || System.currentTimeMillis() - lastUpdate > TimeUnit.DAYS.toMillis(1);
|
||||
if (outOfDate && isEnabled()) {
|
||||
ApplicationManager.getApplication().executeOnPooledThread(() -> {
|
||||
try {
|
||||
final String buildNumber = ApplicationInfo.getInstance().getBuild().asString();
|
||||
final String version = URLEncoder.encode(getVersion(), CharsetToolkit.UTF8);
|
||||
final String os = URLEncoder.encode(SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION, CharsetToolkit.UTF8);
|
||||
final String uid = PermanentInstallationID.get();
|
||||
final String url = "https://plugins.jetbrains.com/plugins/list" +
|
||||
"?pluginId=" +
|
||||
IDEAVIM_PLUGIN_ID +
|
||||
"&build=" +
|
||||
buildNumber +
|
||||
"&pluginVersion=" +
|
||||
version +
|
||||
"&os=" +
|
||||
os +
|
||||
"&uuid=" +
|
||||
uid;
|
||||
PropertiesComponent.getInstance()
|
||||
.setValue(IDEAVIM_STATISTICS_TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
|
||||
HttpRequests.request(url).connect(request -> {
|
||||
LOG.info("Sending statistics: " + url);
|
||||
try {
|
||||
JDOMUtil.load(request.getInputStream());
|
||||
}
|
||||
catch (JDOMException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull ChangeGroup getChange() {
|
||||
return ServiceManager.getService(ChangeGroup.class);
|
||||
}
|
||||
@@ -289,10 +234,15 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
ideavimrcRegistered = true;
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
final File ideaVimRc = VimScriptParser.findIdeaVimRc();
|
||||
if (ideaVimRc != null) {
|
||||
VimScriptParser.executeFile(ideaVimRc);
|
||||
}
|
||||
executeIdeaVimRc();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeIdeaVimRc() {
|
||||
final File ideaVimRc = VimScriptParser.findIdeaVimRc();
|
||||
if (ideaVimRc != null) {
|
||||
List<String> parsedLines = VimScriptParser.executeFile(ideaVimRc);
|
||||
VimRcFileState.INSTANCE.saveFileState(ideaVimRc.getAbsolutePath(), parsedLines);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,8 +250,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
||||
}
|
||||
|
||||
// [VERSION UPDATE] 193+ remove suppress
|
||||
@SuppressWarnings({"MissingRecentApi", "UnstableApiUsage"})
|
||||
public static @NotNull String getVersion() {
|
||||
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId());
|
||||
if (!ApplicationManager.getApplication().isInternal()) {
|
||||
@@ -329,7 +277,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
getInstance().turnOnPlugin();
|
||||
}
|
||||
|
||||
VimStatusBar.INSTANCE.update();
|
||||
StatusBarIconFactory.Companion.updateIcon();
|
||||
}
|
||||
|
||||
public static boolean isError() {
|
||||
@@ -406,7 +354,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
registerIdeavimrc();
|
||||
|
||||
// Turing on should be performed after all commands registration
|
||||
getEditor().turnOn();
|
||||
getSearch().turnOn();
|
||||
VimListenerManager.INSTANCE.turnOn();
|
||||
}
|
||||
@@ -414,10 +361,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
|
||||
private void turnOffPlugin() {
|
||||
KeyHandler.getInstance().fullReset(null);
|
||||
|
||||
EditorGroup editorGroup = getEditorIfCreated();
|
||||
if (editorGroup != null) {
|
||||
editorGroup.turnOff();
|
||||
}
|
||||
SearchGroup searchGroup = getSearchIfCreated();
|
||||
if (searchGroup != null) {
|
||||
searchGroup.turnOff();
|
||||
|
39
src/com/maddyhome/idea/vim/VimProjectService.kt
Normal file
39
src/com/maddyhome/idea/vim/VimProjectService.kt
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
@Service
|
||||
class VimProjectService(val project: Project) : Disposable {
|
||||
override fun dispose() {}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project): VimProjectService {
|
||||
return ServiceManager.getService(project, VimProjectService::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val Project.vimDisposable
|
||||
get() = VimProjectService.getInstance(this)
|
@@ -20,7 +20,7 @@ package com.maddyhome.idea.vim.action
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.project.DumbAwareToggleAction
|
||||
import com.maddyhome.idea.vim.VimActions
|
||||
import com.maddyhome.idea.vim.ui.VimActions
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
|
||||
/**
|
||||
|
@@ -45,6 +45,7 @@ class OperatorAction : VimActionHandler.SingleExecution() {
|
||||
if (!editor.commandState.isDotRepeatInProgress) {
|
||||
VimRepeater.Extension.argumentCaptured = argument
|
||||
}
|
||||
val saveRepeatHandler = VimRepeater.repeatHandler
|
||||
val motion = argument.motion
|
||||
val range = MotionGroup
|
||||
.getMotionRange(editor, editor.caretModel.primaryCaret, context, cmd.count, cmd.rawCount, argument)
|
||||
@@ -52,7 +53,9 @@ class OperatorAction : VimActionHandler.SingleExecution() {
|
||||
VimPlugin.getMark().setChangeMarks(editor, range)
|
||||
val selectionType = SelectionType.fromCommandFlags(motion.flags)
|
||||
KeyHandler.getInstance().reset(editor)
|
||||
return operatorFunction.apply(editor, context, selectionType)
|
||||
val result = operatorFunction.apply(editor, context, selectionType)
|
||||
VimRepeater.repeatHandler = saveRepeatHandler
|
||||
return result
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
@@ -86,7 +86,7 @@ public class CommandState {
|
||||
}
|
||||
|
||||
// Keep the compatibility with the IdeaVim-EasyMotion plugin before the stable release
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.56")
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||
@Deprecated
|
||||
public MappingMode getMappingMode() {
|
||||
return mappingState.getMappingMode();
|
||||
|
@@ -59,9 +59,7 @@ class IndentConfig private constructor(indentOptions: IndentOptions) {
|
||||
val indentOptions = if (project != null) {
|
||||
CodeStyle.getIndentOptions(project, editor.document)
|
||||
} else {
|
||||
// [VERSION UPDATE] 191+
|
||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
CodeStyle.getDefaultSettings().indentOptions!!
|
||||
CodeStyle.getDefaultSettings().indentOptions
|
||||
}
|
||||
return IndentConfig(indentOptions)
|
||||
}
|
||||
|
@@ -67,34 +67,8 @@ public class CommandParser {
|
||||
* Don't let anyone create one of these.
|
||||
*/
|
||||
private CommandParser() {
|
||||
// IdeaVim doesn't support contribution to ex_command_ep extension point, so technically we can skip this update,
|
||||
// but let's support dynamic plugins in a more classic way and reload handlers on every EP change.
|
||||
// TODO: [VERSION UPDATE] since 191 use
|
||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
||||
// TODO: [VERSION UPDATE] since 201 use
|
||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointChangeListener, boolean, Disposable)
|
||||
//noinspection deprecation
|
||||
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(new ExtensionPointListener<ExBeanClass>() {
|
||||
@Override
|
||||
public void extensionAdded(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||
// Suppress listener before the `VimPlugin.turnOn()` function execution. This logic should be rewritten after
|
||||
// version update (or earlier).
|
||||
if (!initialRegistration) return;
|
||||
unregisterHandlers();
|
||||
registerHandlers();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extensionRemoved(@NotNull ExBeanClass extension, PluginDescriptor pluginDescriptor) {
|
||||
if (!initialRegistration) return;
|
||||
unregisterHandlers();
|
||||
registerHandlers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean initialRegistration = false;
|
||||
|
||||
public void unregisterHandlers() {
|
||||
root.clear();
|
||||
}
|
||||
@@ -104,7 +78,16 @@ public class CommandParser {
|
||||
*/
|
||||
public void registerHandlers() {
|
||||
EX_COMMAND_EP.extensions().forEach(ExBeanClass::register);
|
||||
initialRegistration = true;
|
||||
registerEpListener();
|
||||
}
|
||||
|
||||
private void registerEpListener() {
|
||||
// IdeaVim doesn't support contribution to ex_command_ep extension point, so technically we can skip this update,
|
||||
// but let's support dynamic plugins in a more classic way and reload handlers on every EP change.
|
||||
EX_COMMAND_EP.getPoint(null).addExtensionPointListener(() -> {
|
||||
unregisterHandlers();
|
||||
registerHandlers();
|
||||
}, false, VimPlugin.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -32,9 +32,7 @@ class ExBeanClass : AbstractExtensionPointBean() {
|
||||
var names: String? = null
|
||||
|
||||
val handler: CommandHandler by lazy {
|
||||
// FIXME. [VERSION UPDATE] change to instantiateClass for 193+
|
||||
@Suppress("DEPRECATION")
|
||||
this.instantiate<CommandHandler>(
|
||||
this.instantiateClass<CommandHandler>(
|
||||
implementation ?: "", ApplicationManager.getApplication().picoContainer)
|
||||
}
|
||||
|
||||
|
95
src/com/maddyhome/idea/vim/ex/handler/BufferHandler.kt
Normal file
95
src/com/maddyhome/idea/vim/ex/handler/BufferHandler.kt
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler
|
||||
import com.maddyhome.idea.vim.ex.ExCommand
|
||||
import com.maddyhome.idea.vim.ex.flags
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
|
||||
/**
|
||||
* Handles buffer, buf, bu, b.
|
||||
*
|
||||
* @author John Weigel
|
||||
*/
|
||||
class BufferHandler : CommandHandler.SingleExecution() {
|
||||
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
|
||||
val arg = cmd.argument.trim()
|
||||
val overrideModified = arg.startsWith('!')
|
||||
val buffer = if (overrideModified) arg.replace(Regex("^!\\s*"), "") else arg
|
||||
var result = true
|
||||
|
||||
if (buffer.isNotEmpty()) {
|
||||
if (buffer.matches(Regex("^\\d+$"))) {
|
||||
val bufNum = buffer.toInt() - 1
|
||||
|
||||
if (!VimPlugin.getFile().selectFile(bufNum, context)) {
|
||||
VimPlugin.showMessage("Buffer $bufNum does not exist")
|
||||
result = false
|
||||
}
|
||||
} else {
|
||||
val editors = findPartialMatch(context, buffer)
|
||||
|
||||
when(editors.size) {
|
||||
0 -> {
|
||||
VimPlugin.showMessage("No matching buffer for $buffer")
|
||||
result = false
|
||||
}
|
||||
1 -> {
|
||||
if (EditorHelper.hasUnsavedChanges(editor) && !overrideModified) {
|
||||
VimPlugin.showMessage("No write since last change (add ! to override)")
|
||||
result = false
|
||||
}
|
||||
else {
|
||||
VimPlugin.getFile().openFile(EditorHelper.getVirtualFile(editors[0])!!.name, context)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
VimPlugin.showMessage("More than one match for $buffer")
|
||||
result = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun findPartialMatch(context: DataContext, fileName: String): List<Editor> {
|
||||
val matchedFiles = mutableListOf<Editor>()
|
||||
val project = PlatformDataKeys.PROJECT.getData(context) ?: return matchedFiles
|
||||
|
||||
for (file in FileEditorManager.getInstance(project).openFiles) {
|
||||
if (file.name.contains(fileName)) {
|
||||
val editor = EditorHelper.getEditor(file) ?: continue
|
||||
matchedFiles.add(editor)
|
||||
}
|
||||
}
|
||||
|
||||
return matchedFiles
|
||||
}
|
||||
}
|
||||
|
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.vimscript;
|
||||
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class VimScriptParser {
|
||||
public static final String VIMRC_FILE_NAME = "ideavimrc";
|
||||
public static final String[] HOME_VIMRC_PATHS = {"." + VIMRC_FILE_NAME, "_" + VIMRC_FILE_NAME};
|
||||
public static final String XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME;
|
||||
public static final int BUFSIZE = 4096;
|
||||
private static final Pattern EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *");
|
||||
private static final Pattern DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"");
|
||||
private static final Pattern SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'");
|
||||
private static final Pattern REFERENCE_EXPR = Pattern.compile("([A-Za-z_][A-Za-z_0-9]*)");
|
||||
private static final Pattern DEC_NUMBER = Pattern.compile("(\\d+)");
|
||||
|
||||
private VimScriptParser() {
|
||||
}
|
||||
|
||||
public static @Nullable File findIdeaVimRc() {
|
||||
final String homeDirName = System.getProperty("user.home");
|
||||
// Check whether file exists in home dir
|
||||
if (homeDirName != null) {
|
||||
for (String fileName : HOME_VIMRC_PATHS) {
|
||||
final File file = new File(homeDirName, fileName);
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check in XDG config directory
|
||||
final String xdgConfigHomeProperty = System.getenv("XDG_CONFIG_HOME");
|
||||
File xdgConfig = null;
|
||||
if (xdgConfigHomeProperty == null || Objects.equals(xdgConfigHomeProperty, "")) {
|
||||
if (homeDirName != null) {
|
||||
xdgConfig = Paths.get(homeDirName, ".config", XDG_VIMRC_PATH).toFile();
|
||||
}
|
||||
} else {
|
||||
xdgConfig = new File(xdgConfigHomeProperty, XDG_VIMRC_PATH);
|
||||
}
|
||||
if (xdgConfig != null && xdgConfig.exists()) {
|
||||
return xdgConfig;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable File findOrCreateIdeaVimRc() {
|
||||
final File found = findIdeaVimRc();
|
||||
if (found != null) return found;
|
||||
|
||||
final String homeDirName = System.getProperty("user.home");
|
||||
if (homeDirName != null) {
|
||||
for (String fileName : HOME_VIMRC_PATHS) {
|
||||
try {
|
||||
final File file = new File(homeDirName, fileName);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.createNewFile();
|
||||
return file;
|
||||
} catch (IOException ignored) {
|
||||
// Try to create one of two files
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void executeFile(@NotNull File file) {
|
||||
final String data;
|
||||
try {
|
||||
data = readFile(file);
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
return;
|
||||
}
|
||||
executeText(data);
|
||||
}
|
||||
|
||||
public static void executeText(@NotNull String text) {
|
||||
for (String line : EOL_SPLIT_PATTERN.split(text)) {
|
||||
// TODO: Build a proper parse tree for a VimL file instead of ignoring potentially nested lines (VIM-669)
|
||||
if (line.startsWith(" ") || line.startsWith("\t")) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith(":")) {
|
||||
line = line.substring(1);
|
||||
}
|
||||
final CommandParser commandParser = CommandParser.getInstance();
|
||||
try {
|
||||
final ExCommand command = commandParser.parse(line);
|
||||
final CommandHandler commandHandler = commandParser.getCommandHandler(command);
|
||||
if (commandHandler instanceof VimScriptCommandHandler) {
|
||||
final VimScriptCommandHandler handler = (VimScriptCommandHandler)commandHandler;
|
||||
handler.execute(command);
|
||||
}
|
||||
}
|
||||
catch (ExException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
|
||||
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
|
||||
// support mapleader, VIM-650. See also VIM-669.
|
||||
Matcher m;
|
||||
m = DOUBLE_QUOTED_STRING.matcher(expression);
|
||||
if (m.matches()) {
|
||||
return m.group(1);
|
||||
}
|
||||
m = SINGLE_QUOTED_STRING.matcher(expression);
|
||||
if (m.matches()) {
|
||||
return m.group(1);
|
||||
}
|
||||
m = REFERENCE_EXPR.matcher(expression);
|
||||
if (m.matches()) {
|
||||
final String name = m.group(1);
|
||||
final Object value = globals.get(name);
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
throw new ExException(String.format("Undefined variable: %s", name));
|
||||
}
|
||||
}
|
||||
m = DEC_NUMBER.matcher(expression);
|
||||
if (m.matches()) {
|
||||
return Integer.parseInt(m.group(1));
|
||||
}
|
||||
throw new ExException(String.format("Invalid expression: %s", expression));
|
||||
}
|
||||
|
||||
public static @NotNull String expressionToString(@NotNull Object value) throws ExException {
|
||||
// TODO: Return meaningful value representations
|
||||
if (value instanceof String) {
|
||||
return (String)value;
|
||||
} else if (value instanceof Integer) {
|
||||
return value.toString();
|
||||
}
|
||||
throw new ExException(String.format("Cannot convert '%s' to string", value));
|
||||
}
|
||||
|
||||
private static @NotNull String readFile(@NotNull File file) throws IOException {
|
||||
final BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final char[] buffer = new char[BUFSIZE];
|
||||
int n;
|
||||
while ((n = reader.read(buffer)) > 0) {
|
||||
builder.append(buffer, 0, n);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
167
src/com/maddyhome/idea/vim/ex/vimscript/VimScriptParser.kt
Normal file
167
src/com/maddyhome/idea/vim/ex/vimscript/VimScriptParser.kt
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.ex.vimscript
|
||||
|
||||
import com.maddyhome.idea.vim.ex.CommandParser
|
||||
import com.maddyhome.idea.vim.ex.ExException
|
||||
import com.maddyhome.idea.vim.ui.VimRcFileState
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.nio.file.Paths
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
object VimScriptParser {
|
||||
const val VIMRC_FILE_NAME = "ideavimrc"
|
||||
val HOME_VIMRC_PATHS = arrayOf(".$VIMRC_FILE_NAME", "_$VIMRC_FILE_NAME")
|
||||
val XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME
|
||||
private val DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"")
|
||||
private val SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'")
|
||||
private val REFERENCE_EXPR = Pattern.compile("([A-Za-z_][A-Za-z_0-9]*)")
|
||||
private val DEC_NUMBER = Pattern.compile("(\\d+)")
|
||||
|
||||
// This is a pattern used in ideavimrc parsing for a long time. It removes all trailing/leading spaced and blank lines
|
||||
private val EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *")
|
||||
|
||||
@JvmStatic
|
||||
fun findIdeaVimRc(): File? {
|
||||
val homeDirName = System.getProperty("user.home")
|
||||
// Check whether file exists in home dir
|
||||
if (homeDirName != null) {
|
||||
for (fileName in HOME_VIMRC_PATHS) {
|
||||
val file = File(homeDirName, fileName)
|
||||
if (file.exists()) {
|
||||
return file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check in XDG config directory
|
||||
val xdgConfigHomeProperty = System.getenv("XDG_CONFIG_HOME")
|
||||
val xdgConfig = if (xdgConfigHomeProperty == null || xdgConfigHomeProperty == "") {
|
||||
if (homeDirName != null) Paths.get(homeDirName, ".config", XDG_VIMRC_PATH).toFile() else null
|
||||
} else {
|
||||
File(xdgConfigHomeProperty, XDG_VIMRC_PATH)
|
||||
}
|
||||
return if (xdgConfig != null && xdgConfig.exists()) xdgConfig else null
|
||||
}
|
||||
|
||||
fun findOrCreateIdeaVimRc(): File? {
|
||||
val found = findIdeaVimRc()
|
||||
if (found != null) return found
|
||||
|
||||
val homeDirName = System.getProperty("user.home")
|
||||
if (homeDirName != null) {
|
||||
for (fileName in HOME_VIMRC_PATHS) {
|
||||
try {
|
||||
val file = File(homeDirName, fileName)
|
||||
file.createNewFile()
|
||||
VimRcFileState.filePath = file.absolutePath
|
||||
return file
|
||||
} catch (ignored: IOException) {
|
||||
// Try to create one of two files
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun executeFile(file: File): List<String> {
|
||||
val data = try {
|
||||
readFile(file)
|
||||
} catch (ignored: IOException) {
|
||||
return emptyList()
|
||||
}
|
||||
executeText(data)
|
||||
return data
|
||||
}
|
||||
|
||||
fun executeText(text: List<String>) {
|
||||
for (line in text) {
|
||||
// TODO: Build a proper parse tree for a VimL file instead of ignoring potentially nested lines (VIM-669)
|
||||
if (line.startsWith(" ") || line.startsWith("\t")) continue
|
||||
|
||||
val lineToExecute = if (line.startsWith(":")) line.substring(1) else line
|
||||
val commandParser = CommandParser.getInstance()
|
||||
try {
|
||||
val command = commandParser.parse(lineToExecute)
|
||||
val commandHandler = commandParser.getCommandHandler(command)
|
||||
if (commandHandler is VimScriptCommandHandler) {
|
||||
commandHandler.execute(command)
|
||||
}
|
||||
} catch (ignored: ExException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ExException::class)
|
||||
fun evaluate(expression: String, globals: Map<String?, Any?>): Any {
|
||||
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
|
||||
// support mapleader, VIM-650. See also VIM-669.
|
||||
var m: Matcher = DOUBLE_QUOTED_STRING.matcher(expression)
|
||||
if (m.matches()) return m.group(1)
|
||||
|
||||
m = SINGLE_QUOTED_STRING.matcher(expression)
|
||||
if (m.matches()) return m.group(1)
|
||||
|
||||
m = REFERENCE_EXPR.matcher(expression)
|
||||
if (m.matches()) {
|
||||
val name = m.group(1)
|
||||
val value = globals[name]
|
||||
return value ?: throw ExException("Undefined variable: $name")
|
||||
}
|
||||
|
||||
m = DEC_NUMBER.matcher(expression)
|
||||
if (m.matches()) return m.group(1).toInt()
|
||||
|
||||
throw ExException("Invalid expression: $expression")
|
||||
}
|
||||
|
||||
@Throws(ExException::class)
|
||||
fun expressionToString(value: Any): String {
|
||||
// TODO: Return meaningful value representations
|
||||
return when (value) {
|
||||
is String -> value
|
||||
is Int -> value.toString()
|
||||
else -> throw ExException("Cannot convert '$value' to string")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun readFile(file: File): List<String> {
|
||||
val lines = ArrayList<String>()
|
||||
file.forEachLine { line -> lineProcessor(line, lines) }
|
||||
return lines
|
||||
}
|
||||
|
||||
fun readText(data: CharSequence): List<String> {
|
||||
val lines = ArrayList<String>()
|
||||
EOL_SPLIT_PATTERN.split(data).forEach { line -> lineProcessor(line, lines) }
|
||||
return lines
|
||||
}
|
||||
|
||||
fun lineProcessor(line: String, lines: ArrayList<String>) {
|
||||
val trimmedLine = line.trim()
|
||||
if (trimmedLine.isBlank()) return
|
||||
lines += trimmedLine
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ import com.maddyhome.idea.vim.KeyHandler
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.action.change.VimRepeater
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel
|
||||
@@ -45,13 +46,13 @@ import javax.swing.KeyStroke
|
||||
object VimExtensionFacade {
|
||||
/** The 'map' command for mapping keys to handlers defined in extensions. */
|
||||
@JvmStatic
|
||||
@ScheduledForRemoval(inVersion = "0.57")
|
||||
@ScheduledForRemoval(inVersion = "0.58")
|
||||
@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")
|
||||
@ScheduledForRemoval(inVersion = "0.58")
|
||||
@Deprecated("Only for EasyMotion support")
|
||||
@JvmStatic
|
||||
fun putKeyMapping(modes: Set<MappingMode>, fromKeys: List<KeyStroke>,
|
||||
@@ -186,4 +187,10 @@ object VimExtensionFacade {
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?) {
|
||||
VimPlugin.getRegister().setKeys(register, keys ?: emptyList())
|
||||
}
|
||||
|
||||
/** Set the current contents of the given register */
|
||||
@JvmStatic
|
||||
fun setRegister(register: Char, keys: List<KeyStroke?>?, type: SelectionType) {
|
||||
VimPlugin.getRegister().setKeys(register, keys ?: emptyList(), type)
|
||||
}
|
||||
}
|
||||
|
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.extension;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.extensions.ExtensionPointListener;
|
||||
import com.intellij.openapi.extensions.PluginDescriptor;
|
||||
import com.maddyhome.idea.vim.key.MappingOwner;
|
||||
import com.maddyhome.idea.vim.option.OptionsManager;
|
||||
import com.maddyhome.idea.vim.option.ToggleOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* TODO [VERSION UPDATE] this file cannot be converted to kt before 192 because of nullabilities problems in
|
||||
* [ExtensionPointListener]. (In previous versions of IJ pluginDescriptor was nullable)
|
||||
*/
|
||||
public class VimExtensionRegistrar {
|
||||
|
||||
private static final Set<String> registeredExtensions = new HashSet<>();
|
||||
private static boolean extensionRegistered = false;
|
||||
|
||||
private static final Logger logger = Logger.getInstance(VimExtensionRegistrar.class);
|
||||
|
||||
public static void registerExtensions() {
|
||||
if (extensionRegistered) return;
|
||||
extensionRegistered = true;
|
||||
|
||||
// TODO: [VERSION UPDATE] since 191 use
|
||||
// ExtensionPoint.addExtensionPointListener(ExtensionPointListener<T>, boolean, Disposable)
|
||||
//noinspection deprecation
|
||||
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(new ExtensionPointListener<VimExtension>() {
|
||||
@Override
|
||||
public void extensionAdded(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
||||
registerExtension(extension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extensionRemoved(@NotNull VimExtension extension, PluginDescriptor pluginDescriptor) {
|
||||
unregisterExtension(extension);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static synchronized void registerExtension(@NotNull VimExtension extension) {
|
||||
String name = extension.getName();
|
||||
|
||||
if (registeredExtensions.contains(name)) return;
|
||||
|
||||
registeredExtensions.add(name);
|
||||
ToggleOption option = new ToggleOption(name, name, false);
|
||||
option.addOptionChangeListener((oldValue, newValue) -> {
|
||||
for (VimExtension extensionInListener : VimExtension.EP_NAME.getExtensionList()) {
|
||||
if (name.equals(extensionInListener.getName())) {
|
||||
if (OptionsManager.INSTANCE.isSet(name)) {
|
||||
extensionInListener.init();
|
||||
logger.info("IdeaVim extension '" + name + "' initialized");
|
||||
}
|
||||
else {
|
||||
extensionInListener.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
OptionsManager.INSTANCE.addOption(option);
|
||||
}
|
||||
|
||||
private static synchronized void unregisterExtension(@NotNull VimExtension extension) {
|
||||
String name = extension.getName();
|
||||
|
||||
if (!registeredExtensions.contains(name)) return;
|
||||
|
||||
registeredExtensions.remove(name);
|
||||
extension.dispose();
|
||||
OptionsManager.INSTANCE.removeOption(name);
|
||||
MappingOwner.Plugin.Companion.remove(name);
|
||||
logger.info("IdeaVim extension '" + name + "' disposed");
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.maddyhome.idea.vim.extension
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.extensions.ExtensionPointListener
|
||||
import com.intellij.openapi.extensions.PluginDescriptor
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.key.MappingOwner.Plugin.Companion.remove
|
||||
import com.maddyhome.idea.vim.option.OptionsManager.addOption
|
||||
import com.maddyhome.idea.vim.option.OptionsManager.isSet
|
||||
import com.maddyhome.idea.vim.option.OptionsManager.removeOption
|
||||
import com.maddyhome.idea.vim.option.ToggleOption
|
||||
import java.util.*
|
||||
|
||||
object VimExtensionRegistrar {
|
||||
private val registeredExtensions: MutableSet<String> = HashSet()
|
||||
private var extensionRegistered = false
|
||||
private val logger = logger<VimExtensionRegistrar>()
|
||||
|
||||
@JvmStatic
|
||||
fun registerExtensions() {
|
||||
if (extensionRegistered) return
|
||||
extensionRegistered = true
|
||||
VimExtension.EP_NAME.getPoint(null).addExtensionPointListener(object : ExtensionPointListener<VimExtension> {
|
||||
override fun extensionAdded(extension: VimExtension, pluginDescriptor: PluginDescriptor) {
|
||||
registerExtension(extension)
|
||||
}
|
||||
|
||||
override fun extensionRemoved(extension: VimExtension, pluginDescriptor: PluginDescriptor) {
|
||||
unregisterExtension(extension)
|
||||
}
|
||||
}, true, VimPlugin.getInstance())
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun registerExtension(extension: VimExtension) {
|
||||
val name = extension.name
|
||||
if (name in registeredExtensions) return
|
||||
|
||||
registeredExtensions.add(name)
|
||||
val option = ToggleOption(name, name, false)
|
||||
option.addOptionChangeListener { _, _ ->
|
||||
for (extensionInListener in VimExtension.EP_NAME.extensionList) {
|
||||
if (name != extensionInListener.name) continue
|
||||
if (isSet(name)) {
|
||||
extensionInListener.init()
|
||||
logger.info("IdeaVim extension '$name' initialized")
|
||||
} else {
|
||||
extensionInListener.dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
addOption(option)
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun unregisterExtension(extension: VimExtension) {
|
||||
val name = extension.name
|
||||
if (name !in registeredExtensions) return
|
||||
registeredExtensions.remove(name)
|
||||
extension.dispose()
|
||||
removeOption(name)
|
||||
remove(name)
|
||||
logger.info("IdeaVim extension '$name' disposed")
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
* @author vlan
|
||||
*/
|
||||
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||
@Deprecated
|
||||
public abstract class VimNonDisposableExtension implements VimExtension {
|
||||
@Override
|
||||
|
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.extension.exchange
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.command.MappingMode
|
||||
import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.Mark
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.common.VimMark
|
||||
import com.maddyhome.idea.vim.extension.VimExtension
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.executeNormal
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.getRegister
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setOperatorFunction
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionFacade.setRegister
|
||||
import com.maddyhome.idea.vim.extension.VimExtensionHandler
|
||||
import com.maddyhome.idea.vim.group.MarkGroup
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.stringToKeys
|
||||
import com.maddyhome.idea.vim.helper.subMode
|
||||
import com.maddyhome.idea.vim.key.OperatorFunction
|
||||
|
||||
/**
|
||||
* This emulation misses:
|
||||
* - `:ExchangeClear` command
|
||||
* - `g:exchange_no_mappings` variable
|
||||
* - `g:exchange_indent` variable (?)
|
||||
* - Default mappings should not be applied if there is a mapping defined in `~/.ideavimrc`.
|
||||
* This functionality requires rewriting of IdeaVim initialization, so that plugins would be
|
||||
* loaded after `~/.ideavimrc` is executed (as vim works). But the `if no bindings` can be added even now.
|
||||
* It just won't work if the binding is defined after `set exchange`.
|
||||
*/
|
||||
|
||||
class VimExchangeExtension: VimExtension {
|
||||
override fun getName() = "exchange"
|
||||
|
||||
override fun init() {
|
||||
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_CMD), owner, ExchangeHandler(false), false)
|
||||
putExtensionHandlerMapping(MappingMode.X, parseKeys(EXCHANGE_CMD), owner, VExchangeHandler(), false)
|
||||
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_CLEAR_CMD), owner, ExchangeClearHandler(), false)
|
||||
putExtensionHandlerMapping(MappingMode.N, parseKeys(EXCHANGE_LINE_CMD), owner, ExchangeHandler(true), false)
|
||||
|
||||
putKeyMapping(MappingMode.N, parseKeys("cx"), owner, parseKeys(EXCHANGE_CMD), true)
|
||||
putKeyMapping(MappingMode.X, parseKeys("X"), owner, parseKeys(EXCHANGE_CMD), true)
|
||||
putKeyMapping(MappingMode.N, parseKeys("cxc"), owner, parseKeys(EXCHANGE_CLEAR_CMD), true)
|
||||
putKeyMapping(MappingMode.N, parseKeys("cxx"), owner, parseKeys(EXCHANGE_LINE_CMD), true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXCHANGE_CMD = "<Plug>(Exchange)"
|
||||
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
|
||||
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
|
||||
|
||||
val EXCHANGE_KEY = Key<Exchange>("exchange");
|
||||
|
||||
class Exchange(val type: CommandState.SubMode, val start: Mark, val end: Mark, val text: String) {
|
||||
private var myHighlighter: RangeHighlighter? = null
|
||||
fun setHighlighter(highlighter: RangeHighlighter) {
|
||||
myHighlighter = highlighter
|
||||
}
|
||||
|
||||
fun getHighlighter(): RangeHighlighter? = myHighlighter
|
||||
|
||||
}
|
||||
|
||||
fun clearExchange(editor: Editor) {
|
||||
editor.getUserData(EXCHANGE_KEY)?.getHighlighter()?.let {
|
||||
editor.markupModel.removeHighlighter(it)
|
||||
}
|
||||
editor.putUserData(EXCHANGE_KEY, null)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangeHandler(private val isLine: Boolean) : VimExtensionHandler {
|
||||
|
||||
override fun isRepeatable() = true
|
||||
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
setOperatorFunction(Operator(false))
|
||||
executeNormal(parseKeys(if(isLine) "g@_" else "g@"), editor)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExchangeClearHandler: VimExtensionHandler {
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
clearExchange(editor)
|
||||
}
|
||||
}
|
||||
|
||||
private class VExchangeHandler: VimExtensionHandler {
|
||||
override fun execute(editor: Editor, context: DataContext) {
|
||||
runWriteAction {
|
||||
val subMode = editor.subMode
|
||||
// Leave visual mode to create selection marks
|
||||
executeNormal(parseKeys("<Esc>"), editor)
|
||||
Operator(true).apply(editor, context, SelectionType.fromSubMode(subMode))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Operator(private val isVisual: Boolean): OperatorFunction {
|
||||
fun Editor.getMarkOffset(mark: Mark) = EditorHelper.getOffset(this, mark.logicalLine, mark.col)
|
||||
fun CommandState.SubMode.getString() = when(this) {
|
||||
CommandState.SubMode.VISUAL_CHARACTER -> "v"
|
||||
CommandState.SubMode.VISUAL_LINE -> "V"
|
||||
CommandState.SubMode.VISUAL_BLOCK -> "\\<C-V>"
|
||||
else -> throw Error("Invalid SubMode: $this")
|
||||
}
|
||||
|
||||
override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean {
|
||||
fun highlightExchange(ex: Exchange): RangeHighlighter {
|
||||
val attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
|
||||
val hlArea = when(ex.type) {
|
||||
CommandState.SubMode.VISUAL_LINE -> HighlighterTargetArea.LINES_IN_RANGE
|
||||
// TODO: handle other modes
|
||||
else -> HighlighterTargetArea.EXACT_RANGE
|
||||
}
|
||||
return editor.markupModel.addRangeHighlighter(
|
||||
editor.getMarkOffset(ex.start),
|
||||
(editor.getMarkOffset(ex.end) + 1).coerceAtMost(EditorHelper.getFileSize(editor, true)),
|
||||
HighlighterLayer.SELECTION - 1,
|
||||
attributes,
|
||||
hlArea
|
||||
)
|
||||
}
|
||||
val currentExchange = getExchange(editor, isVisual, selectionType)
|
||||
val exchange1 = editor.getUserData(EXCHANGE_KEY)
|
||||
if (exchange1 == null) {
|
||||
val highlighter = highlightExchange(currentExchange)
|
||||
currentExchange.setHighlighter(highlighter)
|
||||
editor.putUserData(EXCHANGE_KEY, currentExchange)
|
||||
return true
|
||||
} else {
|
||||
val cmp = compareExchanges(exchange1, currentExchange)
|
||||
var reverse = false
|
||||
var expand = false
|
||||
val (ex1, ex2) = when(cmp) {
|
||||
ExchangeCompareResult.OVERLAP -> return false
|
||||
ExchangeCompareResult.OUTER -> {
|
||||
reverse = true
|
||||
expand = true
|
||||
Pair(currentExchange, exchange1)
|
||||
}
|
||||
ExchangeCompareResult.INNER -> {
|
||||
expand = true
|
||||
Pair(exchange1, currentExchange)
|
||||
}
|
||||
ExchangeCompareResult.GT -> {
|
||||
reverse = true
|
||||
Pair(currentExchange, exchange1)
|
||||
}
|
||||
ExchangeCompareResult.LT -> {
|
||||
Pair(exchange1, currentExchange)
|
||||
}
|
||||
}
|
||||
exchange(editor, ex1, ex2, reverse, expand)
|
||||
clearExchange(editor)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private fun exchange(editor: Editor, ex1: Exchange, ex2: Exchange, reverse: Boolean, expand: Boolean) {
|
||||
fun pasteExchange(sourceExchange: Exchange, targetExchange: Exchange) {
|
||||
VimPlugin.getMark().setChangeMarks(editor, TextRange(editor.getMarkOffset(targetExchange.start), editor.getMarkOffset(targetExchange.end)+1))
|
||||
// do this instead of direct text manipulation to set change marks
|
||||
setRegister('z', stringToKeys(sourceExchange.text), SelectionType.fromSubMode(sourceExchange.type))
|
||||
executeNormal(stringToKeys("""`[${targetExchange.type.getString()}`]"zp"""), editor)
|
||||
}
|
||||
fun fixCursor(ex1: Exchange, ex2: Exchange, reverse: Boolean) {
|
||||
val primaryCaret = editor.caretModel.primaryCaret
|
||||
if(reverse) {
|
||||
primaryCaret.moveToOffset(editor.getMarkOffset(ex1.start))
|
||||
} else {
|
||||
if (ex1.start.logicalLine == ex2.start.logicalLine) {
|
||||
val horizontalOffset = ex1.end.col - ex2.end.col
|
||||
primaryCaret.moveToLogicalPosition(LogicalPosition(ex1.start.logicalLine, ex1.start.col - horizontalOffset))
|
||||
} else if(ex1.end.logicalLine - ex1.start.logicalLine != ex2.end.logicalLine - ex2.start.logicalLine) {
|
||||
val verticalOffset = ex1.end.logicalLine - ex2.end.logicalLine
|
||||
primaryCaret.moveToLogicalPosition(LogicalPosition(ex1.start.logicalLine - verticalOffset, ex1.start.col))
|
||||
}
|
||||
}
|
||||
}
|
||||
val zRegText = getRegister('z')
|
||||
val unnRegText = getRegister('"')
|
||||
val startRegText = getRegister('*')
|
||||
val plusRegText = getRegister('+')
|
||||
runWriteAction {
|
||||
// TODO handle:
|
||||
// " Compare using =~ because "'==' != 0" returns 0
|
||||
// let indent = s:get_setting('exchange_indent', 1) !~ 0 && a:x.type ==# 'V' && a:y.type ==# 'V'
|
||||
pasteExchange(ex1, ex2)
|
||||
if (!expand) {
|
||||
pasteExchange(ex2, ex1)
|
||||
}
|
||||
// TODO: handle: if ident
|
||||
if (!expand) {
|
||||
fixCursor(ex1, ex2, reverse)
|
||||
}
|
||||
setRegister('z', zRegText)
|
||||
setRegister('"', unnRegText)
|
||||
setRegister('*', startRegText)
|
||||
setRegister('+', plusRegText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult {
|
||||
fun intersects(x: Exchange, y: Exchange) =
|
||||
x.end.logicalLine < y.start.logicalLine ||
|
||||
x.start.logicalLine > y.end.logicalLine ||
|
||||
x.end.col < y.start.col ||
|
||||
x.start.col > y.end.col
|
||||
|
||||
fun comparePos(x: Mark, y: Mark): Int =
|
||||
if (x.logicalLine == y.logicalLine) {
|
||||
x.col - y.col
|
||||
} else {
|
||||
x.logicalLine - y.logicalLine
|
||||
}
|
||||
|
||||
return if (x.type == CommandState.SubMode.VISUAL_BLOCK && y.type == CommandState.SubMode.VISUAL_BLOCK) {
|
||||
when {
|
||||
intersects(x, y) -> {
|
||||
ExchangeCompareResult.OVERLAP
|
||||
}
|
||||
x.start.col <= y.start.col -> {
|
||||
ExchangeCompareResult.LT
|
||||
}
|
||||
else -> {
|
||||
ExchangeCompareResult.GT
|
||||
}
|
||||
}
|
||||
} else if (comparePos(x.start, y.start) <=0 && comparePos(x.end, y.end) >=0) {
|
||||
ExchangeCompareResult.OUTER
|
||||
} else if (comparePos(y.start, x.start) <=0 && comparePos(y.end, x.end) >=0) {
|
||||
ExchangeCompareResult.INNER
|
||||
} else if (comparePos(x.start, y.end) <=0 && comparePos(y.start, x.end) <=0 ||
|
||||
comparePos(y.start, x.end) <=0 && comparePos(x.start, y.end) <=0
|
||||
) {
|
||||
ExchangeCompareResult.OVERLAP
|
||||
} else {
|
||||
val cmp = comparePos(x.start, y.start)
|
||||
when {
|
||||
cmp == 0 -> ExchangeCompareResult.OVERLAP
|
||||
cmp < 0 -> ExchangeCompareResult.LT
|
||||
else -> ExchangeCompareResult.GT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ExchangeCompareResult {
|
||||
OVERLAP,
|
||||
OUTER,
|
||||
INNER,
|
||||
LT,
|
||||
GT,
|
||||
}
|
||||
|
||||
private fun getExchange(editor: Editor, isVisual: Boolean, selectionType: SelectionType): Exchange {
|
||||
fun getEndCol(selectionEnd: Mark, type: CommandState.SubMode): Int {
|
||||
return if (type == CommandState.SubMode.VISUAL_LINE) {
|
||||
EditorHelper.getLineLength(editor, selectionEnd.logicalLine)
|
||||
} else {
|
||||
selectionEnd.col
|
||||
}
|
||||
}
|
||||
// TODO: improve KeyStroke list to sting conversion
|
||||
fun getRegisterText(reg: Char): String = getRegister(reg)?.map { it.keyChar }?.joinToString("") ?: ""
|
||||
fun getMarks(isVisual: Boolean): Pair<Mark, Mark> {
|
||||
val (startMark, endMark) =
|
||||
if (isVisual) {
|
||||
Pair(MarkGroup.MARK_VISUAL_START, MarkGroup.MARK_VISUAL_END)
|
||||
} else {
|
||||
Pair(MarkGroup.MARK_CHANGE_START, MarkGroup.MARK_CHANGE_END)
|
||||
}
|
||||
val marks = VimPlugin.getMark()
|
||||
return Pair(marks.getMark(editor, startMark)!!, marks.getMark(editor, endMark)!!)
|
||||
}
|
||||
|
||||
val unnRegText = getRegister('"')
|
||||
val starRegText = getRegister('*')
|
||||
val plusRegText = getRegister('+')
|
||||
|
||||
var (selectionStart, selectionEnd) = getMarks(isVisual)
|
||||
if (isVisual) {
|
||||
executeNormal(parseKeys("gvy"), editor)
|
||||
// TODO: handle
|
||||
//if &selection ==# 'exclusive' && start != end
|
||||
// let end.column -= len(matchstr(@@, '\_.$'))
|
||||
} else {
|
||||
selectionEnd = selectionEnd.let {
|
||||
VimMark.create(
|
||||
it.key,
|
||||
it.logicalLine,
|
||||
getEndCol(it, selectionType.toSubMode()),
|
||||
it.filename,
|
||||
it.protocol
|
||||
)!!
|
||||
}
|
||||
when (selectionType) {
|
||||
SelectionType.LINE_WISE -> executeNormal(stringToKeys("`[V`]y"), editor)
|
||||
SelectionType.BLOCK_WISE -> executeNormal(stringToKeys("""`[<C-V>`]y"""), editor)
|
||||
SelectionType.CHARACTER_WISE -> executeNormal(stringToKeys("`[v`]y"), editor)
|
||||
}
|
||||
}
|
||||
|
||||
val text = getRegisterText('"')
|
||||
|
||||
setRegister('"', unnRegText)
|
||||
setRegister('*', starRegText)
|
||||
setRegister('+', plusRegText)
|
||||
|
||||
return Exchange(
|
||||
selectionType.toSubMode(),
|
||||
selectionStart,
|
||||
selectionEnd,
|
||||
text
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@@ -32,7 +32,10 @@ import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.actionSystem.ActionPlan;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandlerEx;
|
||||
import com.intellij.openapi.editor.event.*;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent;
|
||||
import com.intellij.openapi.editor.event.EditorMouseListener;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.TextRangeInterval;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
@@ -433,18 +436,12 @@ public class ChangeGroup {
|
||||
}
|
||||
};
|
||||
|
||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
public void editorCreated(Editor editor) {
|
||||
EventFacade.getInstance().addEditorMouseListener(editor, listener);
|
||||
UserDataManager.setVimChangeGroup(editor, true);
|
||||
}
|
||||
|
||||
public void editorReleased(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
if (UserDataManager.getVimChangeGroup(editor)) {
|
||||
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
|
||||
UserDataManager.setVimChangeGroup(editor, false);
|
||||
}
|
||||
public void editorReleased(Editor editor) {
|
||||
EventFacade.getInstance().removeEditorMouseListener(editor, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1752,7 +1749,7 @@ public class ChangeGroup {
|
||||
if (type != null) {
|
||||
final int start = range.getStartOffset();
|
||||
VimPlugin.getMark().setMark(editor, MarkGroup.MARK_CHANGE_POS, start);
|
||||
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, start));
|
||||
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, start+1));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -52,12 +52,10 @@ import java.util.List;
|
||||
*/
|
||||
@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 REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
|
||||
public static final String EDITOR_STORE_ELEMENT = "editor";
|
||||
|
||||
private boolean isBlockCursor = false;
|
||||
private boolean isAnimatedScrolling = false;
|
||||
private boolean isRefrainFromScrolling = false;
|
||||
private Boolean isKeyRepeat = null;
|
||||
|
||||
@@ -69,18 +67,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
||||
}
|
||||
};
|
||||
|
||||
public void turnOn() {
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
VimPlugin.getEditor().editorCreated(editor);
|
||||
}
|
||||
}
|
||||
|
||||
public void turnOff() {
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
VimPlugin.getEditor().editorDeinit(editor, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void initLineNumbers(final @NotNull Editor editor) {
|
||||
if (!supportsVimLineNumbers(editor) || UserDataManager.getVimEditorGroup(editor)) {
|
||||
return;
|
||||
@@ -244,7 +230,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
||||
|
||||
public void editorCreated(@NotNull Editor editor) {
|
||||
isBlockCursor = editor.getSettings().isBlockCursor();
|
||||
isAnimatedScrolling = editor.getSettings().isAnimatedScrolling();
|
||||
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
|
||||
DocumentManager.INSTANCE.addListeners(editor.getDocument());
|
||||
VimPlugin.getKey().registerRequiredShortcutKeys(editor);
|
||||
@@ -258,7 +243,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
||||
KeyHandler.getInstance().reset(editor);
|
||||
}
|
||||
editor.getSettings().setBlockCursor(!CommandStateHelper.inInsertMode(editor));
|
||||
editor.getSettings().setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
||||
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||
}
|
||||
|
||||
@@ -267,7 +251,6 @@ public class EditorGroup implements PersistentStateComponent<Element> {
|
||||
UserDataManager.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(editor);
|
||||
editor.getSettings().setBlockCursor(isBlockCursor);
|
||||
editor.getSettings().setAnimatedScrolling(isAnimatedScrolling);
|
||||
editor.getSettings().setRefrainFromScrolling(isRefrainFromScrolling);
|
||||
DocumentManager.INSTANCE.removeListeners(editor.getDocument());
|
||||
}
|
||||
|
@@ -39,6 +39,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileVisitor;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.VimProjectService;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
@@ -410,9 +411,8 @@ public class FileGroup {
|
||||
lastSelections.put(event.getManager(), event.getOldFile());
|
||||
String disposableKey = FileGroup.disposableKey + event.getManager().hashCode();
|
||||
if (Disposer.get(disposableKey) == null) {
|
||||
Disposer.register(event.getManager().getProject(), () -> {
|
||||
lastSelections.remove(event.getManager());
|
||||
}, disposableKey);
|
||||
VimProjectService parentDisposable = VimProjectService.getInstance(event.getManager().getProject());
|
||||
Disposer.register(parentDisposable, () -> lastSelections.remove(event.getManager()), disposableKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -81,6 +82,11 @@ public class MarkGroup implements PersistentStateComponent<Element> {
|
||||
public void saveJumpLocation(@NotNull Editor editor) {
|
||||
addJump(editor, true);
|
||||
setMark(editor, '\'');
|
||||
|
||||
Project project = editor.getProject();
|
||||
if (project != null) {
|
||||
IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,7 +265,7 @@ public class MarkGroup implements PersistentStateComponent<Element> {
|
||||
|
||||
public void setChangeMarks(@NotNull Editor editor, @NotNull TextRange range) {
|
||||
setMark(editor, MARK_CHANGE_START, range.getStartOffset());
|
||||
setMark(editor, MARK_CHANGE_END, range.getEndOffset());
|
||||
setMark(editor, MARK_CHANGE_END, range.getEndOffset()-1);
|
||||
}
|
||||
|
||||
public @Nullable TextRange getChangeMarks(@NotNull Editor editor) {
|
||||
@@ -276,7 +282,7 @@ public class MarkGroup implements PersistentStateComponent<Element> {
|
||||
if (start != null && end != null) {
|
||||
final int startOffset = EditorHelper.getOffset(editor, start.getLogicalLine(), start.getCol());
|
||||
final int endOffset = EditorHelper.getOffset(editor, end.getLogicalLine(), end.getCol());
|
||||
return new TextRange(startOffset, endOffset);
|
||||
return new TextRange(startOffset, endOffset+1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -756,12 +762,14 @@ public class MarkGroup implements PersistentStateComponent<Element> {
|
||||
|
||||
@Override
|
||||
public void bookmarkAdded(@NotNull Bookmark b) {
|
||||
if (!VimPlugin.isEnabled()) return;
|
||||
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return;
|
||||
bookmarkTemplate = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bookmarkRemoved(@NotNull Bookmark b) {
|
||||
if (!VimPlugin.isEnabled()) return;
|
||||
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return;
|
||||
|
||||
char ch = b.getMnemonic();
|
||||
@@ -775,6 +783,7 @@ public class MarkGroup implements PersistentStateComponent<Element> {
|
||||
|
||||
@Override
|
||||
public void bookmarkChanged(@NotNull Bookmark b) {
|
||||
if (!VimPlugin.isEnabled()) return;
|
||||
/* IJ sets named marks in two steps. Firstly it creates an unnamed mark, then adds a mnemonic */
|
||||
if (!OptionsManager.INSTANCE.getIdeamarks().isSet()) return;
|
||||
if (b != bookmarkTemplate) return;
|
||||
|
@@ -68,28 +68,6 @@ public class MotionGroup {
|
||||
public static final int LAST_t = 4;
|
||||
public static final int LAST_COLUMN = 9999;
|
||||
|
||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
// This ridiculous code ensures that a lot of events are processed BEFORE we finally start listening
|
||||
// to visible area changes. The primary reason for this change is to fix the cursor position bug
|
||||
// using the gd and gD commands (Goto Declaration). This bug has been around since Idea 6.0.4?
|
||||
// Prior to this change the visible area code was moving the cursor around during file load and messing
|
||||
// with the cursor position of the Goto Declaration processing.
|
||||
ApplicationManager.getApplication().invokeLater(() -> ApplicationManager.getApplication()
|
||||
.invokeLater(() -> ApplicationManager.getApplication().invokeLater(() -> {
|
||||
VimListenerManager.EditorListeners.add(editor);
|
||||
UserDataManager.setVimMotionGroup(editor, true);
|
||||
})));
|
||||
}
|
||||
|
||||
public void editorReleased(@NotNull EditorFactoryEvent event) {
|
||||
Editor editor = event.getEditor();
|
||||
if (UserDataManager.getVimMotionGroup(editor)) {
|
||||
VimListenerManager.EditorListeners.remove(editor);
|
||||
UserDataManager.setVimMotionGroup(editor, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper method calculates the complete range a motion will move over taking into account whether
|
||||
* the motion is FLAG_MOT_LINEWISE or FLAG_MOT_CHARACTERWISE (FLAG_MOT_INCLUSIVE or FLAG_MOT_EXCLUSIVE).
|
||||
@@ -259,7 +237,7 @@ public class MotionGroup {
|
||||
}
|
||||
|
||||
private static int getScrollScreenTargetCaretVisualLine(final @NotNull Editor editor, int rawCount, boolean down) {
|
||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||
final Rectangle visibleArea = EditorHelper.getVisibleArea(editor);
|
||||
final int caretVisualLine = editor.getCaretModel().getVisualPosition().line;
|
||||
final int scrollOption = getScrollOption(rawCount);
|
||||
|
||||
@@ -890,7 +868,7 @@ public class MotionGroup {
|
||||
}
|
||||
|
||||
private static void scrollColumnToLeftOfScreen(@NotNull Editor editor, int column) {
|
||||
editor.getScrollingModel().scrollHorizontally(column * EditorHelper.getColumnWidth(editor));
|
||||
EditorHelper.scrollHorizontally(editor, column * EditorHelper.getColumnWidth(editor));
|
||||
}
|
||||
|
||||
public int moveCaretToMiddleColumn(@NotNull Editor editor, @NotNull Caret caret) {
|
||||
@@ -1106,8 +1084,7 @@ public class MotionGroup {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
final Rectangle visibleArea = scrollingModel.getVisibleArea();
|
||||
final Rectangle visibleArea = EditorHelper.getVisibleArea(editor);
|
||||
|
||||
int targetCaretVisualLine = getScrollScreenTargetCaretVisualLine(editor, rawCount, down);
|
||||
|
||||
@@ -1128,7 +1105,7 @@ public class MotionGroup {
|
||||
}
|
||||
if (moved) {
|
||||
// We'll keep the caret at the same position, although that might not be the same line offset as previously
|
||||
targetCaretVisualLine = editor.yToVisualLine(yInitialCaret + scrollingModel.getVisibleArea().y - yPrevious);
|
||||
targetCaretVisualLine = editor.yToVisualLine(yInitialCaret + EditorHelper.getVisibleArea(editor).y - yPrevious);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -20,7 +20,7 @@ package com.maddyhome.idea.vim.group
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.actions.OpenFileAction
|
||||
import com.intellij.ide.actions.ShowFilePathAction
|
||||
import com.intellij.ide.actions.RevealFileAction
|
||||
import com.intellij.ide.browsers.BrowserLauncher
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationDisplayType
|
||||
@@ -198,18 +198,14 @@ class NotificationService(private val project: Project?) {
|
||||
|
||||
private fun createIdeaVimRcManually(message: String, project: Project?) {
|
||||
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 " + RevealFileAction.getFileManagerName()
|
||||
if (!File(System.getProperty("user.home")).exists()) {
|
||||
actionName = ""
|
||||
}
|
||||
notification.addAction(object : AnAction(actionName) {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val homeDir = File(System.getProperty("user.home"))
|
||||
@Suppress("DEPRECATION", "UnstableApiUsage")
|
||||
// [VERSION UPDATE] 193+ com.intellij.ide.actions.RevealFileAction.openDirectory
|
||||
ShowFilePathAction.openDirectory(homeDir)
|
||||
RevealFileAction.openDirectory(homeDir)
|
||||
notification.expire()
|
||||
}
|
||||
})
|
||||
|
@@ -251,7 +251,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
}
|
||||
|
||||
if (start != -1) {
|
||||
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, Math.max(end - 1, 0)));
|
||||
VimPlugin.getMark().setChangeMarks(editor, new TextRange(start, end));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -414,6 +414,10 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
|
||||
registers.put(register, new Register(register, SelectionType.CHARACTER_WISE, keys));
|
||||
}
|
||||
|
||||
public void setKeys(char register, @NotNull List<KeyStroke> keys, SelectionType type) {
|
||||
registers.put(register, new Register(register, type, keys));
|
||||
}
|
||||
|
||||
public void finishRecording(Editor editor) {
|
||||
if (recordRegister != 0) {
|
||||
Register reg = null;
|
||||
|
@@ -396,7 +396,9 @@ public class SearchGroup implements PersistentStateComponent<Element> {
|
||||
int initialOffset, @Nullable LineRange searchRange, boolean forwards, boolean forceUpdate) {
|
||||
int currentMatchOffset = -1;
|
||||
|
||||
Project[] projects = ProjectManager.getInstance().getOpenProjects();
|
||||
ProjectManager projectManager = ProjectManager.getInstanceIfCreated();
|
||||
if (projectManager == null) return currentMatchOffset;
|
||||
Project[] projects = projectManager.getOpenProjects();
|
||||
for (Project project : projects) {
|
||||
Editor current = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
Editor[] editors = current == null ? null : EditorFactory.getInstance().getEditors(current.getDocument(), project);
|
||||
|
@@ -135,7 +135,7 @@ class PutGroup {
|
||||
if (data.visualSelection != null) {
|
||||
val offset = editor.caretModel.primaryCaret.offset
|
||||
VimPlugin.getMark().setMark(editor, MarkGroup.MARK_CHANGE_POS, offset)
|
||||
VimPlugin.getMark().setChangeMarks(editor, TextRange(offset, offset))
|
||||
VimPlugin.getMark().setChangeMarks(editor, TextRange(offset, offset+1))
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -144,7 +144,7 @@ class PutGroup {
|
||||
|
||||
if (data.textData.typeInRegister.isLine && text.isNotEmpty() && text.last() != '\n') text += '\n'
|
||||
|
||||
if (data.textData.typeInRegister.isChar && text.lastOrNull() == '\n' && data.visualSelection?.typeInEditor?.isLine != true) text = text.dropLast(1)
|
||||
if (data.textData.typeInRegister.isChar && text.lastOrNull() == '\n' && data.visualSelection?.typeInEditor?.isLine == false) text = text.dropLast(1)
|
||||
|
||||
return ProcessedTextData(text, data.textData.typeInRegister, data.textData.transferableData)
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group.visual
|
||||
|
||||
import com.intellij.find.FindManager
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
@@ -53,7 +54,7 @@ class VisualMotionGroup {
|
||||
editor.commandState.pushModes(CommandState.Mode.VISUAL, lastSelectionType.toSubMode())
|
||||
|
||||
val primaryCaret = editor.caretModel.primaryCaret
|
||||
primaryCaret.vimSetSelection(visualMarks.startOffset, visualMarks.endOffset, true)
|
||||
primaryCaret.vimSetSelection(visualMarks.startOffset, visualMarks.endOffset-1, true)
|
||||
|
||||
editor.scrollingModel.scrollToCaret(ScrollType.CENTER)
|
||||
|
||||
@@ -216,6 +217,9 @@ class VisualMotionGroup {
|
||||
}
|
||||
|
||||
fun autodetectVisualSubmode(editor: Editor): CommandState.SubMode {
|
||||
// IJ specific. See https://youtrack.jetbrains.com/issue/VIM-1924.
|
||||
if (FindManager.getInstance(editor.project).selectNextOccurrenceWasPerformed()) return CommandState.SubMode.VISUAL_CHARACTER
|
||||
|
||||
if (editor.caretModel.caretCount > 1 && seemsLikeBlockMode(editor)) {
|
||||
return CommandState.SubMode.VISUAL_BLOCK
|
||||
}
|
||||
|
@@ -60,9 +60,7 @@ class ActionBeanClass : AbstractExtensionPointBean() {
|
||||
val actionId: String get() = implementation?.let { EditorActionHandlerBase.getActionId(it) } ?: ""
|
||||
|
||||
val action: EditorActionHandlerBase by lazy {
|
||||
// FIXME. [VERSION UPDATE] change to instantiateClass for 193+
|
||||
@Suppress("DEPRECATION")
|
||||
this.instantiate<EditorActionHandlerBase>(
|
||||
this.instantiateClass<EditorActionHandlerBase>(
|
||||
implementation ?: "", ApplicationManager.getApplication().picoContainer)
|
||||
}
|
||||
|
||||
|
@@ -156,9 +156,8 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
|
||||
}
|
||||
|
||||
private fun Editor.collectSelections(): Map<Caret, VimSelection>? {
|
||||
|
||||
return when {
|
||||
this.inRepeatMode -> {
|
||||
!this.inVisualMode && this.inRepeatMode -> {
|
||||
if (this.vimLastSelectionType == SelectionType.BLOCK_WISE) {
|
||||
val primaryCaret = caretModel.primaryCaret
|
||||
val range = primaryCaret.vimLastVisualOperatorRange ?: return null
|
||||
@@ -232,7 +231,12 @@ sealed class VisualOperatorActionHandler : EditorActionHandlerBase(false) {
|
||||
if (res) {
|
||||
VimRepeater.saveLastChange(cmd)
|
||||
VimRepeater.repeatHandler = false
|
||||
editor.vimForEachCaret { caret -> visualChanges[caret]?.let { caret.vimLastVisualOperatorRange = it } }
|
||||
editor.vimForEachCaret { caret ->
|
||||
val visualChange = visualChanges[caret]
|
||||
if (visualChange != null) {
|
||||
caret.vimLastVisualOperatorRange = visualChange
|
||||
}
|
||||
}
|
||||
editor.caretModel.allCarets.forEach { it.vimLastColumn = it.visualPosition.column }
|
||||
}
|
||||
|
||||
|
@@ -42,19 +42,35 @@ import static java.lang.Integer.max;
|
||||
* This is a set of helper methods for working with editors. All line and column values are zero based.
|
||||
*/
|
||||
public class EditorHelper {
|
||||
public static @NotNull Rectangle getVisibleArea(final @NotNull Editor editor) {
|
||||
return editor.getScrollingModel().getVisibleAreaOnScrollingFinished();
|
||||
}
|
||||
|
||||
public static boolean scrollVertically(@NotNull Editor editor, int verticalOffset) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished();
|
||||
scrollingModel.scroll(area.x, verticalOffset);
|
||||
return scrollingModel.getVisibleAreaOnScrollingFinished().y != area.y;
|
||||
}
|
||||
|
||||
public static void scrollHorizontally(@NotNull Editor editor, int horizontalOffset) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
final Rectangle area = scrollingModel.getVisibleAreaOnScrollingFinished();
|
||||
scrollingModel.scroll(horizontalOffset, area.y);
|
||||
}
|
||||
|
||||
public static int getVisualLineAtTopOfScreen(final @NotNull Editor editor) {
|
||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
return getFullVisualLine(editor, visibleArea.y, visibleArea.y, visibleArea.y + visibleArea.height);
|
||||
}
|
||||
|
||||
public static int getVisualLineAtMiddleOfScreen(final @NotNull Editor editor) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
final Rectangle visibleArea = scrollingModel.getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
return editor.yToVisualLine(visibleArea.y + (visibleArea.height / 2));
|
||||
}
|
||||
|
||||
public static int getVisualLineAtBottomOfScreen(final @NotNull Editor editor) {
|
||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
return getFullVisualLine(editor, visibleArea.y + visibleArea.height, visibleArea.y, visibleArea.y + visibleArea.height);
|
||||
}
|
||||
|
||||
@@ -178,9 +194,8 @@ public class EditorHelper {
|
||||
*/
|
||||
private static int getApproximateScreenHeight(final @NotNull Editor editor) {
|
||||
int lh = editor.getLineHeight();
|
||||
int height = editor.getScrollingModel().getVisibleArea().y +
|
||||
editor.getScrollingModel().getVisibleArea().height -
|
||||
getVisualLineAtTopOfScreen(editor) * lh;
|
||||
Rectangle area = getVisibleArea(editor);
|
||||
int height = area.y + area.height - getVisualLineAtTopOfScreen(editor) * lh;
|
||||
return height / lh;
|
||||
}
|
||||
|
||||
@@ -191,7 +206,7 @@ public class EditorHelper {
|
||||
* @return The number of screen columns
|
||||
*/
|
||||
public static int getScreenWidth(final @NotNull Editor editor) {
|
||||
Rectangle rect = editor.getScrollingModel().getVisibleArea();
|
||||
Rectangle rect = getVisibleArea(editor);
|
||||
Point pt = new Point(rect.width, 0);
|
||||
VisualPosition vp = editor.xyToVisualPosition(pt);
|
||||
|
||||
@@ -205,7 +220,7 @@ public class EditorHelper {
|
||||
* @return The number of pixels
|
||||
*/
|
||||
public static int getColumnWidth(final @NotNull Editor editor) {
|
||||
Rectangle rect = editor.getScrollingModel().getVisibleArea();
|
||||
Rectangle rect = getVisibleArea(editor);
|
||||
if (rect.width == 0) return 0;
|
||||
Point pt = new Point(rect.width, 0);
|
||||
VisualPosition vp = editor.xyToVisualPosition(pt);
|
||||
@@ -223,7 +238,7 @@ public class EditorHelper {
|
||||
public static int getVisualColumnAtLeftOfScreen(final @NotNull Editor editor) {
|
||||
int cw = getColumnWidth(editor);
|
||||
if (cw == 0) return 0;
|
||||
return (editor.getScrollingModel().getHorizontalScrollOffset() + cw - 1) / cw;
|
||||
return (getVisibleArea(editor).x + cw - 1) / cw;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,8 +611,7 @@ public class EditorHelper {
|
||||
* @param visualLine The visual line to scroll to the current caret location
|
||||
*/
|
||||
public static void scrollVisualLineToCaretLocation(final @NotNull Editor editor, int visualLine) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
final Rectangle visibleArea = scrollingModel.getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
final int caretScreenOffset = editor.visualLineToY(editor.getCaretModel().getVisualPosition().line) - visibleArea.y;
|
||||
|
||||
final int yVisualLine = editor.visualLineToY(visualLine);
|
||||
@@ -615,7 +629,7 @@ public class EditorHelper {
|
||||
inlayOffset = -bottomInlayHeight;
|
||||
}
|
||||
|
||||
scrollingModel.scrollVertically(yVisualLine - caretScreenOffset - inlayOffset);
|
||||
scrollVertically(editor, yVisualLine - caretScreenOffset - inlayOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,13 +641,9 @@ public class EditorHelper {
|
||||
* @return Returns true if the window was moved
|
||||
*/
|
||||
public static boolean scrollVisualLineToTopOfScreen(final @NotNull Editor editor, int visualLine) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
int inlayHeight = getHeightOfVisualLineInlays(editor, visualLine, true);
|
||||
int y = editor.visualLineToY(visualLine) - inlayHeight;
|
||||
int verticalPos = scrollingModel.getVerticalScrollOffset();
|
||||
scrollingModel.scrollVertically(y);
|
||||
|
||||
return verticalPos != scrollingModel.getVerticalScrollOffset();
|
||||
return scrollVertically(editor, y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -643,11 +653,10 @@ public class EditorHelper {
|
||||
* @param visualLine The visual line to place in the middle of the current window
|
||||
*/
|
||||
public static void scrollVisualLineToMiddleOfScreen(@NotNull Editor editor, int visualLine) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
int y = editor.visualLineToY(visualLine);
|
||||
int lineHeight = editor.getLineHeight();
|
||||
int height = scrollingModel.getVisibleArea().height;
|
||||
scrollingModel.scrollVertically(y - ((height - lineHeight) / 2));
|
||||
int height = getVisibleArea(editor).height;
|
||||
scrollVertically(editor, y - ((height - lineHeight) / 2));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,7 +670,6 @@ public class EditorHelper {
|
||||
* @return True if the editor was scrolled
|
||||
*/
|
||||
public static boolean scrollVisualLineToBottomOfScreen(@NotNull Editor editor, int visualLine) {
|
||||
final ScrollingModel scrollingModel = editor.getScrollingModel();
|
||||
int inlayHeight = getHeightOfVisualLineInlays(editor, visualLine, false);
|
||||
int exPanelHeight = 0;
|
||||
int exPanelWithoutShortcutsHeight = 0;
|
||||
@@ -672,14 +680,9 @@ public class EditorHelper {
|
||||
exPanelWithoutShortcutsHeight = ExEntryPanel.getInstanceWithoutShortcuts().getHeight();
|
||||
}
|
||||
int y = editor.visualLineToY(visualLine);
|
||||
int verticalPos = scrollingModel.getVerticalScrollOffset();
|
||||
int height = inlayHeight + editor.getLineHeight() + exPanelHeight + exPanelWithoutShortcutsHeight;
|
||||
|
||||
Rectangle visibleArea = scrollingModel.getVisibleArea();
|
||||
|
||||
scrollingModel.scrollVertically(y - visibleArea.height + height);
|
||||
|
||||
return verticalPos != scrollingModel.getVerticalScrollOffset();
|
||||
Rectangle visibleArea = getVisibleArea(editor);
|
||||
return scrollVertically(editor, y - visibleArea.height + height);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,7 +737,7 @@ public class EditorHelper {
|
||||
}
|
||||
|
||||
private static int scrollFullPageDown(final @NotNull Editor editor, int pages) {
|
||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
final int lineCount = getVisualLineCount(editor);
|
||||
|
||||
if (editor.getCaretModel().getVisualPosition().line == lineCount - 1)
|
||||
@@ -777,7 +780,7 @@ public class EditorHelper {
|
||||
}
|
||||
|
||||
private static int scrollFullPageUp(final @NotNull Editor editor, int pages) {
|
||||
final Rectangle visibleArea = editor.getScrollingModel().getVisibleArea();
|
||||
final Rectangle visibleArea = getVisibleArea(editor);
|
||||
final int lineHeight = editor.getLineHeight();
|
||||
|
||||
int y = visibleArea.y;
|
||||
@@ -851,4 +854,21 @@ public class EditorHelper {
|
||||
public static boolean isDiffEditor(@NotNull Editor editor) {
|
||||
return editor.getEditorKind() == EditorKind.DIFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the document in the editor is modified.
|
||||
*/
|
||||
public static boolean hasUnsavedChanges(@NotNull Editor editor) {
|
||||
int line = 0;
|
||||
Document document = editor.getDocument();
|
||||
|
||||
while (line < document.getLineCount()) {
|
||||
if (document.isLineModified(line)) {
|
||||
return true;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@@ -27,11 +27,11 @@ import java.util.*
|
||||
|
||||
/**
|
||||
* This annotation is created for test functions (methods).
|
||||
* It means that original vim behavior has small differences from behavior of IdeaVim.
|
||||
* It means that the original vim behavior has small differences from behavior of IdeaVim.
|
||||
* [shouldBeFixed] flag indicates whether the given functionality should be fixed
|
||||
* or the given behavior is normal for IdeaVim and should be leaved as is.
|
||||
*
|
||||
* E.g. after execution some commands original vim has next text:
|
||||
* E.g. after execution of some commands original vim has the following text:
|
||||
* Hello1
|
||||
* Hello2
|
||||
* Hello3
|
||||
@@ -42,9 +42,11 @@ import java.util.*
|
||||
* Hello2
|
||||
* Hello3
|
||||
*
|
||||
* Why this annotation exists?
|
||||
* In this case you should still create the test function and mark this function with [VimBehaviorDiffers] annotation.
|
||||
*
|
||||
* Why does this annotation exist?
|
||||
* After creating some functionality you can understand that IdeaVim has a bit different behavior, but you
|
||||
* cannot fix it right now because of any reasons (bugs in IDE,
|
||||
* cannot fix it right now because of any reason (bugs in IDE,
|
||||
* the impossibility of this functionality in IDEA (*[shouldBeFixed] == false*), leak of time for fixing).
|
||||
* In that case, you should NOT remove the corresponding test or leave it without any marks that this test
|
||||
* not fully convenient with vim, but leave the test with IdeaVim's behavior and put this annotation
|
||||
|
76
src/com/maddyhome/idea/vim/helper/StatisticReporter.kt
Normal file
76
src/com/maddyhome/idea/vim/helper/StatisticReporter.kt
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.helper
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.application.ApplicationInfo
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.PermanentInstallationID
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.util.JDOMUtil
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.util.io.HttpRequests
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import org.jdom.JDOMException
|
||||
import java.io.IOException
|
||||
import java.net.URLEncoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object StatisticReporter {
|
||||
|
||||
private val logger = logger<StatisticReporter>()
|
||||
private const val IDEAVIM_STATISTICS_TIMESTAMP_KEY = "ideavim.statistics.timestamp"
|
||||
private val DAY_IN_MILLIS = TimeUnit.DAYS.toMillis(1)
|
||||
|
||||
/**
|
||||
* Reports statistics about installed IdeaVim and enabled Vim emulation.
|
||||
*
|
||||
* See https://github.com/go-lang-plugin-org/go-lang-idea-plugin/commit/5182ab4a1d01ad37f6786268a2fe5e908575a217
|
||||
*/
|
||||
fun report() {
|
||||
val lastUpdate = PropertiesComponent.getInstance().getLong(IDEAVIM_STATISTICS_TIMESTAMP_KEY, 0)
|
||||
val outOfDate = lastUpdate == 0L || System.currentTimeMillis() - lastUpdate > DAY_IN_MILLIS
|
||||
|
||||
if (!outOfDate || !VimPlugin.isEnabled()) return
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
try {
|
||||
val buildNumber = ApplicationInfo.getInstance().build.asString()
|
||||
val version = URLEncoder.encode(VimPlugin.getVersion(), CharsetToolkit.UTF8)
|
||||
val os = URLEncoder.encode("${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION}", CharsetToolkit.UTF8)
|
||||
val uid = PermanentInstallationID.get()
|
||||
|
||||
val url = "https://plugins.jetbrains.com/plugins/list?pluginId=${VimPlugin.getPluginId().idString}&build=$buildNumber&pluginVersion=$version&os=$os&uuid=$uid"
|
||||
PropertiesComponent.getInstance().setValue(IDEAVIM_STATISTICS_TIMESTAMP_KEY, System.currentTimeMillis().toString())
|
||||
|
||||
HttpRequests.request(url).connect { request: HttpRequests.Request ->
|
||||
logger.info("Sending statistics: $url")
|
||||
try {
|
||||
JDOMUtil.load(request.inputStream)
|
||||
} catch (e: JDOMException) {
|
||||
logger.warn(e)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
logger.warn(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -86,8 +86,6 @@ var Editor.vimIncsearchCurrentMatchOffset: Int? by userData()
|
||||
*/
|
||||
var Editor.vimLastSelectionType: SelectionType? by userData()
|
||||
var Editor.vimCommandState: CommandState? by userData()
|
||||
var Editor.vimChangeGroup: Boolean by userDataOr { false }
|
||||
var Editor.vimMotionGroup: Boolean by userDataOr { false }
|
||||
var Editor.vimEditorGroup: Boolean by userDataOr { false }
|
||||
var Editor.vimLineNumbersInitialState: Boolean by userDataOr { false }
|
||||
var Editor.vimHasRelativeLineNumbersInstalled: Boolean by userDataOr { false }
|
||||
|
@@ -55,9 +55,6 @@ import java.beans.PropertyChangeListener
|
||||
*/
|
||||
object IdeaSpecifics {
|
||||
fun addIdeaSpecificsListeners(project: Project) {
|
||||
EventFacade.getInstance().connectAnActionListener(project, VimActionListener)
|
||||
EventFacade.getInstance().connectTemplateStartedListener(project, VimTemplateManagerListener)
|
||||
EventFacade.getInstance().connectFindModelListener(project, VimFindModelListener)
|
||||
EventFacade.getInstance().registerLookupListener(project, LookupListener)
|
||||
}
|
||||
|
||||
@@ -65,15 +62,17 @@ object IdeaSpecifics {
|
||||
EventFacade.getInstance().removeLookupListener(project, LookupListener)
|
||||
}
|
||||
|
||||
private object VimActionListener : AnActionListener {
|
||||
class VimActionListener : AnActionListener {
|
||||
private val surrounderItems = listOf("if", "if / else", "for")
|
||||
private val surrounderAction = "com.intellij.codeInsight.generation.surroundWith.SurroundWithHandler\$InvokeSurrounderAction"
|
||||
private var editor: Editor? = null
|
||||
override fun beforeActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
|
||||
if (!VimPlugin.isEnabled()) return
|
||||
editor = dataContext.getData(CommonDataKeys.EDITOR) ?: return
|
||||
}
|
||||
|
||||
override fun afterActionPerformed(action: AnAction, dataContext: DataContext, event: AnActionEvent) {
|
||||
if (!VimPlugin.isEnabled()) return
|
||||
//region Extend Selection for Rider
|
||||
when (ActionManager.getInstance().getId(action)) {
|
||||
IdeActions.ACTION_EDITOR_SELECT_WORD_AT_CARET, IdeActions.ACTION_EDITOR_UNSELECT_WORD_AT_CARET -> {
|
||||
@@ -107,8 +106,9 @@ object IdeaSpecifics {
|
||||
}
|
||||
|
||||
//region Enter insert mode for surround templates without selection
|
||||
private object VimTemplateManagerListener : TemplateManagerListener {
|
||||
class VimTemplateManagerListener : TemplateManagerListener {
|
||||
override fun templateStarted(state: TemplateState) {
|
||||
if (!VimPlugin.isEnabled()) return
|
||||
val editor = state.editor ?: return
|
||||
|
||||
state.addTemplateStateListener(object : TemplateEditingAdapter() {
|
||||
@@ -157,8 +157,9 @@ object IdeaSpecifics {
|
||||
//endregion
|
||||
|
||||
//region Hide Vim search highlights when showing IntelliJ search results
|
||||
private object VimFindModelListener : FindModelListener {
|
||||
class VimFindModelListener : FindModelListener {
|
||||
override fun findNextModelChanged() {
|
||||
if (!VimPlugin.isEnabled()) return
|
||||
VimPlugin.getSearch().clearSearchHighlight()
|
||||
}
|
||||
}
|
||||
|
@@ -18,12 +18,11 @@
|
||||
|
||||
package com.maddyhome.idea.vim.listener
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Caret
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.EditorFactory
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager
|
||||
import com.intellij.openapi.editor.actionSystem.TypedAction
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent
|
||||
import com.intellij.openapi.editor.event.EditorFactoryListener
|
||||
import com.intellij.openapi.editor.event.EditorMouseEvent
|
||||
@@ -48,7 +47,6 @@ import com.maddyhome.idea.vim.ex.ExOutputModel
|
||||
import com.maddyhome.idea.vim.group.ChangeGroup
|
||||
import com.maddyhome.idea.vim.group.EditorGroup
|
||||
import com.maddyhome.idea.vim.group.FileGroup
|
||||
import com.maddyhome.idea.vim.group.MarkGroup
|
||||
import com.maddyhome.idea.vim.group.MotionGroup
|
||||
import com.maddyhome.idea.vim.group.SearchGroup
|
||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
||||
@@ -56,6 +54,7 @@ import com.maddyhome.idea.vim.group.visual.VimVisualTimer
|
||||
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
|
||||
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper
|
||||
import com.maddyhome.idea.vim.helper.StatisticReporter
|
||||
import com.maddyhome.idea.vim.helper.exitSelectMode
|
||||
import com.maddyhome.idea.vim.helper.exitVisualMode
|
||||
import com.maddyhome.idea.vim.helper.inSelectMode
|
||||
@@ -63,7 +62,8 @@ import com.maddyhome.idea.vim.helper.inVisualMode
|
||||
import com.maddyhome.idea.vim.helper.isEndAllowed
|
||||
import com.maddyhome.idea.vim.helper.subMode
|
||||
import com.maddyhome.idea.vim.helper.vimLastColumn
|
||||
import com.maddyhome.idea.vim.helper.vimMotionGroup
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.add
|
||||
import com.maddyhome.idea.vim.listener.VimListenerManager.EditorListeners.remove
|
||||
import com.maddyhome.idea.vim.option.OptionsManager
|
||||
import com.maddyhome.idea.vim.ui.ExEntryPanel
|
||||
import com.maddyhome.idea.vim.ui.ShowCmdOptionChangeListener
|
||||
@@ -161,9 +161,7 @@ object VimListenerManager {
|
||||
|
||||
object GlobalListeners {
|
||||
fun enable() {
|
||||
@Suppress("DEPRECATION")
|
||||
// [VERSION UPDATE] 193+ com.intellij.openapi.editor.actionSystem.TypedAction.getInstance
|
||||
val typedAction = EditorActionManager.getInstance().typedAction
|
||||
val typedAction = TypedAction.getInstance()
|
||||
if (typedAction.rawHandler !is VimTypedActionHandler) {
|
||||
// Actually this if should always be true, but just as protection
|
||||
EventFacade.getInstance().setupTypedActionHandler(VimTypedActionHandler(typedAction.rawHandler))
|
||||
@@ -173,7 +171,7 @@ object VimListenerManager {
|
||||
OptionsManager.relativenumber.addOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.showcmd.addOptionChangeListener(ShowCmdOptionChangeListener)
|
||||
|
||||
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, ApplicationManager.getApplication())
|
||||
EventFacade.getInstance().addEditorFactoryListener(VimEditorFactoryListener, VimPlugin.getInstance())
|
||||
}
|
||||
|
||||
fun disable() {
|
||||
@@ -181,7 +179,7 @@ object VimListenerManager {
|
||||
|
||||
OptionsManager.number.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.relativenumber.removeOptionChangeListener(EditorGroup.NumberChangeListener.INSTANCE)
|
||||
OptionsManager.showcmd.addOptionChangeListener(ShowCmdOptionChangeListener)
|
||||
OptionsManager.showcmd.removeOptionChangeListener(ShowCmdOptionChangeListener)
|
||||
|
||||
EventFacade.getInstance().removeEditorFactoryListener(VimEditorFactoryListener)
|
||||
}
|
||||
@@ -189,15 +187,11 @@ object VimListenerManager {
|
||||
|
||||
object ProjectListeners {
|
||||
fun add(project: Project) {
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
eventFacade.connectBookmarkListener(project, MarkGroup.MarkListener(project))
|
||||
eventFacade.connectFileEditorManagerListener(project, VimFileEditorManagerListener)
|
||||
IdeaSpecifics.addIdeaSpecificsListeners(project)
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
// Project listeners are self-disposable, so there is no need to unregister them on project close
|
||||
EventFacade.getInstance().disableBusConnection()
|
||||
ProjectManager.getInstance().openProjects.filterNot { it.isDisposed }.forEach { IdeaSpecifics.removeIdeaSpecificsListeners(it) }
|
||||
}
|
||||
|
||||
@@ -208,26 +202,17 @@ object VimListenerManager {
|
||||
|
||||
object EditorListeners {
|
||||
fun addAll() {
|
||||
val editors = EditorFactory.getInstance().allEditors
|
||||
for (editor in editors) {
|
||||
if (!editor.vimMotionGroup) {
|
||||
add(editor)
|
||||
editor.vimMotionGroup = true
|
||||
}
|
||||
EditorFactory.getInstance().allEditors.forEach { editor ->
|
||||
this.add(editor)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeAll() {
|
||||
val editors = EditorFactory.getInstance().allEditors
|
||||
for (editor in editors) {
|
||||
if (editor.vimMotionGroup) {
|
||||
remove(editor)
|
||||
editor.vimMotionGroup = false
|
||||
}
|
||||
EditorFactory.getInstance().allEditors.forEach { editor ->
|
||||
this.remove(editor, false)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun add(editor: Editor) {
|
||||
editor.contentComponent.addKeyListener(VimKeyListener)
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
@@ -235,21 +220,29 @@ object VimListenerManager {
|
||||
eventFacade.addEditorMouseMotionListener(editor, EditorMouseHandler)
|
||||
eventFacade.addEditorSelectionListener(editor, EditorSelectionHandler)
|
||||
eventFacade.addComponentMouseListener(editor.contentComponent, ComponentMouseListener)
|
||||
|
||||
VimPlugin.getEditor().editorCreated(editor)
|
||||
|
||||
VimPlugin.getChange().editorCreated(editor)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun remove(editor: Editor) {
|
||||
fun remove(editor: Editor, isReleased: Boolean) {
|
||||
editor.contentComponent.removeKeyListener(VimKeyListener)
|
||||
val eventFacade = EventFacade.getInstance()
|
||||
eventFacade.removeEditorMouseListener(editor, EditorMouseHandler)
|
||||
eventFacade.removeEditorMouseMotionListener(editor, EditorMouseHandler)
|
||||
eventFacade.removeEditorSelectionListener(editor, EditorSelectionHandler)
|
||||
eventFacade.removeComponentMouseListener(editor.contentComponent, ComponentMouseListener)
|
||||
|
||||
VimPlugin.getEditorIfCreated()?.editorDeinit(editor, isReleased)
|
||||
|
||||
VimPlugin.getChange().editorReleased(editor)
|
||||
}
|
||||
}
|
||||
|
||||
object VimFileEditorManagerListener : FileEditorManagerListener {
|
||||
class VimFileEditorManagerListener : FileEditorManagerListener {
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
if (!VimPlugin.isEnabled()) return
|
||||
MotionGroup.fileEditorManagerSelectionChangedCallback(event)
|
||||
FileGroup.fileEditorManagerSelectionChangedCallback(event)
|
||||
SearchGroup.fileEditorManagerSelectionChangedCallback(event)
|
||||
@@ -258,16 +251,12 @@ object VimListenerManager {
|
||||
|
||||
private object VimEditorFactoryListener : EditorFactoryListener {
|
||||
override fun editorCreated(event: EditorFactoryEvent) {
|
||||
VimPlugin.getEditor().editorCreated(event.editor)
|
||||
VimPlugin.getMotion().editorCreated(event)
|
||||
VimPlugin.getChange().editorCreated(event)
|
||||
VimPlugin.statisticReport()
|
||||
add(event.editor)
|
||||
StatisticReporter.report()
|
||||
}
|
||||
|
||||
override fun editorReleased(event: EditorFactoryEvent) {
|
||||
VimPlugin.getEditor().editorDeinit(event.editor, true)
|
||||
VimPlugin.getMotion().editorReleased(event)
|
||||
VimPlugin.getChange().editorReleased(event)
|
||||
remove(event.editor, true)
|
||||
VimPlugin.getMark().editorReleased(event)
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
package com.maddyhome.idea.vim.option;
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -113,5 +114,5 @@ public abstract class Option<T> {
|
||||
|
||||
protected final String name;
|
||||
protected final String abbrev;
|
||||
private final @NotNull List<OptionChangeListener<T>> listeners = new ArrayList<>();
|
||||
private final @NotNull List<OptionChangeListener<T>> listeners = ContainerUtil.createLockFreeCopyOnWriteList();
|
||||
}
|
||||
|
@@ -86,7 +86,7 @@ object OptionsManager {
|
||||
val idearefactormode = addOption(BoundStringOption(IdeaRefactorMode.name, IdeaRefactorMode.name, IdeaRefactorMode.select, IdeaRefactorMode.availableValues))
|
||||
val ideastatusicon = addOption(BoundStringOption(IdeaStatusIcon.name, IdeaStatusIcon.name, IdeaStatusIcon.enabled, IdeaStatusIcon.allValues))
|
||||
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.59")
|
||||
@Deprecated("please use ideastatusicon")
|
||||
val ideastatusbar = addOption(ToggleOption("ideastatusbar", "ideastatusbar", true))
|
||||
|
||||
@@ -390,11 +390,11 @@ object SelectModeOptionData {
|
||||
const val key = "key"
|
||||
const val cmd = "cmd"
|
||||
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||
@Deprecated("Please, use `idearefactormode` option")
|
||||
const val template = "template"
|
||||
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.57")
|
||||
@ApiStatus.ScheduledForRemoval(inVersion = "0.58")
|
||||
@Deprecated("Please, use `ideaselection`")
|
||||
const val refactoring = "refactoring"
|
||||
|
||||
|
@@ -52,7 +52,7 @@ import java.awt.event.ComponentListener;
|
||||
/**
|
||||
* This is used to enter ex commands such as searches and "colon" commands
|
||||
*/
|
||||
public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
public class ExEntryPanel extends JPanel {
|
||||
private static ExEntryPanel instance;
|
||||
private static ExEntryPanel instanceWithoutShortcuts;
|
||||
|
||||
@@ -78,10 +78,6 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
new ExShortcutKeyAction(this).registerCustomShortcutSet();
|
||||
}
|
||||
|
||||
// [VERSION UPDATE] 193+
|
||||
//noinspection deprecation
|
||||
LafManager.getInstance().addLafManagerListener(this);
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@@ -101,12 +97,20 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
return instanceWithoutShortcuts;
|
||||
}
|
||||
|
||||
public static boolean isInstanceWithShortcutsActive() {
|
||||
return instance != null;
|
||||
}
|
||||
|
||||
public static boolean isInstanceWithoutShortcutsActive() {
|
||||
return instanceWithoutShortcuts != null;
|
||||
}
|
||||
|
||||
public static void fullReset() {
|
||||
if (instance != null) {
|
||||
if (isInstanceWithShortcutsActive()) {
|
||||
instance.reset();
|
||||
instance = null;
|
||||
}
|
||||
if (instanceWithoutShortcuts != null) {
|
||||
if (isInstanceWithoutShortcutsActive()) {
|
||||
instanceWithoutShortcuts.reset();
|
||||
instanceWithoutShortcuts = null;
|
||||
}
|
||||
@@ -224,7 +228,7 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
|
||||
private void reset() {
|
||||
deactivate(false);
|
||||
LafManager.getInstance().removeLafManagerListener(this);
|
||||
JTextField.removeKeymap(ExTextField.KEYMAP_NAME);
|
||||
}
|
||||
|
||||
private void resetCaretOffset(@NotNull Editor editor) {
|
||||
@@ -367,12 +371,6 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
entry.handleKey(stroke);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lookAndFeelChanged(@NotNull LafManager source) {
|
||||
// Calls updateUI on this and child components
|
||||
IJSwingUtilities.updateComponentTreeUI(this);
|
||||
}
|
||||
|
||||
// Called automatically when the LAF is changed and the component is visible, and manually by the LAF listener handler
|
||||
@Override
|
||||
public void updateUI() {
|
||||
@@ -453,4 +451,18 @@ public class ExEntryPanel extends JPanel implements LafManagerListener {
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getInstance(ExEntryPanel.class.getName());
|
||||
|
||||
public static class LafListener implements LafManagerListener {
|
||||
@Override
|
||||
public void lookAndFeelChanged(@NotNull LafManager source) {
|
||||
if (!VimPlugin.isEnabled()) return;
|
||||
// Calls updateUI on this and child components
|
||||
if (ExEntryPanel.isInstanceWithShortcutsActive()) {
|
||||
IJSwingUtilities.updateComponentTreeUI(ExEntryPanel.getInstance());
|
||||
}
|
||||
if (ExEntryPanel.isInstanceWithoutShortcutsActive()) {
|
||||
IJSwingUtilities.updateComponentTreeUI(ExEntryPanel.getInstanceWithoutShortcuts());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,11 +18,11 @@
|
||||
|
||||
package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import com.intellij.ide.IdeTooltip;
|
||||
import com.intellij.ide.ui.LafManager;
|
||||
import com.intellij.ide.ui.LafManagerListener;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.util.IJSwingUtilities;
|
||||
@@ -46,7 +46,7 @@ import java.util.List;
|
||||
/**
|
||||
* This panel displays text in a <code>more</code> like window.
|
||||
*/
|
||||
public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
public class ExOutputPanel extends JPanel {
|
||||
private final @NotNull Editor myEditor;
|
||||
|
||||
private final @NotNull JLabel myLabel = new JLabel("more");
|
||||
@@ -86,16 +86,13 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
addKeyListener(moreKeyListener);
|
||||
myText.addKeyListener(moreKeyListener);
|
||||
|
||||
final Project project = editor.getProject();
|
||||
if (project != null) {
|
||||
// [VERSION UPDATE] 193+
|
||||
//noinspection deprecation
|
||||
LafManager.getInstance().addLafManagerListener(this, project);
|
||||
}
|
||||
|
||||
updateUI();
|
||||
}
|
||||
|
||||
public static boolean isPanelActive(@NotNull Editor editor) {
|
||||
return UserDataManager.getVimMorePanel(editor) != null;
|
||||
}
|
||||
|
||||
public static @NotNull ExOutputPanel getInstance(@NotNull Editor editor) {
|
||||
ExOutputPanel panel = UserDataManager.getVimMorePanel(editor);
|
||||
if (panel == null) {
|
||||
@@ -105,12 +102,6 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lookAndFeelChanged(@NotNull LafManager source) {
|
||||
// Calls updateUI on this and child components
|
||||
IJSwingUtilities.updateComponentTreeUI(this);
|
||||
}
|
||||
|
||||
// Called automatically when the LAF is changed and the component is visible, and manually by the LAF listener handler
|
||||
@Override
|
||||
public void updateUI() {
|
||||
@@ -366,4 +357,16 @@ public class ExOutputPanel extends JPanel implements LafManagerListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LafListener implements LafManagerListener {
|
||||
@Override
|
||||
public void lookAndFeelChanged(@NotNull LafManager source) {
|
||||
if (!VimPlugin.isEnabled()) return;
|
||||
// Calls updateUI on this and child components
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
if (!ExOutputPanel.isPanelActive(editor)) continue;
|
||||
IJSwingUtilities.updateComponentTreeUI(ExOutputPanel.getInstance(editor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.VimProjectService;
|
||||
import com.maddyhome.idea.vim.group.HistoryGroup;
|
||||
import com.maddyhome.idea.vim.helper.UiHelper;
|
||||
import kotlin.text.StringsKt;
|
||||
@@ -48,6 +49,8 @@ import static java.lang.Math.min;
|
||||
*/
|
||||
public class ExTextField extends JTextField {
|
||||
|
||||
public final static String KEYMAP_NAME = "ex";
|
||||
|
||||
ExTextField() {
|
||||
// We need to store this in a field, because we can't trust getCaret(), as it will return an instance of
|
||||
// ComposedTextCaret when working with dead keys or input methods
|
||||
@@ -112,7 +115,7 @@ public class ExTextField extends JTextField {
|
||||
}
|
||||
|
||||
setInputMap(WHEN_FOCUSED, new InputMap());
|
||||
Keymap map = addKeymap("ex", getKeymap());
|
||||
Keymap map = addKeymap(KEYMAP_NAME, getKeymap());
|
||||
loadKeymap(map, ExKeyBindings.INSTANCE.getBindings(), actions);
|
||||
map.setDefaultAction(new ExEditorKit.DefaultExKeyHandler());
|
||||
setKeymap(map);
|
||||
@@ -233,7 +236,8 @@ public class ExTextField extends JTextField {
|
||||
String disposeKey = vimExTextFieldDisposeKey + editor.hashCode();
|
||||
Project project = editor.getProject();
|
||||
if (Disposer.get(disposeKey) == null && project != null) {
|
||||
Disposer.register(project, () -> {
|
||||
VimProjectService parentDisposable = VimProjectService.getInstance(project);
|
||||
Disposer.register(parentDisposable, () -> {
|
||||
this.editor = null;
|
||||
this.context = null;
|
||||
}, disposeKey);
|
||||
|
134
src/com/maddyhome/idea/vim/ui/ReloadVimRc.kt
Normal file
134
src/com/maddyhome/idea/vim/ui/ReloadVimRc.kt
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.ui
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys
|
||||
import com.intellij.openapi.editor.Document
|
||||
import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarProvider
|
||||
import com.intellij.openapi.editor.toolbar.floating.FloatingToolbarComponent
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.util.containers.IntArrayList
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser
|
||||
import com.maddyhome.idea.vim.ui.ReloadFloatingToolbarActionGroup.Companion.ACTION_GROUP
|
||||
import icons.VimIcons
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* This file contains a "reload ~/.ideavimrc file" action functionality.
|
||||
* This is small floating action in the top right corner of the editor that appears if user edits configuration file.
|
||||
*
|
||||
* Here you can find:
|
||||
* - Simplified snapshot of config file
|
||||
* - Floating bar
|
||||
* - Action / action group
|
||||
*/
|
||||
|
||||
object VimRcFileState {
|
||||
// List of hashes of non-empty trimmed lines
|
||||
private val state = IntArrayList()
|
||||
|
||||
// ModificationStamp. Can be taken only from document. Doesn't play a big role, but can help speed up [equalTo]
|
||||
private var modificationStamp = 0L
|
||||
|
||||
// This is a pattern used in ideavimrc parsing for a long time. It removes all trailing/leading spaced and blank lines
|
||||
private val EOL_SPLIT_PATTERN = Pattern.compile(" *(\r\n|\n)+ *")
|
||||
|
||||
var filePath: String? = null
|
||||
|
||||
fun saveFileState(filePath: String, data: List<String>) {
|
||||
this.filePath = FileUtil.toSystemDependentName(filePath)
|
||||
|
||||
state.clear()
|
||||
for (line in data) {
|
||||
state.add(line.hashCode())
|
||||
}
|
||||
}
|
||||
|
||||
fun equalTo(document: Document): Boolean {
|
||||
val fileModificationStamp = document.modificationStamp
|
||||
if (fileModificationStamp == modificationStamp) return true
|
||||
|
||||
val stateSize = state.size()
|
||||
var i = 0
|
||||
VimScriptParser.readText(document.charsSequence).forEach { line ->
|
||||
if (i >= stateSize) return false
|
||||
if (state.get(i) != line.hashCode()) return false
|
||||
i++
|
||||
}
|
||||
if (i < stateSize) return false
|
||||
|
||||
modificationStamp = fileModificationStamp
|
||||
return true
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
fun clear() {
|
||||
state.clear()
|
||||
modificationStamp = 0
|
||||
filePath = null
|
||||
}
|
||||
}
|
||||
|
||||
class ReloadVimRc : DumbAwareAction() {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
|
||||
|
||||
// XXX: Actually, it worth to add e.presentation.description, but it doesn't work because of some reason
|
||||
val sameDoc = VimRcFileState.equalTo(editor.document)
|
||||
e.presentation.icon = if (sameDoc) VimIcons.IDEAVIM else AllIcons.Actions.BuildLoadChanges
|
||||
e.presentation.text = if (sameDoc) "No Changes" else "Reload"
|
||||
|
||||
e.presentation.isEnabledAndVisible = true
|
||||
}
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val editor = e.getData(PlatformDataKeys.EDITOR) ?: return
|
||||
|
||||
FileDocumentManager.getInstance().saveDocumentAsIs(editor.document)
|
||||
|
||||
VimPlugin.getInstance().executeIdeaVimRc()
|
||||
}
|
||||
}
|
||||
|
||||
class ReloadFloatingToolbar : AbstractFloatingToolbarProvider(ACTION_GROUP) {
|
||||
override val autoHideable: Boolean = false
|
||||
|
||||
override val priority: Int = 0
|
||||
}
|
||||
|
||||
class ReloadFloatingToolbarActionGroup : DefaultActionGroup() {
|
||||
override fun update(e: AnActionEvent) {
|
||||
val virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return
|
||||
|
||||
if (virtualFile.path == VimRcFileState.filePath) {
|
||||
e.getData(FloatingToolbarComponent.KEY)?.scheduleShow()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ACTION_GROUP = "IdeaVim.ReloadVimRc.group"
|
||||
}
|
||||
}
|
@@ -1,14 +1,16 @@
|
||||
package com.maddyhome.idea.vim.ui
|
||||
|
||||
import com.intellij.ide.lightEdit.LightEditCompatible
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.wm.StatusBar
|
||||
import com.intellij.openapi.wm.StatusBarWidget
|
||||
import com.intellij.openapi.wm.StatusBarWidgetProvider
|
||||
import com.intellij.openapi.wm.StatusBarWidgetFactory
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.openapi.wm.impl.status.EditorBasedWidget
|
||||
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
|
||||
import com.intellij.util.Consumer
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import com.maddyhome.idea.vim.helper.vimCommandState
|
||||
@@ -20,12 +22,14 @@ import java.awt.event.MouseEvent
|
||||
object ShowCmd {
|
||||
// https://github.com/vim/vim/blob/b376ace1aeaa7614debc725487d75c8f756dd773/src/vim.h#L1721
|
||||
private const val SHOWCMD_COLS = 10
|
||||
internal const val ID = "IdeaVim::ShowCmd"
|
||||
internal const val displayName = "IdeaVim showcmd"
|
||||
|
||||
fun update() {
|
||||
val windowManager = WindowManager.getInstance()
|
||||
ProjectManager.getInstance().openProjects.forEach {
|
||||
val statusBar = windowManager.getStatusBar(it)
|
||||
statusBar.updateWidget(ShowCmdStatusBarWidget.ID)
|
||||
statusBar.updateWidget(ID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,54 +50,67 @@ object ShowCmd {
|
||||
object ShowCmdOptionChangeListener: OptionChangeListener<Boolean> {
|
||||
override fun valueChange(oldValue: Boolean?, newValue: Boolean?) {
|
||||
ShowCmd.update()
|
||||
}
|
||||
}
|
||||
|
||||
class ShowCmdStatusBarWidget: StatusBarWidgetProvider {
|
||||
companion object {
|
||||
const val ID = "IdeaVim::ShowCmd"
|
||||
}
|
||||
|
||||
override fun getWidget(project: Project): StatusBarWidget = Widget(project)
|
||||
override fun getAnchor(): String = StatusBar.Anchors.before(StatusBar.StandardWidgets.POSITION_PANEL)
|
||||
|
||||
// `:help 'showcmd'`
|
||||
// Widget shows:
|
||||
// * Partial command, as it's being typed
|
||||
// * When selecting characters within a line, the number of characters
|
||||
// * Tabs are shown as one char
|
||||
// * If the number of bytes is different, this is also shown: "2-6"
|
||||
// * When selecting more than one line, the number of lines
|
||||
// * When selecting a block, the size in screen characters: {lines}x{columns}
|
||||
//
|
||||
// We only need to show partial commands, since the standard PositionPanel shows the other information already, with
|
||||
// the exception of "{lines}x{columns}" (it shows "x carets" instead)
|
||||
class Widget(project: Project)
|
||||
: EditorBasedWidget(project), StatusBarWidget.Multiframe, StatusBarWidget.TextPresentation {
|
||||
|
||||
override fun ID() = ID
|
||||
|
||||
// [VERSION UPDATE] After 193 use `getPresentation()`
|
||||
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||
override fun getPresentation(type: StatusBarWidget.PlatformType): StatusBarWidget.WidgetPresentation? = this
|
||||
|
||||
override fun getClickConsumer(): Consumer<MouseEvent>? = null
|
||||
|
||||
override fun getTooltipText(): String {
|
||||
var count = ShowCmd.getFullText(this.editor)
|
||||
if (!count.isBlank()) count = ": $count"
|
||||
return "IdeaVim showcmd$count"
|
||||
}
|
||||
|
||||
override fun getText(): String = ShowCmd.getWidgetText(editor)
|
||||
override fun getAlignment() = Component.CENTER_ALIGNMENT
|
||||
|
||||
// Multiframe#copy to show the widget on popped out editors
|
||||
override fun copy(): StatusBarWidget = Widget(myProject)
|
||||
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
// Update when changing selected editor
|
||||
myStatusBar?.updateWidget(ID)
|
||||
val extension = StatusBarWidgetFactory.EP_NAME.findExtension(ShowCmdStatusBarWidgetFactory::class.java) ?: return
|
||||
val projectManager = ProjectManager.getInstanceIfCreated() ?: return
|
||||
for (project in projectManager.openProjects) {
|
||||
val statusBarWidgetsManager = project.getService(StatusBarWidgetsManager::class.java) ?: continue
|
||||
statusBarWidgetsManager.updateWidget(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShowCmdStatusBarWidgetFactory : StatusBarWidgetFactory, LightEditCompatible {
|
||||
override fun getId() = ShowCmd.ID
|
||||
|
||||
override fun getDisplayName(): String = ShowCmd.displayName
|
||||
|
||||
override fun disposeWidget(widget: StatusBarWidget) {}
|
||||
|
||||
override fun isAvailable(project: Project): Boolean = OptionsManager.showcmd.isSet
|
||||
|
||||
override fun createWidget(project: Project): StatusBarWidget = Widget(project)
|
||||
|
||||
override fun canBeEnabledOn(statusBar: StatusBar): Boolean = true
|
||||
|
||||
// Should be configured via `set showcmd`
|
||||
override fun isConfigurable(): Boolean = false
|
||||
}
|
||||
|
||||
// `:help 'showcmd'`
|
||||
// Widget shows:
|
||||
// * Partial command, as it's being typed
|
||||
// * When selecting characters within a line, the number of characters
|
||||
// * Tabs are shown as one char
|
||||
// * If the number of bytes is different, this is also shown: "2-6"
|
||||
// * When selecting more than one line, the number of lines
|
||||
// * When selecting a block, the size in screen characters: {lines}x{columns}
|
||||
//
|
||||
// We only need to show partial commands, since the standard PositionPanel shows the other information already, with
|
||||
// the exception of "{lines}x{columns}" (it shows "x carets" instead)
|
||||
class Widget(project: Project) : EditorBasedWidget(project), StatusBarWidget.Multiframe, StatusBarWidget.TextPresentation {
|
||||
|
||||
override fun ID() = ShowCmd.ID
|
||||
|
||||
override fun getPresentation(): StatusBarWidget.WidgetPresentation? = this
|
||||
|
||||
override fun getClickConsumer(): Consumer<MouseEvent>? = null
|
||||
|
||||
override fun getTooltipText(): String {
|
||||
var count = ShowCmd.getFullText(this.editor)
|
||||
if (!count.isBlank()) count = ": $count"
|
||||
return "${ShowCmd.displayName}$count"
|
||||
}
|
||||
|
||||
override fun getText(): String = ShowCmd.getWidgetText(editor)
|
||||
|
||||
override fun getAlignment() = Component.CENTER_ALIGNMENT
|
||||
|
||||
// Multiframe#copy to show the widget on popped out editors
|
||||
override fun copy(): StatusBarWidget = Widget(myProject)
|
||||
|
||||
override fun selectionChanged(event: FileEditorManagerEvent) {
|
||||
// Update when changing selected editor
|
||||
myStatusBar?.updateWidget(ShowCmd.ID)
|
||||
}
|
||||
}
|
||||
|
@@ -16,12 +16,13 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim
|
||||
package com.maddyhome.idea.vim.ui
|
||||
|
||||
import com.intellij.ide.BrowserUtil
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.lightEdit.LightEditCompatible
|
||||
import com.intellij.ide.plugins.InstalledPluginsState
|
||||
import com.intellij.ide.plugins.PluginManager
|
||||
import com.intellij.ide.plugins.PluginManagerCore
|
||||
import com.intellij.ide.plugins.PluginManagerMain
|
||||
import com.intellij.ide.plugins.RepositoryHelper
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
@@ -35,6 +36,7 @@ import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.progress.Task
|
||||
import com.intellij.openapi.project.DumbAwareAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.popup.JBPopupFactory
|
||||
import com.intellij.openapi.ui.popup.ListPopup
|
||||
@@ -45,46 +47,81 @@ import com.intellij.openapi.updateSettings.impl.UpdateSettings
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.openapi.wm.StatusBar
|
||||
import com.intellij.openapi.wm.StatusBarWidget
|
||||
import com.intellij.openapi.wm.StatusBarWidgetProvider
|
||||
import com.intellij.openapi.wm.StatusBarWidgetFactory
|
||||
import com.intellij.openapi.wm.WindowManager
|
||||
import com.intellij.openapi.wm.impl.status.widget.StatusBarWidgetsManager
|
||||
import com.intellij.ui.awt.RelativePoint
|
||||
import com.intellij.util.Consumer
|
||||
import com.intellij.util.text.VersionComparatorUtil
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
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.ui.VimEmulationConfigurable
|
||||
import icons.VimIcons
|
||||
import java.awt.Point
|
||||
import java.awt.event.MouseEvent
|
||||
import javax.swing.Icon
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
private class StatusBarIconProvider : StatusBarWidgetProvider {
|
||||
override fun getWidget(project: Project): VimStatusBar? {
|
||||
const val STATUS_BAR_ICON_ID = "IdeaVim-Icon"
|
||||
const val STATUS_BAR_DISPLAY_NAME = "IdeaVim"
|
||||
|
||||
class StatusBarIconFactory : StatusBarWidgetFactory, LightEditCompatible {
|
||||
|
||||
override fun getId(): String = STATUS_BAR_ICON_ID
|
||||
|
||||
override fun getDisplayName(): String = STATUS_BAR_DISPLAY_NAME
|
||||
|
||||
override fun disposeWidget(widget: StatusBarWidget) {}
|
||||
|
||||
override fun isAvailable(project: Project): Boolean {
|
||||
@Suppress("DEPRECATION")
|
||||
if (!OptionsManager.ideastatusbar.isSet) return null
|
||||
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.disabled) return null
|
||||
return VimStatusBar
|
||||
if (!OptionsManager.ideastatusbar.isSet) return false
|
||||
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.disabled) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun createWidget(project: Project): StatusBarWidget {
|
||||
OptionsManager.ideastatusicon.addOptionChangeListener { _, _ -> updateAll() }
|
||||
return VimStatusBar()
|
||||
}
|
||||
|
||||
override fun canBeEnabledOn(statusBar: StatusBar): Boolean = true
|
||||
|
||||
/* Use can configure this icon using ideastatusicon option, but we should still keep the option to remove
|
||||
* the icon via IJ because this option is hard to discover */
|
||||
override fun isConfigurable(): Boolean = true
|
||||
|
||||
private fun updateAll() {
|
||||
val projectManager = ProjectManager.getInstanceIfCreated() ?: return
|
||||
for (project in projectManager.openProjects) {
|
||||
val statusBarWidgetsManager = project.getService(StatusBarWidgetsManager::class.java) ?: continue
|
||||
statusBarWidgetsManager.updateWidget(this)
|
||||
}
|
||||
|
||||
updateIcon()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun updateIcon() {
|
||||
val projectManager = ProjectManager.getInstanceIfCreated() ?: return
|
||||
for (project in projectManager.openProjects) {
|
||||
val statusBar = WindowManager.getInstance().getStatusBar(project) ?: continue
|
||||
statusBar.updateWidget(STATUS_BAR_ICON_ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
||||
class VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
||||
|
||||
init {
|
||||
OptionsManager.ideastatusicon.addOptionChangeListener { _, _ -> this.update() }
|
||||
}
|
||||
override fun ID(): String = STATUS_BAR_ICON_ID
|
||||
|
||||
private var statusBar: StatusBar? = null
|
||||
|
||||
override fun ID(): String = "IdeaVim-Icon"
|
||||
|
||||
override fun install(statusBar: StatusBar) {
|
||||
this.statusBar = statusBar
|
||||
}
|
||||
override fun install(statusBar: StatusBar) {}
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
override fun getTooltipText() = "IdeaVim"
|
||||
override fun getTooltipText() = STATUS_BAR_DISPLAY_NAME
|
||||
|
||||
override fun getIcon(): Icon {
|
||||
if (OptionsManager.ideastatusicon.value == IdeaStatusIcon.gray) return VimIcons.IDEAVIM_DISABLED
|
||||
@@ -100,13 +137,7 @@ object VimStatusBar : StatusBarWidget, StatusBarWidget.IconPresentation {
|
||||
popup.show(RelativePoint(component, at))
|
||||
}
|
||||
|
||||
// TODO [VERSION UPDATE] After 193 use `getPresentation()`
|
||||
@Suppress("DEPRECATION", "UnstableApiUsage")
|
||||
override fun getPresentation(type: StatusBarWidget.PlatformType): StatusBarWidget.WidgetPresentation? = this
|
||||
|
||||
fun update() {
|
||||
statusBar?.updateWidget(this.ID())
|
||||
}
|
||||
override fun getPresentation(): StatusBarWidget.WidgetPresentation? = this
|
||||
}
|
||||
|
||||
class VimActions : DumbAwareAction() {
|
||||
@@ -130,7 +161,7 @@ private object VimActionsPopup {
|
||||
fun getPopup(dataContext: DataContext): ListPopup {
|
||||
val actions = getActions()
|
||||
val popup = JBPopupFactory.getInstance()
|
||||
.createActionGroupPopup("IdeaVim", actions,
|
||||
.createActionGroupPopup(STATUS_BAR_DISPLAY_NAME, actions,
|
||||
dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false,
|
||||
VimActions.actionPlace)
|
||||
popup.setAdText("Version ${VimPlugin.getVersion()}", SwingConstants.CENTER)
|
||||
@@ -207,8 +238,6 @@ private object JoinEap : DumbAwareAction() {
|
||||
|
||||
val pluginRef = Ref.create<PluginDownloader>()
|
||||
|
||||
// [VERSION UPDATE] 193+ remove suppressing
|
||||
@Suppress("UnstableApiUsage")
|
||||
object : Task.Backgroundable(null, "Checking for IdeaVim EAP version", true) {
|
||||
override fun run(indicator: ProgressIndicator) {
|
||||
val downloaders = mutableListOf<PluginDownloader>()
|
||||
@@ -226,14 +255,12 @@ private object JoinEap : DumbAwareAction() {
|
||||
pluginRef.set(plugin)
|
||||
}
|
||||
|
||||
// [VERSION UPDATE] 193+ remove suppressing
|
||||
@Suppress("MissingRecentApi", "UnstableApiUsage")
|
||||
override fun onSuccess() {
|
||||
val downloader: PluginDownloader = pluginRef.get() ?: run {
|
||||
notificator.notifySubscribedToEap()
|
||||
return
|
||||
}
|
||||
val currentVersion = PluginManager.getPlugin(VimPlugin.getPluginId())?.version ?: ""
|
||||
val currentVersion = PluginManagerCore.getPlugin(VimPlugin.getPluginId())?.version ?: ""
|
||||
if (VersionComparatorUtil.compare(downloader.pluginVersion, currentVersion) <= 0) {
|
||||
notificator.notifySubscribedToEap()
|
||||
return
|
||||
@@ -241,6 +268,7 @@ private object JoinEap : DumbAwareAction() {
|
||||
|
||||
val version = downloader.pluginVersion
|
||||
val message = "Do you want to install the EAP version of IdeaVim?"
|
||||
|
||||
@Suppress("MoveVariableDeclarationIntoWhen")
|
||||
val res = Messages.showYesNoCancelDialog(project, message, "IdeaVim $version", null)
|
||||
when (res) {
|
@@ -1,13 +1,18 @@
|
||||
package icons;
|
||||
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.ui.IconManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public interface VimIcons {
|
||||
Icon IDEAVIM = IconLoader.getIcon("/icons/ideavim.svg");
|
||||
Icon IDEAVIM_DISABLED = IconLoader.getIcon("/icons/ideavim_disabled.svg");
|
||||
Icon GITHUB = IconLoader.getIcon("/icons/github.svg");
|
||||
Icon TWITTER = IconLoader.getIcon("/icons/twitter.svg");
|
||||
Icon YOUTRACK = IconLoader.getIcon("/icons/youtrack.svg");
|
||||
public final class VimIcons {
|
||||
private static @NotNull Icon load(@NotNull String path) {
|
||||
return IconManager.getInstance().getIcon(path, VimIcons.class);
|
||||
}
|
||||
|
||||
public static final @NotNull Icon IDEAVIM = load("/icons/ideavim.svg");
|
||||
public static final @NotNull Icon IDEAVIM_DISABLED = load("/icons/ideavim_disabled.svg");
|
||||
public static final @NotNull Icon GITHUB = load("/icons/github.svg");
|
||||
public static final @NotNull Icon TWITTER = load("/icons/twitter.svg");
|
||||
public static final @NotNull Icon YOUTRACK = load("/icons/youtrack.svg");
|
||||
}
|
||||
|
@@ -120,3 +120,7 @@ Expression is wrapped, vim in insert mode.
|
||||
|
||||

|
||||
|
||||
## #10 [Last run: 2020-05-06]
|
||||
|
||||
_Action:_
|
||||
Test dynamic plugin loading/unloading.
|
@@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.group.visual.VimVisualTimer;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper;
|
||||
import com.maddyhome.idea.vim.helper.TestInputModel;
|
||||
@@ -57,6 +58,10 @@ public abstract class JavaVimTestCase extends JavaCodeInsightFixtureTestCase {
|
||||
protected void tearDown() throws Exception {
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
VimScriptGlobalEnvironment.getInstance().getVariables().clear();
|
||||
Timer swingTimer = VimVisualTimer.INSTANCE.getSwingTimer();
|
||||
if (swingTimer != null) {
|
||||
swingTimer.stop();
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.common.Mark;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
@@ -173,4 +174,17 @@ public class MarkTest extends VimTestCase {
|
||||
"four five\n");
|
||||
assertOffset(14);
|
||||
}
|
||||
|
||||
// |i| |`]|
|
||||
public void testGotoLastChangePositionEnd() {
|
||||
doTest(parseKeys("yiw", "P", "gg", "`]"), "one two\n" +
|
||||
"<caret>three\n" +
|
||||
"four five\n",
|
||||
"one two\n" +
|
||||
"thre<caret>ethree\n" +
|
||||
"four five\n",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -83,4 +83,29 @@ class PutViaIdeaTest : VimTestCase() {
|
||||
typeText(StringHelper.parseKeys("ve", "p"))
|
||||
assertEquals(sizeBefore, CopyPasteManager.getInstance().allContents.size)
|
||||
}
|
||||
|
||||
fun `test insert block with newline`() {
|
||||
val before = """
|
||||
A Discovery
|
||||
$c
|
||||
I found it in a legendary land
|
||||
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, before rangeOf "\nI found it in a legendary land\n", SelectionType.CHARACTER_WISE, false)
|
||||
|
||||
typeText(StringHelper.parseKeys("p"))
|
||||
val after = """
|
||||
A Discovery
|
||||
|
||||
I found it in a legendary land
|
||||
|
||||
I found it in a legendary land
|
||||
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent()
|
||||
myFixture.checkResult(after)
|
||||
}
|
||||
}
|
@@ -20,7 +20,6 @@ package org.jetbrains.plugins.ideavim.action.copy
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import junit.framework.Assert
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class YankLineActionTest : VimTestCase() {
|
||||
@@ -32,6 +31,6 @@ class YankLineActionTest : VimTestCase() {
|
||||
configureByText(before)
|
||||
typeText(parseKeys("\"4yy"))
|
||||
val register = VimPlugin.getRegister().getRegister('4')!!
|
||||
Assert.assertEquals("I found it in a legendary land\n", register.text)
|
||||
assertEquals("I found it in a legendary land\n", register.text)
|
||||
}
|
||||
}
|
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jetbrains.plugins.ideavim.ex.handler
|
||||
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
/**
|
||||
* @author John Weigel
|
||||
*/
|
||||
class BufferHandlerTest : VimTestCase() {
|
||||
fun testBufferActionByNumber() {
|
||||
configureByFileName("aaa.txt")
|
||||
configureByFileName("bbb.txt")
|
||||
typeText(commandToKeys("buffer 2"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBufferActionByName() {
|
||||
configureByFileName("aaa.txt")
|
||||
configureByFileName("bbb.txt")
|
||||
typeText(commandToKeys("buffer aaa"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBufferActionWithNoArg() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("buffer"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBufferActionWithInvalidBufferNumber() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("buffer 999"))
|
||||
|
||||
assertPluginError(true)
|
||||
}
|
||||
|
||||
fun testBufferActionWithInvalidBufferName() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("buffer invalidbuffer"))
|
||||
|
||||
assertPluginError(true)
|
||||
}
|
||||
|
||||
fun testBufferActionWithModifications() {
|
||||
configureByFileName("aaa.txt")
|
||||
configureByFileName("bbb.txt")
|
||||
typeText(StringHelper.parseKeys("aa<esc>:buffer aaa<enter>"))
|
||||
|
||||
assertPluginError(true)
|
||||
}
|
||||
|
||||
fun testBufferActionWithModificationsOverride() {
|
||||
configureByFileName("aaa.txt")
|
||||
configureByFileName("bbb.txt")
|
||||
typeText(StringHelper.parseKeys("aa<esc>:buffer! aaa<enter>"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBufferActionWithMultipleMatches() {
|
||||
configureByFileName("aaa.txt")
|
||||
configureByFileName("aaa2.txt")
|
||||
typeText(commandToKeys("buffer aaa"))
|
||||
|
||||
assertPluginError(true)
|
||||
}
|
||||
|
||||
fun testBufAction() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("buf 1"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBuAction() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("bu 1"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
|
||||
fun testBAction() {
|
||||
configureByText("\n")
|
||||
typeText(commandToKeys("b 1"))
|
||||
|
||||
assertPluginError(false)
|
||||
}
|
||||
}
|
@@ -232,7 +232,7 @@ n ,f <Plug>Foo
|
||||
// VIM-676 |:map|
|
||||
fun testBackspaceCharacterInVimRc() {
|
||||
configureByText("\n")
|
||||
VimScriptParser.executeText("inoremap # X\u0008#\n")
|
||||
VimScriptParser.executeText(VimScriptParser.readText("inoremap # X\u0008#\n"))
|
||||
typeText(StringHelper.parseKeys("i", "#", "<Esc>"))
|
||||
myFixture.checkResult("#\n")
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
@@ -247,7 +247,7 @@ n ,f <Plug>Foo
|
||||
bar
|
||||
|
||||
""".trimIndent())
|
||||
VimScriptParser.executeText("map \u0018i dd\n")
|
||||
VimScriptParser.executeText(VimScriptParser.readText("map \u0018i dd\n"))
|
||||
typeText(StringHelper.parseKeys("i", "#", "<Esc>"))
|
||||
myFixture.checkResult("""
|
||||
#foo
|
||||
@@ -264,7 +264,7 @@ n ,f <Plug>Foo
|
||||
// VIM-679 |:map|
|
||||
fun testBarCtrlVEscaped() {
|
||||
configureByText("${c}foo\n")
|
||||
VimScriptParser.executeText("imap a b \u0016|\u0016| c |\n")
|
||||
VimScriptParser.executeText(listOf("imap a b \u0016|\u0016| c |\n"))
|
||||
typeText(StringHelper.parseKeys("ia"))
|
||||
myFixture.checkResult("b || c foo\n")
|
||||
}
|
||||
@@ -272,7 +272,7 @@ n ,f <Plug>Foo
|
||||
// VIM-679 |:map|
|
||||
fun testCtrlMCtrlLAsNewLine() {
|
||||
configureByText("${c}foo\n")
|
||||
VimScriptParser.executeText("map A :%s/foo/bar/g\r\u000C\n")
|
||||
VimScriptParser.executeText(listOf("map A :%s/foo/bar/g\r\u000C\n"))
|
||||
typeText(StringHelper.parseKeys("A"))
|
||||
myFixture.checkResult("bar\n")
|
||||
}
|
||||
@@ -280,7 +280,7 @@ n ,f <Plug>Foo
|
||||
// VIM-700 |:map|
|
||||
fun testRemappingZero() {
|
||||
configureByText("x${c}yz\n")
|
||||
VimScriptParser.executeText("map 0 ~")
|
||||
VimScriptParser.executeText(listOf("map 0 ~"))
|
||||
typeText(StringHelper.parseKeys("0"))
|
||||
myFixture.checkResult("xYz\n")
|
||||
}
|
||||
@@ -288,7 +288,7 @@ n ,f <Plug>Foo
|
||||
// VIM-700 |:map|
|
||||
fun testRemappingZeroStillAllowsZeroToBeUsedInCount() {
|
||||
configureByText("a${c}bcdefghijklmnop\n")
|
||||
VimScriptParser.executeText("map 0 ^")
|
||||
VimScriptParser.executeText(listOf("map 0 ^"))
|
||||
typeText(StringHelper.parseKeys("10~"))
|
||||
myFixture.checkResult("aBCDEFGHIJKlmnop\n")
|
||||
}
|
||||
@@ -296,7 +296,7 @@ n ,f <Plug>Foo
|
||||
// VIM-700 |:map|
|
||||
fun testRemappingDeleteOverridesRemovingLastDigitFromCount() {
|
||||
configureByText("a${c}bcdefghijklmnop\n")
|
||||
VimScriptParser.executeText("map <Del> ~")
|
||||
VimScriptParser.executeText(listOf("map <Del> ~"))
|
||||
typeText(StringHelper.parseKeys("10<Del>"))
|
||||
myFixture.checkResult("aBCDEFGHIJKlmnop\n")
|
||||
}
|
||||
|
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.extension.exchange
|
||||
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.extension.exchange.VimExchangeExtension
|
||||
import com.maddyhome.idea.vim.helper.StringHelper
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class VimExchangeExtensionTest : VimTestCase() {
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
enableExtensions("exchange")
|
||||
}
|
||||
|
||||
// |cx|
|
||||
fun `test exchange words left to right`() {
|
||||
doTest(StringHelper.parseKeys("cxe", "w", "cxe"),
|
||||
"The quick ${c}brown fox catch over the lazy dog",
|
||||
"The quick fox ${c}brown catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |cx|
|
||||
fun `test exchange words dot repeat`() {
|
||||
doTest(StringHelper.parseKeys("cxiw", "w", "."),
|
||||
"The quick ${c}brown fox catch over the lazy dog",
|
||||
"The quick fox ${c}brown catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |cx|
|
||||
fun `test exchange words right to left`() {
|
||||
doTest(StringHelper.parseKeys("cxe", "b", "cxe"),
|
||||
"The quick brown ${c}fox catch over the lazy dog",
|
||||
"The quick ${c}fox brown catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |cx|
|
||||
fun `test exchange words right to left with dot`() {
|
||||
doTest(StringHelper.parseKeys("cxe", "b", "."),
|
||||
"The quick brown ${c}fox catch over the lazy dog",
|
||||
"The quick ${c}fox brown catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |X|
|
||||
fun `test visual exchange words left to right`() {
|
||||
doTest(StringHelper.parseKeys("veX", "w", "veX"),
|
||||
"The quick ${c}brown fox catch over the lazy dog",
|
||||
"The quick fox ${c}brown catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |X|
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter = "The ${c}brown catch over the lazy dog",
|
||||
shouldBeFixed = true
|
||||
)
|
||||
fun `test visual exchange words from inside`() {
|
||||
doTest(StringHelper.parseKeys("veX", "b", "v3e", "X"),
|
||||
"The quick ${c}brown fox catch over the lazy dog",
|
||||
"The brow${c}n catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |X|
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter = "The brown ${c}catch over the lazy dog",
|
||||
shouldBeFixed = true
|
||||
)
|
||||
fun `test visual exchange words from outside`() {
|
||||
doTest(StringHelper.parseKeys("v3e", "X", "w", "veX"),
|
||||
"The ${c}quick brown fox catch over the lazy dog",
|
||||
"The brow${c}n catch over the lazy dog",
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |cxx|
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter =
|
||||
"""The quick
|
||||
catch over
|
||||
${c}brown fox
|
||||
the lazy dog
|
||||
""",
|
||||
shouldBeFixed = true
|
||||
)
|
||||
fun `test exchange lines top down`() {
|
||||
doTest(StringHelper.parseKeys("cxx", "j", "cxx"),
|
||||
"""The quick
|
||||
brown ${c}fox
|
||||
catch over
|
||||
the lazy dog""".trimIndent(),
|
||||
"""The quick
|
||||
${c}catch over
|
||||
brown fox
|
||||
the lazy dog""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
// |cxx|
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter =
|
||||
"""The quick
|
||||
catch over
|
||||
${c}brown fox
|
||||
the lazy dog
|
||||
""",
|
||||
shouldBeFixed = true
|
||||
)
|
||||
fun `test exchange lines top down with dot`() {
|
||||
doTest(StringHelper.parseKeys("cxx", "j", "."),
|
||||
"""The quick
|
||||
brown ${c}fox
|
||||
catch over
|
||||
the lazy dog""".trimIndent(),
|
||||
"""The quick
|
||||
${c}catch over
|
||||
brown fox
|
||||
the lazy dog""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter = """
|
||||
The quick
|
||||
brown thecatch over
|
||||
fox
|
||||
lazy dog
|
||||
"""
|
||||
)
|
||||
fun `test exchange to the line end`() {
|
||||
doTest(StringHelper.parseKeys("v$", "X", "jj^ve", "X"),
|
||||
"""The quick
|
||||
brown ${c}fox
|
||||
catch over
|
||||
the lazy dog""".trimIndent(),
|
||||
"""The quick
|
||||
brown the
|
||||
catch over
|
||||
fox lazy dog""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers(
|
||||
originalVimAfter =
|
||||
"""
|
||||
catch over
|
||||
the lazy dog
|
||||
${c}The quick
|
||||
brown fox
|
||||
""",
|
||||
shouldBeFixed = true
|
||||
)
|
||||
fun `test exchange visual lines`() {
|
||||
doTest(StringHelper.parseKeys("Vj", "X", "jj", "Vj", "X"),
|
||||
"""
|
||||
The ${c}quick
|
||||
brown fox
|
||||
catch over
|
||||
the lazy dog
|
||||
""".trimIndent(),
|
||||
"""
|
||||
${c}catch over
|
||||
the lazy dog
|
||||
The quick
|
||||
brown fox
|
||||
|
||||
""".trimIndent(),
|
||||
CommandState.Mode.COMMAND,
|
||||
CommandState.SubMode.NONE
|
||||
)
|
||||
}
|
||||
|
||||
fun `test visual char highlighter`() {
|
||||
val before = """
|
||||
The ${c}quick
|
||||
brown fox
|
||||
catch over
|
||||
the lazy dog
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("vlll", "X"))
|
||||
|
||||
assertHighlighter(4, 8, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test visual line highdhitligthhter`() {
|
||||
val before = """
|
||||
The ${c}quick
|
||||
brown fox
|
||||
catch over
|
||||
the lazy dog
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("Vj", "X"))
|
||||
|
||||
assertHighlighter(4, 15, HighlighterTargetArea.LINES_IN_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test till the line end highlighter`() {
|
||||
val before = """
|
||||
The ${c}quick
|
||||
brown fox
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("v$", "X"))
|
||||
|
||||
assertHighlighter(4, 10, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test pre line end highlighter`() {
|
||||
val before = """
|
||||
The ${c}quick
|
||||
brown fox
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("v\$h", "X"))
|
||||
|
||||
assertHighlighter(4, 9, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test pre pre line end highlighter`() {
|
||||
val before = """
|
||||
The ${c}quick
|
||||
brown fox
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("v\$hh", "X"))
|
||||
|
||||
assertHighlighter(4, 8, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test to file end highlighter`() {
|
||||
val before = """
|
||||
The quick
|
||||
brown ${c}fox
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("v\$", "X"))
|
||||
|
||||
assertHighlighter(16, 19, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
fun `test to file end with new line highlighter`() {
|
||||
val before = """
|
||||
The quick
|
||||
brown ${c}fox
|
||||
|
||||
""".trimIndent()
|
||||
configureByText(before)
|
||||
typeText(StringHelper.parseKeys("v\$", "X"))
|
||||
|
||||
assertHighlighter(16, 20, HighlighterTargetArea.EXACT_RANGE)
|
||||
|
||||
// Exit vim-exchange
|
||||
exitExchange()
|
||||
}
|
||||
|
||||
private fun exitExchange() {
|
||||
typeText(StringHelper.parseKeys("cxc"))
|
||||
}
|
||||
|
||||
private fun assertHighlighter(start: Int, end: Int, area: HighlighterTargetArea) {
|
||||
val currentExchange = myFixture.editor.getUserData(VimExchangeExtension.EXCHANGE_KEY)!!
|
||||
val highlighter = currentExchange.getHighlighter()!!
|
||||
assertEquals(start, highlighter.startOffset)
|
||||
assertEquals(end, highlighter.endOffset)
|
||||
assertEquals(area, highlighter.targetArea)
|
||||
}
|
||||
}
|
@@ -24,7 +24,6 @@ import com.maddyhome.idea.vim.command.SelectionType
|
||||
import com.maddyhome.idea.vim.common.TextRange
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
|
||||
import junit.framework.Assert
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
import org.jetbrains.plugins.ideavim.rangeOf
|
||||
|
||||
@@ -51,7 +50,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult("one on${c}e three")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test empty text`() {
|
||||
@@ -78,10 +77,10 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"ayiw", "w", "\"agriw"))
|
||||
myFixture.checkResult("one two tw${c}o four")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
typeText(parseKeys("w", "griw"))
|
||||
myFixture.checkResult("one two two tw${c}o")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test replace use clipboard register`() {
|
||||
@@ -90,7 +89,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
configureByText(text)
|
||||
typeText(parseKeys("\"+yiw", "w", "\"+griw", "w", "\"+griw"))
|
||||
myFixture.checkResult("one two two tw${c}o")
|
||||
Assert.assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("two", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test replace use wrong register`() {
|
||||
@@ -154,7 +153,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("3griw"))
|
||||
myFixture.checkResult("one on${c}e four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
@VimBehaviorDiffers("one on${c}e on${c}e four")
|
||||
@@ -165,7 +164,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw"))
|
||||
myFixture.checkResult("one two one four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test dot repeat`() {
|
||||
@@ -175,7 +174,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
VimPlugin.getRegister().storeText(myFixture.editor, text rangeOf "one", SelectionType.CHARACTER_WISE, false)
|
||||
typeText(parseKeys("griw", "w", "."))
|
||||
myFixture.checkResult("one one on${c}e four")
|
||||
Assert.assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("one", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
// --------------------------------------- grr --------------------------
|
||||
@@ -197,7 +196,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
Assert.assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
}
|
||||
|
||||
fun `test line replace with line`() {
|
||||
@@ -347,7 +346,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
|
||||
where it was settled on some sodden sand
|
||||
hard by the torrent of a mountain pass.
|
||||
""".trimIndent())
|
||||
Assert.assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertEquals("legendary", VimPlugin.getRegister().lastRegister?.text)
|
||||
assertMode(CommandState.Mode.COMMAND)
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,9 @@
|
||||
|
||||
package org.jetbrains.plugins.ideavim.group.visual
|
||||
|
||||
import com.intellij.codeInsight.template.TemplateManager
|
||||
import com.intellij.codeInsight.template.impl.ConstantNode
|
||||
import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
@@ -674,8 +677,6 @@ class IdeaVisualControlTest : VimOptionTestCase(SelectModeOptionData.name) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
// [VERSION UPDATE] 191+ Open this test
|
||||
@VimOptionTestConfiguration(VimTestOption(SelectModeOptionData.name, VimTestOptionType.LIST, [""]))
|
||||
fun `test control selection from line to char visual modes in keep mode`() {
|
||||
configureByText("""
|
||||
@@ -707,5 +708,4 @@ class IdeaVisualControlTest : VimOptionTestCase(SelectModeOptionData.name) {
|
||||
createdTemplate.addVariable(ConstantNode("1"), true)
|
||||
templateManager.startTemplate(myFixture.editor, createdTemplate)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@@ -26,10 +26,8 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.injected.editor.EditorWindow
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.TestModeFlags
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestUtil.doInlineRename
|
||||
import com.maddyhome.idea.vim.command.CommandState
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
@@ -55,10 +53,7 @@ class TemplateTest : VimOptionTestCase(IdeaRefactorMode.name) {
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
// TODO: 24.10.2019 [VERSION UPDATE] 2019.1
|
||||
// TemplateManagerImpl.setTemplateTesting(myFixture.testRootDisposable)
|
||||
@Suppress("DEPRECATION", "UNCHECKED_CAST")
|
||||
TestModeFlags.set(Key.findKeyByName("TemplateTesting") as Key<Boolean>, true, myFixture.testRootDisposable)
|
||||
TemplateManagerImpl.setTemplateTesting(myFixture.testRootDisposable)
|
||||
}
|
||||
|
||||
@VimOptionDefaultAll
|
||||
|
113
test/org/jetbrains/plugins/ideavim/ui/ReloadVimRcTest.kt
Normal file
113
test/org/jetbrains/plugins/ideavim/ui/ReloadVimRcTest.kt
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2020 The IdeaVim authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.jetbrains.plugins.ideavim.ui
|
||||
|
||||
import com.intellij.mock.MockEditorFactory
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser
|
||||
import com.maddyhome.idea.vim.ui.VimRcFileState
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class ReloadVimRcTest : VimTestCase() {
|
||||
val editorFactory = MockEditorFactory()
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
VimRcFileState.clear()
|
||||
}
|
||||
|
||||
fun `test equalTo`() {
|
||||
val file = """
|
||||
map x y
|
||||
""".trimIndent()
|
||||
|
||||
val lines = convertFileToLines(file)
|
||||
VimRcFileState.saveFileState("", lines)
|
||||
|
||||
val document = editorFactory.createDocument(file)
|
||||
|
||||
assertTrue(VimRcFileState.equalTo(document))
|
||||
}
|
||||
|
||||
fun `test equalTo with whitespaces`() {
|
||||
val s = " " // Just to see whitespaces in the following code
|
||||
val origFile = """
|
||||
map x y
|
||||
set myPlugin
|
||||
map z t
|
||||
""".trimIndent()
|
||||
val changedFile = """
|
||||
map x y
|
||||
set myPlugin$s$s$s$s$s$s
|
||||
|
||||
|
||||
map z t
|
||||
""".trimIndent()
|
||||
|
||||
val lines = convertFileToLines(origFile)
|
||||
VimRcFileState.saveFileState("", lines)
|
||||
|
||||
val document = editorFactory.createDocument(changedFile)
|
||||
|
||||
assertTrue(VimRcFileState.equalTo(document))
|
||||
}
|
||||
|
||||
fun `test equalTo add line`() {
|
||||
val s = " " // Just to see whitespaces in the following code
|
||||
val origFile = """
|
||||
map x y
|
||||
set myPlugin
|
||||
""".trimIndent()
|
||||
val changedFile = """
|
||||
map x y
|
||||
set myPlugin
|
||||
map z t
|
||||
""".trimIndent()
|
||||
|
||||
val lines = convertFileToLines(origFile)
|
||||
VimRcFileState.saveFileState("", lines)
|
||||
|
||||
val document = editorFactory.createDocument(changedFile)
|
||||
|
||||
assertFalse(VimRcFileState.equalTo(document))
|
||||
}
|
||||
|
||||
fun `test equalTo remove line`() {
|
||||
val s = " " // Just to see whitespaces in the following code
|
||||
val origFile = """
|
||||
map x y
|
||||
set myPlugin
|
||||
""".trimIndent()
|
||||
val changedFile = """
|
||||
map x y
|
||||
""".trimIndent()
|
||||
|
||||
val lines = convertFileToLines(origFile)
|
||||
VimRcFileState.saveFileState("", lines)
|
||||
|
||||
val document = editorFactory.createDocument(changedFile)
|
||||
|
||||
assertFalse(VimRcFileState.equalTo(document))
|
||||
}
|
||||
|
||||
private fun convertFileToLines(file: String): List<String> {
|
||||
val res = ArrayList<String>()
|
||||
file.split("\n").forEach { VimScriptParser.lineProcessor(it, res) }
|
||||
return res
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user