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

Compare commits

...

225 Commits
0.35 ... 0.43

Author SHA1 Message Date
Andrey Vlasovskikh
4cc784eb95 VIM-1039 Fixed running the plugin with Java 6 2015-11-02 00:45:23 +03:00
Andrey Vlasovskikh
ee3bec9eed Version 0.42 2015-11-01 02:23:09 +03:00
Andrey Vlasovskikh
24788ac86f Updated changelog 2015-10-30 16:11:16 +03:00
Andrey Vlasovskikh
9af3c732b5 Updated CI icons 2015-10-30 16:04:35 +03:00
Andrey Vlasovskikh
e5873fe5f9 Use HTTP by default 2015-10-30 15:41:55 +03:00
Andrey Vlasovskikh
39f088cdcd VIM-970 Use raw typed handlers in order not to require write action in read-only files
This change requires the API of IntelliJ platform branch 143+.
2015-10-30 15:37:17 +03:00
Andrey Vlasovskikh
e805852721 Platform prefix initialization is no longer needed 2015-10-30 15:20:36 +03:00
Andrey Vlasovskikh
e136ecc1f8 Added generated idea/ to excluded folders 2015-10-30 15:19:42 +03:00
Andrey Vlasovskikh
efa4001440 Require minimal platform version 143 2015-10-30 15:19:19 +03:00
Andrey Vlasovskikh
68c20ed48d Version 0.41 2015-06-10 15:42:02 +03:00
Andrey Vlasovskikh
c5760ceaab VIM-957 Fixed compatibility with IDEs other than IntelliJ 2015-06-10 15:41:33 +03:00
Andrey Vlasovskikh
8c7c9a67a6 Version 0.40 2015-06-09 23:01:54 +03:00
Andrey Vlasovskikh
85231b314f More readable test data 2015-04-24 16:29:56 +03:00
Andrey Vlasovskikh
210b2efd47 Updated changelog 2015-04-24 16:12:30 +03:00
Andrey Vlasovskikh
ae5b1385ea Merge branch 'matching-comments' 2015-04-24 16:08:54 +03:00
Andrey Vlasovskikh
011ce28251 More readable tests for '%' 2015-04-24 16:08:32 +03:00
Andrey Vlasovskikh
efb58b45df Cleanup 2015-04-24 16:02:20 +03:00
Andrey Vlasovskikh
7d033787eb Better place for cleaning global variables 2015-04-24 14:31:06 +03:00
Andrey Vlasovskikh
3a47583cf9 Clean global variables 2015-04-24 14:27:38 +03:00
Andrey Vlasovskikh
565b4b3f2f Updated changelog 2015-04-24 14:25:10 +03:00
Andrey Vlasovskikh
574f32a6dd Merge pull request #84 from JetBrains/mapleader
Mapleader support based on the prototype of a Vim script expressions evaluator
2015-04-24 13:33:38 +03:00
Andrey Vlasovskikh
0e67c8ff69 Removed unnecessary 'static' declaration 2015-04-24 13:32:26 +03:00
Andrey Vlasovskikh
b886073c11 VIM-650 Added support for 'mapleader' 2015-04-24 13:32:04 +03:00
Andrey Vlasovskikh
57c35b9a74 Merge remote-tracking branch 'origin/master' 2015-04-22 20:27:31 +03:00
Andrey Vlasovskikh
f5100694d6 VIM-930 Get editor focus after closing Ex entry box on Oracle Java 6 2015-04-22 20:26:41 +03:00
Andrey Vlasovskikh
3ff9bfcc13 Initial support for 'let' and 'echo' commands
These commands evaluate Vim script expressions. Right now they are
limited to a highly limited subset of Vim script expressions that is
however enough for settings the 'mapleader' variable for VIM-650.
2015-04-05 22:54:40 +03:00
Andrey Vlasovskikh
6c8d2bfd9e Merge pull request #83 from khromalabs/master
Added deployment instructions to README.md
2015-04-04 23:10:24 +03:00
Rubén Gómez
637f7f48ad Modified deployment instructions in README.md as suggested in pull request #83 2015-04-02 09:11:52 +02:00
Rubén Gómez
8eae80d30f Added deployment instructions to README.md 2015-03-30 23:55:04 +02:00
Andrey Vlasovskikh
9a103276e3 Merge pull request #82 from jflorian/master
Added note about where .ideavimrc is located
2015-03-30 16:39:52 +03:00
John Florian
6386f011f7 Merge remote-tracking branch 'upstream/master' 2015-03-30 09:32:09 -04:00
John Florian
742d6e63d9 Change - note regarding non-default user.home setting
Some users (e.g., me) have a non-default user.home VM option and this
affects where IdeaVim looks for its configuration file.  This note might
save some head scratching.
2015-03-30 09:26:32 -04:00
Andrey Vlasovskikh
f6141603ef Merge pull request #81 from jflorian/master
Аdd mapping example using :action
2015-03-29 02:20:19 +03:00
John Florian
7a5b4e565a Change - add mapping example using :action
This example may be helpful to regular VIM users since the :action and
:actionlist commands are specific to IdeaVIM.
2015-03-28 11:12:02 -04:00
Andrey Vlasovskikh
373e1527c1 Extracted Vim script parts into 'vimscript' package 2015-03-28 14:16:38 +03:00
Andrey Vlasovskikh
9cb05b6a7d Updated changelog 2015-03-27 21:55:45 +03:00
Andrey Vlasovskikh
74bf5ff044 Formatting and typos 2015-03-27 21:43:36 +03:00
Andrey Vlasovskikh
0f7e9d11b6 Added test for VIM-845 2015-03-27 21:43:19 +03:00
Andrey Vlasovskikh
5fadc2fb9f Removed unused imports 2015-03-27 21:30:05 +03:00
Andrey Vlasovskikh
96bbcf623f Removed unnecessary 'static' in enums 2015-03-27 21:20:17 +03:00
Andrey Vlasovskikh
89e853158c Typos 2015-03-27 21:19:56 +03:00
Andrey Vlasovskikh
5c31fcc03e Extracted CommandState.inVisualBlockMode() 2015-03-27 21:18:08 +03:00
Andrey Vlasovskikh
3fee4a803b Merge remote-tracking branch 'dezgeg/fixes/visual-block-mode-overhaul' 2015-03-27 20:56:58 +03:00
Andrey Vlasovskikh
8b61f559d7 Updated default code style settings 2015-03-24 23:52:04 +03:00
Andrey Vlasovskikh
4bd058f5dc Updated changelog 2015-03-24 23:51:34 +03:00
Andrey Vlasovskikh
999ccf87fc Merge remote-tracking branch 'dezgeg/fixes/vim-700-remap-zero' 2015-03-24 23:48:11 +03:00
Andrey Vlasovskikh
4e45e885b9 Merge remote-tracking branch 'dezgeg/feature/ant-improvements' 2015-03-24 23:25:31 +03:00
Tuomas Tynkkynen
6aee9ccaf1 build.xml: forkmode=once makes tests much faster
Since classloading of the IntelliJ platform takes a lot of time,
using forkmode=once in the Ant file makes running the unit tests from
Ant much faster (down to 16 seconds from 93 seconds) and is also more
closer to how the tests are run in the IDE.
2015-02-21 21:25:47 +02:00
Tuomas Tynkkynen
af3c6ff012 build.xml: Generate XML reports from JUnit test reports
The XML reports are useful for private CI servers like Jenkins.
2015-02-21 21:25:47 +02:00
Tuomas Tynkkynen
0c9bdf5168 Visual block mode overhaul
The current visual block mode implementation has several bugs:

- The GUI never displays the rightmost characters as selected (blue)
- Due to some off-by-one, commands can't act on each line's last character
- It's possible for newlines to get deleted/changed into something else
- If the '$' mode is entered, the GUI doesn't always indicate this
- It's impossible to move to an empty line (VIM-781)

This commit fixes all of those problems. The only remaining problem are
the visible secondary carets, which makes seeing the real cursor a bit
difficult. This could be fixed later if support for per-caret visibility
would be added to IntelliJ core.
2015-02-21 17:17:36 +02:00
Tuomas Tynkkynen
82a9587b4f Support comments in brace matching
In Vim, '%' can be used to jump between the '/*' and '*/' of block
comments. Support this functionality in a language-independent manner.
2015-02-21 15:14:38 +02:00
Tuomas Tynkkynen
850f4d7ec5 Make IdeaVIM build on latest intellij-community master
The zero-parameter overload of createStructureViewModel() has been
marked deprecated and is now removed.
2015-02-21 15:12:16 +02:00
Tuomas Tynkkynen
8d1dcc4568 VIM-700 Remapping '0' should still allow zero to be entered in count
Previously, remapping '0' in command mode would prevent a number zero
from being entered in a command count.
2015-02-21 15:09:13 +02:00
Andrey Vlasovskikh
3650ab85df Updated changelog 2015-01-21 19:50:50 +03:00
Andrey Vlasovskikh
e1e1cc2666 Code style
Use javadoc comments, added nullity annotations, extracted common
expressions.
2015-01-21 19:49:41 +03:00
Tuomas Tynkkynen
79fd32088b VIM-868 Allow count on 'gt' and 'gT'
'{count}gt' can now be used to switch to a specific tab.
'{count}gT' can now be used to switch to a n:th previous tab.
2015-01-21 18:11:48 +02:00
Andrey Vlasovskikh
458e0fc76d Updated changelog 2015-01-21 16:31:26 +03:00
Tuomas Tynkkynen
5090c81632 VIM-856 Fix regex lookbehind bugs
The regex code has the following bugs, mostly related to lookbehind:
    - Comparison of CharPointers with == is wrong, .equals should be
      used instead.

    - In the original C code, the behind_pos variable is declared as
      'static regsave_T behind_pos;', i.e. not a pointer, and the same
      for other variables of type regsave_T. So assignments to these
      types of variables need to make deep copies.
