mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-17 16:31:45 +02:00
Compare commits
202 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0da8625f30 | ||
![]() |
4cc784eb95 | ||
![]() |
ee3bec9eed | ||
![]() |
24788ac86f | ||
![]() |
9af3c732b5 | ||
![]() |
e5873fe5f9 | ||
![]() |
39f088cdcd | ||
![]() |
e805852721 | ||
![]() |
e136ecc1f8 | ||
![]() |
efa4001440 | ||
![]() |
68c20ed48d | ||
![]() |
c5760ceaab | ||
![]() |
8c7c9a67a6 | ||
![]() |
85231b314f | ||
![]() |
210b2efd47 | ||
![]() |
ae5b1385ea | ||
![]() |
011ce28251 | ||
![]() |
efb58b45df | ||
![]() |
7d033787eb | ||
![]() |
3a47583cf9 | ||
![]() |
565b4b3f2f | ||
![]() |
574f32a6dd | ||
![]() |
0e67c8ff69 | ||
![]() |
b886073c11 | ||
![]() |
57c35b9a74 | ||
![]() |
f5100694d6 | ||
![]() |
3ff9bfcc13 | ||
![]() |
6c8d2bfd9e | ||
![]() |
637f7f48ad | ||
![]() |
8eae80d30f | ||
![]() |
9a103276e3 | ||
![]() |
6386f011f7 | ||
![]() |
742d6e63d9 | ||
![]() |
f6141603ef | ||
![]() |
7a5b4e565a | ||
![]() |
373e1527c1 | ||
![]() |
9cb05b6a7d | ||
![]() |
74bf5ff044 | ||
![]() |
0f7e9d11b6 | ||
![]() |
5fadc2fb9f | ||
![]() |
96bbcf623f | ||
![]() |
89e853158c | ||
![]() |
5c31fcc03e | ||
![]() |
3fee4a803b | ||
![]() |
8b61f559d7 | ||
![]() |
4bd058f5dc | ||
![]() |
999ccf87fc | ||
![]() |
4e45e885b9 | ||
![]() |
6aee9ccaf1 | ||
![]() |
af3c6ff012 | ||
![]() |
0c9bdf5168 | ||
![]() |
82a9587b4f | ||
![]() |
850f4d7ec5 | ||
![]() |
8d1dcc4568 | ||
![]() |
3650ab85df | ||
![]() |
e1e1cc2666 | ||
![]() |
79fd32088b | ||
![]() |
458e0fc76d | ||
![]() |
5090c81632 | ||
![]() |
dcd035353b | ||
![]() |
8606ce21bd | ||
![]() |
72537c6ffb | ||
![]() |
30f9888f51 | ||
![]() |
d5a2b92121 | ||
![]() |
fd3eee6870 | ||
![]() |
2d6d1003b7 | ||
![]() |
499c590f5a | ||
![]() |
787b78ef36 | ||
![]() |
88a0b0fa23 | ||
![]() |
1519f04930 | ||
![]() |
e0a1291de0 | ||
![]() |
8a2d881002 | ||
![]() |
4802b6f460 | ||
![]() |
8c0e116a1c | ||
![]() |
8862878011 | ||
![]() |
bdf3361243 | ||
![]() |
6b5216077b | ||
![]() |
7e119f7963 | ||
![]() |
933cbc7e51 | ||
![]() |
65f30d4480 | ||
![]() |
1f8d3f119e | ||
![]() |
45fe858503 | ||
![]() |
0cc06ad186 | ||
![]() |
18cd7547ad | ||
![]() |
7746a26062 | ||
![]() |
a47fc9d3be | ||
![]() |
8fdf75330c | ||
![]() |
d6ebaa26b0 | ||
![]() |
27bd7ec836 | ||
![]() |
6752058922 | ||
![]() |
b965108dad | ||
![]() |
6bf0f36567 | ||
![]() |
69f1a70968 | ||
![]() |
47edfcac5e | ||
![]() |
c34599b954 | ||
![]() |
48b371c985 | ||
![]() |
47165e7b9d | ||
![]() |
69f4611552 | ||
![]() |
31a25449a8 | ||
![]() |
26a247c0bf | ||
![]() |
1c7cd23475 | ||
![]() |
296ef1bdf9 | ||
![]() |
f2a0408801 | ||
![]() |
642659bc9b | ||
![]() |
f0e8d065b7 | ||
![]() |
520d852c04 | ||
![]() |
8d4d7a421a | ||
![]() |
802b83b0fe | ||
![]() |
7ccb6c8411 | ||
![]() |
1518831f37 | ||
![]() |
3bdfaa02e1 | ||
![]() |
e8de9f915c | ||
![]() |
31f598d1e1 | ||
![]() |
46e6fd0847 | ||
![]() |
1441a60f4b | ||
![]() |
ebdf107946 | ||
![]() |
38d672c9f9 | ||
![]() |
75d34abd45 | ||
![]() |
1d98738e4d | ||
![]() |
3cfa0e1844 | ||
![]() |
82211a4373 | ||
![]() |
e324b04a94 | ||
![]() |
49d0c51d97 | ||
![]() |
fe4bc3b4a9 | ||
![]() |
58d964063c | ||
![]() |
17d3e37e1d | ||
![]() |
54f6a16bd6 | ||
![]() |
f4ffc5d198 | ||
![]() |
3d8010bf88 | ||
![]() |
ec2cc3a7f9 | ||
![]() |
52b7b9bcd8 | ||
![]() |
8ad0fcf42d | ||
![]() |
aa6cc45988 | ||
![]() |
da22b8297b | ||
![]() |
798d82e941 | ||
![]() |
ac8ac302ca | ||
![]() |
22c3a73102 | ||
![]() |
1222fdb043 | ||
![]() |
78a50c2f53 | ||
![]() |
6d261a7afa | ||
![]() |
2e37292478 | ||
![]() |
a4907ec9c8 | ||
![]() |
992bfe73b6 | ||
![]() |
c186254a7e | ||
![]() |
1dc739f32c | ||
![]() |
9804cd83a6 | ||
![]() |
aa5b99c47a | ||
![]() |
f95f5e8901 | ||
![]() |
206b303407 | ||
![]() |
751bff53ee | ||
![]() |
b6ef0c509d | ||
![]() |
30304d6836 | ||
![]() |
f5df49b139 | ||
![]() |
bf8ba1a49b | ||
![]() |
9f2697658b | ||
![]() |
36fd59b92c | ||
![]() |
88d946546a | ||
![]() |
6036c0c262 | ||
![]() |
20e831b56a | ||
![]() |
72b74e075c | ||
![]() |
3c6ede2f8f | ||
![]() |
5434edbd54 | ||
![]() |
6a8c7e4b17 | ||
![]() |
0ac659f2d1 | ||
![]() |
7eae40ca9a | ||
![]() |
b3d12c8b58 | ||
![]() |
3f92dba1b7 | ||
![]() |
0aedc08cfa | ||
![]() |
8312f5cffd | ||
![]() |
9f6338441e | ||
![]() |
27efe0c9d6 | ||
![]() |
b5bf6c08d8 | ||
![]() |
e3fce51ea1 | ||
![]() |
13b4e93bf4 | ||
![]() |
4ec0ab281f | ||
![]() |
39c96019b6 | ||
![]() |
21f2f60355 | ||
![]() |
0de654dcaf | ||
![]() |
d59e472814 | ||
![]() |
cc2ed452f0 | ||
![]() |
d4d3843725 | ||
![]() |
dee16da1c2 | ||
![]() |
e09b85870f | ||
![]() |
8596911a0e | ||
![]() |
6c2de9f151 | ||
![]() |
d3a6b1e39e | ||
![]() |
3cb9b19aea | ||
![]() |
86aa59bb29 | ||
![]() |
c6eeaed7da | ||
![]() |
edba90f188 | ||
![]() |
26b49b1a0c | ||
![]() |
ef32648ddc | ||
![]() |
c873524cb1 | ||
![]() |
7943e34bde | ||
![]() |
74970c74b4 | ||
![]() |
2d11561041 | ||
![]() |
f7643b6bb3 | ||
![]() |
d3afd83e8e | ||
![]() |
722431f5b2 | ||
![]() |
df8e455a6d | ||
![]() |
b35bec2839 | ||
![]() |
19365effa9 |
12
.idea/codeStyleSettings.xml
generated
12
.idea/codeStyleSettings.xml
generated
@@ -141,11 +141,8 @@
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="BLOCK_COMMENT_AT_FIRST_COLUMN" value="false" />
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="WHILE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
||||
@@ -154,7 +151,6 @@
|
||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
||||
<option name="ALIGN_MULTILINE_THROWS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_EXTENDS_LIST" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION" value="true" />
|
||||
<option name="SPACE_AFTER_TYPE_CAST" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
||||
@@ -165,12 +161,9 @@
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="5" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="5" />
|
||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
||||
<option name="FOR_STATEMENT_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
||||
<option name="IF_BRACE_FORCE" value="1" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
||||
<option name="FOR_BRACE_FORCE" value="1" />
|
||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
||||
@@ -227,6 +220,10 @@
|
||||
<option name="TAB_SIZE" value="8" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<option name="KEEP_LINE_BREAKS" value="false" />
|
||||
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSP">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
@@ -277,4 +274,3 @@
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
|
||||
|
@@ -27,6 +27,13 @@ Contributors:
|
||||
* [Dathan Bennett](mailto:dbennett@palantir.com)
|
||||
* [salaam](mailto:kphayen@gmail.com)
|
||||
* [Alexey Shmalko](mailto:rasen.dubi@gmail.com)
|
||||
* [Andrew Brookins](mailto:a.m.brookins@gmail.com)
|
||||
* [Chang Wang](mailto:changwang83@gmail.com)
|
||||
* [Jaime Sanchez](mailto:josejaime.sanchez@gmail.com)
|
||||
* [Thomas B Homburg](mailto:thomas@homburg.dk)
|
||||
* [smartbomb](mailto:smartbomb@server.fake)
|
||||
* [Tuomas Tynkkynen](mailto:tuomas.tynkkynen@iki.fi)
|
||||
* [Jackson Popkin](mailto:jackson@donorschoose.org)
|
||||
|
||||
If you are a contributor and your name is not listed here, feel free to
|
||||
contact the maintainer.
|
||||
|
121
CHANGES.md
121
CHANGES.md
@@ -3,6 +3,127 @@ The Changelog
|
||||
|
||||
History of changes in IdeaVim for the IntelliJ platform.
|
||||
|
||||
0.44, 2015-11-02
|
||||
----------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
|
||||
* VIM-1040 Fixed typing keys in completion menus and typing with the
|
||||
plugin disabled
|
||||
|
||||
|
||||
0.43, 2015-11-02
|
||||
----------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
* VIM-1039 Fixed running the plugin with Java 6
|
||||
|
||||
|
||||
0.42, 2015-11-01
|
||||
----------------
|
||||
|
||||
This release is compatible with IntelliJ 15+ and other IDEs based on the
|
||||
IntelliJ platform branch 143+.
|
||||
|
||||
* VIM-970 Fixed move commands in read-only files
|
||||
|
||||
|
||||
0.41, 2015-06-10
|
||||
----------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
* VIM-957 Fixed plugin version 0.40 is not compatible with IDEs other than
|
||||
IntelliJ
|
||||
|
||||
|
||||
0.40, 2015-06-09
|
||||
----------------
|
||||
|
||||
Added support for `mapleader`. Support comments in `%` brace matching. Various
|
||||
bug fixes.
|
||||
|
||||
Features:
|
||||
|
||||
* VIM-650 Added support for `mapleader`
|
||||
* VIM-932 Support comments in `%` brace matching
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* VIM-586 Invoke Vim shortcuts handler later to restore the sequence of input
|
||||
events
|
||||
* VIM-838 `J` shouldn't add whitespace if there is a trailing space
|
||||
* VIM-855 Fixed regexp character class problem
|
||||
* VIM-210 Fix focus issues with the Ex panel and splits
|
||||
* VIM-575 Don't change cursor position of other splits in visual mode
|
||||
* VIM-864 Fixed visual marks getting changed during visual substitute
|
||||
* VIM-856 Fixed regex look-behind problem
|
||||
* VIM-868 Allow count on `gt` and `gT`
|
||||
* VIM-700 Remapping `0` should still allow it to be entered in command count
|
||||
* VIM-781 Fixed expanding visual block selection past empty lines
|
||||
* VIM-845 Fixed `c` and `x` functionality for visual block selections
|
||||
* VIM-930 Fixed editor focus issues after closing Ex entry box on Oracle Java 6
|
||||
|
||||
|
||||
0.39, 2014-12-03
|
||||
----------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* VIM-848 Show line numbers if they are enabled in the settings and there is
|
||||
no `set number`
|
||||
* VIM-702 Fix infinite loop on `s/$/\r/g`
|
||||
* EA-63022 Don't update line numbers in the caret movement event listener
|
||||
|
||||
|
||||
0.38, 2014-12-01
|
||||
----------------
|
||||
|
||||
Added support for `number` and `relativenumber` options, `clipboard=unnamed`
|
||||
option. Added `:action` and `:actionlist` commands for executing arbitrary
|
||||
IDE actions. Various bug fixes.
|
||||
|
||||
Features:
|
||||
|
||||
* VIM-476 Added support for `clipboard=unnamed` option
|
||||
* VIM-410 Added support for `relativenumber` option
|
||||
* VIM-483 Added support for `number` option
|
||||
* VIM-652 Added `:action` and `:actionlist` commands for executing arbitrary
|
||||
IDE actions
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* VIM-818 Enable key repeat on Mac OS X every time it gets reset by the OS
|
||||
* VIM-624 Deselect visual selection range on opening the Ex entry field
|
||||
* VIM-511 Fixed editing offset after `<BS>` for `.` command
|
||||
* VIM-792 Fixed line-wise and block-wise paste commands for `*` and `+`
|
||||
registers
|
||||
* VIM-501 Fixed off-by-1 error in visual block-wise selection
|
||||
* VIM-613 Fixed repeat after `d$`
|
||||
* VIM-705 Fixed repeated multiline indent
|
||||
* VIM-567 Fixed `:!` to allow running non-filter commands
|
||||
* VIM-536 Fixed `cc` on the second-to-last line
|
||||
* VIM-515 Fixed `cW` command detecting end-of-word incorrectly
|
||||
* VIM-794 Fixed NCDFE related to 'number' in IDEs other than IntelliJ
|
||||
* VIM-771 Fix semicolon repeat for 'till char' motion
|
||||
* VIM-723 Fix pasting to an empty line
|
||||
|
||||
|
||||
0.37, 2014-10-15
|
||||
----------------
|
||||
|
||||
A bugfix release.
|
||||
|
||||
Bug fixes:
|
||||
|
||||
* VIM-784 Fixed visual line selection where the start of the selection range
|
||||
was greater than its end
|
||||
* VIM-407 Fixed `>>` to work if a line contains only one character
|
||||
|
||||
|
||||
0.36, 2014-10-14
|
||||
----------------
|
||||
|
36
README.md
36
README.md
@@ -2,15 +2,15 @@ IdeaVim
|
||||
=======
|
||||
|
||||
<div>
|
||||
<a href="http://teamcity.jetbrains.com/viewType.html?buildTypeId=bt299&guest=1">
|
||||
<img src="http://teamcity.jetbrains.com/app/rest/builds/buildType:(id:bt299)/statusIcon"/>
|
||||
<a href="http://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_Build&guest=1">
|
||||
<img src="http://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_Build)/statusIcon"/>
|
||||
</a>
|
||||
<span>Build<span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="http://teamcity.jetbrains.com/viewType.html?buildTypeId=bt453&guest=1">
|
||||
<img src="http://teamcity.jetbrains.com/app/rest/builds/buildType:(id:bt453)/statusIcon"/>
|
||||
<a href="http://teamcity.jetbrains.com/viewType.html?buildTypeId=IdeaVim_TestsForIntelliJ15&guest=1">
|
||||
<img src="http://teamcity.jetbrains.com/app/rest/builds/buildType:(id:IdeaVim_TestsForIntelliJ15)/statusIcon"/>
|
||||
</a>
|
||||
<span>Tests</span>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@ Start the IDE normally and enable the Vim emulation using "Tools | Vim
|
||||
Emulator" menu item. At this point you must use Vim keystrokes in all editors.
|
||||
|
||||
If you wish to disable the plugin, select the "Tools | Vim Emulator" menu so
|
||||
it is unchecked. At this point IDE will work with it's regular keyboard
|
||||
it is unchecked. At this point IDE will work with its regular keyboard
|
||||
shortcuts.
|
||||
|
||||
Keyboard shortcut conflicts between the Vim emulation and the IDE can be
|
||||
@@ -91,6 +91,11 @@ Note, that IdeaVim currently parses ~/.ideavimrc file via simple pattern matchin
|
||||
see [VIM-669](http://youtrack.jetbrains.com/issue/VIM-669) for proper parsing
|
||||
of VimL files.
|
||||
|
||||
Also note that if you have overridden the `user.home` JVM option, this will
|
||||
will affect where IdeaVim looks for your .ideavimrc file. For example, if you
|
||||
have `-Duser.home=/my/alternate/home` then IdeaVim will source
|
||||
`/my/alternate/home/.ideavimrc` instead of `~/.ideavimrc`.
|
||||
|
||||
|
||||
Changes to the IDE
|
||||
------------------
|
||||
@@ -112,6 +117,20 @@ usage of the Vim emulator in dialog windows is an area for improvements.
|
||||
|
||||
See also [unresolved escape issues](http://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+Help+topic%3A+i_Esc).
|
||||
|
||||
### Executing IDE Actions
|
||||
|
||||
IdeaVim adds two commands for listing and executing arbitrary IDE actions as
|
||||
Ex commands or via `:map` command mappings:
|
||||
|
||||
* `:actionlist [pattern]`
|
||||
* Find IDE actions by name pattern
|
||||
* `:action {name}`
|
||||
* Execute an action named `NAME`
|
||||
|
||||
For example, here `\r` is mapped to the Reformat Code action:
|
||||
|
||||
:map \r :action ReformatCode<CR>
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
@@ -152,7 +171,12 @@ in the issue tracker.
|
||||
previous step.
|
||||
|
||||
6. Build IdeaVim and run IntelliJ with IdeaVim enabled using the "IdeaVim" run
|
||||
configuration (use "Run | Run... | IdeaVim").
|
||||
configuration (use "Run | Run... | IdeaVim"). This will launch a spare
|
||||
instance of IntelliJ running the compiled plugin in a sandboxed enviroment.
|
||||
To actually deploy the plugin the recommended way is executing `ant dist`
|
||||
from command line, which will update versions in plugin.xml among other
|
||||
tasks. Install the generated "ideavim.jar" file from
|
||||
"Settings | Plugins | Install plugin from disk".
|
||||
|
||||
7. In order to be able to run tests in your IntelliJ edition uncomment the
|
||||
appropriate lines in the constructor of the `VimTestCase` class.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
version-id:0.36
|
||||
platform-version:135.0
|
||||
idea.download.url=http://download.jetbrains.com/idea/ideaIU-13.1.zip
|
||||
version-id:0.44
|
||||
platform-version:143.0
|
||||
idea.download.url=http://download.jetbrains.com/idea/ideaIC-143.380.20.tar.gz
|
||||
build.number=dev
|
||||
|
@@ -105,9 +105,9 @@
|
||||
<delete dir="${idea}/unzip"/>
|
||||
<mkdir dir="${idea}/unzip"/>
|
||||
<basename property="idea.filename" file="${idea.download.url}"/>
|
||||
<unzip dest="${idea}/unzip">
|
||||
<fileset dir="${idea}" includes="${idea.filename}"/>
|
||||
</unzip>
|
||||
<untar src="${idea}/${idea.filename}" dest="${idea}/unzip" compression="gzip">
|
||||
<cutdirsmapper dirs="1"/>
|
||||
</untar>
|
||||
</target>
|
||||
|
||||
<target name="dist" depends="dist-src, dist-bin" description="Creates the src and bin distribution files"/>
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
<target name="test" depends="unzip, clean, prepare-tests">
|
||||
<mkdir dir="${test-reports}"/>
|
||||
<junit fork="true" logfailedtests="false" printsummary="true">
|
||||
<junit fork="true" forkmode="once" logfailedtests="false" printsummary="true">
|
||||
<classpath refid="test.classpath"/>
|
||||
|
||||
<jvmarg value="-Xmx256M"/>
|
||||
@@ -169,6 +169,7 @@
|
||||
<jvmarg value="-Didea.load.plugins.id=IdeaVIM"/>
|
||||
|
||||
<formatter type="plain"/>
|
||||
<formatter type="xml" usefile="true" />
|
||||
|
||||
<batchtest todir="${test-reports}">
|
||||
<fileset dir="${test}">
|
||||
|
@@ -3,22 +3,26 @@ List of Supported Set Commands
|
||||
|
||||
The following `:set` commands can appear in `~/.ideavimrc` or set manually in the command mode:
|
||||
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
'gdefault' 'gd' the ":substitute" flag 'g' is default on
|
||||
'history' 'hi' number of command-lines that are remembered
|
||||
'hlsearch' 'hls' highlight matches with last search pattern
|
||||
'ignorecase' 'ic' ignore case in search patterns
|
||||
'matchpairs' 'mps' pairs of characters that "%" can match
|
||||
'nrformats' 'nf' number formats recognized for CTRL-A command
|
||||
'scroll' 'scr' lines to scroll with CTRL-U and CTRL-D
|
||||
'scrolljump' 'sj' minimum number of lines to scroll
|
||||
'scrolloff' 'so' minimum nr. of lines above and below cursor
|
||||
'selection' 'sel' what type of selection to use
|
||||
'showmode' 'smd' message on status line to show current mode
|
||||
'sidescroll' 'ss' minimum number of columns to scroll horizontal
|
||||
'sidescrolloff' 'siso' min. nr. of columns to left and right of cursor
|
||||
'smartcase' 'scs' no ignore case when pattern has uppercase
|
||||
'timeoutlen' 'tm' time that is waited for a mapped key sequence
|
||||
'undolevels' 'ul' maximum number of changes that can be undone
|
||||
'visualbell' 'vb' use visual bell instead of beeping
|
||||
'wrapscan' 'ws' searches wrap around the end of the file
|
||||
'clipboard' 'cb' clipboard options
|
||||
'digraph' 'dg' enable the entering of digraphs in Insert mode
|
||||
'gdefault' 'gd' the ":substitute" flag 'g' is default on
|
||||
'history' 'hi' number of command-lines that are remembered
|
||||
'hlsearch' 'hls' highlight matches with last search pattern
|
||||
'ignorecase' 'ic' ignore case in search patterns
|
||||
'matchpairs' 'mps' pairs of characters that "%" can match
|
||||
'nrformats' 'nf' number formats recognized for CTRL-A command
|
||||
'number' 'nu' print the line number in front of each line
|
||||
'relativenumber' 'rnu' show the line number relative to the line with
|
||||
the cursor
|
||||
'scroll' 'scr' lines to scroll with CTRL-U and CTRL-D
|
||||
'scrolljump' 'sj' minimum number of lines to scroll
|
||||
'scrolloff' 'so' minimum nr. of lines above and below cursor
|
||||
'selection' 'sel' what type of selection to use
|
||||
'showmode' 'smd' message on status line to show current mode
|
||||
'sidescroll' 'ss' minimum number of columns to scroll horizontal
|
||||
'sidescrolloff' 'siso' min. nr. of columns to left and right of cursor
|
||||
'smartcase' 'scs' no ignore case when pattern has uppercase
|
||||
'timeoutlen' 'tm' time that is waited for a mapped key sequence
|
||||
'undolevels' 'ul' maximum number of changes that can be undone
|
||||
'visualbell' 'vb' use visual bell instead of beeping
|
||||
'wrapscan' 'ws' searches wrap around the end of the file
|
||||
|
@@ -7,9 +7,9 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/idea" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
@@ -1,8 +1,44 @@
|
||||
<idea-plugin url="http://plugins.jetbrains.com/plugin/164">
|
||||
<name>IdeaVim</name>
|
||||
<id>IdeaVIM</id>
|
||||
<change-notes>
|
||||
<![CDATA[
|
||||
<change-notes><![CDATA[
|
||||
<p>0.44:</p>
|
||||
<ul>
|
||||
<li>Fixed typing keys in completion menus and typing with the plugin disabled</li>
|
||||
</ul>
|
||||
<p>0.43:</p>
|
||||
<ul>
|
||||
<li>Fixed running the plugin with Java 6</li>
|
||||
</ul>
|
||||
<p>0.42:</p>
|
||||
<ul>
|
||||
<li>Fixed move commands in read-only files</li>
|
||||
</ul>
|
||||
<p>0.41:</p>
|
||||
<ul>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.40:</p>
|
||||
<ul>
|
||||
<li>Support for <code>mapleader</code></li>
|
||||
<li>Support comments in <code>%</code> brace matching</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.39:</p>
|
||||
<ul>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.38:</p>
|
||||
<ul>
|
||||
<li>Support for <code>:action</code> and <code>:actionlist</code> for executing arbitrary IDE actions</li>
|
||||
<li>Support for <code>number</code> and <code>relativenumber</code> options</li>
|
||||
<li>Support for <code>clipboard=unnamed</code> option</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.37:</p>
|
||||
<ul>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.36:</p>
|
||||
<ul>
|
||||
<li>Window commands from the <code><C-W></code> family</li>
|
||||
@@ -10,66 +46,18 @@
|
||||
<li>Fixed visual block selection mode</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.35:</p>
|
||||
<ul>
|
||||
<li><code>~/.vimrc</code> is no longer read by default, use <code>~/.ideavimrc</code> instead</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.34:</p>
|
||||
<ul>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.33:</p>
|
||||
<ul>
|
||||
<li>Support for <code>:map</code> key mapping commands</li>
|
||||
<li>New keyboard shortcuts handler that doesn't require a separate keymap for Vim emulation</li>
|
||||
<li>Support for <code>:source</code> command</li>
|
||||
<li>Support for <code>:sort</code> command</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.32:</p>
|
||||
<ul>
|
||||
<li>Fixed API compatibility with IntelliJ platform builds 132.1052+</li>
|
||||
</ul>
|
||||
<p>0.31:</p>
|
||||
<ul>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>0.30:</p>
|
||||
<ul>
|
||||
<li>Support for a separate <code>.ideavimrc</code> config file</li>
|
||||
<li>Fixed long-standing issues with merged undo/redo commands and <code><Esc></code> during completion</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>
|
||||
]]>
|
||||
</change-notes>
|
||||
<description>
|
||||
<![CDATA[
|
||||
]]></change-notes>
|
||||
<description><![CDATA[
|
||||
<p>Build @VERSION@-@BUILD-NUMBER@</p>
|
||||
<p>Vim emulation plug-in for IDEs based on the IntelliJ platform. IdeaVim can be used with IntelliJ IDEA, RubyMine, PyCharm, PhpStorm, WebStorm, AppCode, CLion and Android Studio.</p>
|
||||
<p>Supported functionality:</p>
|
||||
<p>Vim emulation plug-in for IDEs based on the IntelliJ platform.</p>
|
||||
<p>IdeaVim supports many Vim features including normal/insert/visual modes, motion keys, deletion/changing, marks, registers, some Ex commands, Vim regexps, configuration via ~/.ideavimrc, macros, window commands, etc.</p>
|
||||
<p>See also:</p>
|
||||
<ul>
|
||||
<li>Motion keys</li>
|
||||
<li>Deletion/changing</li>
|
||||
<li>Insert mode commands</li>
|
||||
<li>Marks</li>
|
||||
<li>Registers</li>
|
||||
<li>Undo/redo</li>
|
||||
<li>Visual mode commands</li>
|
||||
<li>Some Ex commands</li>
|
||||
<li>Some :set options</li>
|
||||
<li>Full Vim regexps for search and search/replace</li>
|
||||
<li>Key mappings</li>
|
||||
<li>Configuration via ~/.ideavimrc</li>
|
||||
<li>Macros</li>
|
||||
<li>Digraphs</li>
|
||||
<li>Command line and search history</li>
|
||||
<li>Window commands</li>
|
||||
<li>Vim web help</li>
|
||||
<li><a href="https://github.com/JetBrains/ideavim">GitHub repository</a>: documentation and contributing</li>
|
||||
<li><a href="http://youtrack.jetbrains.com/issues/VIM">Issue tracker</a>: feature requests and bug reports</li>
|
||||
</ul>
|
||||
]]>
|
||||
</description>
|
||||
]]></description>
|
||||
<version>@VERSION@</version>
|
||||
<vendor>JetBrains</vendor>
|
||||
|
||||
|
@@ -42,13 +42,13 @@ public class EventFacade {
|
||||
|
||||
public void setupTypedActionHandler(@NotNull TypedActionHandler handler) {
|
||||
final TypedAction typedAction = getTypedAction();
|
||||
myOriginalTypedActionHandler = typedAction.getHandler();
|
||||
typedAction.setupHandler(handler);
|
||||
myOriginalTypedActionHandler = typedAction.getRawHandler();
|
||||
typedAction.setupRawHandler(handler);
|
||||
}
|
||||
|
||||
public void restoreTypedActionHandler() {
|
||||
if (myOriginalTypedActionHandler != null) {
|
||||
getTypedAction().setupHandler(myOriginalTypedActionHandler);
|
||||
getTypedAction().setupRawHandler(myOriginalTypedActionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -111,29 +111,29 @@ public class KeyHandler {
|
||||
editor = InjectedLanguageUtil.getTopLevelEditor(editor);
|
||||
final CommandState editorState = CommandState.getInstance(editor);
|
||||
|
||||
if (allowKeyMappings && handleKeyMapping(editor, key, context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isRecording = editorState.isRecording();
|
||||
boolean shouldRecord = true;
|
||||
// If this is a "regular" character keystroke, get the character
|
||||
char chKey = key.getKeyChar() == KeyEvent.CHAR_UNDEFINED ? 0 : key.getKeyChar();
|
||||
|
||||
if (isEditorReset(key, editorState)) {
|
||||
handleEditorReset(editor, key, context);
|
||||
}
|
||||
// At this point the user must be typing in a command. Most commands can be preceded by a number. Let's
|
||||
// check if a number can be entered at this point, and if so, did the user send us a digit.
|
||||
else if (isCommandCount(editorState, chKey)) {
|
||||
final boolean isRecording = editorState.isRecording();
|
||||
boolean shouldRecord = true;
|
||||
|
||||
// Check for command count before key mappings - otherwise e.g. ':map 0 ^' breaks command counts that contain a zero
|
||||
if (isCommandCount(editorState, chKey)) {
|
||||
// Update the count
|
||||
count = count * 10 + (chKey - '0');
|
||||
}
|
||||
else if (allowKeyMappings && handleKeyMapping(editor, key, context)) {
|
||||
return;
|
||||
}
|
||||
// Pressing delete while entering a count "removes" the last digit entered
|
||||
// Unlike the digits, this must be checked *after* checking for key mappings
|
||||
else if (isDeleteCommandCount(key, editorState)) {
|
||||
// "Remove" the last digit sent to us
|
||||
count /= 10;
|
||||
}
|
||||
else if (isEditorReset(key, editorState)) {
|
||||
handleEditorReset(editor, key, context);
|
||||
}
|
||||
// If we got this far the user is entering a command or supplying an argument to an entered command.
|
||||
// First let's check to see if we are at the point of expecting a single character argument to a command.
|
||||
else if (currentArg == Argument.Type.CHARACTER) {
|
||||
@@ -292,17 +292,19 @@ public class KeyHandler {
|
||||
}
|
||||
|
||||
private void handleEditorReset(@NotNull Editor editor, @NotNull KeyStroke key, @NotNull final DataContext context) {
|
||||
if (state != State.COMMAND && count == 0 && currentArg == Argument.Type.NONE && currentCmd.size() == 0 &&
|
||||
VimPlugin.getRegister().getCurrentRegister() == RegisterGroup.REGISTER_DEFAULT) {
|
||||
if (key.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
CommandProcessor.getInstance().executeCommand(editor.getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
KeyHandler.executeAction("EditorEscape", context);
|
||||
}
|
||||
}, "", null);
|
||||
if (state != State.COMMAND && count == 0 && currentArg == Argument.Type.NONE && currentCmd.size() == 0) {
|
||||
RegisterGroup register = VimPlugin.getRegister();
|
||||
if (register.getCurrentRegister() == register.getDefaultRegister()) {
|
||||
if (key.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
CommandProcessor.getInstance().executeCommand(editor.getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
KeyHandler.executeAction("EditorEscape", context);
|
||||
}
|
||||
}, "", null);
|
||||
}
|
||||
VimPlugin.indicateError();
|
||||
}
|
||||
VimPlugin.indicateError();
|
||||
}
|
||||
reset(editor);
|
||||
}
|
||||
@@ -560,12 +562,10 @@ public class KeyHandler {
|
||||
* @param name The name of the action to execute
|
||||
* @param context The context to run it in
|
||||
*/
|
||||
public static void executeAction(@NotNull String name, @NotNull DataContext context) {
|
||||
public static boolean executeAction(@NotNull String name, @NotNull DataContext context) {
|
||||
ActionManager aMgr = ActionManager.getInstance();
|
||||
AnAction action = aMgr.getAction(name);
|
||||
if (action != null) {
|
||||
executeAction(action, context);
|
||||
}
|
||||
return action != null && executeAction(action, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,16 +574,20 @@ public class KeyHandler {
|
||||
* @param action The action to execute
|
||||
* @param context The context to run it in
|
||||
*/
|
||||
public static void executeAction(@NotNull AnAction action, @NotNull DataContext context) {
|
||||
public static boolean executeAction(@NotNull AnAction action, @NotNull DataContext context) {
|
||||
// Hopefully all the arguments are sufficient. So far they all seem to work OK.
|
||||
// We don't have a specific InputEvent so that is null
|
||||
// What is "place"? Leave it the empty string for now.
|
||||
// Is the template presentation sufficient?
|
||||
// What are the modifiers? Is zero OK?
|
||||
action.actionPerformed(
|
||||
new AnActionEvent(null, context, "", action.getTemplatePresentation(), ActionManager.getInstance(),
|
||||
// API change - don't merge
|
||||
0));
|
||||
final AnActionEvent event = new AnActionEvent(null, context, "", action.getTemplatePresentation(),
|
||||
ActionManager.getInstance(), 0);
|
||||
action.update(event);
|
||||
if (event.getPresentation().isEnabled()) {
|
||||
action.actionPerformed(event);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -630,11 +630,11 @@ public class RegisterActions {
|
||||
new Shortcut("=="));
|
||||
parser.registerAction(MappingMode.N, "VimShiftLeftLines", Command.Type.CHANGE,
|
||||
new Shortcut("<<"));
|
||||
parser.registerAction(MappingMode.N, "VimShiftLeftMotion", Command.Type.CHANGE,
|
||||
parser.registerAction(MappingMode.N, "VimShiftLeftMotion", Command.Type.CHANGE, Command.FLAG_OP_PEND,
|
||||
new Shortcut('<'), Argument.Type.MOTION);
|
||||
parser.registerAction(MappingMode.N, "VimShiftRightLines", Command.Type.CHANGE,
|
||||
new Shortcut(">>"));
|
||||
parser.registerAction(MappingMode.N, "VimShiftRightMotion", Command.Type.CHANGE,
|
||||
parser.registerAction(MappingMode.N, "VimShiftRightMotion", Command.Type.CHANGE, Command.FLAG_OP_PEND,
|
||||
new Shortcut('>'), Argument.Type.MOTION);
|
||||
|
||||
// Jump Actions
|
||||
@@ -722,11 +722,11 @@ public class RegisterActions {
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, KeyEvent.CTRL_MASK)));
|
||||
parser.registerAction(MappingMode.I, "VimMotionLastColumn", Command.Type.INSERT, Command.FLAG_SAVE_STROKE,
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0)));
|
||||
parser.registerAction(MappingMode.I, "VimMotionLeft", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||
parser.registerAction(MappingMode.I, "VimMotionLeft", Command.Type.INSERT, new Shortcut[]{
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0)),
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, 0))
|
||||
});
|
||||
parser.registerAction(MappingMode.I, "VimMotionRight", Command.Type.INSERT, Command.FLAG_SAVE_STROKE, new Shortcut[]{
|
||||
parser.registerAction(MappingMode.I, "VimMotionRight", Command.Type.INSERT, new Shortcut[]{
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0)),
|
||||
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, 0))
|
||||
});
|
||||
|
@@ -18,7 +18,6 @@
|
||||
package com.maddyhome.idea.vim;
|
||||
|
||||
import com.intellij.notification.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.ApplicationEx;
|
||||
import com.intellij.openapi.application.ex.ApplicationManagerEx;
|
||||
@@ -27,13 +26,8 @@ import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.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.EditorFactoryAdapter;
|
||||
import com.intellij.openapi.editor.event.EditorFactoryEvent;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.extensions.PluginId;
|
||||
import com.intellij.openapi.keymap.Keymap;
|
||||
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
|
||||
@@ -46,13 +40,10 @@ import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||
import com.maddyhome.idea.vim.ex.VimScriptParser;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import com.maddyhome.idea.vim.group.*;
|
||||
import com.maddyhome.idea.vim.helper.DocumentManager;
|
||||
import com.maddyhome.idea.vim.helper.EditorData;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import com.maddyhome.idea.vim.helper.MacKeyRepeat;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
|
||||
@@ -60,7 +51,6 @@ import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
@@ -88,13 +78,6 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
public static final String IDEAVIM_NOTIFICATION_TITLE = "IdeaVim";
|
||||
public static final int STATE_VERSION = 4;
|
||||
|
||||
private static final boolean BLOCK_CURSOR_VIM_VALUE = true;
|
||||
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
||||
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
|
||||
|
||||
private boolean isBlockCursor = false;
|
||||
private boolean isAnimatedScrolling = false;
|
||||
private boolean isRefrainFromScrolling = false;
|
||||
private boolean error = false;
|
||||
|
||||
private int previousStateVersion = 0;
|
||||
@@ -105,8 +88,6 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(VimPlugin.class);
|
||||
|
||||
private final Application myApp;
|
||||
|
||||
@NotNull private final MotionGroup motion;
|
||||
@NotNull private final ChangeGroup change;
|
||||
@NotNull private final CopyGroup copy;
|
||||
@@ -119,11 +100,10 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
@NotNull private final DigraphGroup digraph;
|
||||
@NotNull private final HistoryGroup history;
|
||||
@NotNull private final KeyGroup key;
|
||||
@NotNull private WindowGroup window;
|
||||
|
||||
public VimPlugin(final Application app) {
|
||||
myApp = app;
|
||||
@NotNull private final WindowGroup window;
|
||||
@NotNull private final EditorGroup editor;
|
||||
|
||||
public VimPlugin() {
|
||||
motion = new MotionGroup();
|
||||
change = new ChangeGroup();
|
||||
copy = new CopyGroup();
|
||||
@@ -137,6 +117,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
history = new HistoryGroup();
|
||||
key = new KeyGroup();
|
||||
window = new WindowGroup();
|
||||
editor = new EditorGroup();
|
||||
|
||||
LOG.debug("VimPlugin ctr");
|
||||
}
|
||||
@@ -160,7 +141,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
});
|
||||
|
||||
final TypedAction typedAction = EditorActionManager.getInstance().getTypedAction();
|
||||
EventFacade.getInstance().setupTypedActionHandler(new VimTypedActionHandler(typedAction.getHandler()));
|
||||
EventFacade.getInstance().setupTypedActionHandler(new VimTypedActionHandler(typedAction.getRawHandler()));
|
||||
|
||||
// Register vim actions in command mode
|
||||
RegisterActions.registerActions();
|
||||
@@ -205,6 +186,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
search.saveData(element);
|
||||
history.saveData(element);
|
||||
key.saveData(element);
|
||||
editor.saveData(element);
|
||||
|
||||
return element;
|
||||
}
|
||||
@@ -230,6 +212,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
search.readData(element);
|
||||
history.readData(element);
|
||||
key.readData(element);
|
||||
editor.readData(element);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -297,6 +280,11 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
return getInstance().window;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static EditorGroup getEditor() {
|
||||
return getInstance().editor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PluginId getPluginId() {
|
||||
return PluginId.getId(IDEAVIM_PLUGIN_ID);
|
||||
@@ -367,38 +355,38 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
|
||||
private void turnOnPlugin() {
|
||||
KeyHandler.getInstance().fullReset(null);
|
||||
setCursors(BLOCK_CURSOR_VIM_VALUE);
|
||||
setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
||||
setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||
|
||||
getEditor().turnOn();
|
||||
getMotion().turnOn();
|
||||
}
|
||||
|
||||
private void turnOffPlugin() {
|
||||
KeyHandler.getInstance().fullReset(null);
|
||||
setCursors(isBlockCursor);
|
||||
setAnimatedScrolling(isAnimatedScrolling);
|
||||
setRefrainFromScrolling(isRefrainFromScrolling);
|
||||
|
||||
getEditor().turnOff();
|
||||
getMotion().turnOff();
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
if (isEnabled() && !ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
boolean requiresRestart = false;
|
||||
if (previousStateVersion < 2 && SystemInfo.isMac) {
|
||||
if (SystemInfo.isMac) {
|
||||
final MacKeyRepeat keyRepeat = MacKeyRepeat.getInstance();
|
||||
final Boolean enabled = keyRepeat.isEnabled();
|
||||
if (enabled == null || !enabled) {
|
||||
final Boolean isKeyRepeat = editor.isKeyRepeat();
|
||||
if ((enabled == null || !enabled) && (isKeyRepeat == null || isKeyRepeat)) {
|
||||
if (Messages.showYesNoDialog("Do you want to enable repeating keys in Mac OS X on press and hold " +
|
||||
"(requires restart)?\n\n" +
|
||||
"(You can do it manually by running 'defaults write -g " +
|
||||
"ApplePressAndHoldEnabled 0' in the console).", IDEAVIM_NOTIFICATION_TITLE,
|
||||
Messages.getQuestionIcon()
|
||||
) == Messages.YES) {
|
||||
Messages.getQuestionIcon()) == Messages.YES) {
|
||||
editor.setKeyRepeat(true);
|
||||
keyRepeat.setEnabled(true);
|
||||
requiresRestart = true;
|
||||
}
|
||||
else {
|
||||
editor.setKeyRepeat(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (previousStateVersion > 0 && previousStateVersion < 3) {
|
||||
@@ -456,40 +444,6 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
DocumentManager.getInstance().addDocumentListener(new MarkGroup.MarkUpdater());
|
||||
DocumentManager.getInstance().addDocumentListener(new SearchGroup.DocumentSearchListener());
|
||||
|
||||
eventFacade.addEditorFactoryListener(new EditorFactoryAdapter() {
|
||||
@Override
|
||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
isBlockCursor = editor.getSettings().isBlockCursor();
|
||||
isAnimatedScrolling = editor.getSettings().isAnimatedScrolling();
|
||||
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
|
||||
EditorData.initializeEditor(editor);
|
||||
DocumentManager.getInstance().addListeners(editor.getDocument());
|
||||
key.registerRequiredShortcutKeys(editor);
|
||||
|
||||
if (VimPlugin.isEnabled()) {
|
||||
// Turn on insert mode if editor doesn't have any file
|
||||
if (!EditorData.isFileEditor(editor) && editor.getDocument().isWritable() &&
|
||||
!CommandState.inInsertMode(editor)) {
|
||||
KeyHandler.getInstance().handleKey(editor, KeyStroke.getKeyStroke('i'), new EditorDataContext(editor));
|
||||
}
|
||||
editor.getSettings().setBlockCursor(!CommandState.inInsertMode(editor));
|
||||
editor.getSettings().setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
||||
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editorReleased(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
EditorData.uninitializeEditor(editor);
|
||||
key.unregisterShortcutKeys(editor);
|
||||
editor.getSettings().setAnimatedScrolling(isAnimatedScrolling);
|
||||
editor.getSettings().setRefrainFromScrolling(isRefrainFromScrolling);
|
||||
DocumentManager.getInstance().removeListeners(editor.getDocument());
|
||||
}
|
||||
}, myApp);
|
||||
|
||||
eventFacade.addProjectManagerListener(new ProjectManagerAdapter() {
|
||||
@Override
|
||||
public void projectOpened(@NotNull final Project project) {
|
||||
@@ -499,27 +453,4 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setCursors(boolean isBlock) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
// Vim plugin should be turned on in insert mode
|
||||
((EditorEx)editor).setInsertMode(true);
|
||||
editor.getSettings().setBlockCursor(isBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAnimatedScrolling(boolean isOn) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
editor.getSettings().setAnimatedScrolling(isOn);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRefrainFromScrolling(boolean isOn) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
editor.getSettings().setRefrainFromScrolling(isOn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -53,7 +54,7 @@ public class VimTypedActionHandler implements TypedActionHandler {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
handler.handleKey(editor, KeyStroke.getKeyStroke(charTyped), context);
|
||||
handler.handleKey(editor, KeyStroke.getKeyStroke(charTyped), new EditorDataContext(editor));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
logger.error(e);
|
||||
|
@@ -24,6 +24,7 @@ import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationListener;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.keymap.KeymapUtil;
|
||||
import com.intellij.openapi.options.ShowSettingsUtil;
|
||||
@@ -34,6 +35,7 @@ import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.action.change.insert.InsertExitModeAction;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.helper.EditorData;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import com.maddyhome.idea.vim.key.ShortcutOwner;
|
||||
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -76,12 +78,15 @@ public class VimShortcutKeyAction extends AnAction implements DumbAware {
|
||||
.build();
|
||||
|
||||
@NotNull private static final Set<KeyStroke> NON_FILE_EDITOR_KEYS = ImmutableSet.<KeyStroke>builder()
|
||||
.addAll(getKeyStrokes(VK_ENTER, 0))
|
||||
.addAll(getKeyStrokes(VK_ESCAPE, 0))
|
||||
.addAll(getKeyStrokes(VK_TAB, 0))
|
||||
.addAll(getKeyStrokes(VK_UP, 0))
|
||||
.addAll(getKeyStrokes(VK_DOWN, 0))
|
||||
.build();
|
||||
|
||||
private static final Logger ourLogger = Logger.getInstance(VimShortcutKeyAction.class.getName());
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
final Editor editor = getEditor(e);
|
||||
@@ -92,7 +97,18 @@ public class VimShortcutKeyAction extends AnAction implements DumbAware {
|
||||
notifyAboutShortcutConflict(keyStroke);
|
||||
}
|
||||
// Should we use InjectedLanguageUtil.getTopLevelEditor(editor) here, as we did in former EditorKeyHandler?
|
||||
KeyHandler.getInstance().handleKey(editor, keyStroke, e.getDataContext());
|
||||
// Run key handler later to restore input events sequence due to VimTypedActionHandler
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
KeyHandler.getInstance().handleKey(editor, keyStroke, new EditorDataContext(editor));
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
ourLogger.error(throwable);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,8 +160,8 @@ public class VimShortcutKeyAction extends AnAction implements DumbAware {
|
||||
return isExitInsertMode(keyStroke);
|
||||
}
|
||||
if (CommandState.inInsertMode(editor)) {
|
||||
// XXX: <Enter> and <Tab> won't be recorded in macros
|
||||
if (keyCode == VK_ENTER || keyCode == VK_TAB) {
|
||||
// XXX: <Tab> won't be recorded in macros
|
||||
if (keyCode == VK_TAB) {
|
||||
return false;
|
||||
}
|
||||
// Debug watch, Python console, etc.
|
||||
|
@@ -42,7 +42,7 @@ public class MotionLastColumnAction extends MotionEditorAction {
|
||||
private static class Handler extends MotionEditorActionHandler {
|
||||
public int getOffset(@NotNull Editor editor, DataContext context, int count, int rawCount, Argument argument) {
|
||||
boolean allow = false;
|
||||
if (CommandState.inInsertMode(editor) || CommandState.inRepeatMode(editor)) {
|
||||
if (CommandState.inInsertMode(editor)) {
|
||||
allow = true;
|
||||
}
|
||||
else if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
||||
|
@@ -36,7 +36,7 @@ public class MotionNextTabAction extends MotionEditorAction {
|
||||
|
||||
private static class Handler extends MotionEditorActionHandler {
|
||||
public int getOffset(@NotNull final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
||||
return VimPlugin.getMotion().moveCaretGotoNextTab(editor, context);
|
||||
return VimPlugin.getMotion().moveCaretGotoNextTab(editor, context, rawCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ public class MotionPreviousTabAction extends MotionEditorAction {
|
||||
|
||||
private static class Handler extends MotionEditorActionHandler {
|
||||
public int getOffset(@NotNull final Editor editor, @NotNull final DataContext context, final int count, final int rawCount, final Argument argument) {
|
||||
return VimPlugin.getMotion().moveCaretGotoPreviousTab(editor, context);
|
||||
return VimPlugin.getMotion().moveCaretGotoPreviousTab(editor, context, rawCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ package com.maddyhome.idea.vim.command;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.group.RegisterGroup;
|
||||
import com.maddyhome.idea.vim.helper.EditorData;
|
||||
import com.maddyhome.idea.vim.key.ParentNode;
|
||||
import com.maddyhome.idea.vim.option.NumberOption;
|
||||
@@ -38,7 +37,7 @@ public class CommandState {
|
||||
public static final int DEFAULT_TIMEOUT_LENGTH = 1000;
|
||||
|
||||
@Nullable private static Command ourLastChange = null;
|
||||
private static char ourLastRegister = RegisterGroup.REGISTER_DEFAULT;
|
||||
private char myLastChangeRegister;
|
||||
|
||||
@NotNull private final Stack<State> myStates = new Stack<State>();
|
||||
@NotNull private final State myDefaultState = new State(Mode.COMMAND, SubMode.NONE, MappingMode.NORMAL);
|
||||
@@ -53,6 +52,7 @@ public class CommandState {
|
||||
myMappingTimer = new Timer(DEFAULT_TIMEOUT_LENGTH, null);
|
||||
myMappingTimer.setRepeats(false);
|
||||
myStates.push(new State(Mode.COMMAND, SubMode.NONE, MappingMode.NORMAL));
|
||||
myLastChangeRegister = VimPlugin.getRegister().getDefaultRegister();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -85,6 +85,11 @@ public class CommandState {
|
||||
return state.getMode() == Mode.VISUAL && state.getSubMode() == SubMode.VISUAL_CHARACTER;
|
||||
}
|
||||
|
||||
public static boolean inVisualBlockMode(@Nullable Editor editor) {
|
||||
final CommandState state = getInstance(editor);
|
||||
return state.getMode() == Mode.VISUAL && state.getSubMode() == SubMode.VISUAL_BLOCK;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Command getCommand() {
|
||||
return myCommand;
|
||||
@@ -255,7 +260,7 @@ public class CommandState {
|
||||
* @return The register key
|
||||
*/
|
||||
public char getLastChangeRegister() {
|
||||
return ourLastRegister;
|
||||
return myLastChangeRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,7 +270,7 @@ public class CommandState {
|
||||
*/
|
||||
public void saveLastChangeCommand(Command cmd) {
|
||||
ourLastChange = cmd;
|
||||
ourLastRegister = VimPlugin.getRegister().getCurrentRegister();
|
||||
myLastChangeRegister = VimPlugin.getRegister().getCurrentRegister();
|
||||
}
|
||||
|
||||
public boolean isRecording() {
|
||||
@@ -311,7 +316,7 @@ public class CommandState {
|
||||
VimPlugin.showMode(msg.toString());
|
||||
}
|
||||
|
||||
public static enum Mode {
|
||||
public enum Mode {
|
||||
COMMAND,
|
||||
INSERT,
|
||||
REPLACE,
|
||||
@@ -320,7 +325,7 @@ public class CommandState {
|
||||
EX_ENTRY
|
||||
}
|
||||
|
||||
public static enum SubMode {
|
||||
public enum SubMode {
|
||||
NONE,
|
||||
SINGLE_COMMAND,
|
||||
VISUAL_CHARACTER,
|
||||
|
@@ -70,6 +70,7 @@ public class CommandParser {
|
||||
public void registerHandlers() {
|
||||
if (registered) return;
|
||||
|
||||
new ActionListHandler();
|
||||
new AsciiHandler();
|
||||
new CmdFilterHandler();
|
||||
new CopyTextHandler();
|
||||
@@ -77,6 +78,8 @@ public class CommandParser {
|
||||
new DigraphHandler();
|
||||
new DumpLineHandler();
|
||||
new EditFileHandler();
|
||||
new ActionHandler();
|
||||
new EchoHandler();
|
||||
new ExitHandler();
|
||||
new FindClassHandler();
|
||||
new FindFileHandler();
|
||||
@@ -87,6 +90,7 @@ public class CommandParser {
|
||||
new HistoryHandler();
|
||||
new JoinLinesHandler();
|
||||
new JumpsHandler();
|
||||
new LetHandler();
|
||||
new MapHandler();
|
||||
new MarkHandler();
|
||||
new MarksHandler();
|
||||
@@ -299,7 +303,7 @@ public class CommandParser {
|
||||
state = STATE_RANGE_MARK;
|
||||
}
|
||||
else if (ch == '+' || ch == '-') {
|
||||
location.append('0');
|
||||
location.append('.');
|
||||
state = STATE_RANGE_OFFSET;
|
||||
}
|
||||
else if (ch == '\\') {
|
||||
|
@@ -56,7 +56,7 @@ public class ExOutputModel {
|
||||
public void clear() {
|
||||
myText = null;
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
ExOutputPanel.getInstance(myEditor).deactivate();
|
||||
ExOutputPanel.getInstance(myEditor).deactivate(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
74
src/com/maddyhome/idea/vim/ex/handler/ActionHandler.java
Normal file
74
src/com/maddyhome/idea/vim/ex/handler/ActionHandler.java
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler;
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
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.ExException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author smartbomb
|
||||
*/
|
||||
public class ActionHandler extends CommandHandler {
|
||||
public ActionHandler() {
|
||||
super("action", "", RANGE_FORBIDDEN | DONT_REOPEN);
|
||||
}
|
||||
|
||||
public boolean execute(@NotNull Editor editor, @NotNull final DataContext context,
|
||||
@NotNull ExCommand cmd) throws ExException {
|
||||
final String actionName = cmd.getArgument().trim();
|
||||
final AnAction action = ActionManager.getInstance().getAction(actionName);
|
||||
if (action == null) {
|
||||
VimPlugin.showMessage("Action not found: " + actionName);
|
||||
return false;
|
||||
}
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
if (application.isUnitTestMode()) {
|
||||
executeAction(action, context, actionName);
|
||||
}
|
||||
else {
|
||||
application.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
executeAction(action, context, actionName);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void executeAction(@NotNull AnAction action, @NotNull DataContext context, @NotNull String actionName) {
|
||||
try {
|
||||
KeyHandler.executeAction(action, context);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// TODO: Find out if any runtime exceptions may happen here
|
||||
assert false : "Error while executing :action " + actionName + " (" + action + "): " + e;
|
||||
}
|
||||
}
|
||||
}
|
85
src/com/maddyhome/idea/vim/ex/handler/ActionListHandler.java
Normal file
85
src/com/maddyhome/idea/vim/ex/handler/ActionListHandler.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2014 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler;
|
||||
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author smartbomb
|
||||
*/
|
||||
public class ActionListHandler extends CommandHandler {
|
||||
public ActionListHandler() {
|
||||
super("actionlist", "", RANGE_FORBIDDEN | DONT_REOPEN | ARGUMENT_OPTIONAL);
|
||||
}
|
||||
|
||||
public boolean execute(@NotNull Editor editor, @NotNull final DataContext context,
|
||||
@NotNull ExCommand cmd) throws ExException {
|
||||
final String arg = cmd.getArgument().trim().toLowerCase();
|
||||
final List<String> args = StringUtil.split(arg, "*");
|
||||
final ActionManager actionManager = ActionManager.getInstance();
|
||||
final List<String> actionNames = Arrays.asList(actionManager.getActionIds(""));
|
||||
Collections.sort(actionNames, String.CASE_INSENSITIVE_ORDER);
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("--- Actions ---\n");
|
||||
|
||||
for (String actionName : actionNames) {
|
||||
if (match(actionName, args)) {
|
||||
builder.append(StringHelper.leftJustify(actionName, 50, ' '));
|
||||
final AnAction action = actionManager.getAction(actionName);
|
||||
final Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
|
||||
for (Shortcut shortcut : shortcuts) {
|
||||
builder.append(" ");
|
||||
if (shortcut instanceof KeyboardShortcut) {
|
||||
final KeyboardShortcut keyboardShortcut = (KeyboardShortcut)shortcut;
|
||||
builder.append(StringHelper.toKeyNotation(keyboardShortcut.getFirstKeyStroke()));
|
||||
}
|
||||
else {
|
||||
builder.append(shortcut.toString());
|
||||
}
|
||||
}
|
||||
builder.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
ExOutputModel.getInstance(editor).output(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean match(@NotNull String actionName, @NotNull List<String> args) {
|
||||
for (String argChunk : args) {
|
||||
if (!actionName.toLowerCase().contains(argChunk)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -23,10 +23,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.Ranges;
|
||||
import com.maddyhome.idea.vim.ex.*;
|
||||
import com.maddyhome.idea.vim.helper.MessageHelper;
|
||||
import com.maddyhome.idea.vim.helper.Msg;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -38,41 +35,42 @@ import java.io.IOException;
|
||||
*/
|
||||
public class CmdFilterHandler extends CommandHandler {
|
||||
public CmdFilterHandler() {
|
||||
super("!", "", RANGE_REQUIRED | ARGUMENT_OPTIONAL | WRITABLE);
|
||||
super("!", "", RANGE_OPTIONAL | ARGUMENT_OPTIONAL | WRITABLE);
|
||||
}
|
||||
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull ExCommand cmd) throws ExException {
|
||||
logger.info("execute");
|
||||
|
||||
Ranges ranges = cmd.getRanges();
|
||||
if (ranges.size() == 0) {
|
||||
// Need some range
|
||||
String command = cmd.getArgument();
|
||||
if (command.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// Filter
|
||||
TextRange range = cmd.getTextRange(editor, context, false);
|
||||
String command = cmd.getArgument();
|
||||
if (command.indexOf('!') != -1) {
|
||||
String last = VimPlugin.getProcess().getLastCommand();
|
||||
if (last == null || last.length() == 0) {
|
||||
VimPlugin.showMessage(MessageHelper.message(Msg.e_noprev));
|
||||
return false;
|
||||
}
|
||||
|
||||
command = command.replaceAll("!", last);
|
||||
}
|
||||
|
||||
if (command == null || command.length() == 0) {
|
||||
if (command.indexOf('!') != -1) {
|
||||
String last = VimPlugin.getProcess().getLastCommand();
|
||||
if (last == null || last.length() == 0) {
|
||||
VimPlugin.showMessage(MessageHelper.message(Msg.e_noprev));
|
||||
return false;
|
||||
}
|
||||
command = command.replaceAll("!", last);
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
Ranges ranges = cmd.getRanges();
|
||||
if (ranges.size() == 0) {
|
||||
// Show command output in a window
|
||||
String commandOutput = VimPlugin.getProcess().executeCommand(command, null);
|
||||
ExOutputModel.getInstance(editor).output(commandOutput);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Filter
|
||||
TextRange range = cmd.getTextRange(editor, context, false);
|
||||
return VimPlugin.getProcess().executeFilter(editor, range, command);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ExException(e.getMessage());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new ExException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.group.RegisterGroup;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -39,7 +38,7 @@ public class DeleteLinesHandler extends CommandHandler {
|
||||
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull ExCommand cmd) throws ExException {
|
||||
StringBuilder arg = new StringBuilder(cmd.getArgument());
|
||||
char register = RegisterGroup.REGISTER_DEFAULT;
|
||||
char register = VimPlugin.getRegister().getDefaultRegister();
|
||||
if (arg.length() > 0 && (arg.charAt(0) < '0' || arg.charAt(0) > '9')) {
|
||||
register = arg.charAt(0);
|
||||
arg.deleteCharAt(0);
|
||||
|
54
src/com/maddyhome/idea/vim/ex/handler/EchoHandler.java
Normal file
54
src/com/maddyhome/idea/vim/ex/handler/EchoHandler.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2015 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class EchoHandler extends CommandHandler {
|
||||
|
||||
public EchoHandler() {
|
||||
super("ec", "ho", RANGE_FORBIDDEN | ARGUMENT_OPTIONAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context,
|
||||
@NotNull ExCommand cmd) throws ExException {
|
||||
final String argument = cmd.getArgument();
|
||||
final VimScriptGlobalEnvironment env = VimScriptGlobalEnvironment.getInstance();
|
||||
final Map<String, Object> globals = env.getVariables();
|
||||
final Object value = VimScriptParser.evaluate(argument, globals);
|
||||
final String text = VimScriptParser.expressionToString(value) + "\n";
|
||||
ExOutputModel.getInstance(editor).output(text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
78
src/com/maddyhome/idea/vim/ex/handler/LetHandler.java
Normal file
78
src/com/maddyhome/idea/vim/ex/handler/LetHandler.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2015 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.handler;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class LetHandler extends CommandHandler implements VimScriptCommandHandler {
|
||||
private static Pattern SIMPLE_ASSIGNMENT = Pattern.compile("([A-Za-z_][A-Za-z_0-9]*)[ \\t]*=[ \\t]*(.*)");
|
||||
|
||||
public LetHandler() {
|
||||
super("let", "", RANGE_FORBIDDEN | ARGUMENT_OPTIONAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context,
|
||||
@NotNull ExCommand cmd) throws ExException {
|
||||
execute(cmd);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NotNull ExCommand cmd) throws ExException {
|
||||
final String argument = cmd.getArgument();
|
||||
if (argument.trim().isEmpty()) {
|
||||
showVariables();
|
||||
}
|
||||
else {
|
||||
final Matcher matcher = SIMPLE_ASSIGNMENT.matcher(argument);
|
||||
if (matcher.matches()) {
|
||||
final String name = matcher.group(1);
|
||||
// TODO: Check that 'name' is global
|
||||
final String expression = matcher.group(2);
|
||||
final VimScriptGlobalEnvironment env = VimScriptGlobalEnvironment.getInstance();
|
||||
final Map<String, Object> globals = env.getVariables();
|
||||
final Object value = VimScriptParser.evaluate(expression, globals);
|
||||
globals.put(name, value);
|
||||
}
|
||||
else {
|
||||
throw new ExException("Only simple '=' assignments are supported in 'let' expressions");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showVariables() throws ExException {
|
||||
throw new ExException("'let' without arguments is not supported yet");
|
||||
}
|
||||
}
|
@@ -24,6 +24,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.ex.*;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@@ -49,7 +49,7 @@ public class PutLinesHandler extends CommandHandler {
|
||||
}
|
||||
}
|
||||
else {
|
||||
registerGroup.selectRegister(RegisterGroup.REGISTER_DEFAULT);
|
||||
registerGroup.selectRegister(registerGroup.getDefaultRegister());
|
||||
}
|
||||
|
||||
final int offset = EditorHelper.getLineStartOffset(editor, line + 1);
|
||||
|
@@ -23,7 +23,7 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.ex.VimScriptCommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@@ -21,6 +21,8 @@ package com.maddyhome.idea.vim.ex.handler;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.ex.*;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
@@ -26,7 +26,6 @@ import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.CommandHandler;
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.group.RegisterGroup;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -39,7 +38,7 @@ public class YankLinesHandler extends CommandHandler {
|
||||
|
||||
public boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull ExCommand cmd) throws ExException {
|
||||
StringBuilder arg = new StringBuilder(cmd.getArgument());
|
||||
char register = RegisterGroup.REGISTER_DEFAULT;
|
||||
char register = VimPlugin.getRegister().getDefaultRegister();
|
||||
if (arg.length() > 0 && (arg.charAt(0) < '0' || arg.charAt(0) > '9')) {
|
||||
register = arg.charAt(0);
|
||||
arg.deleteCharAt(0);
|
||||
|
@@ -16,8 +16,10 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex;
|
||||
package com.maddyhome.idea.vim.ex.vimscript;
|
||||
|
||||
import com.maddyhome.idea.vim.ex.ExCommand;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||
* Copyright (C) 2003-2015 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex.vimscript;
|
||||
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class VimScriptGlobalEnvironment {
|
||||
private static final VimScriptGlobalEnvironment ourInstance = new VimScriptGlobalEnvironment();
|
||||
|
||||
private final Map<String, Object> myVariables = new HashMap<String, Object>();
|
||||
|
||||
private VimScriptGlobalEnvironment() {}
|
||||
|
||||
@NotNull
|
||||
public static VimScriptGlobalEnvironment getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getVariables() {
|
||||
return myVariables;
|
||||
}
|
||||
}
|
@@ -16,8 +16,12 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.maddyhome.idea.vim.ex;
|
||||
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;
|
||||
|
||||
@@ -25,6 +29,8 @@ import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -34,6 +40,9 @@ public class VimScriptParser {
|
||||
public static final String[] VIMRC_FILES = {".ideavimrc", "_ideavimrc"};
|
||||
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 VimScriptParser() {
|
||||
}
|
||||
@@ -86,6 +95,42 @@ public class VimScriptParser {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Object evaluate(@NotNull String expression, @NotNull Map<String, Object> globals) throws ExException {
|
||||
// This evaluator is very basic, no proper parsing whatsoever. It is here as the very first step necessary to
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
throw new ExException(String.format("Invalid expression: %s", expression));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String expressionToString(@NotNull Object value) throws ExException {
|
||||
// TODO: Return meaningful value representations
|
||||
if (value instanceof String) {
|
||||
return (String)value;
|
||||
}
|
||||
throw new ExException(String.format("Cannot convert '%s' to string", value));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String readFile(@NotNull File file) throws IOException {
|
||||
final BufferedReader reader = new BufferedReader(new FileReader(file));
|
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -36,6 +35,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
@@ -54,10 +54,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Provides all the insert/replace related functionality
|
||||
@@ -385,6 +382,8 @@ public class ChangeGroup {
|
||||
public void documentChanged(@NotNull DocumentEvent e) {
|
||||
final String newFragment = e.getNewFragment().toString();
|
||||
final String oldFragment = e.getOldFragment().toString();
|
||||
final int newFragmentLength = newFragment.length();
|
||||
final int oldFragmentLength = oldFragment.length();
|
||||
|
||||
// Repeat buffer limits
|
||||
if (repeatCharsCount > MAX_REPEAT_CHARS_COUNT) {
|
||||
@@ -394,41 +393,46 @@ public class ChangeGroup {
|
||||
// <Enter> is added to strokes as an action during processing in order to indent code properly in the repeat
|
||||
// command
|
||||
if (newFragment.startsWith("\n") && newFragment.trim().isEmpty()) {
|
||||
strokes.addAll(getAdjustCaretActions(e));
|
||||
oldOffset = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore multi-character indents as they should be inserted automatically while repeating <Enter> actions
|
||||
if (newFragment.length() > 1 && newFragment.trim().isEmpty()) {
|
||||
if (newFragmentLength > 1 && newFragment.trim().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int delta = e.getOffset() + oldFragment.length() - oldOffset;
|
||||
strokes.addAll(getAdjustCaretActions(e));
|
||||
|
||||
if (oldFragmentLength > 0) {
|
||||
final AnAction editorDelete = ActionManager.getInstance().getAction("EditorDelete");
|
||||
for (int i = 0; i < oldFragmentLength; i++) {
|
||||
strokes.add(editorDelete);
|
||||
}
|
||||
}
|
||||
|
||||
if (newFragmentLength > 0) {
|
||||
strokes.add(newFragment.toCharArray());
|
||||
}
|
||||
repeatCharsCount += newFragmentLength;
|
||||
oldOffset = e.getOffset() + newFragmentLength;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<AnAction> getAdjustCaretActions(DocumentEvent e) {
|
||||
final int delta = e.getOffset() - oldOffset;
|
||||
if (oldOffset >= 0 && delta != 0) {
|
||||
final List<AnAction> positionCaretActions = new ArrayList<AnAction>();
|
||||
final String motionName = delta < 0 ? "VimMotionLeft" : "VimMotionRight";
|
||||
final AnAction action = ActionManager.getInstance().getAction(motionName);
|
||||
final int count = Math.abs(delta);
|
||||
for (int i = 0; i < count; i++) {
|
||||
strokes.add(action);
|
||||
positionCaretActions.add(action);
|
||||
}
|
||||
return positionCaretActions;
|
||||
}
|
||||
|
||||
if (oldFragment.length() > 0) {
|
||||
final AnAction editorBackSpace = ActionManager.getInstance().getAction("EditorBackSpace");
|
||||
for (int i = 0; i < oldFragment.length(); i++) {
|
||||
strokes.add(editorBackSpace);
|
||||
}
|
||||
}
|
||||
|
||||
strokes.add(newFragment.toCharArray());
|
||||
repeatCharsCount += newFragment.length();
|
||||
|
||||
if (newFragment.length() > 0) {
|
||||
// TODO: If newFragment is shorter than oldFragment?
|
||||
oldOffset = e.getOffset() + newFragment.length();
|
||||
}
|
||||
else {
|
||||
oldOffset = e.getOffset() - oldFragment.length();
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,9 +500,7 @@ public class ChangeGroup {
|
||||
}
|
||||
else if (lastStroke instanceof char[]) {
|
||||
final char[] chars = (char[])lastStroke;
|
||||
for (char c : chars) {
|
||||
processKey(editor, context, KeyStroke.getKeyStroke(c));
|
||||
}
|
||||
insertText(editor, editor.getCaretModel().getOffset(), new String(chars));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -546,8 +548,10 @@ public class ChangeGroup {
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the user pressing the Enter key. If this is REPLACE mode we need to turn off OVERWRITE before and
|
||||
* then turn OVERWRITE back on after sending the "Enter" key.
|
||||
* Processes the Enter key by running the first successful action registered for "ENTER" keystroke.
|
||||
*
|
||||
* If this is REPLACE mode we need to turn off OVERWRITE before and then turn OVERWRITE back on after sending the
|
||||
* "ENTER" key.
|
||||
*
|
||||
* @param editor The editor to press "Enter" in
|
||||
* @param context The data context
|
||||
@@ -556,7 +560,13 @@ public class ChangeGroup {
|
||||
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.REPLACE) {
|
||||
KeyHandler.executeAction("EditorToggleInsertState", context);
|
||||
}
|
||||
KeyHandler.executeAction("EditorEnter", context);
|
||||
final KeyStroke enterKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
|
||||
final List<AnAction> actions = VimPlugin.getKey().getActions(editor.getComponent(), enterKeyStroke);
|
||||
for (AnAction action : actions) {
|
||||
if (KeyHandler.executeAction(action, context)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.REPLACE) {
|
||||
KeyHandler.executeAction("EditorToggleInsertState", context);
|
||||
}
|
||||
@@ -780,6 +790,9 @@ public class ChangeGroup {
|
||||
MotionGroup.moveCaret(editor, VimPlugin.getMotion().moveCaretToLineEnd(editor, startLine, true));
|
||||
for (int i = 1; i < count; i++) {
|
||||
int start = VimPlugin.getMotion().moveCaretToLineEnd(editor);
|
||||
int trailingWhitespaceStart = VimPlugin.getMotion().moveCaretToLineEndSkipLeading(editor);
|
||||
boolean hasTrailingWhitespace = start != trailingWhitespaceStart + 1;
|
||||
|
||||
MotionGroup.moveCaret(editor, start);
|
||||
int offset;
|
||||
if (spaces) {
|
||||
@@ -789,7 +802,7 @@ public class ChangeGroup {
|
||||
offset = VimPlugin.getMotion().moveCaretToLineStartOffset(editor);
|
||||
}
|
||||
deleteText(editor, new TextRange(editor.getCaretModel().getOffset(), offset), null);
|
||||
if (spaces) {
|
||||
if (spaces && !hasTrailingWhitespace) {
|
||||
insertText(editor, start, " ");
|
||||
MotionGroup.moveCaret(editor, VimPlugin.getMotion().moveCaretHorizontal(editor, -1, false));
|
||||
}
|
||||
@@ -1018,11 +1031,12 @@ public class ChangeGroup {
|
||||
* @return true if able to delete count lines, false if not
|
||||
*/
|
||||
public boolean changeLine(@NotNull Editor editor, @NotNull DataContext context, int count) {
|
||||
final LogicalPosition pos = editor.offsetToLogicalPosition(editor.getCaretModel().getOffset());
|
||||
final boolean insertBelow = pos.line + count >= EditorHelper.getLineCount(editor);
|
||||
|
||||
boolean res = deleteLine(editor, count);
|
||||
if (res) {
|
||||
final int lastLine = EditorHelper.getLineCount(editor) - 1;
|
||||
final LogicalPosition pos = editor.offsetToLogicalPosition(editor.getCaretModel().getOffset());
|
||||
if (pos.line >= lastLine) {
|
||||
if (insertBelow) {
|
||||
insertNewLineBelow(editor, context);
|
||||
}
|
||||
else {
|
||||
@@ -1070,13 +1084,13 @@ public class ChangeGroup {
|
||||
}
|
||||
String id = ActionManager.getInstance().getId(motion.getAction());
|
||||
boolean kludge = false;
|
||||
boolean bigWord = false;
|
||||
boolean bigWord = id.equals("VimMotionBigWordRight");
|
||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
final int offset = editor.getCaretModel().getOffset();
|
||||
final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), false);
|
||||
final CharacterHelper.CharacterType charType = CharacterHelper.charType(chars.charAt(offset), bigWord);
|
||||
if (EditorHelper.getFileSize(editor) > 0 && charType != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
final boolean lastWordChar = offset > EditorHelper.getFileSize(editor) ||
|
||||
CharacterHelper.charType(chars.charAt(offset + 1), false) != charType;
|
||||
CharacterHelper.charType(chars.charAt(offset + 1), bigWord) != charType;
|
||||
final ImmutableSet<String> wordMotions = ImmutableSet.of(
|
||||
"VimMotionWordRight", "VimMotionBigWordRight", "VimMotionCamelRight");
|
||||
if (wordMotions.contains(id) && lastWordChar) {
|
||||
@@ -1093,7 +1107,6 @@ public class ChangeGroup {
|
||||
}
|
||||
else if (id.equals("VimMotionBigWordRight")) {
|
||||
kludge = true;
|
||||
bigWord = true;
|
||||
motion.setAction(ActionManager.getInstance().getAction("VimMotionBigWordEndRight"));
|
||||
motion.setFlags(Command.FLAG_MOT_INCLUSIVE);
|
||||
}
|
||||
@@ -1282,18 +1295,14 @@ public class ChangeGroup {
|
||||
end = start;
|
||||
start = t;
|
||||
}
|
||||
end = EditorHelper.normalizeOffset(editor, end);
|
||||
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = start; i < end; i++) {
|
||||
if (i >= chars.length()) {
|
||||
break;
|
||||
}
|
||||
|
||||
char ch = CharacterHelper.changeCase(chars.charAt(i), type);
|
||||
if (ch != chars.charAt(i)) {
|
||||
replaceText(editor, i, i + 1, Character.toString(ch));
|
||||
}
|
||||
sb.append(CharacterHelper.changeCase(chars.charAt(i), type));
|
||||
}
|
||||
replaceText(editor, start, end, sb.toString());
|
||||
}
|
||||
|
||||
public void autoIndentLines(@NotNull DataContext context) {
|
||||
@@ -1337,10 +1346,6 @@ public class ChangeGroup {
|
||||
|
||||
int sline = editor.offsetToLogicalPosition(range.getStartOffset()).line;
|
||||
int eline = editor.offsetToLogicalPosition(range.getEndOffset()).line;
|
||||
int eoff = EditorHelper.getLineStartForOffset(editor, range.getEndOffset());
|
||||
if (eoff == range.getEndOffset()) {
|
||||
eline--;
|
||||
}
|
||||
|
||||
if (range.isMultiple()) {
|
||||
int col = editor.offsetToLogicalPosition(range.getStartOffset()).column;
|
||||
@@ -1400,10 +1405,11 @@ public class ChangeGroup {
|
||||
// Shift non-blockwise selection
|
||||
for (int l = sline; l <= eline; l++) {
|
||||
int soff = EditorHelper.getLineStartOffset(editor, l);
|
||||
int eoff = EditorHelper.getLineEndOffset(editor, l, true);
|
||||
int woff = VimPlugin.getMotion().moveCaretToLineStartSkipLeading(editor, l);
|
||||
int col = editor.offsetToVisualPosition(woff).column;
|
||||
int newCol = Math.max(0, col + dir * indentSize * count);
|
||||
if (dir == 1 || col > 0) {
|
||||
if (col > 0 || soff != eoff) {
|
||||
StringBuilder space = new StringBuilder();
|
||||
int tabCnt = 0;
|
||||
int spcCnt;
|
||||
@@ -1540,16 +1546,12 @@ public class ChangeGroup {
|
||||
private boolean sortTextRange(@NotNull Editor editor, int start, int end,
|
||||
@NotNull Comparator<String> lineComparator) {
|
||||
final String selectedText = editor.getDocument().getText(new TextRangeInterval(start, end));
|
||||
final String lineSeparator = CodeStyleSettingsManager.getSettings(editor.getProject()).getLineSeparator();
|
||||
final List<String> lines = Lists.newArrayList(Splitter.on(lineSeparator).split(selectedText));
|
||||
|
||||
final List<String> lines = Lists.newArrayList(Splitter.on("\n").split(selectedText));
|
||||
if (lines.size() < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Collections.sort(lines, lineComparator);
|
||||
replaceText(editor, start, end, Joiner.on(lineSeparator).join(lines));
|
||||
|
||||
replaceText(editor, start, end, StringUtil.join(lines, "\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -162,7 +162,10 @@ public class CopyGroup {
|
||||
}
|
||||
}
|
||||
else {
|
||||
pos = editor.getCaretModel().getOffset() + 1;
|
||||
pos = editor.getCaretModel().getOffset();
|
||||
if (!EditorHelper.isLineEmpty(editor, editor.getCaretModel().getLogicalPosition().line, false)) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
// In case when text is empty this can occur
|
||||
if (pos > 0 && pos > editor.getDocument().getTextLength()) {
|
||||
|
294
src/com/maddyhome/idea/vim/group/EditorGroup.java
Normal file
294
src/com/maddyhome/idea/vim/group/EditorGroup.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.*;
|
||||
import com.intellij.openapi.editor.colors.ColorKey;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorFontType;
|
||||
import com.intellij.openapi.editor.event.*;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.helper.*;
|
||||
import com.maddyhome.idea.vim.option.OptionChangeEvent;
|
||||
import com.maddyhome.idea.vim.option.OptionChangeListener;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class EditorGroup {
|
||||
private static final boolean BLOCK_CURSOR_VIM_VALUE = true;
|
||||
private static final boolean ANIMATED_SCROLLING_VIM_VALUE = false;
|
||||
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
|
||||
|
||||
private boolean isBlockCursor = false;
|
||||
private boolean isAnimatedScrolling = false;
|
||||
private boolean isRefrainFromScrolling = false;
|
||||
private Boolean isKeyRepeat = null;
|
||||
|
||||
private final CaretListener myLineNumbersCaretListener = new CaretAdapter() {
|
||||
@Override
|
||||
public void caretPositionChanged(CaretEvent e) {
|
||||
updateLineNumbers(e.getEditor());
|
||||
}
|
||||
};
|
||||
|
||||
private final LineNumbersGutterProvider myLineNumbersGutterProvider = new LineNumbersGutterProvider();
|
||||
|
||||
public EditorGroup() {
|
||||
final Options options = Options.getInstance();
|
||||
final OptionChangeListener numbersChangeListener = new OptionChangeListener() {
|
||||
@Override
|
||||
public void valueChange(OptionChangeEvent event) {
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
updateLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
};
|
||||
options.getOption(Options.NUMBER).addOptionChangeListener(numbersChangeListener);
|
||||
options.getOption(Options.RELATIVE_NUMBER).addOptionChangeListener(numbersChangeListener);
|
||||
|
||||
EventFacade.getInstance().addEditorFactoryListener(new EditorFactoryAdapter() {
|
||||
@Override
|
||||
public void editorCreated(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
isBlockCursor = editor.getSettings().isBlockCursor();
|
||||
isAnimatedScrolling = editor.getSettings().isAnimatedScrolling();
|
||||
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
|
||||
EditorData.initializeEditor(editor);
|
||||
DocumentManager.getInstance().addListeners(editor.getDocument());
|
||||
VimPlugin.getKey().registerRequiredShortcutKeys(editor);
|
||||
|
||||
if (VimPlugin.isEnabled()) {
|
||||
initLineNumbers(editor);
|
||||
// Turn on insert mode if editor doesn't have any file
|
||||
if (!EditorData.isFileEditor(editor) && editor.getDocument().isWritable() &&
|
||||
!CommandState.inInsertMode(editor)) {
|
||||
KeyHandler.getInstance().handleKey(editor, KeyStroke.getKeyStroke('i'), new EditorDataContext(editor));
|
||||
}
|
||||
editor.getSettings().setBlockCursor(!CommandState.inInsertMode(editor));
|
||||
editor.getSettings().setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
||||
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void editorReleased(@NotNull EditorFactoryEvent event) {
|
||||
final Editor editor = event.getEditor();
|
||||
deinitLineNumbers(editor);
|
||||
EditorData.unInitializeEditor(editor);
|
||||
VimPlugin.getKey().unregisterShortcutKeys(editor);
|
||||
editor.getSettings().setAnimatedScrolling(isAnimatedScrolling);
|
||||
editor.getSettings().setRefrainFromScrolling(isRefrainFromScrolling);
|
||||
DocumentManager.getInstance().removeListeners(editor.getDocument());
|
||||
}
|
||||
}, ApplicationManager.getApplication());
|
||||
}
|
||||
|
||||
public void turnOn() {
|
||||
setCursors(BLOCK_CURSOR_VIM_VALUE);
|
||||
setAnimatedScrolling(ANIMATED_SCROLLING_VIM_VALUE);
|
||||
setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
|
||||
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
if (!EditorData.getEditorGroup(editor)) {
|
||||
initLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void turnOff() {
|
||||
setCursors(isBlockCursor);
|
||||
setAnimatedScrolling(isAnimatedScrolling);
|
||||
setRefrainFromScrolling(isRefrainFromScrolling);
|
||||
|
||||
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
|
||||
deinitLineNumbers(editor);
|
||||
}
|
||||
}
|
||||
|
||||
private void initLineNumbers(@NotNull final Editor editor) {
|
||||
editor.getCaretModel().addCaretListener(myLineNumbersCaretListener);
|
||||
EditorData.setEditorGroup(editor, true);
|
||||
|
||||
final EditorSettings settings = editor.getSettings();
|
||||
EditorData.setLineNumbersShown(editor, settings.isLineNumbersShown());
|
||||
updateLineNumbers(editor);
|
||||
}
|
||||
|
||||
private void deinitLineNumbers(@NotNull Editor editor) {
|
||||
editor.getCaretModel().removeCaretListener(myLineNumbersCaretListener);
|
||||
EditorData.setEditorGroup(editor, false);
|
||||
|
||||
editor.getGutter().closeAllAnnotations();
|
||||
editor.getSettings().setLineNumbersShown(EditorData.isLineNumbersShown(editor));
|
||||
}
|
||||
|
||||
private void updateLineNumbers(@NotNull Editor editor) {
|
||||
if (!EditorData.isFileEditor(editor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Options options = Options.getInstance();
|
||||
final boolean relativeLineNumber = options.isSet(Options.RELATIVE_NUMBER);
|
||||
final boolean lineNumber = options.isSet(Options.NUMBER);
|
||||
|
||||
final EditorSettings settings = editor.getSettings();
|
||||
final boolean showEditorLineNumbers = (EditorData.isLineNumbersShown(editor) || lineNumber) && !relativeLineNumber;
|
||||
|
||||
if (settings.isLineNumbersShown() ^ showEditorLineNumbers) {
|
||||
// Update line numbers later since it may be called from a caret listener
|
||||
// on the caret move and it may move the caret internally
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
settings.setLineNumbersShown(showEditorLineNumbers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (relativeLineNumber) {
|
||||
final EditorGutter gutter = editor.getGutter();
|
||||
gutter.closeAllAnnotations();
|
||||
gutter.registerTextAnnotation(myLineNumbersGutterProvider);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCursors(boolean isBlock) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
// Vim plugin should be turned on in insert mode
|
||||
((EditorEx)editor).setInsertMode(true);
|
||||
editor.getSettings().setBlockCursor(isBlock);
|
||||
}
|
||||
}
|
||||
|
||||
private void setAnimatedScrolling(boolean isOn) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
editor.getSettings().setAnimatedScrolling(isOn);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRefrainFromScrolling(boolean isOn) {
|
||||
Editor[] editors = EditorFactory.getInstance().getAllEditors();
|
||||
for (Editor editor : editors) {
|
||||
editor.getSettings().setRefrainFromScrolling(isOn);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData(@NotNull Element element) {
|
||||
if (isKeyRepeat != null) {
|
||||
final Element editor = new Element("editor");
|
||||
element.addContent(editor);
|
||||
final Element keyRepeat = new Element("key-repeat");
|
||||
keyRepeat.setAttribute("enabled", Boolean.toString(isKeyRepeat));
|
||||
editor.addContent(keyRepeat);
|
||||
}
|
||||
}
|
||||
|
||||
public void readData(@NotNull Element element) {
|
||||
final Element editor = element.getChild("editor");
|
||||
if (editor != null) {
|
||||
final Element keyRepeat = editor.getChild("key-repeat");
|
||||
if (keyRepeat != null) {
|
||||
final String enabled = keyRepeat.getAttributeValue("enabled");
|
||||
if (enabled != null) {
|
||||
isKeyRepeat = Boolean.valueOf(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Boolean isKeyRepeat() {
|
||||
return isKeyRepeat;
|
||||
}
|
||||
|
||||
public void setKeyRepeat(@Nullable Boolean value) {
|
||||
this.isKeyRepeat = value;
|
||||
}
|
||||
|
||||
private static class LineNumbersGutterProvider implements TextAnnotationGutterProvider {
|
||||
@Nullable
|
||||
@Override
|
||||
public String getLineText(int line, @NotNull Editor editor) {
|
||||
if (VimPlugin.isEnabled() && EditorData.isFileEditor(editor)) {
|
||||
final Options options = Options.getInstance();
|
||||
final boolean relativeLineNumber = options.isSet(Options.RELATIVE_NUMBER);
|
||||
final boolean lineNumber = options.isSet(Options.NUMBER);
|
||||
if (relativeLineNumber && lineNumber && isCaretLine(line, editor)) {
|
||||
return lineNumberToString(getLineNumber(line), editor);
|
||||
}
|
||||
else if (relativeLineNumber) {
|
||||
return lineNumberToString(getRelativeLineNumber(line, editor), editor);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isCaretLine(int line, @NotNull Editor editor) {
|
||||
return line == editor.getCaretModel().getLogicalPosition().line;
|
||||
}
|
||||
|
||||
private int getLineNumber(int line) {
|
||||
return line + 1;
|
||||
}
|
||||
|
||||
private int getRelativeLineNumber(int line, @NotNull Editor editor) {
|
||||
final int visualLine = EditorHelper.logicalLineToVisualLine(editor, line);
|
||||
final int currentLine = editor.getCaretModel().getLogicalPosition().line;
|
||||
final int currentVisualLine = EditorHelper.logicalLineToVisualLine(editor, currentLine);
|
||||
return Math.abs(currentVisualLine - visualLine);
|
||||
}
|
||||
|
||||
private String lineNumberToString(int lineNumber, @NotNull Editor editor) {
|
||||
final int lineCount = editor.getDocument().getLineCount();
|
||||
final int digitsCount = (int)Math.ceil(Math.log10(lineCount));
|
||||
return StringHelper.leftJustify("" + lineNumber, digitsCount, ' ');
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getToolTip(int line, Editor editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EditorFontType getStyle(int line, Editor editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ColorKey getColor(int line, Editor editor) {
|
||||
return EditorColors.LINE_NUMBERS_COLOR;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Color getBgColor(int line, Editor editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AnAction> getPopupActions(int line, Editor editor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gutterClosed() {
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,9 +21,11 @@ package com.maddyhome.idea.vim.group;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.actionSystem.ex.ActionManagerEx;
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.keymap.Keymap;
|
||||
import com.intellij.openapi.keymap.KeymapManager;
|
||||
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
|
||||
import com.maddyhome.idea.vim.EventFacade;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
@@ -41,8 +43,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.leftJustify;
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.toKeyNotation;
|
||||
@@ -429,4 +433,48 @@ public class KeyGroup {
|
||||
// TODO: Add more codes
|
||||
return "";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<AnAction> getActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||
final List<AnAction> results = new ArrayList<AnAction>();
|
||||
results.addAll(getLocalActions(component, keyStroke));
|
||||
results.addAll(getKeymapActions(keyStroke));
|
||||
return results;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<AnAction> getLocalActions(@NotNull Component component, @NotNull KeyStroke keyStroke) {
|
||||
final List<AnAction> results = new ArrayList<AnAction>();
|
||||
final KeyboardShortcut keyStrokeShortcut = new KeyboardShortcut(keyStroke, null);
|
||||
for (Component c = component; c != null; c = c.getParent()) {
|
||||
if (c instanceof JComponent) {
|
||||
final List<AnAction> actions = ActionUtil.getActions((JComponent)c);
|
||||
for (AnAction action : actions) {
|
||||
if (action instanceof VimShortcutKeyAction) {
|
||||
continue;
|
||||
}
|
||||
final com.intellij.openapi.actionSystem.Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
|
||||
for (com.intellij.openapi.actionSystem.Shortcut shortcut : shortcuts) {
|
||||
if (shortcut.isKeyboard() && shortcut.startsWith(keyStrokeShortcut) && !results.contains(action)) {
|
||||
results.add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<AnAction> getKeymapActions(@NotNull KeyStroke keyStroke) {
|
||||
final List<AnAction> results = new ArrayList<AnAction>();
|
||||
final Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
|
||||
for (String id : keymap.getActionIds(keyStroke)) {
|
||||
final AnAction action = ActionManager.getInstance().getAction(id);
|
||||
if (action != null) {
|
||||
results.add(action);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,8 @@ import com.intellij.openapi.fileEditor.FileEditor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerAdapter;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.fileEditor.TextEditor;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer;
|
||||
import com.intellij.openapi.fileEditor.impl.EditorWindow;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.maddyhome.idea.vim.EventFacade;
|
||||
@@ -140,7 +142,7 @@ public class MotionGroup {
|
||||
*/
|
||||
private void processMouseClick(@NotNull Editor editor, @NotNull MouseEvent event) {
|
||||
if (ExEntryPanel.getInstance().isActive()) {
|
||||
ExEntryPanel.getInstance().deactivate();
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
}
|
||||
|
||||
ExOutputModel.getInstance(editor).clear();
|
||||
@@ -203,7 +205,7 @@ public class MotionGroup {
|
||||
*/
|
||||
private void processLineSelection(@NotNull Editor editor, boolean update) {
|
||||
if (ExEntryPanel.getInstance().isActive()) {
|
||||
ExEntryPanel.getInstance().deactivate();
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
}
|
||||
|
||||
ExOutputModel.getInstance(editor).clear();
|
||||
@@ -233,7 +235,7 @@ public class MotionGroup {
|
||||
|
||||
private void processMouseReleased(@NotNull Editor editor, @NotNull CommandState.SubMode mode, int startOff, int endOff) {
|
||||
if (ExEntryPanel.getInstance().isActive()) {
|
||||
ExEntryPanel.getInstance().deactivate();
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
}
|
||||
|
||||
ExOutputModel.getInstance(editor).clear();
|
||||
@@ -678,6 +680,7 @@ public class MotionGroup {
|
||||
|
||||
public int repeatLastMatchChar(@NotNull Editor editor, int count) {
|
||||
int res = -1;
|
||||
int startPos = editor.getCaretModel().getOffset();
|
||||
switch (lastFTCmd) {
|
||||
case LAST_F:
|
||||
res = moveCaretToNextCharacterOnLine(editor, -count, lastFTChar);
|
||||
@@ -687,9 +690,15 @@ public class MotionGroup {
|
||||
break;
|
||||
case LAST_T:
|
||||
res = moveCaretToBeforeNextCharacterOnLine(editor, -count, lastFTChar);
|
||||
if (res == startPos && Math.abs(count) == 1) {
|
||||
res = moveCaretToBeforeNextCharacterOnLine(editor, 2 * count, lastFTChar);
|
||||
}
|
||||
break;
|
||||
case LAST_t:
|
||||
res = moveCaretToBeforeNextCharacterOnLine(editor, count, lastFTChar);
|
||||
if (res == startPos && Math.abs(count) == 1) {
|
||||
res = moveCaretToBeforeNextCharacterOnLine(editor, 2 * count, lastFTChar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1036,6 +1045,11 @@ public class MotionGroup {
|
||||
return EditorHelper.getLeadingCharacterOffset(editor, line);
|
||||
}
|
||||
|
||||
public int moveCaretToLineEndSkipLeading(@NotNull Editor editor) {
|
||||
int logicalLine = editor.getCaretModel().getLogicalPosition().line;
|
||||
return moveCaretToLineEndSkipLeading(editor, logicalLine);
|
||||
}
|
||||
|
||||
public int moveCaretToLineEndSkipLeading(@NotNull Editor editor, int line) {
|
||||
int start = EditorHelper.getLineStartOffset(editor, line);
|
||||
int end = EditorHelper.getLineEndOffset(editor, line, true);
|
||||
@@ -1186,8 +1200,12 @@ public class MotionGroup {
|
||||
}
|
||||
|
||||
public static void moveCaret(@NotNull Editor editor, int offset) {
|
||||
moveCaret(editor, offset, false);
|
||||
}
|
||||
|
||||
private static void moveCaret(@NotNull Editor editor, int offset, boolean forceKeepVisual) {
|
||||
if (offset >= 0 && offset <= editor.getDocument().getTextLength()) {
|
||||
final boolean keepVisual = keepVisual(editor);
|
||||
final boolean keepVisual = forceKeepVisual || keepVisual(editor);
|
||||
if (editor.getCaretModel().getOffset() != offset) {
|
||||
if (!keepVisual) {
|
||||
// XXX: Hack for preventing the merge multiple carets that results in loosing the primary caret for |v_d|
|
||||
@@ -1218,17 +1236,32 @@ public class MotionGroup {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int moveCaretGotoPreviousTab(@NotNull Editor editor, @NotNull DataContext context) {
|
||||
final AnAction previousTab = ActionManager.getInstance().getAction("PreviousTab");
|
||||
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
|
||||
previousTab.actionPerformed(e);
|
||||
/**
|
||||
* If 'absolute' is true, then set tab index to 'value', otherwise add 'value' to tab index with wraparound.
|
||||
*/
|
||||
private void switchEditorTab(@Nullable EditorWindow editorWindow, int value, boolean absolute) {
|
||||
if (editorWindow != null) {
|
||||
final EditorTabbedContainer tabbedPane = editorWindow.getTabbedPane();
|
||||
if (tabbedPane != null) {
|
||||
if (absolute) {
|
||||
tabbedPane.setSelectedIndex(value);
|
||||
}
|
||||
else {
|
||||
int tabIndex = (value + tabbedPane.getSelectedIndex()) % tabbedPane.getTabCount();
|
||||
tabbedPane.setSelectedIndex(tabIndex < 0 ? tabIndex + tabbedPane.getTabCount() : tabIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int moveCaretGotoPreviousTab(@NotNull Editor editor, @NotNull DataContext context, int rawCount) {
|
||||
switchEditorTab(EditorWindow.DATA_KEY.getData(context), rawCount >= 1 ? -rawCount : -1, false);
|
||||
return editor.getCaretModel().getOffset();
|
||||
}
|
||||
|
||||
public int moveCaretGotoNextTab(@NotNull Editor editor, @NotNull DataContext context) {
|
||||
final AnAction nextTab = ActionManager.getInstance().getAction("NextTab");
|
||||
final AnActionEvent e = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0);
|
||||
nextTab.actionPerformed(e);
|
||||
public int moveCaretGotoNextTab(@NotNull Editor editor, @NotNull DataContext context, int rawCount) {
|
||||
final boolean absolute = rawCount >= 1;
|
||||
switchEditorTab(EditorWindow.DATA_KEY.getData(context), absolute ? rawCount - 1 : 1, absolute);
|
||||
return editor.getCaretModel().getOffset();
|
||||
}
|
||||
|
||||
@@ -1461,7 +1494,7 @@ public class MotionGroup {
|
||||
CommandState.getInstance(editor).pushState(CommandState.Mode.VISUAL, mode, MappingMode.VISUAL);
|
||||
visualStart = start;
|
||||
updateSelection(editor, end);
|
||||
MotionGroup.moveCaret(editor, visualEnd);
|
||||
MotionGroup.moveCaret(editor, visualEnd, true);
|
||||
}
|
||||
else if (mode == currentMode) {
|
||||
exitVisual(editor);
|
||||
@@ -1572,21 +1605,8 @@ public class MotionGroup {
|
||||
|
||||
@NotNull
|
||||
public TextRange getVisualRange(@NotNull Editor editor) {
|
||||
final TextRange res = new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
|
||||
editor.getSelectionModel().getBlockSelectionEnds());
|
||||
// If the last left/right motion was the $ command, simulate each line being selected to end-of-line
|
||||
final CommandState.SubMode subMode = CommandState.getInstance(editor).getSubMode();
|
||||
if (subMode == CommandState.SubMode.VISUAL_BLOCK && EditorData.getLastColumn(editor) >= MotionGroup.LAST_COLUMN) {
|
||||
final int[] starts = res.getStartOffsets();
|
||||
final int[] ends = res.getEndOffsets();
|
||||
for (int i = 0; i < starts.length; i++) {
|
||||
if (ends[i] > starts[i]) {
|
||||
ends[i] = EditorHelper.getLineEndForOffset(editor, starts[i]);
|
||||
}
|
||||
}
|
||||
return new TextRange(starts, ends);
|
||||
}
|
||||
return res;
|
||||
return new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
|
||||
editor.getSelectionModel().getBlockSelectionEnds());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -1594,6 +1614,10 @@ public class MotionGroup {
|
||||
return new TextRange(visualStart, visualEnd);
|
||||
}
|
||||
|
||||
public void updateSelection(@NotNull Editor editor) {
|
||||
updateSelection(editor, visualEnd);
|
||||
}
|
||||
|
||||
private void updateSelection(@NotNull Editor editor, int offset) {
|
||||
visualEnd = offset;
|
||||
visualOffset = offset;
|
||||
@@ -1614,14 +1638,38 @@ public class MotionGroup {
|
||||
editor.getSelectionModel().setSelection(start, end);
|
||||
}
|
||||
else if (subMode == CommandState.SubMode.VISUAL_LINE) {
|
||||
if (start > end) {
|
||||
int t = start;
|
||||
start = end;
|
||||
end = t;
|
||||
}
|
||||
start = EditorHelper.getLineStartForOffset(editor, start);
|
||||
end = EditorHelper.getLineEndForOffset(editor, end);
|
||||
editor.getSelectionModel().setSelection(start, end);
|
||||
}
|
||||
else if (subMode == CommandState.SubMode.VISUAL_BLOCK) {
|
||||
final LogicalPosition lineStart = editor.offsetToLogicalPosition(start);
|
||||
final LogicalPosition lineEnd = editor.offsetToLogicalPosition(end);
|
||||
editor.getSelectionModel().setBlockSelection(lineStart, lineEnd);
|
||||
LogicalPosition blockStart = editor.offsetToLogicalPosition(start);
|
||||
LogicalPosition blockEnd = editor.offsetToLogicalPosition(end);
|
||||
if (blockStart.column < blockEnd.column) {
|
||||
blockEnd = new LogicalPosition(blockEnd.line, blockEnd.column + 1);
|
||||
}
|
||||
else {
|
||||
blockStart = new LogicalPosition(blockStart.line, blockStart.column + 1);
|
||||
}
|
||||
editor.getSelectionModel().setBlockSelection(blockStart, blockEnd);
|
||||
|
||||
for (Caret caret : editor.getCaretModel().getAllCarets()) {
|
||||
int line = caret.getLogicalPosition().line;
|
||||
int lineEndOffset = EditorHelper.getLineEndOffset(editor, line, true);
|
||||
|
||||
if (EditorData.getLastColumn(editor) >= MotionGroup.LAST_COLUMN) {
|
||||
caret.setSelection(caret.getSelectionStart(), lineEndOffset);
|
||||
}
|
||||
if (!EditorHelper.isLineEmpty(editor, line, false)) {
|
||||
caret.moveToOffset(caret.getSelectionEnd() - 1);
|
||||
}
|
||||
}
|
||||
editor.getCaretModel().moveToOffset(end);
|
||||
}
|
||||
|
||||
VimPlugin.getMark().setVisualSelectionMarks(editor, new TextRange(start, end));
|
||||
@@ -1676,7 +1724,7 @@ public class MotionGroup {
|
||||
public static class MotionEditorChange extends FileEditorManagerAdapter {
|
||||
public void selectionChanged(@NotNull FileEditorManagerEvent event) {
|
||||
if (ExEntryPanel.getInstance().isActive()) {
|
||||
ExEntryPanel.getInstance().deactivate();
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
}
|
||||
final FileEditor fileEditor = event.getOldEditor();
|
||||
if (fileEditor instanceof TextEditor) {
|
||||
@@ -1704,7 +1752,6 @@ public class MotionGroup {
|
||||
for (Editor e : EditorFactory.getInstance().getEditors(editor.getDocument())) {
|
||||
if (!e.equals(editor)) {
|
||||
e.getSelectionModel().setSelection(newRange.getStartOffset(), newRange.getEndOffset());
|
||||
e.getCaretModel().moveToOffset(editor.getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1770,7 +1817,7 @@ public class MotionGroup {
|
||||
}
|
||||
|
||||
@Nullable private Editor dragEditor = null;
|
||||
@NotNull private CommandState.SubMode mode;
|
||||
@NotNull private CommandState.SubMode mode = CommandState.SubMode.NONE;
|
||||
private int startOff;
|
||||
private int endOff;
|
||||
}
|
||||
|
@@ -19,13 +19,9 @@
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.PlatformDataKeys;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.text.CharSequenceReader;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.Command;
|
||||
@@ -34,9 +30,10 @@ import com.maddyhome.idea.vim.command.MappingMode;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.CommandParser;
|
||||
import com.maddyhome.idea.vim.ex.ExException;
|
||||
import com.maddyhome.idea.vim.helper.EditorData;
|
||||
import com.maddyhome.idea.vim.helper.UiHelper;
|
||||
import com.maddyhome.idea.vim.ui.ExEntryPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
@@ -67,17 +64,7 @@ public class ProcessGroup {
|
||||
|
||||
public String endSearchCommand(@NotNull final Editor editor, @NotNull DataContext context) {
|
||||
ExEntryPanel panel = ExEntryPanel.getInstance();
|
||||
panel.deactivate();
|
||||
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(context); // API change - don't merge
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
VirtualFile vf = EditorData.getVirtualFile(editor);
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode() && vf != null) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
panel.deactivate(true);
|
||||
|
||||
record(editor, panel.getText());
|
||||
return panel.getText();
|
||||
@@ -101,7 +88,7 @@ public class ProcessGroup {
|
||||
|
||||
ExEntryPanel panel = ExEntryPanel.getInstance();
|
||||
if (panel.isActive()) {
|
||||
panel.requestFocus();
|
||||
UiHelper.requestFocus(panel);
|
||||
panel.handleKey(stroke);
|
||||
|
||||
return true;
|
||||
@@ -115,7 +102,7 @@ public class ProcessGroup {
|
||||
|
||||
public boolean processExEntry(@NotNull final Editor editor, @NotNull final DataContext context) {
|
||||
ExEntryPanel panel = ExEntryPanel.getInstance();
|
||||
panel.deactivate();
|
||||
panel.deactivate(true);
|
||||
boolean res = true;
|
||||
int flags = 0;
|
||||
try {
|
||||
@@ -151,23 +138,6 @@ public class ProcessGroup {
|
||||
VimPlugin.indicateError();
|
||||
res = false;
|
||||
}
|
||||
finally {
|
||||
final int flg = flags;
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(context); // API change - don't merge
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
//editor.getContentComponent().requestFocus();
|
||||
// Reopening the file was the only way I could solve the focus problem introduced in IDEA at
|
||||
// version 1050.
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode() && (flg & CommandParser.RES_DONT_REOPEN) == 0) {
|
||||
VirtualFile vf = EditorData.getVirtualFile(editor);
|
||||
if (vf != null) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -176,17 +146,7 @@ public class ProcessGroup {
|
||||
CommandState.getInstance(editor).popState();
|
||||
KeyHandler.getInstance().reset(editor);
|
||||
ExEntryPanel panel = ExEntryPanel.getInstance();
|
||||
panel.deactivate();
|
||||
final Project project = PlatformDataKeys.PROJECT.getData(context); // API change - don't merge
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
//editor.getContentComponent().requestFocus();
|
||||
VirtualFile vf = EditorData.getVirtualFile(editor);
|
||||
if (vf != null) {
|
||||
FileEditorManager.getInstance(project).openFile(vf, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
panel.deactivate(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -222,33 +182,37 @@ public class ProcessGroup {
|
||||
return initText;
|
||||
}
|
||||
|
||||
public boolean executeFilter(@NotNull Editor editor, @NotNull TextRange range, String command) throws IOException {
|
||||
if (logger.isDebugEnabled()) logger.debug("command=" + command);
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
StringReader car = new StringReader(chars.subSequence(range.getStartOffset(),
|
||||
range.getEndOffset()).toString());
|
||||
StringWriter sw = new StringWriter();
|
||||
public boolean executeFilter(@NotNull Editor editor, @NotNull TextRange range,
|
||||
@NotNull String command) throws IOException {
|
||||
final CharSequence charsSequence = editor.getDocument().getCharsSequence();
|
||||
final int startOffset = range.getStartOffset();
|
||||
final int endOffset = range.getEndOffset();
|
||||
final String output = executeCommand(command, charsSequence.subSequence(startOffset, endOffset));
|
||||
editor.getDocument().replaceString(startOffset, endOffset, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.debug("about to create filter");
|
||||
Process filter = Runtime.getRuntime().exec(command);
|
||||
logger.debug("filter created");
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(filter.getOutputStream()));
|
||||
logger.debug("sending text");
|
||||
copy(car, writer);
|
||||
@NotNull
|
||||
public String executeCommand(@NotNull String command, @Nullable CharSequence input) throws IOException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("command=" + command);
|
||||
}
|
||||
|
||||
final Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
if (input != null) {
|
||||
final BufferedWriter outputWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
|
||||
copy(new CharSequenceReader(input), outputWriter);
|
||||
outputWriter.close();
|
||||
}
|
||||
|
||||
final BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
final StringWriter writer = new StringWriter();
|
||||
copy(inputReader, writer);
|
||||
writer.close();
|
||||
logger.debug("sent");
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(filter.getInputStream()));
|
||||
logger.debug("getting result");
|
||||
copy(reader, sw);
|
||||
sw.close();
|
||||
logger.debug("received");
|
||||
|
||||
editor.getDocument().replaceString(range.getStartOffset(), range.getEndOffset(), sw.toString());
|
||||
|
||||
lastCommand = command;
|
||||
|
||||
return true;
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
private void copy(@NotNull Reader from, @NotNull Writer to) throws IOException {
|
||||
|
@@ -18,8 +18,12 @@
|
||||
|
||||
package com.maddyhome.idea.vim.group;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.command.SelectionType;
|
||||
@@ -27,6 +31,10 @@ import com.maddyhome.idea.vim.common.Register;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.helper.EditorHelper;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
import com.maddyhome.idea.vim.option.ListOption;
|
||||
import com.maddyhome.idea.vim.option.OptionChangeEvent;
|
||||
import com.maddyhome.idea.vim.option.OptionChangeListener;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import com.maddyhome.idea.vim.ui.ClipboardHandler;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -34,33 +42,45 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This group works with command associated with copying and pasting text
|
||||
*/
|
||||
public class RegisterGroup {
|
||||
/**
|
||||
* The register key for the default register
|
||||
*/
|
||||
public static final char REGISTER_DEFAULT = '"';
|
||||
|
||||
private static final String WRITABLE_REGISTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-*+_/\"";
|
||||
private static final String READONLY_REGISTERS = ":.%#=/";
|
||||
private static final String RECORDABLE_REGISTER = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
private static final String PLAYBACK_REGISTER = RECORDABLE_REGISTER + "\".*+";
|
||||
private static final String VALID_REGISTERS = WRITABLE_REGISTERS + READONLY_REGISTERS;
|
||||
|
||||
private static final List<Character> CLIPBOARD_REGISTERS = ImmutableList.of('*', '+');
|
||||
private static final Logger logger = Logger.getInstance(RegisterGroup.class.getName());
|
||||
|
||||
private char lastRegister = REGISTER_DEFAULT;
|
||||
private char defaultRegister = '"';
|
||||
private char lastRegister = defaultRegister;
|
||||
@NotNull private final HashMap<Character, Register> registers = new HashMap<Character, Register>();
|
||||
private char recordRegister = 0;
|
||||
@Nullable private List<KeyStroke> recordList = null;
|
||||
public RegisterGroup() {}
|
||||
|
||||
public RegisterGroup() {
|
||||
final ListOption clipboardOption = Options.getInstance().getListOption(Options.CLIPBOARD);
|
||||
if (clipboardOption != null) {
|
||||
clipboardOption.addOptionChangeListener(new OptionChangeListener() {
|
||||
public void valueChange(OptionChangeEvent event) {
|
||||
if (clipboardOption.contains("unnamed")) {
|
||||
defaultRegister = '*';
|
||||
}
|
||||
else if (clipboardOption.contains("unnamedplus")) {
|
||||
defaultRegister = '+';
|
||||
}
|
||||
else {
|
||||
defaultRegister = '"';
|
||||
}
|
||||
lastRegister = defaultRegister;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the last selected register can be written to.
|
||||
@@ -91,7 +111,7 @@ public class RegisterGroup {
|
||||
* Reset the selected register back to the default register.
|
||||
*/
|
||||
public void resetRegister() {
|
||||
lastRegister = REGISTER_DEFAULT;
|
||||
lastRegister = defaultRegister;
|
||||
logger.debug("register reset");
|
||||
}
|
||||
|
||||
@@ -147,7 +167,7 @@ public class RegisterGroup {
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
|
||||
}
|
||||
}
|
||||
else if (register == '*' || register == '+') {
|
||||
else if (CLIPBOARD_REGISTERS.contains(register)) {
|
||||
ClipboardHandler.setClipboardText(text);
|
||||
}
|
||||
// Put the text in the specified register
|
||||
@@ -157,8 +177,8 @@ public class RegisterGroup {
|
||||
}
|
||||
|
||||
// Also add it to the default register if the default wasn't specified
|
||||
if (register != REGISTER_DEFAULT && ".:/".indexOf(register) == -1) {
|
||||
registers.put(REGISTER_DEFAULT, new Register(REGISTER_DEFAULT, type, text));
|
||||
if (register != defaultRegister && ".:/".indexOf(register) == -1) {
|
||||
registers.put(defaultRegister, new Register(defaultRegister, type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
|
||||
}
|
||||
|
||||
@@ -181,7 +201,7 @@ public class RegisterGroup {
|
||||
}
|
||||
}
|
||||
// Yanks also go to register 0 if the default register was used
|
||||
else if (register == REGISTER_DEFAULT) {
|
||||
else if (register == defaultRegister) {
|
||||
registers.put('0', new Register('0', type, text));
|
||||
if (logger.isDebugEnabled()) logger.debug("register '" + '0' + "' contains: \"" + text + "\"");
|
||||
}
|
||||
@@ -220,19 +240,7 @@ public class RegisterGroup {
|
||||
if (Character.isUpperCase(r)) {
|
||||
r = Character.toLowerCase(r);
|
||||
}
|
||||
|
||||
Register reg = null;
|
||||
if (r == '*' || r == '+') {
|
||||
String text = ClipboardHandler.getClipboardText();
|
||||
if (text != null) {
|
||||
reg = new Register(r, SelectionType.CHARACTER_WISE, text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
reg = registers.get(new Character(r));
|
||||
}
|
||||
|
||||
return reg;
|
||||
return CLIPBOARD_REGISTERS.contains(r) ? refreshClipboardRegister(r) : registers.get(new Character(r));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,11 +252,23 @@ public class RegisterGroup {
|
||||
return lastRegister;
|
||||
}
|
||||
|
||||
/**
|
||||
* The register key for the default register.
|
||||
*/
|
||||
public char getDefaultRegister() {
|
||||
return defaultRegister;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Register> getRegisters() {
|
||||
ArrayList<Register> res = new ArrayList<Register>(registers.values());
|
||||
final List<Register> res = new ArrayList<Register>(registers.values());
|
||||
for (Character r : CLIPBOARD_REGISTERS) {
|
||||
final Register register = refreshClipboardRegister(r);
|
||||
if (register != null) {
|
||||
res.add(register);
|
||||
}
|
||||
}
|
||||
Collections.sort(res, new Register.KeySorter<Register>());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -375,4 +395,33 @@ public class RegisterGroup {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Register refreshClipboardRegister(char r) {
|
||||
final String text = ClipboardHandler.getClipboardText();
|
||||
if (text != null) {
|
||||
return new Register(r, guessSelectionType(text), text);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SelectionType guessSelectionType(@NotNull String text) {
|
||||
final String[] lines = StringUtil.splitByLines(text);
|
||||
final HashSet<Integer> lengths = new HashSet<Integer>(ContainerUtil.map(lines, new Function<String, Integer>() {
|
||||
@Override
|
||||
public Integer fun(String s) {
|
||||
return s.length();
|
||||
}
|
||||
}));
|
||||
if (lines.length > 1 && lengths.size() == 1) {
|
||||
return SelectionType.BLOCK_WISE;
|
||||
}
|
||||
else if (text.endsWith("\n")) {
|
||||
return SelectionType.LINE_WISE;
|
||||
}
|
||||
else {
|
||||
return SelectionType.CHARACTER_WISE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.Command;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.command.SelectionType;
|
||||
import com.maddyhome.idea.vim.common.CharacterPosition;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
@@ -93,6 +94,11 @@ public class SearchGroup {
|
||||
public boolean searchAndReplace(@NotNull Editor editor, @NotNull LineRange range, @NotNull String excmd, String exarg) {
|
||||
boolean res = true;
|
||||
|
||||
// Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
|
||||
if (CommandState.getInstance(editor).getMode() == CommandState.Mode.VISUAL) {
|
||||
VimPlugin.getMotion().exitVisual(editor);
|
||||
}
|
||||
|
||||
CharPointer cmd = new CharPointer(new StringBuffer(exarg));
|
||||
//sub_nsubs = 0;
|
||||
//sub_nlines = 0;
|
||||
@@ -375,6 +381,7 @@ public class SearchGroup {
|
||||
lastMatch = startoff;
|
||||
newpos = EditorHelper.offsetToCharacterPosition(editor, newend);
|
||||
|
||||
lnum += newpos.line - endpos.line;
|
||||
line2 += newpos.line - endpos.line;
|
||||
}
|
||||
}
|
||||
|
@@ -25,11 +25,13 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.command.SelectionType;
|
||||
import com.maddyhome.idea.vim.command.VisualChange;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.ex.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.group.MotionGroup;
|
||||
import com.maddyhome.idea.vim.ui.ExOutputPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -57,7 +59,7 @@ public class EditorData {
|
||||
*
|
||||
* @param editor The editor to cleanup
|
||||
*/
|
||||
public static void uninitializeEditor(@NotNull Editor editor) {
|
||||
public static void unInitializeEditor(@NotNull Editor editor) {
|
||||
if (logger.isDebugEnabled()) logger.debug("editor closed: " + editor);
|
||||
editor.putUserData(COMMAND_STATE, null);
|
||||
editor.putUserData(LAST_HIGHLIGHTS, null);
|
||||
@@ -71,7 +73,7 @@ public class EditorData {
|
||||
/**
|
||||
* This gets the last column the cursor was in for the editor.
|
||||
*
|
||||
* @param editor The editr to get the last column from
|
||||
* @param editor The editor to get the last column from
|
||||
* @return Returns the last column as set by {@link #setLastColumn} or the current cursor column
|
||||
*/
|
||||
public static int getLastColumn(@NotNull Editor editor) {
|
||||
@@ -91,9 +93,16 @@ public class EditorData {
|
||||
* @param editor The editor
|
||||
*/
|
||||
public static void setLastColumn(@NotNull Editor editor, int col) {
|
||||
boolean previousWasDollar = getLastColumn(editor) >= MotionGroup.LAST_COLUMN;
|
||||
boolean currentIsDollar = col >= MotionGroup.LAST_COLUMN;
|
||||
|
||||
editor.putUserData(LAST_COLUMN, col);
|
||||
int t = getLastColumn(editor);
|
||||
if (logger.isDebugEnabled()) logger.debug("setLastColumn(" + col + ") is now " + t);
|
||||
|
||||
if (previousWasDollar != currentIsDollar && CommandState.inVisualBlockMode(editor)) {
|
||||
VimPlugin.getMotion().updateSelection(editor);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -187,6 +196,22 @@ public class EditorData {
|
||||
editor.putUserData(MOTION_GROUP, adapter);
|
||||
}
|
||||
|
||||
public static boolean getEditorGroup(@NotNull Editor editor) {
|
||||
return editor.getUserData(EDITOR_GROUP) == Boolean.TRUE;
|
||||
}
|
||||
|
||||
public static void setEditorGroup(@NotNull Editor editor, boolean value) {
|
||||
editor.putUserData(EDITOR_GROUP, value);
|
||||
}
|
||||
|
||||
public static boolean isLineNumbersShown(@NotNull Editor editor) {
|
||||
return editor.getUserData(LINE_NUMBERS_SHOWN) == Boolean.TRUE;
|
||||
}
|
||||
|
||||
public static void setLineNumbersShown(@NotNull Editor editor, boolean value) {
|
||||
editor.putUserData(LINE_NUMBERS_SHOWN, value);
|
||||
}
|
||||
|
||||
public static boolean isConsoleOutput(@NotNull Editor editor) {
|
||||
Object res = editor.getUserData(CONSOLE_VIEW_IN_EDITOR_VIEW);
|
||||
logger.debug("isConsoleOutput for editor " + editor + " - " + res);
|
||||
@@ -238,6 +263,8 @@ public class EditorData {
|
||||
private static final Key<CommandState> COMMAND_STATE = new Key<CommandState>("commandState");
|
||||
private static final Key<Boolean> CHANGE_GROUP = new Key<Boolean>("changeGroup");
|
||||
private static final Key<Boolean> MOTION_GROUP = new Key<Boolean>("motionGroup");
|
||||
public static final Key<Boolean> EDITOR_GROUP = new Key<Boolean>("editorGroup");
|
||||
public static final Key<Boolean> LINE_NUMBERS_SHOWN = new Key<Boolean>("lineNumbersShown");
|
||||
private static final Key<ExOutputPanel> MORE_PANEL = new Key<ExOutputPanel>("IdeaVim.morePanel");
|
||||
private static final Key<ExOutputModel> EX_OUTPUT_MODEL = new Key<ExOutputModel>("IdeaVim.exOutputModel");
|
||||
|
||||
@@ -258,8 +285,8 @@ public class EditorData {
|
||||
// coded such that two keys with the same name are treated as different keys - oh well.
|
||||
// This code will work as long as the key I need is the first one in the ConsoleViewImpl.
|
||||
Class cvi = Class.forName("com.intellij.execution.impl.ConsoleViewImpl");
|
||||
Field[] flds = cvi.getDeclaredFields();
|
||||
for (Field f : flds) {
|
||||
Field[] fields = cvi.getDeclaredFields();
|
||||
for (Field f : fields) {
|
||||
if (f.getType().equals(Key.class)) {
|
||||
f.setAccessible(true);
|
||||
CONSOLE_VIEW_IN_EDITOR_VIEW = (Key)f.get(null);
|
||||
@@ -276,7 +303,7 @@ public class EditorData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if editor is file editor, also it takes into account that editor can be placed in editors hierarhy
|
||||
* Checks if editor is file editor, also it takes into account that editor can be placed in editors hierarchy
|
||||
*/
|
||||
public static boolean isFileEditor(@NotNull Editor editor){
|
||||
final VirtualFile virtualFile = EditorData.getVirtualFile(editor);
|
||||
|
@@ -52,7 +52,7 @@ public class PsiHelper {
|
||||
StructureViewBuilder structureViewBuilder = LanguageStructureViewBuilder.INSTANCE.getStructureViewBuilder(file);
|
||||
if (!(structureViewBuilder instanceof TreeBasedStructureViewBuilder)) return -1;
|
||||
TreeBasedStructureViewBuilder builder = (TreeBasedStructureViewBuilder)structureViewBuilder;
|
||||
StructureViewModel model = builder.createStructureViewModel();
|
||||
StructureViewModel model = builder.createStructureViewModel(editor);
|
||||
|
||||
TIntArrayList navigationOffsets = new TIntArrayList();
|
||||
addNavigationElements(model.getRoot(), navigationOffsets, isStart);
|
||||
@@ -109,7 +109,7 @@ public class PsiHelper {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiFile getFile(@NotNull Editor editor) {
|
||||
public static PsiFile getFile(@NotNull Editor editor) {
|
||||
VirtualFile vf = EditorData.getVirtualFile(editor);
|
||||
if (vf != null) {
|
||||
Project proj = editor.getProject();
|
||||
|
@@ -18,9 +18,14 @@
|
||||
|
||||
package com.maddyhome.idea.vim.helper;
|
||||
|
||||
import com.intellij.lang.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.maddyhome.idea.vim.common.TextRange;
|
||||
import com.maddyhome.idea.vim.option.ListOption;
|
||||
import com.maddyhome.idea.vim.option.OptionChangeEvent;
|
||||
@@ -113,6 +118,9 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
int bend = findBlockLocation(chars, type, close, 1, bstart + 1, 1);
|
||||
if (bend == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isOuter) {
|
||||
bstart++;
|
||||
@@ -140,6 +148,42 @@ public class SearchHelper {
|
||||
return new TextRange(bstart, bend);
|
||||
}
|
||||
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiComment comment, int pos, @Nullable String prefix,
|
||||
@Nullable String suffix) {
|
||||
if (prefix != null && suffix != null) {
|
||||
final String commentText = comment.getText();
|
||||
if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) {
|
||||
final int endOffset = comment.getTextOffset() + comment.getTextLength();
|
||||
if (pos < comment.getTextOffset() + prefix.length()) {
|
||||
return endOffset;
|
||||
}
|
||||
else if (pos >= endOffset - suffix.length()) {
|
||||
return comment.getTextOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int findMatchingBlockCommentPair(@NotNull PsiElement element, int pos) {
|
||||
final Language language = element.getLanguage();
|
||||
final Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(language);
|
||||
final PsiComment comment = PsiTreeUtil.getParentOfType(element, PsiComment.class, false);
|
||||
if (comment != null) {
|
||||
final int ret = findMatchingBlockCommentPair(comment, pos, commenter.getBlockCommentPrefix(),
|
||||
commenter.getBlockCommentSuffix());
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
if (commenter instanceof CodeDocumentationAwareCommenter) {
|
||||
final CodeDocumentationAwareCommenter docCommenter = (CodeDocumentationAwareCommenter)commenter;
|
||||
return findMatchingBlockCommentPair(comment, pos, docCommenter.getDocumentationCommentPrefix(),
|
||||
docCommenter.getDocumentationCommentSuffix());
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This looks on the current line, starting at the cursor position for one of {, }, (, ), [, or ]. It then searches
|
||||
* forward or backward, as appropriate for the associated match pair. String in double quotes are skipped over.
|
||||
@@ -150,10 +194,16 @@ public class SearchHelper {
|
||||
* were found on the remainder of the current line.
|
||||
*/
|
||||
public static int findMatchingPairOnCurrentLine(@NotNull Editor editor) {
|
||||
int pos = editor.getCaretModel().getOffset();
|
||||
|
||||
final int commentPos = findMatchingComment(editor, pos);
|
||||
if (commentPos >= 0) {
|
||||
return commentPos;
|
||||
}
|
||||
|
||||
int line = editor.getCaretModel().getLogicalPosition().line;
|
||||
int end = EditorHelper.getLineEndOffset(editor, line, true);
|
||||
CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
int pos = editor.getCaretModel().getOffset();
|
||||
int loc = -1;
|
||||
// Search the remainder of the current line for one of the candidate characters
|
||||
while (pos < end) {
|
||||
@@ -179,6 +229,20 @@ public class SearchHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* If on the start/end of a block comment, jump to the matching of that comment, or vice versa.
|
||||
*/
|
||||
private static int findMatchingComment(@NotNull Editor editor, int pos) {
|
||||
final PsiFile psiFile = PsiHelper.getFile(editor);
|
||||
if (psiFile != null) {
|
||||
final PsiElement element = psiFile.findElementAt(pos);
|
||||
if (element != null) {
|
||||
return findMatchingBlockCommentPair(element, pos);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static int findBlockLocation(@NotNull CharSequence chars, char found, char match, int dir, int pos, int cnt) {
|
||||
int res = -1;
|
||||
final int inCheckPos = dir < 0 && pos > 0 ? pos - 1 : pos;
|
||||
@@ -232,7 +296,7 @@ public class SearchHelper {
|
||||
|
||||
private final int value;
|
||||
|
||||
private Direction(int i) {
|
||||
Direction(int i) {
|
||||
value = i;
|
||||
}
|
||||
|
||||
@@ -530,7 +594,7 @@ public class SearchHelper {
|
||||
if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && !spaceWords) {
|
||||
pos = skipSpace(chars, pos - 1, step, size) + 1;
|
||||
}
|
||||
if (CharacterHelper.charType(chars.charAt(pos), bigWord) != CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) {
|
||||
if (pos > 0 && CharacterHelper.charType(chars.charAt(pos), bigWord) != CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) {
|
||||
pos += step;
|
||||
}
|
||||
}
|
||||
@@ -1272,7 +1336,7 @@ public class SearchHelper {
|
||||
}
|
||||
}
|
||||
else if (ch == '\n') {
|
||||
int end = offset; // Save where we found the punctuation.
|
||||
int end = offset; // Save where we found the newline.
|
||||
if (dir > 0) {
|
||||
offset++;
|
||||
while (offset < max) {
|
||||
@@ -1305,15 +1369,17 @@ public class SearchHelper {
|
||||
}
|
||||
}
|
||||
else {
|
||||
offset--;
|
||||
while (offset >= 0) {
|
||||
ch = chars.charAt(offset);
|
||||
if (ch != '\n') {
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
offset--;
|
||||
while (offset >= 0) {
|
||||
ch = chars.charAt(offset);
|
||||
if (ch != '\n') {
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset < end) {
|
||||
|
@@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.helper;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -44,6 +45,10 @@ public class StringHelper {
|
||||
.put("return", VK_ENTER)
|
||||
.put("ins", VK_INSERT)
|
||||
.put("insert", VK_INSERT)
|
||||
.put("home", VK_HOME)
|
||||
.put("end", VK_END)
|
||||
.put("pageup", VK_PAGE_UP)
|
||||
.put("pagedown", VK_PAGE_DOWN)
|
||||
.put("del", VK_DELETE)
|
||||
.put("delete", VK_DELETE)
|
||||
.put("esc", VK_ESCAPE)
|
||||
@@ -70,9 +75,10 @@ public class StringHelper {
|
||||
private static final Map<Integer, String> VIM_KEY_VALUES = invertMap(VIM_KEY_NAMES);
|
||||
|
||||
private static final Map<String, Character> VIM_TYPED_KEY_NAMES = ImmutableMap.<String, Character>builder()
|
||||
.put("leader", '\\')
|
||||
.put("space", ' ')
|
||||
.put("bar", '|')
|
||||
.put("bslash", '\\')
|
||||
.put("lt", '<')
|
||||
.build();
|
||||
|
||||
private static final Set<String> UPPERCASE_DISPLAY_KEY_NAMES = ImmutableSet.<String>builder()
|
||||
@@ -121,7 +127,7 @@ public class StringHelper {
|
||||
return res;
|
||||
}
|
||||
|
||||
private static enum KeyParserState {
|
||||
private enum KeyParserState {
|
||||
INIT,
|
||||
ESCAPE,
|
||||
SPECIAL,
|
||||
@@ -183,8 +189,12 @@ public class StringHelper {
|
||||
throw new IllegalArgumentException("<" + specialKeyName + "> is not supported");
|
||||
}
|
||||
if (!"nop".equals(lower)) {
|
||||
final List<KeyStroke> leader = parseMapLeader(specialKeyName);
|
||||
final KeyStroke specialKey = parseSpecialKey(specialKeyName, 0);
|
||||
if (specialKey != null) {
|
||||
if (leader != null) {
|
||||
result.addAll(leader);
|
||||
}
|
||||
else if (specialKey != null) {
|
||||
result.add(specialKey);
|
||||
}
|
||||
else {
|
||||
@@ -211,6 +221,20 @@ public class StringHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<KeyStroke> parseMapLeader(@NotNull String s) {
|
||||
if ("leader".equals(s.toLowerCase())) {
|
||||
final Object mapLeader = VimScriptGlobalEnvironment.getInstance().getVariables().get("mapleader");
|
||||
if (mapLeader instanceof String) {
|
||||
return stringToKeys((String)mapLeader);
|
||||
}
|
||||
else {
|
||||
return stringToKeys("\\");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isControlCharacter(char c) {
|
||||
return c < '\u0020';
|
||||
}
|
||||
|
@@ -18,10 +18,13 @@
|
||||
|
||||
package com.maddyhome.idea.vim.helper;
|
||||
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
@@ -31,10 +34,28 @@ public class UiHelper {
|
||||
private UiHelper() {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public static Font getEditorFont() {
|
||||
final EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
|
||||
return new Font(scheme.getEditorFontName(), Font.PLAIN, scheme.getEditorFontSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get focus reliably.
|
||||
*/
|
||||
public static void requestFocus(@NotNull final JComponent component) {
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
// XXX: This workaround is required at least for Oracle Java 6
|
||||
application.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
application.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
component.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,10 @@ import java.util.*;
|
||||
* Maintains the set of support options
|
||||
*/
|
||||
public class Options {
|
||||
public static final String RELATIVE_NUMBER = "relativenumber";
|
||||
public static final String NUMBER = "number";
|
||||
public static final String CLIPBOARD = "clipboard";
|
||||
|
||||
/**
|
||||
* Gets the singleton instance of the options
|
||||
*
|
||||
@@ -80,6 +84,15 @@ public class Options {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ListOption getListOption(@NotNull String name) {
|
||||
final Option option = getOption(name);
|
||||
if (option instanceof ListOption) {
|
||||
return (ListOption)option;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all options
|
||||
*
|
||||
@@ -447,6 +460,9 @@ public class Options {
|
||||
addOption(new NumberOption("undolevels", "ul", 1000, -1, Integer.MAX_VALUE));
|
||||
addOption(new ToggleOption("visualbell", "vb", false));
|
||||
addOption(new ToggleOption("wrapscan", "ws", true));
|
||||
addOption(new ToggleOption(NUMBER, "nu", false));
|
||||
addOption(new ToggleOption(RELATIVE_NUMBER, "rnu", false));
|
||||
addOption(new ListOption(CLIPBOARD, "cb", new String[]{"autoselect,exclude:cons\\|linux"}, null));
|
||||
}
|
||||
|
||||
private void addOption(@NotNull Option option) {
|
||||
|
@@ -273,45 +273,39 @@ public class CharPointer {
|
||||
|
||||
@Nullable
|
||||
public CharPointer strchr(char c) {
|
||||
if (end()) {
|
||||
if (seq == null || end()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = seq.length();
|
||||
final int len = seq.length();
|
||||
for (int i = pointer; i < len; i++) {
|
||||
if (seq.charAt(i) == c) {
|
||||
final char ch = seq.charAt(i);
|
||||
if (ch == '\u0000') {
|
||||
return null;
|
||||
}
|
||||
if (ch == c) {
|
||||
return ref(i - pointer);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
/*
|
||||
String str = seq.subSequence(pointer, pointer + strlen()).toString();
|
||||
int pos = str.indexOf(c);
|
||||
if (pos != -1)
|
||||
{
|
||||
return ref(pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CharPointer istrchr(char c) {
|
||||
if (end()) {
|
||||
if (seq == null || end()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int len = seq.length();
|
||||
char cc = Character.toUpperCase(c);
|
||||
final int len = seq.length();
|
||||
final char cc = Character.toUpperCase(c);
|
||||
c = Character.toLowerCase(c);
|
||||
|
||||
for (int i = pointer; i < len; i++) {
|
||||
char ch = seq.charAt(i);
|
||||
final char ch = seq.charAt(i);
|
||||
if (ch == '\u0000') {
|
||||
return null;
|
||||
}
|
||||
if (ch == c || ch == cc) {
|
||||
return ref(i - pointer);
|
||||
}
|
||||
@@ -329,11 +323,7 @@ public class CharPointer {
|
||||
}
|
||||
|
||||
public boolean end(int offset) {
|
||||
if (seq == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return pointer + offset >= seq.length();
|
||||
return seq == null || pointer + offset >= seq.length();
|
||||
}
|
||||
|
||||
public int OP() {
|
||||
|
@@ -3051,7 +3051,7 @@ public class RegExp {
|
||||
if (--count < minval) {
|
||||
break;
|
||||
}
|
||||
if (reginput == regline) {
|
||||
if (reginput.equals(regline)) {
|
||||
/* backup to last char of previous line */
|
||||
--reglnum;
|
||||
regline = reg_getline(reglnum);
|
||||
@@ -3132,7 +3132,7 @@ public class RegExp {
|
||||
case BEHIND:
|
||||
case NOBEHIND: {
|
||||
regsave_T save_after = new regsave_T(), save_start = new regsave_T();
|
||||
regsave_T save_behind_pos = new regsave_T();
|
||||
regsave_T save_behind_pos;
|
||||
boolean needmatch = (op == BEHIND);
|
||||
|
||||
/*
|
||||
@@ -3153,8 +3153,8 @@ public class RegExp {
|
||||
* line (for multi-line matching).
|
||||
* Set behind_pos to where the match should end, BHPOS
|
||||
* will match it. */
|
||||
save_behind_pos = behind_pos;
|
||||
behind_pos = save_start;
|
||||
save_behind_pos = behind_pos == null ? null : new regsave_T(behind_pos);
|
||||
behind_pos = new regsave_T(save_start);
|
||||
while (true) {
|
||||
reg_restore(save_start);
|
||||
if (regmatch(scan.OPERAND()) && reg_save_equal(behind_pos)) {
|
||||
@@ -3676,9 +3676,9 @@ public class RegExp {
|
||||
*/
|
||||
private boolean reg_save_equal(@NotNull regsave_T save) {
|
||||
if (reg_match == null) {
|
||||
return reglnum == save.pos.lnum && reginput == regline.ref(save.pos.col);
|
||||
return reglnum == save.pos.lnum && reginput.equals(regline.ref(save.pos.col));
|
||||
}
|
||||
return reginput == save.ptr;
|
||||
return reginput.equals(save.ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4694,6 +4694,14 @@ public class RegExp {
|
||||
private static class regsave_T {
|
||||
CharPointer ptr; /* reginput pointer, for single-line regexp */
|
||||
@NotNull lpos_T pos = new lpos_T(); /* reginput pos, for multi-line regexp */
|
||||
|
||||
public regsave_T() {
|
||||
}
|
||||
|
||||
public regsave_T(regsave_T rhs) {
|
||||
ptr = rhs.ptr == null ? null : new CharPointer("").assign(rhs.ptr);
|
||||
pos = new lpos_T(rhs.pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* struct to save start/end pointer/position in for \(\) */
|
||||
|
@@ -30,7 +30,6 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
|
||||
/**
|
||||
* This is used to enter ex commands such as searches and "colon" commands
|
||||
@@ -88,7 +87,7 @@ public class ExEntryPanel extends JPanel {
|
||||
/**
|
||||
* Turns on the ex entry field for the given editor
|
||||
*
|
||||
* @param editor The editor to use for dislay
|
||||
* @param editor The editor to use for display
|
||||
* @param context The data context
|
||||
* @param label The label for the ex entry (i.e. :, /, or ?)
|
||||
* @param initText The initial text for the entry
|
||||
@@ -114,7 +113,7 @@ public class ExEntryPanel extends JPanel {
|
||||
oldGlass.addComponentListener(adapter);
|
||||
positionPanel();
|
||||
oldGlass.setVisible(true);
|
||||
entry.requestFocus();
|
||||
entry.requestFocusInWindow();
|
||||
}
|
||||
active = true;
|
||||
}
|
||||
@@ -146,22 +145,20 @@ public class ExEntryPanel extends JPanel {
|
||||
entry.handleKey(stroke);
|
||||
}
|
||||
|
||||
public void processKey(KeyEvent event) {
|
||||
entry.processKeyEvent(event);
|
||||
}
|
||||
|
||||
private void positionPanel() {
|
||||
if (parent == null) return;
|
||||
|
||||
Container scroll = SwingUtilities.getAncestorOfClass(JScrollPane.class, parent);
|
||||
int height = (int)getPreferredSize().getHeight();
|
||||
Rectangle bounds = scroll.getBounds();
|
||||
bounds.translate(0, scroll.getHeight() - height);
|
||||
bounds.height = height;
|
||||
Point pos = SwingUtilities.convertPoint(scroll.getParent(), bounds.getLocation(), oldGlass);
|
||||
bounds.setLocation(pos);
|
||||
setBounds(bounds);
|
||||
repaint();
|
||||
if (scroll != null) {
|
||||
Rectangle bounds = scroll.getBounds();
|
||||
bounds.translate(0, scroll.getHeight() - height);
|
||||
bounds.height = height;
|
||||
Point pos = SwingUtilities.convertPoint(scroll.getParent(), bounds.getLocation(), oldGlass);
|
||||
bounds.setLocation(pos);
|
||||
setBounds(bounds);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,14 +176,17 @@ public class ExEntryPanel extends JPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off the ex entry field and puts the focus back to the original component
|
||||
*
|
||||
* Turns off the ex entry field and optionally puts the focus back to the original component
|
||||
*/
|
||||
public void deactivate() {
|
||||
public void deactivate(boolean refocusOwningEditor) {
|
||||
logger.info("deactivate");
|
||||
if (!active) return;
|
||||
active = false;
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
if (refocusOwningEditor && parent != null) {
|
||||
UiHelper.requestFocus(parent);
|
||||
}
|
||||
|
||||
oldGlass.removeComponentListener(adapter);
|
||||
oldGlass.setVisible(false);
|
||||
oldGlass.remove(this);
|
||||
|
@@ -19,9 +19,7 @@
|
||||
package com.maddyhome.idea.vim.ui;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.helper.EditorData;
|
||||
@@ -113,22 +111,20 @@ public class ExOutputPanel extends JPanel {
|
||||
myText.setText(data);
|
||||
myText.setCaretPosition(0);
|
||||
if (data.length() > 0) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activate();
|
||||
}
|
||||
});
|
||||
activate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off the ex entry field and puts the focus back to the original component
|
||||
* Turns off the ex entry field and optionally puts the focus back to the original component
|
||||
*/
|
||||
public void deactivate() {
|
||||
public void deactivate(boolean refocusOwningEditor) {
|
||||
if (!myActive) return;
|
||||
myActive = false;
|
||||
myText.setText("");
|
||||
if (refocusOwningEditor) {
|
||||
UiHelper.requestFocus(myEditor.getContentComponent());
|
||||
}
|
||||
if (myOldGlass != null) {
|
||||
myOldGlass.removeComponentListener(myAdapter);
|
||||
myOldGlass.setVisible(false);
|
||||
@@ -136,7 +132,6 @@ public class ExOutputPanel extends JPanel {
|
||||
myOldGlass.setOpaque(myWasOpaque);
|
||||
myOldGlass.setLayout(myOldLayout);
|
||||
}
|
||||
myEditor.getContentComponent().requestFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,17 +155,9 @@ public class ExOutputPanel extends JPanel {
|
||||
if (myOldGlass != null) {
|
||||
myOldGlass.setVisible(true);
|
||||
}
|
||||
myActive = true;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myText.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
myActive = true;
|
||||
UiHelper.requestFocus(myText);
|
||||
}
|
||||
|
||||
private void setFontForElements() {
|
||||
@@ -277,11 +264,7 @@ public class ExOutputPanel extends JPanel {
|
||||
private void close(@Nullable final KeyEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
deactivate();
|
||||
final VirtualFile vf = EditorData.getVirtualFile(myEditor);
|
||||
if (vf != null) {
|
||||
FileEditorManager.getInstance(myEditor.getProject()).openFile(vf, true);
|
||||
}
|
||||
deactivate(true);
|
||||
|
||||
final Project project = myEditor.getProject();
|
||||
|
||||
|
@@ -32,6 +32,8 @@ import javax.swing.*;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Keymap;
|
||||
import java.awt.*;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -59,6 +61,16 @@ public class ExTextField extends JTextField {
|
||||
loadKeymap(map, ExKeyBindings.getBindings(), actions);
|
||||
map.setDefaultAction(new ExEditorKit.DefaultExKeyHandler());
|
||||
setKeymap(map);
|
||||
addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
setCaretPosition(getText().length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
}
|
||||
});
|
||||
|
||||
//origCaret = getCaret();
|
||||
//blockCaret = new BlockCaret();
|
||||
|
@@ -1,12 +1,13 @@
|
||||
package org.jetbrains.plugins.ideavim;
|
||||
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.ide.highlighter.XmlFileType;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileTypes.PlainTextFileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PlatformTestCase;
|
||||
import com.intellij.testFramework.UsefulTestCase;
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestFixture;
|
||||
import com.intellij.testFramework.fixtures.IdeaProjectTestFixture;
|
||||
@@ -17,6 +18,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.ExOutputModel;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper;
|
||||
import com.maddyhome.idea.vim.helper.StringHelper;
|
||||
@@ -33,16 +35,8 @@ import java.util.List;
|
||||
* @author vlan
|
||||
*/
|
||||
public abstract class VimTestCase extends UsefulTestCase {
|
||||
private static final String ULTIMATE_MARKER_CLASS = "com.intellij.psi.css.CssFile";
|
||||
protected CodeInsightTestFixture myFixture;
|
||||
|
||||
public VimTestCase() {
|
||||
// Only in IntelliJ IDEA Ultimate Edition
|
||||
PlatformTestCase.initPlatformLangPrefix();
|
||||
// XXX: IntelliJ IDEA Community and Ultimate 12+
|
||||
//PlatformTestCase.initPlatformPrefix(ULTIMATE_MARKER_CLASS, "PlatformLangXml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -67,7 +61,8 @@ public abstract class VimTestCase extends UsefulTestCase {
|
||||
protected void tearDown() throws Exception {
|
||||
myFixture.tearDown();
|
||||
myFixture = null;
|
||||
ExEntryPanel.getInstance().deactivate();
|
||||
ExEntryPanel.getInstance().deactivate(false);
|
||||
VimScriptGlobalEnvironment.getInstance().getVariables().clear();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@@ -83,6 +78,18 @@ public abstract class VimTestCase extends UsefulTestCase {
|
||||
return myFixture.getEditor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Editor configureByJavaText(@NotNull String content) {
|
||||
myFixture.configureByText(JavaFileType.INSTANCE, content);
|
||||
return myFixture.getEditor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Editor configureByXmlText(@NotNull String content) {
|
||||
myFixture.configureByText(XmlFileType.INSTANCE, content);
|
||||
return myFixture.getEditor();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected Editor typeText(@NotNull final List<KeyStroke> keys) {
|
||||
final Editor editor = myFixture.getEditor();
|
||||
@@ -142,4 +149,21 @@ public abstract class VimTestCase extends UsefulTestCase {
|
||||
public void assertPluginError(boolean isError) {
|
||||
assertEquals(isError, VimPlugin.isError());
|
||||
}
|
||||
|
||||
public void doTest(final List<KeyStroke> keys, String before, String after) {
|
||||
myFixture.configureByText(PlainTextFileType.INSTANCE, before);
|
||||
final Editor editor = myFixture.getEditor();
|
||||
final KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
final EditorDataContext dataContext = new EditorDataContext(editor);
|
||||
final Project project = myFixture.getProject();
|
||||
RunnableHelper.runWriteCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (KeyStroke key : keys) {
|
||||
keyHandler.handleKey(editor, key, dataContext);
|
||||
}
|
||||
}
|
||||
}, null, null);
|
||||
myFixture.checkResult(after);
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,8 @@
|
||||
package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.maddyhome.idea.vim.KeyHandler;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.helper.EditorDataContext;
|
||||
import com.maddyhome.idea.vim.helper.RunnableHelper;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.stringToKeys;
|
||||
|
||||
@@ -20,20 +12,12 @@ import static com.maddyhome.idea.vim.helper.StringHelper.stringToKeys;
|
||||
public class ChangeActionTest extends VimTestCase {
|
||||
// |c| |t|
|
||||
public void testChangeLinesTillForwards() {
|
||||
doTest(parseKeys("ct(", "for "),
|
||||
"<caret>if (condition) {\n" +
|
||||
"}\n",
|
||||
"for (condition) {\n" +
|
||||
"}\n");
|
||||
doTest(parseKeys("ct(", "for "), "<caret>if (condition) {\n" + "}\n", "for (condition) {\n" + "}\n");
|
||||
}
|
||||
|
||||
// VIM-276 |c| |T|
|
||||
public void testChangeLinesTillBackwards() {
|
||||
doTest(parseKeys("cT("),
|
||||
"if (condition) {<caret>\n" +
|
||||
"}\n",
|
||||
"if (\n" +
|
||||
"}\n");
|
||||
doTest(parseKeys("cT("), "if (condition) {<caret>\n" + "}\n", "if (\n" + "}\n");
|
||||
}
|
||||
|
||||
// VIM-276 |c| |F|
|
||||
@@ -67,9 +51,7 @@ public class ChangeActionTest extends VimTestCase {
|
||||
|
||||
// VIM-157 |~|
|
||||
public void testToggleCharCase() {
|
||||
doTest(parseKeys("~~"),
|
||||
"<caret>hello world\n",
|
||||
"HEllo world\n");
|
||||
doTest(parseKeys("~~"), "<caret>hello world\n", "HEllo world\n");
|
||||
}
|
||||
|
||||
// VIM-157 |~|
|
||||
@@ -79,6 +61,30 @@ public class ChangeActionTest extends VimTestCase {
|
||||
"hello worLD\n");
|
||||
}
|
||||
|
||||
public void testToggleCaseMotion() {
|
||||
doTest(parseKeys("g~w"), "<caret>FooBar Baz\n", "fOObAR Baz\n");
|
||||
}
|
||||
|
||||
public void testChangeUpperCase() {
|
||||
doTest(parseKeys("gUw"), "<caret>FooBar Baz\n", "FOOBAR Baz\n");
|
||||
}
|
||||
|
||||
public void testChangeLowerCase() {
|
||||
doTest(parseKeys("guw"), "<caret>FooBar Baz\n", "foobar Baz\n");
|
||||
}
|
||||
|
||||
public void testToggleCaseVisual() {
|
||||
doTest(parseKeys("ve~"), "<caret>FooBar Baz\n", "fOObAR Baz\n");
|
||||
}
|
||||
|
||||
public void testChangeUpperCaseVisual() {
|
||||
doTest(parseKeys("veU"), "<caret>FooBar Baz\n", "FOOBAR Baz\n");
|
||||
}
|
||||
|
||||
public void testChangeLowerCaseVisual() {
|
||||
doTest(parseKeys("veu"), "<caret>FooBar Baz\n", "foobar Baz\n");
|
||||
}
|
||||
|
||||
// VIM-85 |i| |gi| |gg|
|
||||
public void testInsertAtPreviousAction() {
|
||||
doTest(parseKeys("i", "hello", "<Esc>", "gg", "gi", " world! "), "one\n" +
|
||||
@@ -100,11 +106,7 @@ public class ChangeActionTest extends VimTestCase {
|
||||
|
||||
// |d| |w|
|
||||
public void testDeleteLastWordBeforeEOL() {
|
||||
doTest(parseKeys("dw"),
|
||||
"one <caret>two\n" +
|
||||
"three\n",
|
||||
"one \n" +
|
||||
"three\n");
|
||||
doTest(parseKeys("dw"), "one <caret>two\n" + "three\n", "one \n" + "three\n");
|
||||
}
|
||||
|
||||
// VIM-105 |d| |w|
|
||||
@@ -128,17 +130,17 @@ public class ChangeActionTest extends VimTestCase {
|
||||
|
||||
// VIM-105 |d| |w| |count|
|
||||
public void testDeleteTwoWordsOnTwoLines() {
|
||||
doTest(parseKeys("d2w"),
|
||||
"one <caret>two\n" +
|
||||
"three four\n",
|
||||
"one four\n");
|
||||
doTest(parseKeys("d2w"), "one <caret>two\n" + "three four\n", "one four\n");
|
||||
}
|
||||
|
||||
// VIM-200 |c| |w|
|
||||
public void testChangeWordAtLastChar() {
|
||||
doTest(parseKeys("cw"),
|
||||
"on<caret>e two three\n",
|
||||
"on two three\n");
|
||||
doTest(parseKeys("cw"), "on<caret>e two three\n", "on two three\n");
|
||||
}
|
||||
|
||||
// VIM-515 |c| |W|
|
||||
public void testChangeBigWordWithPunctuationAndAlpha() {
|
||||
doTest(parseKeys("cW"), "foo<caret>(bar baz\n", "foo baz\n");
|
||||
}
|
||||
|
||||
// VIM-300 |c| |w|
|
||||
@@ -156,13 +158,18 @@ public class ChangeActionTest extends VimTestCase {
|
||||
assertOffset(4);
|
||||
}
|
||||
|
||||
// VIM-536 |cc|
|
||||
public void testChangeLineAtSecondLastLine() {
|
||||
doTest(parseKeys("ccbaz"),
|
||||
"<caret>foo\n" +
|
||||
"bar\n",
|
||||
"baz\n" +
|
||||
"bar\n");
|
||||
}
|
||||
|
||||
// VIM-394 |d| |v_aw|
|
||||
public void testDeleteIndentedWordBeforePunctuation() {
|
||||
doTest(parseKeys("daw"),
|
||||
"foo\n" +
|
||||
" <caret>bar, baz\n",
|
||||
"foo\n" +
|
||||
" , baz\n");
|
||||
doTest(parseKeys("daw"), "foo\n" + " <caret>bar, baz\n", "foo\n" + " , baz\n");
|
||||
}
|
||||
|
||||
// |d| |v_aw|
|
||||
@@ -182,18 +189,13 @@ public class ChangeActionTest extends VimTestCase {
|
||||
|
||||
// VIM-393 |d|
|
||||
public void testDeleteBadArgument() {
|
||||
doTest(parseKeys("dD", "dd"),
|
||||
"one\n" +
|
||||
"two\n",
|
||||
"two\n");
|
||||
doTest(parseKeys("dD", "dd"), "one\n" + "two\n", "two\n");
|
||||
}
|
||||
|
||||
// VIM-262 |i_CTRL-R|
|
||||
public void testInsertFromRegister() {
|
||||
VimPlugin.getRegister().setKeys('a', stringToKeys("World"));
|
||||
doTest(parseKeys("A", ", ", "<C-R>", "a", "!"),
|
||||
"<caret>Hello\n",
|
||||
"Hello, World!\n");
|
||||
doTest(parseKeys("A", ", ", "<C-R>", "a", "!"), "<caret>Hello\n", "Hello, World!\n");
|
||||
}
|
||||
|
||||
// VIM-421 |c| |w|
|
||||
@@ -212,9 +214,7 @@ public class ChangeActionTest extends VimTestCase {
|
||||
|
||||
// VIM-421 |c| |w|
|
||||
public void testChangeLastCharInLine() {
|
||||
doTest(parseKeys("cw"),
|
||||
"fo<caret>o\n",
|
||||
"fo<caret>\n");
|
||||
doTest(parseKeys("cw"), "fo<caret>o\n", "fo<caret>\n");
|
||||
}
|
||||
|
||||
// VIM-404 |O|
|
||||
@@ -270,26 +270,270 @@ public class ChangeActionTest extends VimTestCase {
|
||||
"bar\n" +
|
||||
"baz\n" +
|
||||
"quux\n",
|
||||
"<caret>oo\n" +
|
||||
"ar\n" +
|
||||
"az\n" +
|
||||
"<caret>o\n" +
|
||||
"r\n" +
|
||||
"z\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
private void doTest(final List<KeyStroke> keys, String before, String after) {
|
||||
myFixture.configureByText("a.java", before);
|
||||
final Editor editor = myFixture.getEditor();
|
||||
final KeyHandler keyHandler = KeyHandler.getInstance();
|
||||
final EditorDataContext dataContext = new EditorDataContext(editor);
|
||||
final Project project = myFixture.getProject();
|
||||
RunnableHelper.runWriteCommand(project, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (KeyStroke key : keys) {
|
||||
keyHandler.handleKey(editor, key, dataContext);
|
||||
}
|
||||
}
|
||||
}, null, null);
|
||||
myFixture.checkResult(after);
|
||||
public void testDeleteCharVisualBlock() {
|
||||
doTest(parseKeys("<C-V>", "jjl", "x"),
|
||||
"<caret>foo\n" +
|
||||
"bar\n" +
|
||||
"baz\n" +
|
||||
"quux\n",
|
||||
"<caret>o\n" +
|
||||
"r\n" +
|
||||
"z\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
public void testDeleteJoinLinesSpaces() {
|
||||
doTest(parseKeys("3J"),
|
||||
" a<caret> 1\n" +
|
||||
" b 2\n" +
|
||||
" c 3\n" +
|
||||
"quux\n",
|
||||
" a 1 b 2 c 3\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
public void testDeleteJoinLines() {
|
||||
doTest(parseKeys("3gJ"),
|
||||
" a<caret> 1\n" +
|
||||
" b 2\n" +
|
||||
" c 3\n" +
|
||||
"quux\n",
|
||||
" a 1 b 2 c 3\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
public void testDeleteJoinLinesWithTrailingSpaceThenEmptyLine() {
|
||||
doTest(parseKeys("3J"),
|
||||
"foo \n" +
|
||||
"\n" +
|
||||
"bar",
|
||||
"foo bar");
|
||||
}
|
||||
|
||||
public void testDeleteJoinLinesWithTwoTrailingSpaces() {
|
||||
doTest(parseKeys("J"),
|
||||
"foo \n" +
|
||||
"bar",
|
||||
"foo bar");
|
||||
}
|
||||
|
||||
public void testDeleteJoinVisualLinesSpaces() {
|
||||
doTest(parseKeys("v2jJ"),
|
||||
" a<caret> 1\n" +
|
||||
" b 2\n" +
|
||||
" c 3\n" +
|
||||
"quux\n",
|
||||
" a 1 b 2 c 3\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
public void testDeleteJoinVisualLines() {
|
||||
doTest(parseKeys("v2jgJ"),
|
||||
" a<caret> 1\n" +
|
||||
" b 2\n" +
|
||||
" c 3\n" +
|
||||
"quux\n",
|
||||
" a 1 b 2 c 3\n" +
|
||||
"quux\n");
|
||||
}
|
||||
|
||||
public void testDeleteCharVisualBlockOnLastCharOfLine() {
|
||||
doTest(parseKeys("<C-V>", "x"),
|
||||
"fo<caret>o\n",
|
||||
"fo\n");
|
||||
}
|
||||
|
||||
public void testDeleteCharVisualBlockOnEmptyLinesDoesntDeleteAnything() {
|
||||
doTest(parseKeys("<C-V>", "j", "x"),
|
||||
"\n\n",
|
||||
"\n\n");
|
||||
}
|
||||
|
||||
// VIM-781 |CTRL-V| |j|
|
||||
public void testDeleteCharVisualBlockWithEmptyLineInTheMiddle() {
|
||||
doTest(parseKeys("l", "<C-V>", "jj", "x"),
|
||||
"foo\n" +
|
||||
"\n" +
|
||||
"bar\n",
|
||||
"fo\n" +
|
||||
"\n" +
|
||||
"br\n");
|
||||
}
|
||||
|
||||
// VIM-781 |CTRL-V| |j|
|
||||
public void testDeleteCharVisualBlockWithShorterLineInTheMiddle() {
|
||||
doTest(parseKeys("l", "<C-V>", "jj", "x"),
|
||||
"foo\n" +
|
||||
"x\n" +
|
||||
"bar\n",
|
||||
"fo\n" +
|
||||
"x\n" +
|
||||
"br\n");
|
||||
}
|
||||
|
||||
// VIM-845 |CTRL-V| |x|
|
||||
public void testDeleteVisualBlockOneCharWide() {
|
||||
configureByText("foo\n" +
|
||||
"bar\n");
|
||||
typeText(parseKeys("<C-V>", "j", "x"));
|
||||
myFixture.checkResult("oo\n" +
|
||||
"ar\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceOneChar() {
|
||||
doTest(parseKeys("rx"),
|
||||
"b<caret>ar\n",
|
||||
"b<caret>xr\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceMultipleCharsWithCount() {
|
||||
doTest(parseKeys("3rX"),
|
||||
"fo<caret>obar\n",
|
||||
"fo<caret>XXXr\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceMultipleCharsWithCountPastEndOfLine() {
|
||||
doTest(parseKeys("6rX"),
|
||||
"fo<caret>obar\n",
|
||||
"fo<caret>obar\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceMultipleCharsWithVisual() {
|
||||
doTest(parseKeys("v", "ll", "j", "rZ"),
|
||||
"fo<caret>obar\n" +
|
||||
"foobaz\n",
|
||||
"foZZZZ\n" +
|
||||
"ZZZZZz\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceOneCharWithNewline() {
|
||||
doTest(parseKeys("r<Enter>"),
|
||||
" fo<caret>obar\n" +
|
||||
"foobaz\n",
|
||||
" fo\n" +
|
||||
" bar\n" +
|
||||
"foobaz\n");
|
||||
}
|
||||
|
||||
// |r|
|
||||
public void testReplaceCharWithNewlineAndCountAddsOnlySingleNewline() {
|
||||
doTest(parseKeys("3r<Enter>"),
|
||||
" fo<caret>obar\n" +
|
||||
"foobaz\n",
|
||||
" fo\n" +
|
||||
" r\n" +
|
||||
"foobaz\n");
|
||||
}
|
||||
|
||||
// |s|
|
||||
public void testReplaceOneCharWithText() {
|
||||
doTest(parseKeys("sxy<Esc>"),
|
||||
"b<caret>ar\n",
|
||||
"bx<caret>yr\n");
|
||||
}
|
||||
|
||||
// |s|
|
||||
public void testReplaceMultipleCharsWithTextWithCount() {
|
||||
doTest(parseKeys("3sxy<Esc>"),
|
||||
"fo<caret>obar\n",
|
||||
"fox<caret>yr\n");
|
||||
}
|
||||
|
||||
// |s|
|
||||
public void testReplaceMultipleCharsWithTextWithCountPastEndOfLine() {
|
||||
doTest(parseKeys("99sxyz<Esc>"),
|
||||
"foo<caret>bar\n" +
|
||||
"biff\n",
|
||||
"fooxy<caret>z\n" +
|
||||
"biff\n");
|
||||
}
|
||||
|
||||
// |R|
|
||||
public void testReplaceMode() {
|
||||
doTest(parseKeys("Rbaz<Esc>"),
|
||||
"foo<caret>bar\n",
|
||||
"fooba<caret>z\n");
|
||||
}
|
||||
|
||||
// |R| |i_<Insert>|
|
||||
public void testReplaceModeSwitchToInsertModeAndBack() {
|
||||
doTest(parseKeys("RXXX<Ins>YYY<Ins>ZZZ<Esc>"),
|
||||
"aaa<caret>bbbcccddd\n",
|
||||
"aaaXXXYYYZZ<caret>Zddd\n");
|
||||
}
|
||||
|
||||
// |i| |i_<Insert>|
|
||||
public void testInsertModeSwitchToReplaceModeAndBack() {
|
||||
doTest(parseKeys("iXXX<Ins>YYY<Ins>ZZZ<Esc>"),
|
||||
"aaa<caret>bbbcccddd\n",
|
||||
"aaaXXXYYYZZ<caret>Zcccddd\n");
|
||||
}
|
||||
|
||||
// VIM-511 |.|
|
||||
public void testRepeatWithBackspaces() {
|
||||
doTest(parseKeys("ce", "foo", "<BS><BS><BS>", "foo", "<Esc>", "j0", "."),
|
||||
"<caret>foo baz\n" +
|
||||
"baz quux\n",
|
||||
"foo baz\n" +
|
||||
"fo<caret>o quux\n");
|
||||
}
|
||||
|
||||
// VIM-511 |.|
|
||||
public void testRepeatWithParensAndQuotesAutoInsertion() {
|
||||
configureByJavaText("class C <caret>{\n" +
|
||||
"}\n");
|
||||
typeText(parseKeys("o", "foo(\"<Right>, \"<Right><Right>;", "<Esc>", "."));
|
||||
myFixture.checkResult("class C {\n" +
|
||||
" foo(\"\", \"\");\n" +
|
||||
" foo(\"\", \"\");\n" +
|
||||
"}\n");
|
||||
}
|
||||
|
||||
// VIM-511 |.|
|
||||
public void testDeleteBothParensAndStartAgain() {
|
||||
configureByJavaText("class C <caret>{\n" +
|
||||
"}\n");
|
||||
typeText(parseKeys("o", "C(", "<BS>", "(int i) {}", "<Esc>", "."));
|
||||
myFixture.checkResult("class C {\n" +
|
||||
" C(int i) {}\n" +
|
||||
" C(int i) {}\n" +
|
||||
"}\n");
|
||||
}
|
||||
|
||||
// VIM-613 |.|
|
||||
public void testDeleteEndOfLineAndAgain() {
|
||||
configureByText("<caret>- 1\n" +
|
||||
"- 2\n" +
|
||||
"- 3\n");
|
||||
typeText(parseKeys("d$", "j", "."));
|
||||
myFixture.checkResult("\n" +
|
||||
"\n" +
|
||||
"- 3\n");
|
||||
}
|
||||
|
||||
// VIM-511 |.|
|
||||
public void testAutoCompleteCurlyBraceWithEnterWithinFunctionBody() {
|
||||
configureByJavaText("class C <caret>{\n" +
|
||||
"}\n");
|
||||
typeText(parseKeys("o", "C(", "<BS>", "(int i) {", "<Enter>", "i = 3;", "<Esc>", "<Down>", "."));
|
||||
myFixture.checkResult("class C {\n" +
|
||||
" C(int i) {\n" +
|
||||
" i = 3;\n" +
|
||||
" }\n" +
|
||||
" C(int i) {\n" +
|
||||
" i = 3;\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,122 @@
|
||||
package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
/**
|
||||
* @author Tuomas Tynkkynen
|
||||
*/
|
||||
public class ChangeNumberActionTest extends VimTestCase {
|
||||
public void testIncrementDecimalZero() {
|
||||
doTest(parseKeys("<C-A>"), "0", "1");
|
||||
}
|
||||
|
||||
public void testIncrementHexZero() {
|
||||
doTest(parseKeys("<C-A>"), "0x0", "0x1");
|
||||
}
|
||||
|
||||
public void testDecrementZero() {
|
||||
doTest(parseKeys("<C-X>"), "0", "-1");
|
||||
}
|
||||
|
||||
public void testIncrementDecimal() {
|
||||
doTest(parseKeys("<C-A>"), "199", "200");
|
||||
}
|
||||
|
||||
public void testDecrementDecimal() {
|
||||
doTest(parseKeys("<C-X>"), "1000", "999");
|
||||
}
|
||||
|
||||
public void testIncrementOctal() {
|
||||
doTest(parseKeys("<C-A>"), "0477", "0500");
|
||||
}
|
||||
|
||||
public void testDecrementOctal() {
|
||||
doTest(parseKeys("<C-X>"), "010", "007");
|
||||
}
|
||||
|
||||
public void testIncrementHex() {
|
||||
doTest(parseKeys("<C-A>"), "0xff", "0x100");
|
||||
}
|
||||
|
||||
public void testDecrementHex() {
|
||||
doTest(parseKeys("<C-X>"), "0xa100", "0xa0ff");
|
||||
}
|
||||
|
||||
public void testIncrementNegativeDecimal() {
|
||||
doTest(parseKeys("<C-A>"), "-199", "-198");
|
||||
}
|
||||
|
||||
public void testDecrementNegativeDecimal() {
|
||||
doTest(parseKeys("<C-X>"), "-1000", "-1001");
|
||||
}
|
||||
|
||||
public void testIncrementNegativeOctal() {
|
||||
doTest(parseKeys("<C-A>"), "-0477", "-0500");
|
||||
}
|
||||
|
||||
public void testDecrementNegativeOctal() {
|
||||
doTest(parseKeys("<C-X>"), "-010", "-007");
|
||||
}
|
||||
|
||||
public void testIncrementNegativeHex() {
|
||||
doTest(parseKeys("<C-A>"), "-0xff", "-0x100");
|
||||
}
|
||||
|
||||
public void testDecrementNegativeHex() {
|
||||
doTest(parseKeys("<C-X>"), "-0xa100", "-0xa0ff");
|
||||
}
|
||||
|
||||
public void testIncrementWithCount() {
|
||||
doTest(parseKeys("123<C-A>"), "456", "579");
|
||||
}
|
||||
|
||||
public void testDecrementWithCount() {
|
||||
doTest(parseKeys("200<C-X>"), "100", "-100");
|
||||
}
|
||||
|
||||
public void testIncrementAlphaWithoutNumberFormatAlpha() {
|
||||
doTest(parseKeys("<C-A>"), "foo", "foo");
|
||||
}
|
||||
|
||||
public void testIncrementAlphaWithNumberFormatAlpha() {
|
||||
doTest(parseKeys(":set nf=alpha<Enter>", "<C-A>"), "foo", "goo");
|
||||
}
|
||||
|
||||
public void testIncrementZWithNumberFormatAlpha() {
|
||||
doTest(parseKeys(":set nf=alpha<Enter>", "<C-A>"), "zzz", "zzz");
|
||||
}
|
||||
|
||||
public void testIncrementXInHexNumberWithNumberFormatAlphaButNotHex() {
|
||||
doTest(parseKeys(":set nf=alpha<Enter>", "<C-A>"), "0<caret>x1", "0y1");
|
||||
}
|
||||
|
||||
public void testIncrementXInHexNumberWithNumberFormatHexAlpha() {
|
||||
doTest(parseKeys(":set nf=alpha,hex<Enter>", "<C-A>"), "0<caret>x1", "0x2");
|
||||
}
|
||||
|
||||
public void testIncrementHexNumberWithoutNumberFormatHex() {
|
||||
doTest(parseKeys(":set nf=octal<Enter>", "<C-A>"), "0x42", "1x42");
|
||||
}
|
||||
|
||||
public void testIncrementOctalNumberWithoutNumberFormatOctal() {
|
||||
doTest(parseKeys(":set nf=hex<Enter>", "<C-A>"), "077", "078");
|
||||
}
|
||||
|
||||
public void testIncrementNegativeOctalNumberWithoutNumberFormatOctal() {
|
||||
doTest(parseKeys(":set nf=hex<Enter>", "<C-A>"), "-077", "-076");
|
||||
}
|
||||
|
||||
public void testIncrementHexPreservesCaseOfX() {
|
||||
doTest(parseKeys("<C-A>"), "0X88", "0X89");
|
||||
}
|
||||
|
||||
public void testIncrementHexTakesCaseFromLastLetter() {
|
||||
doTest(parseKeys("<C-A>"), "0xaB0", "0xAB1");
|
||||
}
|
||||
|
||||
public void testIncrementLocatesNumberOnTheSameLine() {
|
||||
doTest(parseKeys("<C-A>"), "foo ->* bar 123\n", "foo ->* bar 12<caret>4\n");
|
||||
}
|
||||
}
|
@@ -2,6 +2,10 @@ package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.common.Register;
|
||||
import com.maddyhome.idea.vim.option.ListOption;
|
||||
import com.maddyhome.idea.vim.option.Options;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
@@ -29,6 +33,17 @@ public class CopyActionTest extends VimTestCase {
|
||||
"three\n");
|
||||
}
|
||||
|
||||
// VIM-723 |p|
|
||||
public void testYankPasteToEmptyLine() {
|
||||
typeTextInFile(parseKeys("yiw", "j", "p"),
|
||||
"foo\n" +
|
||||
"\n" +
|
||||
"bar\n");
|
||||
myFixture.checkResult("foo\n" +
|
||||
"foo\n" +
|
||||
"bar\n");
|
||||
}
|
||||
|
||||
// VIM-390 |yy| |p|
|
||||
public void testYankLinePasteAtLastLine() {
|
||||
typeTextInFile(parseKeys("yy", "p"),
|
||||
@@ -64,10 +79,9 @@ public class CopyActionTest extends VimTestCase {
|
||||
|
||||
public void testWrongYankQuoteYankLine() {
|
||||
assertPluginError(false);
|
||||
typeTextInFile(parseKeys("y\"", "yy", "p"),
|
||||
"one <caret>two\n" +
|
||||
"three\n" +
|
||||
"four\n");
|
||||
typeTextInFile(parseKeys("y\"", "yy", "p"), "one <caret>two\n" +
|
||||
"three\n" +
|
||||
"four\n");
|
||||
assertPluginError(false);
|
||||
myFixture.checkResult("one two\n" +
|
||||
"one two\n" +
|
||||
@@ -97,8 +111,8 @@ public class CopyActionTest extends VimTestCase {
|
||||
//
|
||||
// The problem is that the selection range should be 1-char wide when entering the visual block mode
|
||||
|
||||
myFixture.checkResult("* *one\n" +
|
||||
"* *two\n");
|
||||
myFixture.checkResult("* * one\n" +
|
||||
"* * two\n");
|
||||
assertSelection(null);
|
||||
assertOffset(2);
|
||||
}
|
||||
@@ -112,4 +126,49 @@ public class CopyActionTest extends VimTestCase {
|
||||
assertMode(CommandState.Mode.COMMAND);
|
||||
assertSelection(null);
|
||||
}
|
||||
|
||||
// VIM-476 |yy| |'clipboard'|
|
||||
public void testClipboardUnnamed() {
|
||||
assertEquals('\"', VimPlugin.getRegister().getDefaultRegister());
|
||||
final ListOption clipboardOption = Options.getInstance().getListOption(Options.CLIPBOARD);
|
||||
assertNotNull(clipboardOption);
|
||||
clipboardOption.set("unnamed");
|
||||
assertEquals('*', VimPlugin.getRegister().getDefaultRegister());
|
||||
typeTextInFile(parseKeys("yy"),
|
||||
"foo\n" +
|
||||
"<caret>bar\n" +
|
||||
"baz\n");
|
||||
final Register starRegister = VimPlugin.getRegister().getRegister('*');
|
||||
if (starRegister != null) {
|
||||
assertEquals("bar\n", starRegister.getText());
|
||||
}
|
||||
}
|
||||
|
||||
// VIM-792 |"*| |yy| |p|
|
||||
public void testLineWiseClipboardYankPaste() {
|
||||
configureByText("<caret>foo\n");
|
||||
typeText(parseKeys("\"*yy", "\"*p"));
|
||||
final Register register = VimPlugin.getRegister().getRegister('*');
|
||||
if (register != null) {
|
||||
assertEquals("foo\n", register.getText());
|
||||
myFixture.checkResult("foo\n" +
|
||||
"<caret>foo\n");
|
||||
}
|
||||
}
|
||||
|
||||
// VIM-792 |"*| |CTRL-V| |v_y| |p|
|
||||
public void testBlockWiseClipboardYankPaste() {
|
||||
configureByText("<caret>foo\n" +
|
||||
"bar\n" +
|
||||
"baz\n");
|
||||
typeText(parseKeys("<C-V>j", "\"*y", "\"*p"));
|
||||
final Register register = VimPlugin.getRegister().getRegister('*');
|
||||
if (register != null) {
|
||||
assertEquals("f\n" +
|
||||
"b", register.getText());
|
||||
myFixture.checkResult("ffoo\n" +
|
||||
"bbar\n" +
|
||||
"baz\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
118
test/org/jetbrains/plugins/ideavim/action/MarkTest.java
Normal file
118
test/org/jetbrains/plugins/ideavim/action/MarkTest.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import com.maddyhome.idea.vim.VimPlugin;
|
||||
import com.maddyhome.idea.vim.common.Mark;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
/**
|
||||
* @author Tuomas Tynkkynen
|
||||
*/
|
||||
public class MarkTest extends VimTestCase {
|
||||
// |m|
|
||||
public void testLocalMark() {
|
||||
typeTextInFile(parseKeys("ma"), " foo\n" +
|
||||
" ba<caret>r\n" +
|
||||
" baz\n");
|
||||
Mark mark = VimPlugin.getMark().getMark(myFixture.getEditor(), 'a');
|
||||
assertNotNull(mark);
|
||||
assertEquals(1, mark.getLogicalLine());
|
||||
assertEquals(6, mark.getCol());
|
||||
}
|
||||
|
||||
// |m|
|
||||
public void testGlobalMark() {
|
||||
typeTextInFile(parseKeys("mG"), " foo\n" +
|
||||
" ba<caret>r\n" +
|
||||
" baz\n");
|
||||
Mark mark = VimPlugin.getMark().getMark(myFixture.getEditor(), 'G');
|
||||
assertNotNull(mark);
|
||||
assertEquals(1, mark.getLogicalLine());
|
||||
assertEquals(6, mark.getCol());
|
||||
}
|
||||
|
||||
// |m|
|
||||
public void testMarkIsDeletedWhenLineIsDeleted() {
|
||||
typeTextInFile(parseKeys("mx", "dd"), " foo\n" +
|
||||
" ba<caret>r\n" +
|
||||
" baz\n");
|
||||
Mark mark = VimPlugin.getMark().getMark(myFixture.getEditor(), 'x');
|
||||
assertNull(mark);
|
||||
}
|
||||
|
||||
// |m|
|
||||
public void testMarkIsMovedUpWhenLinesAreDeletedAbove() {
|
||||
typeTextInFile(parseKeys("mx", "2k", "2dd"), " foo\n" +
|
||||
" ba<r\n" +
|
||||
" ba<caret>z\n");
|
||||
Mark mark = VimPlugin.getMark().getMark(myFixture.getEditor(), 'x');
|
||||
assertNotNull(mark);
|
||||
assertEquals(0, mark.getLogicalLine());
|
||||
assertEquals(6, mark.getCol());
|
||||
}
|
||||
|
||||
// |m|
|
||||
public void testMarkIsMovedDownWhenLinesAreInsertedAbove() {
|
||||
typeTextInFile(parseKeys("mY", "Obiff"), " foo\n" +
|
||||
" ba<caret>r\n" +
|
||||
" baz\n");
|
||||
Mark mark = VimPlugin.getMark().getMark(myFixture.getEditor(), 'Y');
|
||||
assertNotNull(mark);
|
||||
assertEquals(2, mark.getLogicalLine());
|
||||
assertEquals(6, mark.getCol());
|
||||
}
|
||||
|
||||
// |m| |`|
|
||||
public void testMarkAndJumpToMark() {
|
||||
typeTextInFile(parseKeys("6l", "mZ", "G$", "`Z"), " foo\n" +
|
||||
" bar\n" +
|
||||
" baz\n");
|
||||
assertOffset(6);
|
||||
}
|
||||
|
||||
// |m| |'|
|
||||
public void testMarkAndJumpToMarkLeadingSpace() {
|
||||
typeTextInFile(parseKeys("6l", "mb", "G$", "'b"), " foo\n" +
|
||||
" bar\n" +
|
||||
" baz\n");
|
||||
assertOffset(4);
|
||||
}
|
||||
|
||||
// |m| |`|
|
||||
public void testDeleteBacktickMotionIsCharacterWise() {
|
||||
typeTextInFile(parseKeys("mk", "kh", "d`k"), " abcd\n" +
|
||||
" efgh\n" +
|
||||
" ij<caret>kl\n" +
|
||||
" mnop\n");
|
||||
myFixture.checkResult(" abcd\n" +
|
||||
" ekl\n" +
|
||||
" mnop\n");
|
||||
}
|
||||
|
||||
// |m| |`|
|
||||
public void testDeleteSingleQuoteMotionIsLineWise() {
|
||||
typeTextInFile(parseKeys("mk", "kh", "d'k"), " abcd\n" +
|
||||
" efgh\n" +
|
||||
" ij<caret>kl\n" +
|
||||
" mnop\n");
|
||||
myFixture.checkResult(" abcd\n" +
|
||||
" mnop\n");
|
||||
}
|
||||
|
||||
// VIM-43 |i| |`.|
|
||||
public void testGotoLastChangePosition() {
|
||||
typeTextInFile(parseKeys("i", "hello ", "<Esc>", "gg", "`."), "one two\n" +
|
||||
"<caret>hello world\n" +
|
||||
"three four\n");
|
||||
assertOffset(13);
|
||||
}
|
||||
|
||||
// VIM-43 |p| |`.|
|
||||
public void testGotoLastPutPosition() {
|
||||
typeTextInFile(parseKeys("yy", "p", "gg", "`."), "one two\n" +
|
||||
"<caret>three\n" +
|
||||
"four five\n");
|
||||
assertOffset(14);
|
||||
}
|
||||
}
|
@@ -6,6 +6,7 @@ import com.maddyhome.idea.vim.VimPlugin;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.command.CommandState.Mode.COMMAND;
|
||||
import static com.maddyhome.idea.vim.command.CommandState.Mode.VISUAL;
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.stringToKeys;
|
||||
|
||||
@@ -79,10 +80,58 @@ public class MotionActionTest extends VimTestCase {
|
||||
public void testBackToDigraph() {
|
||||
typeTextInFile(parseKeys("F<C-K>O:"),
|
||||
"Hallo, Öster<caret>reich!\n");
|
||||
assertOffset(7);
|
||||
myFixture.checkResult("Hallo, <caret>Österreich!\n");
|
||||
assertMode(COMMAND);
|
||||
}
|
||||
|
||||
// VIM-771 |t| |;|
|
||||
public void testTillCharRight() {
|
||||
typeTextInFile(parseKeys("t:;"),
|
||||
"<caret> 1:a 2:b 3:c \n");
|
||||
myFixture.checkResult(" 1:a <caret>2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |;|
|
||||
public void testTillCharRightRepeated() {
|
||||
typeTextInFile(parseKeys("t:;"),
|
||||
"<caret> 1:a 2:b 3:c \n");
|
||||
myFixture.checkResult(" 1:a <caret>2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |;|
|
||||
public void testTillCharRightRepeatedWithCount2() {
|
||||
typeTextInFile(parseKeys("t:2;"),
|
||||
"<caret> 1:a 2:b 3:c \n");
|
||||
myFixture.checkResult(" 1:a <caret>2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |;|
|
||||
public void testTillCharRightRepeatedWithCountHigherThan2() {
|
||||
typeTextInFile(parseKeys("t:3;"), "<caret> 1:a 2:b 3:c \n");
|
||||
myFixture.checkResult(" 1:a 2:b <caret>3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |,|
|
||||
public void testTillCharRightReverseRepeated() {
|
||||
typeTextInFile(parseKeys("t:,,"),
|
||||
" 1:a 2:b<caret> 3:c \n");
|
||||
myFixture.checkResult(" 1:<caret>a 2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |,|
|
||||
public void testTillCharRightReverseRepeatedWithCount2() {
|
||||
typeTextInFile(parseKeys("t:,2,"),
|
||||
" 1:a 2:b<caret> 3:c \n");
|
||||
myFixture.checkResult(" 1:<caret>a 2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-771 |t| |,|
|
||||
public void testTillCharRightReverseRepeatedWithCountHigherThan3() {
|
||||
typeTextInFile(parseKeys("t:,3,"),
|
||||
" 0:_ 1:a 2:b<caret> 3:c \n");
|
||||
myFixture.checkResult(" 0:<caret>_ 1:a 2:b 3:c \n");
|
||||
}
|
||||
|
||||
// VIM-326 |d| |v_ib|
|
||||
public void testDeleteInnerBlock() {
|
||||
typeTextInFile(parseKeys("di)"),
|
||||
@@ -112,6 +161,12 @@ public class MotionActionTest extends VimTestCase {
|
||||
assertOffset(4);
|
||||
}
|
||||
|
||||
// |v_ib|
|
||||
public void testInnerBlockCrashWhenNoDelimiterFound() {
|
||||
typeTextInFile(parseKeys("di)"), "(x\n");
|
||||
myFixture.checkResult("(x\n");
|
||||
}
|
||||
|
||||
// VIM-314 |d| |v_iB|
|
||||
public void testDeleteInnerCurlyBraceBlock() {
|
||||
typeTextInFile(parseKeys("di{"),
|
||||
@@ -197,6 +252,12 @@ public class MotionActionTest extends VimTestCase {
|
||||
myFixture.checkResult("Hello World! Bye.\n");
|
||||
}
|
||||
|
||||
// |v_as|
|
||||
public void testSentenceMotionPastStartOfFile() {
|
||||
typeTextInFile(parseKeys("8("), "\n" +
|
||||
"P<caret>.\n");
|
||||
}
|
||||
|
||||
// |d| |v_ip|
|
||||
public void testDeleteInnerParagraph() {
|
||||
typeTextInFile(parseKeys("dip"),
|
||||
@@ -365,6 +426,76 @@ public class MotionActionTest extends VimTestCase {
|
||||
assertOffset(3);
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchXmlCommentStart() {
|
||||
configureByXmlText("<caret><!-- foo -->");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<!-- foo --<caret>>");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentDoesntMatchPartialXmlComment() {
|
||||
configureByXmlText("<!<caret>-- ");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<!<caret>-- ");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchXmlCommentEnd() {
|
||||
configureByXmlText("<!-- foo --<caret>>");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<caret><!-- foo -->");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchJavaCommentStart() {
|
||||
configureByJavaText("/<caret>* foo */");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("/* foo *<caret>/");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentDoesntMatchPartialJavaComment() {
|
||||
configureByJavaText("<caret>/* ");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<caret>/* ");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchJavaCommentEnd() {
|
||||
configureByJavaText("/* foo <caret>*/");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<caret>/* foo */");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchJavaDocCommentStart() {
|
||||
configureByJavaText("/*<caret>* foo */");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("/** foo *<caret>/");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentMatchJavaDocCommentEnd() {
|
||||
configureByJavaText("/** foo *<caret>/");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("<caret>/** foo */");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentDoesntMatchAfterCommentStart() {
|
||||
configureByJavaText("/*<caret> foo */");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("/*<caret> foo */");
|
||||
}
|
||||
|
||||
// |%|
|
||||
public void testPercentDoesntMatchBeforeCommentEnd() {
|
||||
configureByJavaText("/* foo <caret> */");
|
||||
typeText(parseKeys("%"));
|
||||
myFixture.checkResult("/* foo <caret> */");
|
||||
}
|
||||
|
||||
// |[(|
|
||||
public void testUnmatchedOpenParenthesis() {
|
||||
typeTextInFile(parseKeys("[("),
|
||||
@@ -499,6 +630,13 @@ public class MotionActionTest extends VimTestCase {
|
||||
assertOffset(2);
|
||||
}
|
||||
|
||||
// |b|
|
||||
public void testWordBackwardsAtFirstLineWithWhitespaceInFront() {
|
||||
typeTextInFile(parseKeys("b"),
|
||||
" <caret>x\n");
|
||||
assertOffset(0);
|
||||
}
|
||||
|
||||
public void testRightToLastChar() {
|
||||
typeTextInFile(parseKeys("i<Right>"),
|
||||
"on<caret>e\n");
|
||||
@@ -512,24 +650,6 @@ public class MotionActionTest extends VimTestCase {
|
||||
assertOffset(4);
|
||||
}
|
||||
|
||||
// VIM-43 |i| |`.|
|
||||
public void testGotoLastChangePosition() {
|
||||
typeTextInFile(parseKeys("i", "hello ", "<Esc>", "gg", "`."),
|
||||
"one two\n" +
|
||||
"<caret>hello world\n" +
|
||||
"three four\n");
|
||||
assertOffset(13);
|
||||
}
|
||||
|
||||
// VIM-43 |p| |`.|
|
||||
public void testGotoLastPutPosition() {
|
||||
typeTextInFile(parseKeys("yy", "p", "gg", "`."),
|
||||
"one two\n" +
|
||||
"<caret>three\n" +
|
||||
"four five\n");
|
||||
assertOffset(14);
|
||||
}
|
||||
|
||||
// VIM-262 |c_CTRL-R|
|
||||
public void testSearchFromRegister() {
|
||||
VimPlugin.getRegister().setKeys('a', stringToKeys("two"));
|
||||
@@ -547,6 +667,35 @@ public class MotionActionTest extends VimTestCase {
|
||||
myFixture.checkResult("foo \n");
|
||||
}
|
||||
|
||||
// |CTRL-V|
|
||||
public void testVisualBlockSelectionsDisplayedCorrectlyMovingRight() {
|
||||
typeTextInFile(parseKeys("<C-V>jl"),
|
||||
"<caret>foo\n" +
|
||||
"bar\n");
|
||||
myFixture.checkResult("<selection>fo</selection>o\n" +
|
||||
"<selection>ba</selection>r\n");
|
||||
}
|
||||
|
||||
// |CTRL-V|
|
||||
public void testVisualBlockSelectionsDisplayedCorrectlyMovingLeft() {
|
||||
typeTextInFile(parseKeys("<C-V>jh"),
|
||||
"fo<caret>o\n" +
|
||||
"bar\n");
|
||||
myFixture.checkResult("f<selection>oo</selection>\n" +
|
||||
"b<selection>ar</selection>\n");
|
||||
}
|
||||
|
||||
// |CTRL-V|
|
||||
public void testVisualBlockSelectionsDisplayedCorrectlyInDollarMode() {
|
||||
typeTextInFile(parseKeys("<C-V>jj$"),
|
||||
"a<caret>b\n" +
|
||||
"abc\n" +
|
||||
"ab\n");
|
||||
myFixture.checkResult("a<selection>b</selection>\n" +
|
||||
"a<selection>bc</selection>\n" +
|
||||
"a<selection>b</selection>\n");
|
||||
}
|
||||
|
||||
// |v_o|
|
||||
public void testSwapVisualSelectionEnds() {
|
||||
typeTextInFile(parseKeys("v", "l", "o", "l", "d"),
|
||||
@@ -593,4 +742,29 @@ public class MotionActionTest extends VimTestCase {
|
||||
" bar\n" +
|
||||
" baz");
|
||||
}
|
||||
|
||||
public void testVisualLineSelectDown() {
|
||||
typeTextInFile(parseKeys("Vj"),
|
||||
"foo\n" +
|
||||
"<caret>bar\n" +
|
||||
"baz\n" +
|
||||
"quux\n");
|
||||
assertMode(VISUAL);
|
||||
assertSelection("bar\n" +
|
||||
"baz");
|
||||
assertOffset(8);
|
||||
}
|
||||
|
||||
// VIM-784
|
||||
public void testVisualLineSelectUp() {
|
||||
typeTextInFile(parseKeys("Vk"),
|
||||
"foo\n" +
|
||||
"bar\n" +
|
||||
"<caret>baz\n" +
|
||||
"quux\n");
|
||||
assertMode(VISUAL);
|
||||
assertSelection("bar\n" +
|
||||
"baz");
|
||||
assertOffset(4);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,85 @@
|
||||
package org.jetbrains.plugins.ideavim.action;
|
||||
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
|
||||
/**
|
||||
* @author abrookins
|
||||
*/
|
||||
public class ShiftRightLinesActionTest extends VimTestCase {
|
||||
// VIM-407
|
||||
public void testShiftShiftsOneCharacterSingleLine() {
|
||||
myFixture.configureByText("a.txt", "<caret>w\n");
|
||||
typeText(parseKeys(">>"));
|
||||
myFixture.checkResult(" w\n");
|
||||
}
|
||||
|
||||
// VIM-407
|
||||
public void testShiftShiftsOneCharacterMultiLine() {
|
||||
myFixture.configureByText("a.txt", "Hello\n<caret>w\nWorld");
|
||||
typeText(parseKeys(">>"));
|
||||
myFixture.checkResult("Hello\n w\nWorld");
|
||||
}
|
||||
|
||||
public void testShiftShiftsMultipleCharactersOneLine() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello, world!\n");
|
||||
typeText(parseKeys(">>"));
|
||||
myFixture.checkResult(" Hello, world!\n");
|
||||
}
|
||||
|
||||
public void testShiftShiftsMultipleCharactersMultipleLines() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\nworld!\n");
|
||||
typeText(parseKeys("j>>"));
|
||||
myFixture.checkResult("Hello,\n world!\n");
|
||||
}
|
||||
|
||||
public void testShiftsSingleLineSelection() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\nworld!\n");
|
||||
typeText(parseKeys("jv$>>"));
|
||||
myFixture.checkResult("Hello,\n world!\n");
|
||||
}
|
||||
|
||||
public void testShiftsMultiLineSelection() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\nworld!\n");
|
||||
typeText(parseKeys("vj$>>"));
|
||||
myFixture.checkResult(" Hello,\n world!\n");
|
||||
}
|
||||
|
||||
public void testShiftsMultiLineSelectionSkipsNewline() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\nworld!\n\n");
|
||||
typeText(parseKeys("vG$>>"));
|
||||
myFixture.checkResult(" Hello,\n world!\n\n");
|
||||
}
|
||||
|
||||
public void testShiftsMultiLineSelectionSkipsNewlineWhenCursorNotInFirstColumn() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\n\nworld!\n");
|
||||
typeText(parseKeys("lVG>"));
|
||||
myFixture.checkResult(" Hello,\n\n world!\n");
|
||||
}
|
||||
|
||||
public void testShiftsMultiLineSelectionAddsTrailingWhitespaceIfTherePreviouslyWas() {
|
||||
myFixture.configureByText("a.txt", "<caret>Hello,\n \nworld!\n");
|
||||
typeText(parseKeys("lVG>"));
|
||||
myFixture.checkResult(" Hello,\n \n world!\n");
|
||||
}
|
||||
|
||||
// VIM-705 repeating a multiline indent would only affect last line
|
||||
public void testShiftsMultiLineSelectionRepeat() {
|
||||
myFixture.configureByText("a.txt", "<caret>a\nb\n");
|
||||
typeText(parseKeys("Vj>."));
|
||||
myFixture.checkResult(" a\n b\n");
|
||||
}
|
||||
|
||||
public void testShiftsDontCrashKeyHandler() {
|
||||
myFixture.configureByText("a.txt", "\n");
|
||||
typeText(parseKeys("<I<>", "<I<>"));
|
||||
}
|
||||
|
||||
public void testShiftsVisualBlockMode() {
|
||||
myFixture.configureByText("a.txt", "foo<caret>foo\nfoobar\nfoobaz\n");
|
||||
typeText(parseKeys("<C-V>jjl>"));
|
||||
myFixture.checkResult("foo foo\nfoo bar\nfoo baz\n");
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
package org.jetbrains.plugins.ideavim.ex;
|
||||
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import com.maddyhome.idea.vim.ex.VimScriptParser;
|
||||
import com.maddyhome.idea.vim.ex.vimscript.VimScriptParser;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
@@ -243,4 +243,37 @@ public class MapCommandTest extends VimTestCase {
|
||||
typeText(parseKeys("A"));
|
||||
myFixture.checkResult("bar\n");
|
||||
}
|
||||
|
||||
// VIM-700 |:map|
|
||||
public void testRemappingZero() {
|
||||
configureByText("x<caret>yz\n");
|
||||
VimScriptParser.executeText("map 0 ~");
|
||||
typeText(parseKeys("0"));
|
||||
myFixture.checkResult("xYz\n");
|
||||
}
|
||||
|
||||
// VIM-700 |:map|
|
||||
public void testRemappingZeroStillAllowsZeroToBeUsedInCount() {
|
||||
configureByText("a<caret>bcdefghijklmnop\n");
|
||||
VimScriptParser.executeText("map 0 ^");
|
||||
typeText(parseKeys("10~"));
|
||||
myFixture.checkResult("aBCDEFGHIJKlmnop\n");
|
||||
}
|
||||
|
||||
// VIM-700 |:map|
|
||||
public void testRemappingDeleteOverridesRemovingLastDigitFromCount() {
|
||||
configureByText("a<caret>bcdefghijklmnop\n");
|
||||
VimScriptParser.executeText("map <Del> ~");
|
||||
typeText(parseKeys("10<Del>"));
|
||||
myFixture.checkResult("aBCDEFGHIJKlmnop\n");
|
||||
}
|
||||
|
||||
// VIM-650 |mapleader|
|
||||
public void testMapLeader() {
|
||||
configureByText("\n");
|
||||
typeText(commandToKeys("let mapleader = \",\""));
|
||||
typeText(commandToKeys("nmap <Leader>z izzz<Esc>"));
|
||||
typeText(parseKeys(",z"));
|
||||
myFixture.checkResult("zzz\n");
|
||||
}
|
||||
}
|
||||
|
122
test/org/jetbrains/plugins/ideavim/ex/RangeTest.java
Normal file
122
test/org/jetbrains/plugins/ideavim/ex/RangeTest.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package org.jetbrains.plugins.ideavim.ex;
|
||||
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
/**
|
||||
* @author Tuomas Tynkkynen
|
||||
*/
|
||||
public class RangeTest extends VimTestCase {
|
||||
public void testNoRange() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n<caret>3\n4\n5\n");
|
||||
typeText(commandToKeys("d"));
|
||||
myFixture.checkResult("1\n2\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testCurrentLine() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n<caret>3\n4\n5\n");
|
||||
typeText(commandToKeys(".d"));
|
||||
myFixture.checkResult("1\n2\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testLastLine() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("$s/5/x/"));
|
||||
myFixture.checkResult("1\n2\n3\n4\nx\n");
|
||||
}
|
||||
|
||||
public void testOneLineNumber() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("3d"));
|
||||
myFixture.checkResult("1\n2\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testPositiveOffset() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n<caret>3\n4\n5\n");
|
||||
typeText(commandToKeys(".+1d"));
|
||||
myFixture.checkResult("1\n2\n3\n5\n");
|
||||
}
|
||||
|
||||
public void testNegativeOffset() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("$-2d"));
|
||||
myFixture.checkResult("1\n2\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testOffsetWithNoNumber() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n<caret>3\n4\n5\n");
|
||||
typeText(commandToKeys(".+d"));
|
||||
myFixture.checkResult("1\n2\n3\n5\n");
|
||||
}
|
||||
|
||||
public void testTwoOffsetsWithSameSign() {
|
||||
myFixture.configureByText("a.txt", "1\n<caret>2\n3\n4\n5\n");
|
||||
typeText(commandToKeys(".+1+1d"));
|
||||
myFixture.checkResult("1\n2\n3\n5\n");
|
||||
}
|
||||
|
||||
public void testTwoOffsetsWithDifferentSign() {
|
||||
myFixture.configureByText("a.txt", "1\n<caret>2\n3\n4\n5\n");
|
||||
typeText(commandToKeys(".+2-1d"));
|
||||
myFixture.checkResult("1\n2\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testSearchForward() {
|
||||
myFixture.configureByText("a.txt", "c\na\n<caret>b\nc\nd\ne\n");
|
||||
typeText(commandToKeys("/c/d"));
|
||||
myFixture.checkResult("c\na\nb\nd\ne\n");
|
||||
}
|
||||
|
||||
public void testSearchBackward() {
|
||||
myFixture.configureByText("a.txt", "c\na\n<caret>b\nc\nd\ne\n");
|
||||
typeText(commandToKeys("?c?d"));
|
||||
myFixture.checkResult("a\nb\nc\nd\ne\n");
|
||||
}
|
||||
|
||||
public void testSearchWithBackslashInPattern() {
|
||||
myFixture.configureByText("a.txt", "+ add\n<caret>- sub\n/ div\n* mul\n");
|
||||
typeText(commandToKeys("/\\/ div/d"));
|
||||
myFixture.checkResult("+ add\n- sub\n* mul\n");
|
||||
}
|
||||
|
||||
public void testAllLinesRange() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("%d"));
|
||||
myFixture.checkResult("\n");
|
||||
}
|
||||
|
||||
public void testMultipleLineNumbersRange() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("2,4d"));
|
||||
myFixture.checkResult("1\n5\n");
|
||||
}
|
||||
|
||||
public void testMultipleLineNumbersWithOffsetInFirst() {
|
||||
myFixture.configureByText("a.txt", "<caret>1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys(".+1,4d"));
|
||||
myFixture.checkResult("1\n5\n");
|
||||
}
|
||||
|
||||
public void testMultipleLineNumbersWithOffsetInSecond() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("2,$-1d"));
|
||||
myFixture.checkResult("1\n5\n");
|
||||
}
|
||||
|
||||
public void testSearchStartPositionWithComma() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("/2/,/[0-9]/d"));
|
||||
myFixture.checkResult("1\n3\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testSearchStartPositionWithSemicolon() {
|
||||
myFixture.configureByText("a.txt", "1\n2\n3\n4\n5\n");
|
||||
typeText(commandToKeys("/2/;/[0-9]/d"));
|
||||
myFixture.checkResult("1\n4\n5\n");
|
||||
}
|
||||
|
||||
public void testMultipleSearches() {
|
||||
myFixture.configureByText("a.txt", "a\nfoo\nbar\nfoo\nbar\nbaz\n");
|
||||
typeText(commandToKeys("/bar//foo/d"));
|
||||
myFixture.checkResult("a\nfoo\nbar\nbar\nbaz\n");
|
||||
}
|
||||
}
|
@@ -92,4 +92,10 @@ public class SortCommandTest extends VimTestCase {
|
||||
typeText(commandToKeys("sort"));
|
||||
myFixture.checkResult("a\nb\nc\nwhatever\nzee");
|
||||
}
|
||||
|
||||
public void testSortWithPrecedingWhiteSpace() {
|
||||
myFixture.configureByText("a.txt", " zee\n c\n a\n b\n whatever");
|
||||
typeText(commandToKeys("sort"));
|
||||
myFixture.checkResult(" a\n b\n c\n whatever\n zee");
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@ package org.jetbrains.plugins.ideavim.ex;
|
||||
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
@@ -75,6 +77,33 @@ public class SubstituteCommandTest extends VimTestCase {
|
||||
"one\n.two\n.three\n");
|
||||
}
|
||||
|
||||
// VIM-702 |:substitute|
|
||||
public void testEndOfLineToNL() {
|
||||
doTest("%s/$/\\r/g",
|
||||
"<caret>one\ntwo\nthree\n",
|
||||
"one\n\ntwo\n\nthree\n\n");
|
||||
}
|
||||
|
||||
// VIM-702 |:substitute|
|
||||
public void testStartOfLineToNL() {
|
||||
doTest("%s/^/\\r/g",
|
||||
"<caret>one\ntwo\nthree\n",
|
||||
"\none\n\ntwo\n\nthree\n");
|
||||
}
|
||||
|
||||
// VIM-864 |:substitute|
|
||||
public void testVisualSubstituteDoesntChangeVisualMarks() {
|
||||
myFixture.configureByText("a.java", "foo\nbar\nbaz\n");
|
||||
typeText(parseKeys("V", "j", ":'<,'>s/foo/fuu/<Enter>", "gv", "~"));
|
||||
myFixture.checkResult("FUU\nBAR\nbaz\n");
|
||||
}
|
||||
|
||||
public void testOffsetRange() {
|
||||
doTest(".,+2s/a/b/g",
|
||||
"aaa\naa<caret>a\naaa\naaa\naaa\n",
|
||||
"aaa\nbbb\nbbb\nbbb\naaa\n");
|
||||
}
|
||||
|
||||
private void doTest(final String command, String before, String after) {
|
||||
myFixture.configureByText("a.java", before);
|
||||
typeText(commandToKeys(command));
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package org.jetbrains.plugins.ideavim.ex;
|
||||
|
||||
import com.maddyhome.idea.vim.command.CommandState;
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
import static com.maddyhome.idea.vim.helper.StringHelper.parseKeys;
|
||||
@@ -27,4 +28,14 @@ public class VariousCommandsTest extends VimTestCase {
|
||||
myFixture.checkResult("Hello World!\n" +
|
||||
"<caret>Hello \n");
|
||||
}
|
||||
|
||||
// VIM-652 |:action|
|
||||
public void testEditorRightAction() {
|
||||
configureByText("<caret>foo\n" +
|
||||
"bar\n");
|
||||
typeText(commandToKeys("action EditorRight"));
|
||||
assertMode(CommandState.Mode.COMMAND);
|
||||
myFixture.checkResult("f<caret>oo\n" +
|
||||
"bar\n");
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package org.jetbrains.plugins.ideavim.ex;
|
||||
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class VimScriptParserTest extends VimTestCase {
|
||||
public void testEchoStringLiteral() {
|
||||
configureByText("\n");
|
||||
typeText(commandToKeys("echo \"Hello, World!\""));
|
||||
assertExOutput("Hello, World!\n");
|
||||
}
|
||||
|
||||
public void testLetStringLiteralEcho() {
|
||||
configureByText("\n");
|
||||
typeText(commandToKeys("let s = \"foo\""));
|
||||
typeText(commandToKeys("echo s"));
|
||||
assertExOutput("foo\n");
|
||||
}
|
||||
}
|
@@ -74,6 +74,26 @@ public class SearchGroupTest extends VimTestCase {
|
||||
assertEquals(5, pos);
|
||||
}
|
||||
|
||||
// VIM-855 |/|
|
||||
public void testCharacterClassRegression() {
|
||||
final int pos = search("[^c]b",
|
||||
"<caret>bb\n");
|
||||
assertEquals(0, pos);
|
||||
}
|
||||
|
||||
// VIM-855 |/|
|
||||
public void testCharacterClassRegressionCaseInsensitive() {
|
||||
final int pos = search("\\c[ABC]D",
|
||||
"<caret>dd\n");
|
||||
assertEquals(-1, pos);
|
||||
}
|
||||
|
||||
// VIM-856 |/|
|
||||
public void testNegativeLookbehindRegression() {
|
||||
final int pos = search("a\\@<!b",
|
||||
"<caret>ab\n");
|
||||
assertEquals(-1, pos);
|
||||
}
|
||||
|
||||
// |/|
|
||||
public void testSearchMotion() {
|
||||
|
Reference in New Issue
Block a user