2015-01-20 17:52:08 +02:00
Andrey Vlasovskikh
dcd035353b Updated IntelliJ version to the latest one available for download 2015-01-20 18:41:00 +03:00
Andrey Vlasovskikh
8606ce21bd Added Jackson Popkin to the contributors 2015-01-20 17:47:50 +03:00
Andrey Vlasovskikh
72537c6ffb Merge remote-tracking branch 'jdpopkin/relative_range'
Conflicts:
	test/org/jetbrains/plugins/ideavim/ex/SubstituteCommandTest.java
2015-01-20 17:17:27 +03:00
Andrey Vlasovskikh
30f9888f51 Merge remote-tracking branch 'selesse/sort-fix' 2015-01-20 16:06:36 +03:00
Andrey Vlasovskikh
d5a2b92121 Updated changelog 2015-01-20 15:59:39 +03:00
Andrey Vlasovskikh
fd3eee6870 Merge remote-tracking branch 'dezgeg/fixes/vim-864-visual-substitute-incorrect-visual-marks' 2015-01-20 15:50:17 +03:00
Andrey Vlasovskikh
2d6d1003b7 Merge remote-tracking branch 'dezgeg/fixes/vim-575-split-cursor' 2015-01-20 15:44:46 +03:00
Andrey Vlasovskikh
499c590f5a Merge remote-tracking branch 'dezgeg/unit-tests' 2015-01-20 15:36:18 +03:00
Andrey Vlasovskikh
787b78ef36 Typo 2015-01-20 15:31:46 +03:00
Andrey Vlasovskikh
88a0b0fa23 Removed unused ExEntryPanel.processKey() 2015-01-20 15:31:33 +03:00
Andrey Vlasovskikh
1519f04930 Fixed potential NPEs 2015-01-20 15:30:17 +03:00
Andrey Vlasovskikh
e0a1291de0 Merge remote-tracking branch 'dezgeg/fixes/vim-210-ex-focus' 2015-01-20 15:27:55 +03:00
Andrey Vlasovskikh
8a2d881002 Cleanup
Removed commented code, renamed '\0' to '\u0000' for consistency,
fixed nullable warnings, declared some variables final.
2015-01-20 14:50:55 +03:00
Tuomas Tynkkynen
4802b6f460 VIM-864 Fix visual marks getting changed during visual substitute
Previously, running a substitute command in visual mode would
incorrectly set the visual mode end mark (>) to the location of the last
search match.
2014-12-24 18:19:53 +02:00
Jackson Popkin
8c0e116a1c Fix incorrect behavior in substitution with offset ranges
Substitution commands with offset ranges like .,+2s/a/b/g previously
did not work the way they do in Vim (replace a with b on the current
line and the next two lines). This change fixes that bug.
2014-12-23 21:14:42 -05:00
Alex Selesse
8862878011 Fix broken sorting when sorting with leading whitespace 2014-12-14 12:17:51 -05:00
Tuomas Tynkkynen
bdf3361243 VIM-575 Don't change cursor position of other splits in visual mode
If a file is opened in multiple splits, entering visual mode in one
split would change the cursor position in other splits as well.
2014-12-14 15:20:36 +02:00
Tuomas Tynkkynen
6b5216077b Add few simple mark tests. 2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
7e119f7963 Add tests for change number operations (<C-A>, <C-X>) 2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
933cbc7e51 Add unit tests for some replace commands
Adds tests for: |r|, |s|, |R|, and <Ins> in insert mode.
2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
65f30d4480 Add testcase for '>' in visual block mode 2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
1f8d3f119e Add unit tests for Ex ranges
Adds tests for ranges in Ex commands. Almost every kind of range has
basic tests, except the following are still totally untested:
    - \/, \?, \&
    - register ranges
    - syntax errors in ranges
2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
45fe858503 Add tests for case changing operations
Add tests for the following commands:
- gu, gU, g~ in normal mode
- u, U, ~ in visual mode
2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
0cc06ad186 Move ChangeActionTest.doTest() to VimTestCase.
This method is convenient in other tests as well.
2014-12-12 23:05:28 +02:00
Tuomas Tynkkynen
18cd7547ad VIM-210 Fix focus issues with the Ex panel
Previously, if the editor window had been splitted, under certain
conditions some actions related to the Ex panel would cause the editor
focus to change to a different split.

The required conditions for this bug to occur:
    - no docked windows (like the Project sidebar) are open
    - 'View -> Navigation bar' is disabled

At least these actions triggered the bug:
    - search with /
    - successfully executing an Ex command
    - dismissing the Ex window with Esc
    - Ex commands that opened the output panel (e.g. :!)

All the deleted lines of the form
    FileEditorManager.getInstance(project).openFile(vf, true);
seem to be very old, non-functional workarounds for the focus issue.
2014-12-09 13:36:03 +02:00
Tuomas Tynkkynen
7746a26062 VIM-855 Fix regexp character class bugs
Previously strchr and istrchr didn't consider NUL chars as end-of-string
terminators. This caused problems in regexps using character classes:
a regexp like "[^a]bc" would be effectively treated as "[^abc]bc" - i.e.
some literal characters from the rest of the pattern would accidentally be
included in the character class.
2014-12-09 01:01:53 +02:00
Andrey Vlasovskikh
a47fc9d3be Don't share editor data contexts
The previous code threw "cannot share data context between Swing events"
throwables from DataManagerImpl$MyDataContext.getData since we used to
share DataContext objects passed to
VimShortcutKeyAction.actionPerformed() and
VimTypedActionHandler.execute() inside AnActionEvent objects with
runnables we invoked via SwingUtilities.invokeLater().
2014-12-07 18:40:51 +03:00
Andrey Vlasovskikh
8fdf75330c Updated changelog 2014-12-07 16:43:52 +03:00
Andrey Vlasovskikh
d6ebaa26b0 Merge branch 'invoke-shortcuts-handler-later' 2014-12-07 16:42:31 +03:00
Andrey Vlasovskikh
27bd7ec836 Merge remote-tracking branch 'dezgeg/fixes/misc-issues' 2014-12-07 16:28:15 +03:00
Andrey Vlasovskikh
6752058922 Version 0.39 2014-12-03 16:07:30 +03:00
Andrey Vlasovskikh
b965108dad Updated changelog 2014-12-03 16:06:20 +03:00
Andrey Vlasovskikh
6bf0f36567 Merge remote-tracking branch 'dezgeg/fixes/vim-702-fix-substitute-infinite-loop' 2014-12-03 15:59:43 +03:00
Andrey Vlasovskikh
69f1a70968 VIM-848 Show line numbers if they are enabled in the settings and there is no 'set number' 2014-12-03 15:42:59 +03:00
Tuomas Tynkkynen
47edfcac5e VIM-702 Fix infinite loop on s/$/\r/g
If the replacement contains newlines, the line number of the current
search position needs to be adjusted. Without this, e.g. s/$/\r/g would
get into an infinite loop.
2014-12-03 14:00:31 +02:00
Andrey Vlasovskikh
c34599b954 Updated the changelog 2014-12-01 16:03:12 +03:00
Andrey Vlasovskikh
48b371c985 Don't show line numbers in non-file editors 2014-12-01 15:54:48 +03:00
Andrey Vlasovskikh
47165e7b9d EA-63002 Don't update line numbers in the caret movement event listener
This update may require moving the caret.
2014-12-01 15:54:31 +03:00
Andrey Vlasovskikh
69f4611552 Version 0.38 2014-12-01 14:01:25 +03:00
Andrey Vlasovskikh
31a25449a8 Updated the changelist 2014-12-01 13:59:34 +03:00
Andrey Vlasovskikh
26a247c0bf Revert "Unified tests initialization for IntelliJ 13+ Community and Ultimate"
This reverts commit 296ef1bdf9.
2014-11-28 22:29:15 +03:00
Andrey Vlasovskikh
1c7cd23475 Updated IntelliJ version 2014-11-28 21:44:37 +03:00
Andrey Vlasovskikh
296ef1bdf9 Unified tests initialization for IntelliJ 13+ Community and Ultimate 2014-11-28 20:22:43 +03:00
Andrey Vlasovskikh
f2a0408801 VIM-586 Try to invoke Vim shortcuts handler later to restore input events sequence 2014-11-27 15:09:06 +03:00
Andrey Vlasovskikh
642659bc9b VIM-410 Use the standard editor line numbers for supporting 'nu'
Annotations-based line numbers reset all other annotations on caret
move, so the results of VCS annotate are reset all the time. It is
necessary for 'rnu', but for 'nu' we can use standard editor line
numbers and preserve the ability to show VCS annotations during caret
movement operations.
2014-11-26 19:49:09 +03:00
Andrey Vlasovskikh
f0e8d065b7 Merge remote-tracking branch 'dezgeg/fixes/vim-723-paste-wrong-line' 2014-11-26 18:18:54 +03:00
Andrey Vlasovskikh
520d852c04 Merge remote-tracking branch 'dezgeg/fixes/vim-771-repeated-semicolon' 2014-11-26 18:12:14 +03:00
Andrey Vlasovskikh
8d4d7a421a Merge remote-tracking branch 'dezgeg/fixes/crashes' 2014-11-26 17:58:27 +03:00
Andrey Vlasovskikh
802b83b0fe VIM-818 Enable key repeat on Mac OS X every time it gets reset by the OS 2014-11-26 17:21:18 +03:00
Tuomas Tynkkynen
7ccb6c8411 Optimize character case changing operations
Previously, performing e.g. visual '~' on a large file would completely
lock up the IDE. The culprit seemed to be the calls to replaceText() one
character at a time, which is not a cheap operation since each
replaceText() will for example trigger a DocumentChanged event.
2014-11-22 13:15:15 +02:00
Tuomas Tynkkynen
1518831f37 Add some missing key names. 2014-11-22 13:15:15 +02:00
Tuomas Tynkkynen
3bdfaa02e1 'J' shouldn't add whitespace if there is trailing whitespace 2014-11-22 13:15:11 +02:00
Tuomas Tynkkynen
e8de9f915c Make '>' not add trailing whitespace to empty lines. 2014-11-22 13:14:23 +02:00
Tuomas Tynkkynen
31f598d1e1 VIM-723 Fix pasting to an empty line
When pasting on an empty line, the pasted text would go to the start of
next line instead.
2014-11-17 03:47:50 +02:00
Tuomas Tynkkynen
46e6fd0847 VIM-771 Fix semicolon repeat for 'till char' motion
Based on empirical testing with Vim, ';' should work like this:
assuming "<caret>1:a 2:b 3:c" with ':' as the last f/t character:

- "t:" does nothing
- ";"  cursor goes to '2'
- "2;" cursor goes to '2', same as the previous
- "3;" cursor goes to '3'
2014-11-16 18:58:56 +02:00
Tuomas Tynkkynen
1441a60f4b Fix AIOOBE when '(' motion goes past start of file
If first character of the file is a newline, then findSentenceEnd()
could return -2 when the 'previous sentence' motion is performed,
leading to an eventual crash. Found by fuzzing.
2014-11-16 17:08:47 +02:00
Tuomas Tynkkynen
ebdf107946 Fix AIOOBE crash when using 'b' on first line
If 'b' were used on the first word of the file, and the word was
preceded by whitespace, ArrayIndexOutOfBoundsException would occur.
Found by fuzzing.
2014-11-16 17:08:47 +02:00
Tuomas Tynkkynen
38d672c9f9 Fix KeyHandler crash when using '<' or '>'
The '<' and '>' commands weren't marked with FLAG_OP_PEND even though
they should, which sometimes caused EmptyStackException in KeyHandler
(when typing <I<><I<> for example). Found by fuzzing.
2014-11-16 17:08:46 +02:00
Tuomas Tynkkynen
75d34abd45 Fix IOOBE when 'i)' motion doesn't find other delimiter
Found by fuzzing.
2014-11-16 17:08:43 +02:00
Andrey Vlasovskikh
1d98738e4d Updated the changelog 2014-11-15 00:11:35 +03:00
Andrey Vlasovskikh
3cfa0e1844 Merge remote-tracking branch 'dezgeg/fixes/vim-515-cW-command' 2014-11-15 00:09:00 +03:00
Andrey Vlasovskikh
82211a4373 Updated the changelog 2014-11-15 00:08:32 +03:00
Andrey Vlasovskikh
e324b04a94 Added Tuomas Tynkkynen to the list of contributors 2014-11-15 00:02:09 +03:00
Andrey Vlasovskikh
49d0c51d97 Merge branch 'vim-536-cc-second-to-last-line' 2014-11-15 00:00:43 +03:00
Andrey Vlasovskikh
fe4bc3b4a9 Moved line line position calculation before deletion 2014-11-15 00:00:13 +03:00
Tuomas Tynkkynen
58d964063c VIM-515 Fix cW command detecting end-of-word incorrectly
A cW command on text like 'x$$$$' or '$xxxx' would incorrectly delete
just the first character, and not the rest.
2014-11-14 22:51:46 +02:00
Andrey Vlasovskikh
17d3e37e1d Merge branch 'vim-567-run-external-command-in-window' 2014-11-14 23:43:42 +03:00
Andrey Vlasovskikh
54f6a16bd6 Nullity checks 2014-11-14 23:41:17 +03:00
Andrey Vlasovskikh
f4ffc5d198 Get rid of Reader and Writer classes in executeCommand() 2014-11-14 23:35:27 +03:00
Andrey Vlasovskikh
3d8010bf88 Removed debug output 2014-11-14 23:18:53 +03:00
Andrey Vlasovskikh
ec2cc3a7f9 Code style 2014-11-14 23:17:50 +03:00
Andrey Vlasovskikh
52b7b9bcd8 Merge branch 'vim-705-multi-line-indent-broken' 2014-11-14 23:05:03 +03:00
Andrey Vlasovskikh
8ad0fcf42d Default value for @NotNull field mode 2014-11-14 23:04:33 +03:00
Andrey Vlasovskikh
aa6cc45988 Made moveCaret(Editor, int, boolean) private 2014-11-14 23:02:32 +03:00
Tuomas Tynkkynen
da22b8297b VIM-536 Fix 'cc' on second-to-last line in file
Previously, 'cc' on the second-to-last line of a file would instead open
a new line after the last line of the file.
2014-11-14 16:56:05 +02:00
Tuomas Tynkkynen
798d82e941 VIM-567 Extend :! to allow running non-filter commands
This commit allows the Ex command '!' to be ran without a range, with
the results displayed in a window.

For example, run ':! ls' in normal mode for a quick directory listing.
2014-11-11 20:32:24 +02:00
Tuomas Tynkkynen
ac8ac302ca VIM-705 Fix repeated multiline indent
When a visual mode command is repeated, code calls into
MotionGroup#toggleVisual() to setup a 'fake' visual mode selection. But
when MotionGroup.moveCaret() is called, it notices that the indent
command has the FLAG_EXIT_VISUAL flag, and leaves visual mode right
away.
2014-11-10 23:32:09 +02:00
Tuomas Tynkkynen
22c3a73102 VIM-613 Fix repeat after 'd$'
Previously, repeating a 'd$' command would incorrectly delete the
newline from the line.
2014-11-10 16:36:40 +02:00
Andrey Vlasovskikh
1222fdb043 Merge pull request #47 from TylerNHansen/master
Update README.md - typo fix
2014-10-29 22:43:24 +03:00
Tyler Hansen
78a50c2f53 Update README.md 2014-10-29 12:19:13 -07:00
Andrey Vlasovskikh
6d261a7afa Merge pull request #46 from Baldrs/patch-1
Typo
2014-10-24 18:41:40 +04:00
Baldrs
2e37292478 Typo 2014-10-24 17:40:34 +03:00
Andrey Vlasovskikh
a4907ec9c8 Links to GitHub and YouTrack in the plugin info 2014-10-24 16:41:00 +04:00
Andrey Vlasovskikh
992bfe73b6 Updated changes in plugin.xml 2014-10-24 16:37:29 +04:00
Andrey Vlasovskikh
c186254a7e Updated dev version 2014-10-24 16:36:21 +04:00
Andrey Vlasovskikh
1dc739f32c Added :action and :actionlist to the docs 2014-10-24 16:35:56 +04:00
Andrey Vlasovskikh
9804cd83a6 VIM-652 Added a test for :action 2014-10-24 16:13:40 +04:00
Andrey Vlasovskikh
aa5b99c47a Added smartbomb to the list of contributors 2014-10-24 16:04:57 +04:00
Andrey Vlasovskikh
f95f5e8901 Merge branch 'action-command' 2014-10-24 16:02:21 +04:00
Andrey Vlasovskikh
206b303407 Renamed ExecuteActionByNameHandler to ActionHandler
The name of an Ex command handler should correspond to the Ex command
name.
2014-10-24 16:01:38 +04:00
Andrey Vlasovskikh
751bff53ee Removed checks for action execution problems not reproducible at the moment
We need to collect more data from the beta testers about these problems.
2014-10-24 16:00:23 +04:00
Andrey Vlasovskikh
b6ef0c509d We don't need two Alexeys at the moment 2014-10-23 22:47:41 +04:00
Andrey Vlasovskikh
30304d6836 VIM-794 Fixed NCDFE related to 'number' in IDEs other than IntelliJ 2014-10-23 22:45:19 +04:00
Andrey Vlasovskikh
f5df49b139 Don't allow short names for IdeaVim-specific :action and :actionlist 2014-10-23 19:01:58 +04:00
Andrey Vlasovskikh
bf8ba1a49b Prettier output of :actionlist 2014-10-23 18:58:50 +04:00
Andrey Vlasovskikh
9f2697658b Fixed code style 2014-10-23 18:25:01 +04:00
smartbomb
36fd59b92c ExecuteActionByName use the content context on actions with the EnabledInModalContext flag set
(cherry picked from commit d375740)
2014-10-23 18:02:12 +04:00
Andrey Vlasovskikh
88d946546a Added Alexey Shmalko to the list of contributors 2014-10-23 17:30:06 +04:00
Andrey Vlasovskikh
6036c0c262 Updated CHANGES 2014-10-23 16:59:41 +04:00
Andrey Vlasovskikh
20e831b56a Updated tests after fixing VIM-501 2014-10-23 16:58:43 +04:00
Andrey Vlasovskikh
72b74e075c Merge branch 'visual_block_delete' 2014-10-23 16:55:17 +04:00
Andrey Vlasovskikh
3c6ede2f8f Normalize offsets in order to prevent selecting newlines 2014-10-23 16:53:31 +04:00
Andrey Vlasovskikh
5434edbd54 VIM-792 Fixed line-wise and block-wise paste commands for * and + registers 2014-10-23 16:09:56 +04:00
Andrey Vlasovskikh
6a8c7e4b17 VIM-511 Record caret adjustment changes to the document during <Enter> processing
We have to handle Enter by our VimShortcutKeyAction in order to be
able to record it as an action for repeating it via '.'. Since original
Enter handlers are not run in this case we invoke them manually by
collecting a list of actions that can be run for the editor component
on Enter and then running the first action ready to run.

Caret adjustments are detected on changes to the document caused by
Enter. This allows to position the caret properly (e.g. between {})
before recording Enter.
2014-10-23 15:26:22 +04:00
Alexey Shmalko
0ac659f2d1 Fix visual block deleting
Currently deleting visual block leaves last char in line untouched. This
patch fixes that as well as tests.
2014-10-21 21:40:43 +03:00
Tony Kay
7eae40ca9a added a failing test 2014-10-21 11:05:43 -07:00
Andrey Vlasovskikh
b3d12c8b58 VIM-511 Fixed removing a paren just after inserting it with auto-inserting matching parens on 2014-10-21 21:47:06 +04:00
Andrey Vlasovskikh
3f92dba1b7 VIM-511 Updated test data 2014-10-21 21:16:11 +04:00
Andrey Vlasovskikh
0aedc08cfa More tests for VIM-511 2014-10-21 20:42:04 +04:00
Andrey Vlasovskikh
8312f5cffd VIM-511 Added a test for repeating a change that includes auto-inserted parens and quotes 2014-10-21 19:41:11 +04:00
Andrey Vlasovskikh
9f6338441e Updated CHANGES 2014-10-21 19:09:56 +04:00
Andrey Vlasovskikh
27efe0c9d6 Handle left and right motions during key repeat only as document (caret) changes
Previously they have been recorded twice: implicitly by a document
change listener as changes to the caret position and by a command
processor.
2014-10-21 18:24:36 +04:00
Andrey Vlasovskikh
b5bf6c08d8 Repeat typing commands by inserting text instead of handling a typed key
It will prevent auto-completion and other auto-inserting typed key
handlers from messing up with the repeated text.
2014-10-21 18:22:19 +04:00
Andrey Vlasovskikh
e3fce51ea1 VIM-511 Fixed editing offset after <BS> for '.' command 2014-10-21 16:56:54 +04:00
Andrey Vlasovskikh
13b4e93bf4 Run the test only if the system clipboard is available 2014-10-20 16:53:25 +04:00
Andrey Vlasovskikh
4ec0ab281f Removed the list of compatible IDEs
The same list is shown on the plugin's page and duplicated in the
README.
2014-10-20 16:09:32 +04:00
Andrey Vlasovskikh
39c96019b6 Typo 2014-10-20 16:08:29 +04:00
Andrey Vlasovskikh
21f2f60355 Updated CHANGES 2014-10-20 16:07:50 +04:00
Andrey Vlasovskikh
0de654dcaf Added Thomas B Homburg to the list of contributors 2014-10-20 16:02:11 +04:00
Andrey Vlasovskikh
d59e472814 Added 'clipboard' to the list of :set commands 2014-10-20 16:01:09 +04:00
Andrey Vlasovskikh
cc2ed452f0 Merge branch 'clipboard-unnamed'
Conflicts:
	src/com/maddyhome/idea/vim/option/Options.java
	test/org/jetbrains/plugins/ideavim/action/CopyActionTest.java
2014-10-20 15:59:15 +04:00
Andrey Vlasovskikh
d4d3843725 VIM-476 Added default register reset on 'clipboard' change
Added a test for :set clipboard=unnamed.
2014-10-20 15:53:33 +04:00
Andrey Vlasovskikh
dee16da1c2 Coding style 2014-10-20 14:52:22 +04:00
Andrey Vlasovskikh
e09b85870f Added Jaime Sanchez to the list of contributors 2014-10-20 13:27:31 +04:00
Andrey Vlasovskikh
8596911a0e VIM-483 VIM-410 Added support for 'number' and 'relativenumber' commands
These commands override the line numbers setting in the IDE when the
Vim emulation is enabled.
2014-10-20 13:27:09 +04:00
Andrey Vlasovskikh
6c2de9f151 Extracted EditorGroup 2014-10-17 17:07:39 +04:00
Andrey Vlasovskikh
d3a6b1e39e Use '\n' as universal line separator for sorting lines
The Document class takes care of detecting and using the correct
platform-specific line separators.
2014-10-17 14:54:48 +04:00
Andrey Vlasovskikh
3cb9b19aea Added Chang Wang to the list of contributors 2014-10-16 23:32:42 +04:00
Andrey Vlasovskikh
86aa59bb29 Merge branch 'vim-624' 2014-10-16 23:29:33 +04:00
smartbomb
c6eeaed7da :actionlist added support for wildcards 2014-10-15 22:49:08 +02:00
smartbomb
edba90f188 Renamed :findaction => :actionlist + added statusbar error message when calling invalid action names 2014-10-15 21:17:26 +02:00
Andrey Vlasovskikh
26b49b1a0c Made the description shorter 2014-10-15 15:22:40 +04:00
Andrey Vlasovskikh
ef32648ddc Bumped version to 0.37 2014-10-15 15:17:42 +04:00
Andrey Vlasovskikh
c873524cb1 VIM-784 Fixed visual line selection where start > end of the selection range
It was a regression introduced by the fix of VIM-632.
2014-10-15 15:10:22 +04:00
smartbomb
7943e34bde Fix action execution contexts, use a delay on popup actions to allow the command handler to unwind 2014-10-15 08:31:44 +02:00
smartbomb
74970c74b4 Implemented :findaction 2014-10-14 22:47:03 +02:00
Andrey Vlasovskikh
2d11561041 Added Andrew Brookins to the list of contributors 2014-10-14 15:28:23 +04:00
Andrey Vlasovskikh
f7643b6bb3 Updated changelist 2014-10-14 15:28:07 +04:00
Andrey Vlasovskikh
d3afd83e8e Merge branch 'VIM-407' 2014-10-14 15:25:43 +04:00
Andrey Vlasovskikh
969ca0119a Bumped version to 0.36 2014-10-14 14:22:38 +04:00
smartbomb
722431f5b2 VIM-652 Add support for executing and mapping arbitrary IDEA actions
* no tests performed..! *

Example usage:
:nnoremap gi :action GotoImplementation<CR>
:nnoremap gu :action FindUsages<CR>
:nnoremap gcw :action RenameElement<CR>

Action overview:
6c6cb47c5a/platform/platform-resources-en/src/messages/ActionsBundle.properties
2014-10-13 21:58:38 +02:00
Andrey Vlasovskikh
6a1c792cda VIM-171 Added support for window navigation commands: left/right/up/down 2014-10-13 23:08:56 +04:00
Andrey Vlasovskikh
c0ba39ab40 Use multi-caret API available from branches 135+ 2014-10-09 18:33:07 +04:00
Andrey Vlasovskikh
3e0e08c5e1 VIM-632 Restored visual block mode that was broken due to multiple carets support
Vim actions with multiple carets in modes other than visual block are
not supported yet. It will be a separate feature. The idea behind this
commit is to fix the regression in visual block mode.

Bounds of visual selection are no longer reversed if they are
overlapped, this fixes expanding the block selection in all directions.

IdeaVim no longer uses SelectionModel.hasBlockSelection() since it
always returns 'true' now.

There are a couple of places where we remove secondary carets when
moving the primary caret or re-setting visual mode. It may be
incompatible with the forthcoming support for multi-caret Vim actions.
2014-10-09 18:01:47 +04:00
Andrey Vlasovskikh
3d64373c22 Updated the changelog 2014-10-07 15:24:45 +04:00
Andrey Vlasovskikh
2557688657 Added Alexey Shmalko to the list of contributors 2014-10-07 15:11:55 +04:00
Andrey Vlasovskikh
fcc564df25 Cleanup 2014-10-07 14:29:30 +04:00
Andrey Vlasovskikh
4ecbb93d01 Converted window actions into subclasses of VimCommandAction 2014-10-07 13:52:46 +04:00
Alexey Shmalko
9bdc9b3634 VIM-171 Add checks for that window is not null 2014-10-04 01:29:41 +03:00
Alexey Shmalko
f46c3b0aa9 VIM-171 Add support for window cycling
Adds following keystrokes:
<C-W>w <C-W><C-W> next window
<C-W>W            previous window

With number both commands go to window with specified index.
2014-10-04 00:52:24 +03:00
Alexey Shmalko
095fdf07c5 VIM-171 Add support for closing all windows except current
Adds <C-W>o, <C-W><C-O> keystrokes.
2014-10-04 00:02:15 +03:00
Alexey Shmalko
648e988b64 VIM-171 Add support for closing window
Adds <C-W>c keystroke.
2014-10-03 23:41:53 +03:00
Alexey Shmalko
b1add735d6 Add split action
This patch adds following keystrokes:
<C-W>s <C-W>S <C-W><C-S> horizontal split
<C-W>v <C-W><C-V>        vertical split
2014-10-02 20:52:13 +03:00
Andrey Vlasovskikh
3f5882118e Compatibility with builds 133+ / IntelliJ 13+ 2014-09-10 15:03:01 +04:00
Andrey Vlasovskikh
4e83f56696 Merge branch 'vim-265'
Conflicts:
	AUTHORS.md
2014-09-10 13:58:29 +04:00
Andrey Vlasovskikh
ab250f1d9c Added salaam to the list of contributors 2014-09-10 13:57:40 +04:00
Andrey Vlasovskikh
4672dece51 VIM-770 Close the current tab on :quit instead of all tabs with the current file 2014-09-10 13:52:00 +04:00
Andrey Vlasovskikh
a632de9214 Cleanup 2014-09-09 18:30:21 +04:00
Andrey Vlasovskikh
e8ebba8b3d Removed unused declarations 2014-09-09 18:25:45 +04:00
Andrey Vlasovskikh
5cf8181474 Added Dathan Bennett to the list of contributors 2014-09-09 00:38:42 +04:00
Andrey Vlasovskikh
a717e4785d VIM-569 Fixed <C-W> when the caret is at the end of a line 2014-09-09 00:36:39 +04:00
Dathan Bennett
16ce16c632 Add test for VIM-569 fix 2014-09-06 02:59:49 -07:00
Dathan Bennett
8d7bf2661a Set isChange to true when calling deleteRange from ctrl-w handler 2014-09-06 01:07:03 -07:00
salaam
9e1b026a88 VIM-265 Add window split commands
Conflicts:
	src/com/maddyhome/idea/vim/VimPlugin.java
	src/com/maddyhome/idea/vim/ex/CommandParser.java
2014-08-31 23:57:22 -05:00
Thomas B Homburg
df8e455a6d Get default register from clipboard=unnamed setting 2014-08-26 22:51:17 +02:00
Chang Wang
b35bec2839 deselect visual selection range '<,'> when poping up ex entry field. 2014-08-13 16:40:50 -07:00
Andrew Brookins
19365effa9 VIM-407 Add tests. Only skip the ending line if it is empty.
Conflicts:
	src/com/maddyhome/idea/vim/group/ChangeGroup.java
	test/org/jetbrains/plugins/ideavim/action/ShiftRightLinesActionTest.java
2014-05-22 22:04:14 -07:00
82 changed files with 3891 additions and 752 deletions

View File

@@ -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" />
@@ -276,5 +273,4 @@
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>
</project>

View File

@@ -24,6 +24,16 @@ Contributors:
* [John Lindquist](mailto:johnlindquist@gmail.com)
* [Ira Klotzko](mailto:iklotzko@ltech.com)
* [Alex Selesse](mailto:alex@selesse.com)
* [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.

View File

@@ -3,6 +3,136 @@ The Changelog
History of changes in IdeaVim for the IntelliJ platform.
0.43, 2015-11-02
----------------
A bugfix release.
* VIM-1039 Fixed running the plugin with Java 6
M
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
----------------
Added support for common window splitting and navigation commands. Various bug
fixes.
Features:
* VIM-171 Window `<C-W>` commands: split, close, next/previous windows,
left/right/up/down windows
* VIM-265 Window `:split` and `:vsplit` commands
Bug fixes:
* VIM-632 Restored visual block mode that was broken due to multiple carets support
* VIM-770 Close the current tab on `:quit` instead of all tabs with the current
file
* VIM-569 Fixed `<C-W>` when the caret is at the end of a line
0.35, 2014-05-15
----------------

View File

@@ -2,22 +2,22 @@ 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>
IdeaVim is a Vim emulation plug-in for IDEs based on the IntelliJ platform.
IdeaVim can be used with IntelliJ IDEA, RubyMine, PyCharm, PhpStorm, WebStorm,
AppCode and Android Studio.
AppCode, CLion and Android Studio.
Resources:
@@ -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
@@ -63,18 +63,17 @@ Supported:
* Macros
* Digraphs
* Command line and search history
* Window commands
* Vim web help
Not supported (yet):
* Window commands
* Jump lists
* Various less used commands
See also:
* [List of recently added commands](https://github.com/JetBrains/ideavim/blob/master/src/com/maddyhome/idea/vim/package-info.java)
* [List of commands covered with tests](https://github.com/JetBrains/ideavim/blob/master/index.txt)
* [Top features and bugs](http://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+sort+by%3A+votes)
@@ -92,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
------------------
@@ -113,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
------------
@@ -135,7 +153,7 @@ in the issue tracker.
1. Fork IdeaVim on GitHub and clone the repository on your local machine.
2. Open the project in IntelliJ IDEA 12+ (Community or Ultimate) using "File |
2. Open the project in IntelliJ IDEA 13.1+ (Community or Ultimate) using "File |
Open... | /path/to/ideavim".
3. Set up a JDK if you haven't got it yet. Use "File | Project Structure | SDKs
@@ -153,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.

View File

@@ -1,4 +1,4 @@
version-id:0.35
platform-version:120.0
idea.download.url=http://download.jetbrains.com/idea/ideaIU-13.zip
version-id:0.43
platform-version:143.0
idea.download.url=http://download.jetbrains.com/idea/ideaIC-143.380.20.tar.gz
build.number=dev

View File

@@ -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}">

View File

@@ -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

View 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>
</module>

View File

@@ -1,67 +1,59 @@
<idea-plugin url="http://plugins.jetbrains.com/plugin/164">
<name>IdeaVim</name>
<id>IdeaVIM</id>
<change-notes>
<![CDATA[
<p>0.35:</p>
<change-notes><![CDATA[
<p>0.43:</p>
<ul>
<li><code>~/.vimrc</code> is no longer read by default, use <code>~/.ideavimrc</code> instead</li>
<li>Various bug fixes</li>
<li>Fixed running the plugin with Java 6</li>
</ul>
<p>0.34:</p>
<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.33:</p>
<p>0.40:</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>Support for <code>mapleader</code></li>
<li>Support comments in <code>%</code> brace matching</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>
<p>0.39:</p>
<ul>
<li>Various bug fixes</li>
</ul>
<p>0.30:</p>
<p>0.38:</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>&lt;Esc&gt;</code> during completion</li>
<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>&lt;C-W&gt;</code> family</li>
<li>Support for <code>:split</code>/<code>:vsplit</code> commands</li>
<li>Fixed visual block selection mode</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 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>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>
@@ -316,6 +308,18 @@
<action id="VimFileGetFileInfo" class="com.maddyhome.idea.vim.action.file.FileGetFileInfoAction" text="Get File Info"/>
<action id="VimFileGetLocationInfo" class="com.maddyhome.idea.vim.action.file.FileGetLocationInfoAction" text="Get Location Info"/>
<!-- Window -->
<action id="VimWindowSplitVertical" class="com.maddyhome.idea.vim.action.window.VerticalSplitAction" text="Split window vertically"/>
<action id="VimWindowSplitHorizontal" class="com.maddyhome.idea.vim.action.window.HorizontalSplitAction" text="Split window horizontally"/>
<action id="VimWindowClose" class="com.maddyhome.idea.vim.action.window.CloseWindowAction" text="Close current window"/>
<action id="VimWindowOnly" class="com.maddyhome.idea.vim.action.window.WindowOnlyAction" text="Close all windows except current"/>
<action id="VimWindowNext" class="com.maddyhome.idea.vim.action.window.WindowNextAction" text="Select next window"/>
<action id="VimWindowPrev" class="com.maddyhome.idea.vim.action.window.WindowPrevAction" text="Select previous window"/>
<action id="VimWindowLeft" class="com.maddyhome.idea.vim.action.window.WindowLeftAction" text="Go to left window"/>
<action id="VimWindowRight" class="com.maddyhome.idea.vim.action.window.WindowRightAction" text="Go to right window"/>
<action id="VimWindowUp" class="com.maddyhome.idea.vim.action.window.WindowUpAction" text="Go to window up"/>
<action id="VimWindowDown" class="com.maddyhome.idea.vim.action.window.WindowDownAction" text="Go to window down"/>
<!-- Search -->
<action id="VimSearchFwdEntry" class="com.maddyhome.idea.vim.action.motion.search.SearchEntryFwdAction" text="Search Forward"/>
<action id="VimSearchRevEntry" class="com.maddyhome.idea.vim.action.motion.search.SearchEntryRevAction" text="Search Backward"/>

View File

@@ -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);
}
}

View File

@@ -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;
}
/**

View File

@@ -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
@@ -657,9 +657,6 @@ public class RegisterActions {
parser.registerAction(MappingMode.N, "VimFileGetFileInfo", Command.Type.OTHER_READONLY,
new Shortcut(KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_MASK)));
// Window Actions
// TODO - CTRL-W commands: +, -, =, S, s, _, b, c, n, o, q, s, t, <up>, <down>
// Macro Actions
parser.registerAction(MappingMode.N, "VimPlaybackLastRegister", Command.Type.OTHER_WRITABLE,
new Shortcut("@@"));
@@ -725,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))
});

View File

@@ -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,10 +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 final WindowGroup window;
@NotNull private final EditorGroup editor;
public VimPlugin(final Application app) {
myApp = app;
public VimPlugin() {
motion = new MotionGroup();
change = new ChangeGroup();
copy = new CopyGroup();
@@ -135,6 +116,8 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
digraph = new DigraphGroup();
history = new HistoryGroup();
key = new KeyGroup();
window = new WindowGroup();
editor = new EditorGroup();
LOG.debug("VimPlugin ctr");
}
@@ -203,6 +186,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
search.saveData(element);
history.saveData(element);
key.saveData(element);
editor.saveData(element);
return element;
}
@@ -228,6 +212,7 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
search.readData(element);
history.readData(element);
key.readData(element);
editor.readData(element);
}
@NotNull
@@ -290,6 +275,16 @@ public class VimPlugin implements ApplicationComponent, PersistentStateComponent
return getInstance().key;
}
@NotNull
public static WindowGroup getWindow() {
return getInstance().window;
}
@NotNull
public static EditorGroup getEditor() {
return getInstance().editor;
}
@NotNull
public static PluginId getPluginId() {
return PluginId.getId(IDEAVIM_PLUGIN_ID);
@@ -360,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) {
@@ -449,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) {
@@ -492,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);
}
}
}

View File

@@ -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);

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author rasendubi
*/
public class CloseWindowAction extends VimCommandAction {
public CloseWindowAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().closeCurrentWindow(context);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>c");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author rasendubi
*/
public class HorizontalSplitAction extends VimCommandAction {
public HorizontalSplitAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().splitWindowHorizontal(context, "");
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>s", "<C-W>S", "<C-W><C-S>");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author rasendubi
*/
public class VerticalSplitAction extends VimCommandAction {
public VerticalSplitAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().splitWindowVertical(context, "");
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>v", "<C-W><C-V>");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author vlan
*/
public class WindowDownAction extends VimCommandAction {
public WindowDownAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().selectWindowInRow(context, cmd.getCount(), true);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>j");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author vlan
*/
public class WindowLeftAction extends VimCommandAction {
public WindowLeftAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().selectWindowInRow(context, cmd.getCount() * -1, false);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>h");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author rasendubi
*/
public class WindowNextAction extends VimCommandAction {
public WindowNextAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
if (cmd.getRawCount() == 0) {
VimPlugin.getWindow().selectNextWindow(context);
} else {
VimPlugin.getWindow().selectWindow(context, cmd.getCount());
}
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>w", "<C-W><C-W>");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author rasendubi
*/
public class WindowOnlyAction extends VimCommandAction {
public WindowOnlyAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().closeAllExceptCurrent(context);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>o", "<C-W><C-O>");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
public class WindowPrevAction extends VimCommandAction {
public WindowPrevAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
if (cmd.getRawCount() == 0) {
VimPlugin.getWindow().selectPreviousWindow(context);
} else {
VimPlugin.getWindow().selectWindow(context, cmd.getCount());
}
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>W");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author vlan
*/
public class WindowRightAction extends VimCommandAction {
public WindowRightAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().selectWindowInRow(context, cmd.getCount(), false);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>l");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.action.window;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.action.VimCommandAction;
import com.maddyhome.idea.vim.command.Command;
import com.maddyhome.idea.vim.command.MappingMode;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author vlan
*/
public class WindowUpAction extends VimCommandAction {
public WindowUpAction() {
super(new EditorActionHandlerBase() {
@Override
protected boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull Command cmd) {
VimPlugin.getWindow().selectWindowInRow(context, cmd.getCount() * -1, true);
return true;
}
});
}
@NotNull
@Override
public Set<MappingMode> getMappingModes() {
return MappingMode.N;
}
@NotNull
@Override
public Set<List<KeyStroke>> getKeyStrokesSet() {
return parseKeysSet("<C-W>k");
}
@NotNull
@Override
public Command.Type getType() {
return Command.Type.OTHER_READONLY;
}
}

View File

@@ -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,

View File

@@ -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();
@@ -110,6 +114,7 @@ public class CommandParser {
new ShiftRightHandler();
new SourceHandler();
new SortHandler();
new SplitHandler();
new SubstituteHandler();
new UndoHandler();
new WriteAllHandler();
@@ -298,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 == '\\') {

View File

@@ -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);
}
}

View 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;
}
}
}

View 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;
}
}

View File

@@ -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());
}
}

View 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 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);

View 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;
}
}

View 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");
}
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -0,0 +1,49 @@
/*
* 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.DataContext;
import com.intellij.openapi.editor.Editor;
import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.ex.CommandHandler;
import com.maddyhome.idea.vim.ex.CommandName;
import com.maddyhome.idea.vim.ex.ExCommand;
import org.jetbrains.annotations.NotNull;
/**
*
*/
public class SplitHandler extends CommandHandler {
public SplitHandler() {
super(new CommandName[]{
new CommandName("vs", "plit"),
new CommandName("sp", "lit")
}, RANGE_FORBIDDEN | ARGUMENT_OPTIONAL | DONT_REOPEN);
}
public boolean execute(@NotNull Editor editor, @NotNull DataContext context, @NotNull ExCommand cmd) {
if (cmd.getCommand().startsWith("v")) {
VimPlugin.getWindow().splitWindowVertical(context, cmd.getArgument());
} else {
VimPlugin.getWindow().splitWindowHorizontal(context, cmd.getArgument());
}
return true;
}
}

View 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);

View File

@@ -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;
/**

View File

@@ -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;
}
}

View File

@@ -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));

View 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
@@ -323,7 +320,7 @@ public class ChangeGroup {
return false;
}
final TextRange range = new TextRange(deleteTo, editor.getCaretModel().getOffset());
deleteRange(editor, range, SelectionType.CHARACTER_WISE, false);
deleteRange(editor, range, SelectionType.CHARACTER_WISE, true);
return true;
}
@@ -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;
}

View File

@@ -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()) {

View 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() {
}
}
}

View File

@@ -26,6 +26,9 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.fileEditor.*;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorTabbedContainer;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
@@ -60,36 +63,7 @@ public class FileGroup {
}
Project proj = PlatformDataKeys.PROJECT.getData(context); // API change - don't merge
VirtualFile found = null;
if (filename.length() > 2 && filename.charAt(0) == '~' && filename.charAt(1) == File.separatorChar) {
String homefile = filename.substring(2);
String dir = System.getProperty("user.home");
if (logger.isDebugEnabled()) {
logger.debug("home dir file");
logger.debug("looking for " + homefile + " in " + dir);
}
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, homefile));
}
else {
if (proj == null) {
return false;
}
ProjectRootManager prm = ProjectRootManager.getInstance(proj);
VirtualFile[] roots = prm.getContentRoots();
for (int i = 0; i < roots.length; i++) {
if (logger.isDebugEnabled()) {
logger.debug("root[" + i + "] = " + roots[i].getPath());
}
found = findFile(roots[i], filename);
if (found != null) {
break;
}
}
if (found == null) {
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
}
}
VirtualFile found = findFile(filename, proj);
if (found != null) {
if (logger.isDebugEnabled()) {
@@ -117,6 +91,42 @@ public class FileGroup {
}
}
@Nullable
public VirtualFile findFile(@NotNull String filename, @NotNull Project proj) {
VirtualFile found = null;
if (filename.length() > 2 && filename.charAt(0) == '~' && filename.charAt(1) == File.separatorChar) {
String homefile = filename.substring(2);
String dir = System.getProperty("user.home");
if (logger.isDebugEnabled()) {
logger.debug("home dir file");
logger.debug("looking for " + homefile + " in " + dir);
}
found = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(dir, homefile));
}
else {
if (proj == null) {
return null;
}
ProjectRootManager prm = ProjectRootManager.getInstance(proj);
VirtualFile[] roots = prm.getContentRoots();
for (int i = 0; i < roots.length; i++) {
if (logger.isDebugEnabled()) {
logger.debug("root[" + i + "] = " + roots[i].getPath());
}
found = findFile(roots[i], filename);
if (found != null) {
break;
}
}
if (found == null) {
found = LocalFileSystem.getInstance().findFileByIoFile(new File(filename));
}
}
return found;
}
@Nullable
private VirtualFile findFile(@NotNull VirtualFile root, @NotNull String filename) {
VirtualFile res = root.findFileByRelativePath(filename);
@@ -146,11 +156,26 @@ public class FileGroup {
* @param context The data context
*/
public void closeFile(@NotNull Editor editor, @NotNull DataContext context) {
Project proj = PlatformDataKeys.PROJECT.getData(context);
FileEditorManager fem = FileEditorManager.getInstance(proj); // API change - don't merge
VirtualFile vf = EditorData.getVirtualFile(editor);
if (vf != null) {
fem.closeFile(vf);
final Project project = PlatformDataKeys.PROJECT.getData(context);
if (project != null) {
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
final EditorWindow window = fileEditorManager.getCurrentWindow();
final EditorTabbedContainer tabbedPane = window.getTabbedPane();
if (tabbedPane != null) {
if (tabbedPane.getTabCount() > 1) {
final int index = tabbedPane.getSelectedIndex();
tabbedPane.removeTabAt(index, index + 1);
}
else {
tabbedPane.close();
}
}
else {
VirtualFile virtualFile = EditorData.getVirtualFile(editor);
if (virtualFile != null) {
fileEditorManager.closeFile(virtualFile);
}
}
}
}

View File

@@ -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;
}
}

View File

@@ -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,14 +1200,23 @@ 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 = 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|
editor.getCaretModel().removeSecondaryCarets();
}
editor.getCaretModel().moveToOffset(offset);
EditorData.setLastColumn(editor, editor.getCaretModel().getVisualPosition().column);
scrollCaretIntoView(editor);
}
if (keepVisual(editor)) {
if (keepVisual) {
VimPlugin.getMotion().updateSelection(editor, offset);
}
else {
@@ -1213,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();
}
@@ -1456,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);
@@ -1518,6 +1556,7 @@ public class MotionGroup {
}
if (removeSelection) {
editor.getSelectionModel().removeSelection();
editor.getCaretModel().removeSecondaryCarets();
}
CommandState.getInstance(editor).setSubMode(CommandState.SubMode.NONE);
}
@@ -1566,29 +1605,8 @@ public class MotionGroup {
@NotNull
public TextRange getVisualRange(@NotNull Editor editor) {
if (editor.getSelectionModel().hasBlockSelection()) {
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
if (EditorData.getLastColumn(editor) >= MotionGroup.LAST_COLUMN) {
int[] starts = res.getStartOffsets();
int[] ends = res.getEndOffsets();
for (int i = 0; i < starts.length; i++) {
if (ends[i] > starts[i]) {
ends[i] = EditorHelper.getLineEndForOffset(editor, starts[i]);
}
}
res = new TextRange(starts, ends);
}
return res;
}
else {
return new TextRange(editor.getSelectionModel().getSelectionStart(),
editor.getSelectionModel().getSelectionEnd());
}
return new TextRange(editor.getSelectionModel().getBlockSelectionStarts(),
editor.getSelectionModel().getBlockSelectionEnds());
}
@NotNull
@@ -1596,36 +1614,62 @@ 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;
int start = visualStart;
int end = visualEnd;
if (start > end) {
int t = start;
start = end;
end = t;
}
final CommandState.SubMode subMode = CommandState.getInstance(editor).getSubMode();
if (CommandState.getInstance(editor).getSubMode() == CommandState.SubMode.VISUAL_CHARACTER) {
BoundStringOption opt = (BoundStringOption)Options.getInstance().getOption("selection");
int lineEnd = EditorHelper.getLineEndForOffset(editor, end);
int adj = 1;
if (opt.getValue().equals("exclusive") || end == lineEnd) {
adj = 0;
if (subMode == CommandState.SubMode.VISUAL_CHARACTER) {
if (start > end) {
int t = start;
start = end;
end = t;
}
final BoundStringOption opt = (BoundStringOption)Options.getInstance().getOption("selection");
int lineEnd = EditorHelper.getLineEndForOffset(editor, end);
final int adj = opt.getValue().equals("exclusive") || end == lineEnd ? 0 : 1;
end = Math.min(EditorHelper.getFileSize(editor), end + adj);
editor.getSelectionModel().setSelection(start, end);
}
else if (CommandState.getInstance(editor).getSubMode() == CommandState.SubMode.VISUAL_LINE) {
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 {
LogicalPosition lineStart = editor.offsetToLogicalPosition(start);
LogicalPosition lend = editor.offsetToLogicalPosition(end);
editor.getSelectionModel().setBlockSelection(lineStart, lend);
else if (subMode == CommandState.SubMode.VISUAL_BLOCK) {
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));
@@ -1680,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) {
@@ -1708,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());
}
}
}
@@ -1774,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;
}

View File

@@ -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 {

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,178 @@
/*
* 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.group;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx;
import com.intellij.openapi.fileEditor.impl.EditorWindow;
import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.maddyhome.idea.vim.VimPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
public class WindowGroup {
public WindowGroup() {
}
public void closeCurrentWindow(@NotNull DataContext context) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow window = fileEditorManager.getSplitters().getCurrentWindow();
if (window != null) {
window.closeAllExcept(null);
}
}
public void closeAllExceptCurrent(@NotNull DataContext context) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow current = fileEditorManager.getCurrentWindow();
for (final EditorWindow window : fileEditorManager.getWindows()) {
if (window != current) {
window.closeAllExcept(null);
}
}
}
public void selectNextWindow(@NotNull DataContext context) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow current = fileEditorManager.getCurrentWindow();
if (current != null) {
fileEditorManager.getNextWindow(current).setAsCurrentWindow(true);
}
}
public void selectPreviousWindow(@NotNull DataContext context) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow current = fileEditorManager.getCurrentWindow();
if (current != null) {
fileEditorManager.getPrevWindow(current).setAsCurrentWindow(true);
}
}
public void selectWindow(@NotNull DataContext context, int index) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow[] windows = fileEditorManager.getWindows();
if (index - 1 < windows.length) {
windows[index - 1].setAsCurrentWindow(true);
}
}
public void splitWindowHorizontal(@NotNull DataContext context, @NotNull String filename) {
splitWindow(SwingConstants.HORIZONTAL, context, filename);
}
public void splitWindowVertical(@NotNull DataContext context, @NotNull String filename) {
splitWindow(SwingConstants.VERTICAL, context, filename);
}
public void selectWindowInRow(@NotNull DataContext context, int relativePosition, boolean vertical) {
final FileEditorManagerEx fileEditorManager = getFileEditorManager(context);
final EditorWindow currentWindow = fileEditorManager.getCurrentWindow();
if (currentWindow != null) {
final EditorWindow[] windows = fileEditorManager.getWindows();
final List<EditorWindow> row = findWindowsInRow(currentWindow, Arrays.asList(windows), vertical);
selectWindow(currentWindow, row, relativePosition);
}
}
private void selectWindow(@NotNull EditorWindow currentWindow, @NotNull List<EditorWindow> windows,
int relativePosition) {
final int pos = windows.indexOf(currentWindow);
final int selected = pos + relativePosition;
final int normalized = Math.max(0, Math.min(selected, windows.size() - 1));
windows.get(normalized).setAsCurrentWindow(true);
}
@NotNull
private static List<EditorWindow> findWindowsInRow(@NotNull EditorWindow anchor,
@NotNull List<EditorWindow> windows, final boolean vertical) {
final Rectangle anchorRect = getEditorWindowRectangle(anchor);
if (anchorRect != null) {
final List<EditorWindow> result = new ArrayList<EditorWindow>();
final double coord = vertical ? anchorRect.getX() : anchorRect.getY();
for (EditorWindow window : windows) {
final Rectangle rect = getEditorWindowRectangle(window);
if (rect != null) {
final double min = vertical ? rect.getX() : rect.getY();
final double max = min + (vertical ? rect.getWidth() : rect.getHeight());
if (coord >= min && coord <= max) {
result.add(window);
}
}
}
Collections.sort(result, new Comparator<EditorWindow>() {
@Override
public int compare(EditorWindow window1, EditorWindow window2) {
final Rectangle rect1 = getEditorWindowRectangle(window1);
final Rectangle rect2 = getEditorWindowRectangle(window2);
if (rect1 != null && rect2 != null) {
final double diff = vertical ? (rect1.getY() - rect2.getY()) : (rect1.getX() - rect2.getX());
return diff < 0 ? -1 : diff > 0 ? 1 : 0;
}
return 0;
}
});
return result;
}
return Collections.singletonList(anchor);
}
@NotNull
private static FileEditorManagerEx getFileEditorManager(@NotNull DataContext context) {
final Project project = PlatformDataKeys.PROJECT.getData(context);
return FileEditorManagerEx.getInstanceEx(project);
}
private void splitWindow(int orientation, @NotNull DataContext context, @NotNull String filename) {
final Project project = PlatformDataKeys.PROJECT.getData(context);
final FileEditorManagerEx fileEditorManager = FileEditorManagerEx.getInstanceEx(project);
VirtualFile virtualFile = null;
if (filename.length() > 0 && project != null) {
virtualFile = VimPlugin.getFile().findFile(filename, project);
if (virtualFile == null) {
VimPlugin.showMessage("Could not find file: " + filename);
return;
}
}
final EditorWindow editorWindow = fileEditorManager.getSplitters().getCurrentWindow();
if (editorWindow != null) {
editorWindow.split(orientation, true, virtualFile, true);
}
}
@Nullable
private static Rectangle getEditorWindowRectangle(@NotNull EditorWindow window) {
final EditorWithProviderComposite editor = window.getSelectedEditor();
if (editor != null) {
final Point point = editor.getComponent().getLocationOnScreen();
final Dimension dimension = editor.getComponent().getSize();
return new Rectangle(point, dimension);
}
return null;
}
}

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;
}
@@ -524,13 +588,13 @@ public class SearchHelper {
private static int findNextWordOne(@NotNull CharSequence chars, int pos, int size, int step, boolean bigWord, boolean spaceWords) {
boolean found = false;
pos = pos < size ? pos : size - 1;
pos = pos < size ? pos : Math.min(size, chars.length() - 1);
// For back searches, skip any current whitespace so we start at the end of a word
if (step < 0 && pos > 0) {
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) {

View File

@@ -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';
}

View File

@@ -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();
}
});
}
});
}
}

View File

@@ -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) {

View File

@@ -42,6 +42,53 @@
* |gv| {@link com.maddyhome.idea.vim.action.motion.visual.VisualSelectPreviousAction}
*
*
* 2.2. Window commands
*
* tag action
* ---------------------------------------------------------------------------------------------------------------------
*
* |CTRL-W_+| TODO
* |CTRL-W_-| TODO
* |CTRL-W_<| TODO
* |CTRL-W_=| TODO
* |CTRL-W_>| TODO
* |CTRL-W_H| TODO
* |CTRL-W_J| TODO
* |CTRL-W_K| TODO
* |CTRL-W_L| TODO
* |CTRL-W_R| TODO
* |CTRL-W_W| {@link com.maddyhome.idea.vim.action.window.WindowPrevAction}
* |CTRL-W_]| TODO
* |CTRL-W_^| TODO
* |CTRL-W__| TODO
* |CTRL-W_b| TODO
* |CTRL-W_c| {@link com.maddyhome.idea.vim.action.window.CloseWindowAction}
* |CTRL-W_h| {@link com.maddyhome.idea.vim.action.window.WindowLeftAction}
* |CTRL-W_<Left>| ...
* |CTRL-W_j| {@link com.maddyhome.idea.vim.action.window.WindowDownAction}
* |CTRL-W_<Down>| ...
* |CTRL-W_k| {@link com.maddyhome.idea.vim.action.window.WindowUpAction}
* |CTRL-W_<Up>| ...
* |CTRL-W_l| {@link com.maddyhome.idea.vim.action.window.WindowRightAction}
* |CTRL-W_<Right>| ...
* |CTRL-W_n| TODO
* |CTRL-W_o| {@link com.maddyhome.idea.vim.action.window.WindowOnlyAction}
* |CTRL-W_CTRL-O| ...
* |CTRL-W_p| TODO
* |CTRL-W_q| TODO
* |CTRL-W_r| TODO
* |CTRL-W_s| {@link com.maddyhome.idea.vim.action.window.HorizontalSplitAction}
* |CTRL-W_S| ...
* |CTRL-W_CTRL-S| ...
* |CTRL-W_t| TODO
* |CTRL-W_v| {@link com.maddyhome.idea.vim.action.window.VerticalSplitAction}
* |CTRL-W_CTRL-V| ...
* |CTRL-W_w| {@link com.maddyhome.idea.vim.action.window.WindowNextAction}
* |CTRL-W_CTRL-W| ...
* |CTRL-W_z| TODO
* |CTRL-W_bar| TODO
*
*
* 3. Visual mode
*
* tag action

View File

@@ -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() {

View File

@@ -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 \(\) */

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();

View File

@@ -1,11 +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;
@@ -16,12 +18,14 @@ 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;
import com.maddyhome.idea.vim.option.Options;
import com.maddyhome.idea.vim.ui.ExEntryPanel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
@@ -31,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();
@@ -65,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();
}
@@ -81,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();
@@ -113,9 +122,12 @@ public abstract class VimTestCase extends UsefulTestCase {
return keys;
}
public void assertOffset(int expectedOffset) {
final int offset = myFixture.getEditor().getCaretModel().getOffset();
assertEquals(expectedOffset, offset);
public void assertOffset(int... expectedOffsets) {
final List<Caret> carets = myFixture.getEditor().getCaretModel().getAllCarets();
assertEquals("Wrong amount of carets", expectedOffsets.length, carets.size());
for (int i = 0; i < expectedOffsets.length; i++) {
assertEquals(expectedOffsets[i], carets.get(i).getOffset());
}
}
public void assertMode(@NotNull CommandState.Mode expectedMode) {
@@ -123,7 +135,7 @@ public abstract class VimTestCase extends UsefulTestCase {
assertEquals(expectedMode, mode);
}
public void assertSelection(@NotNull String expected) {
public void assertSelection(@Nullable String expected) {
final String selected = myFixture.getEditor().getSelectionModel().getSelectedText();
assertEquals(expected, selected);
}
@@ -137,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);
}
}

View File

@@ -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|
@@ -54,9 +38,7 @@ public class ChangeActionTest extends VimTestCase {
// VIM-321 |d| |count|
public void testDeleteEmptyRange() {
doTest(parseKeys("d0"),
"<caret>hello\n",
"hello\n");
doTest(parseKeys("d0"), "<caret>hello\n", "hello\n");
}
// VIM-112 |i| |i_CTRL-W|
@@ -64,15 +46,12 @@ public class ChangeActionTest extends VimTestCase {
typeTextInFile(parseKeys("i", "one two three", "<C-W>"),
"hello\n" +
"<caret>\n");
myFixture.checkResult("hello\n" +
"one two \n");
myFixture.checkResult("hello\n" + "one two \n");
}
// 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 |~|
@@ -82,15 +61,37 @@ 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" +
"two <caret>three\n" +
"four\n",
"one\n" +
"two hello world! three\n" +
"four\n");
doTest(parseKeys("i", "hello", "<Esc>", "gg", "gi", " world! "), "one\n" +
"two <caret>three\n" +
"four\n", "one\n" +
"two hello world! three\n" +
"four\n");
}
// VIM-312 |d| |w|
@@ -105,22 +106,16 @@ 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|
public void testDeleteLastWordBeforeEOLs() {
doTest(parseKeys("dw"),
"one <caret>two\n" +
"\n" +
"three\n",
"one \n" +
"\n" +
"three\n");
doTest(parseKeys("dw"), "one <caret>two\n" +
"\n" +
"three\n", "one \n" +
"\n" +
"three\n");
}
// VIM-105 |d| |w|
@@ -135,24 +130,22 @@ 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|
public void testChangeWordTwoWordsWithoutWhitespace() {
doTest(parseKeys("cw"),
"<caret>$value\n",
"value\n");
doTest(parseKeys("cw"), "<caret>$value\n", "value\n");
}
// VIM-296 |cc|
@@ -165,22 +158,23 @@ 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|
public void testDeleteLastWordAfterPunctuation() {
doTest(parseKeys("daw"),
"foo(<caret>bar\n" +
"baz\n",
"foo(\n" +
"baz\n");
doTest(parseKeys("daw"), "foo(<caret>bar\n" + "baz\n", "foo(\n" + "baz\n");
}
// VIM-244 |d| |l|
@@ -195,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|
@@ -225,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|
@@ -244,20 +231,309 @@ public class ChangeActionTest extends VimTestCase {
"fooar\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);
// VIM-569 |a| |i_CTRL-W|
public void testDeletePreviousWordDotEOL() {
doTest(parseKeys("a", "<C-W>"),
"this is a sentence<caret>.\n",
"this is a sentence<caret>\n");
}
// VIM-569 |a| |i_CTRL-W|
public void testDeletePreviousWordLastAfterWhitespace() {
doTest(parseKeys("A", "<C-W>"),
"<caret>this is a sentence\n",
"this is a <caret>\n");
}
// VIM-513 |A| |i_CTRL-W|
public void testDeletePreviousWordEOL() {
doTest(parseKeys("A", "<C-W>"),
"<caret>$variable\n",
"$<caret>\n");
}
// VIM-632 |CTRL-V| |v_b_I|
public void testChangeVisualBlock() {
doTest(parseKeys("<C-V>", "j", "I", "quux ", "<Esc>"),
"foo bar\n" +
"<caret>baz quux\n" +
"spam eggs\n",
"foo bar\n" +
"<caret>quux baz quux\n" +
"quux spam eggs\n");
}
// VIM-632 |CTRL-V| |v_d|
public void testDeleteVisualBlock() {
doTest(parseKeys("<C-V>", "jjl", "d"),
"<caret>foo\n" +
"bar\n" +
"baz\n" +
"quux\n",
"<caret>o\n" +
"r\n" +
"z\n" +
"quux\n");
}
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");
}
}

View File

@@ -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");
}
}

View File

@@ -1,6 +1,11 @@
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;
@@ -28,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"),
@@ -63,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" +
@@ -82,7 +97,7 @@ public class CopyActionTest extends VimTestCase {
assertEquals(0, editor.getCaretModel().getOffset());
}
// |v_y|
// VIM-632 |CTRL-V| |v_y| |p|
public void testYankVisualBlock() {
typeTextInFile(parseKeys("<C-V>", "jl", "yl", "p"),
"<caret>* one\n" +
@@ -96,7 +111,64 @@ 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);
}
// VIM-632 |CTRL-V| |v_y|
public void testStateAfterYankVisualBlock() {
typeTextInFile(parseKeys("<C-V>", "jl", "y"),
"<caret>foo\n" +
"bar\n");
assertOffset(0);
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");
}
}
}

View 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);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View 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");
}
}

View File

@@ -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");
}
}

View File

@@ -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));

View File

@@ -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");
}
}

View File

@@ -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");
}
}

View File

@@ -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() {