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

Compare commits

...

130 Commits

Author SHA1 Message Date
Alex Plate
f9cf62472c Prepare to 0.62 release 2020-12-15 10:12:31 +03:00
Alex Plate
ace584d294 Fix settings saving issues because of NPE 2020-12-15 09:35:35 +03:00
Alex Plate
5cd669202b Remove time calculation for isIdeavimDisabledHere 2020-12-10 10:06:29 +03:00
Alex Plate
4a09848720 Revert incorrect update 2020-12-10 10:03:51 +03:00
Alex Plate
f998f1be9f Update teamcity ide version for pull requests builds 2020-12-10 09:52:07 +03:00
Alex Plate
3352bdfabb Remove some deprecations in code 2020-12-10 09:50:05 +03:00
Alex Plate
ce43a9648f Update profile settings 2020-12-10 09:40:57 +03:00
Alex Plate
440a0bf393 Remove incorrect link in package-info.java 2020-12-10 09:36:59 +03:00
Alex Plate
64a7555f42 [VIM-2188] Remove deprecated configuration store 2020-12-10 09:35:07 +03:00
Alex Plate
3aa6fe3dc0 Update algorithm of number generation 2020-12-08 12:11:41 +03:00
Alex Plate
3522228d45 Add new line at the end of test 2020-12-08 11:36:23 +03:00
Alex Plate
9b42d9a5c6 Add test for incrementing fancy number 2020-12-08 11:35:11 +03:00
Alex Plate
4e8d98f956 Add Shaun Patterson to contributors list 2020-12-08 10:54:15 +03:00
Alex Plate
f6c3d27bbc Reset caret shape after <C-O> command 2020-12-08 10:50:06 +03:00
Alex Plate
21daf83fbd Include caret shape assertions into checks 2020-12-08 10:50:05 +03:00
Shaun Patterson
7f1203c207 VIM-1756: startSel works in insert mode 2020-12-08 10:49:19 +03:00
Alex Plate
89b1f90973 Fix detekt issues 2020-12-04 11:21:40 +03:00
Alex Plate
8002a5497f [VIM-1913] Update changes 2020-12-04 11:07:11 +03:00
Alex Plate
770d12d79b [VIM-1913] Enable enter for AppCode templates 2020-12-04 11:05:32 +03:00
Alex Plate
7e4ac22d23 [VIM-1913] Enable tab for AppCode templates 2020-12-04 10:45:56 +03:00
Alex Plate
4f4b26d3e1 Update CHANGES.md 2020-12-04 09:54:58 +03:00
Matt Ellis
4ea7c421a8 Extract and use common Direction enum 2020-12-04 09:53:58 +03:00
Matt Ellis
3c8b7e2de4 Convert SearchHighlightsHelper to Kotlin 2020-12-04 09:53:58 +03:00
Matt Ellis
b13acaf823 Rename .java to .kt 2020-12-04 09:53:58 +03:00
Matt Ellis
709cd6ad6e Extract SearchHighlightsHelper 2020-12-04 09:53:58 +03:00
Matt Ellis
1316ccc56f Update default for history to match Vim 2020-12-04 09:53:58 +03:00
Matt Ellis
881ddd0e11 Refactor setting special registers 2020-12-04 09:53:58 +03:00
Matt Ellis
49611ee6b9 Show the correct handler class in :map 2020-12-04 09:33:14 +03:00
Alex Plate
539465bb56 Update property based test 2020-12-03 10:50:57 +03:00
Alex Plate
bc54a73d69 Add suggested options to README.md 2020-12-03 10:13:28 +03:00
Alex Plate
5eb12f5d14 Update mappings in .ideavimrc example 2020-12-03 10:09:05 +03:00
Alex Plate
557e47650f Disable octal for nrformats 2020-12-03 10:07:27 +03:00
Alex Plate
f50753bfd7 Add tests for 2020.3 on TeamCity 2020-12-03 10:02:21 +03:00
Alex Plate
7a164d6d5f Updates to block caret in insert mode 2020-12-02 10:58:04 +03:00
Alex Plate
60bc936cd9 Take back: VIM-1475: Respect the "use block caret" when in insert mode 2020-12-02 10:31:01 +03:00
Alex Plate
d097e636ea Add test for double exiting visual mode 2020-12-02 10:30:37 +03:00
Alex Plate
3ce2bbb624 Rename test for visual exit 2020-12-02 10:27:28 +03:00
Alex Plate
4926d2554e Revert "VIM-1475: Respect the "use block caret" when in insert mode"
Reverting this commit due to VIM-2182

This reverts commit 62c828d7
2020-12-02 09:45:08 +03:00
Alex Plate
eae135acba Fix detekt issues 2020-12-01 12:19:08 +03:00
Alex Plate
8ce3801b87 [VIM-1913] Improve interaction with AppCode templates 2020-12-01 11:55:47 +03:00
Alex Plate
0c7c11f817 Ignore ui test 2020-12-01 10:02:53 +03:00
Alex Plate
b3a39c05d7 Update plugin.xml 2020-12-01 09:56:05 +03:00
Alex Plate
7370012667 Add getUnreleasedChangelog task to build.gradle 2020-12-01 09:52:56 +03:00
Alex Plate
412838fd4b Remove implemented manual tests 2020-11-27 12:05:00 +03:00
Alex Plate
273a614389 Add gutter ui test 2020-11-27 12:03:28 +03:00
Alex Plate
3944c80ae2 Update gradle intellij plugin 2020-11-27 10:54:22 +03:00
Alex Plate
726ab3f91b Add test with mouse dragging 2020-11-26 12:12:54 +03:00
Alex Plate
ab307dd78f Injecting text instead of typing it 2020-11-26 11:53:07 +03:00
Alex Plate
e3079912ae Remove unused handlers 2020-11-26 10:24:22 +03:00
Alex Plate
52da3ed0e4 Update info file 2020-11-26 09:44:48 +03:00
Alex Plate
cda85169ea Add new line for ExEditorKit.kt 2020-11-25 17:22:26 +03:00
Alex Plate
978d1df598 Disable internationalization inspections 2020-11-25 17:13:25 +03:00
Alex Plate
f90d22cc20 Refactoring of ex actions 2020-11-25 10:49:50 +03:00
Alex Plate
bae69d92ae Rename .java to .kt 2020-11-25 10:49:50 +03:00
Alex Plate
b910239825 Put ex panels into the separate package 2020-11-25 10:49:50 +03:00
Alex Plate
6421a5969a Update changes 2020-11-25 10:05:43 +03:00
sumoooru2
5c9faba7f4 Implement cmap 2020-11-25 10:00:15 +03:00
Alex Plate
c1af738cc5 Add another ui test 2020-11-24 12:47:10 +03:00
Alex Plate
23a80a9260 [VIM-2170] Fix alternative range format for s command 2020-11-23 11:49:30 +03:00
Alex Plate
d9d92f7eee Add another ui test 2020-11-20 20:18:19 +03:00
Alex Plate
c8be6c203e Update intellij plugin version 2020-11-20 12:39:00 +03:00
Alex Plate
612c599d9b Update compatibility a bit 2020-11-20 11:38:22 +03:00
Alex Plate
1292c7d1ae Remove unused import 2020-11-20 11:17:40 +03:00
Alex Plate
69f07967b5 Fix compilation issus for <203 2020-11-20 11:00:29 +03:00
Alex Plate
aedd427c07 Optimize imports 2020-11-20 10:45:26 +03:00
Alex Plate
9b6c3dc511 Fix unresolved references 2020-11-20 10:17:52 +03:00
Alex Plate
b4fa7f4317 Replace @NlsSafe annotation from utils with our own 2020-11-20 09:58:43 +03:00
Alex Plate
f053d305d5 Remove neovim from implementation 2020-11-20 09:35:48 +03:00
Alex Plate
1d95b7ddf7 Move run configurations to the .idea 2020-11-19 16:43:27 +03:00
Alex Plate
01a6964d68 Use neovim for tests only 2020-11-19 11:59:41 +03:00
Alex Plate
d661f96a9b Reformat for MessageHelper.kt 2020-11-19 11:26:27 +03:00
Alex Plate
c79fae8ab3 [i18n] Mark some strings as @NonNls 2020-11-19 11:22:11 +03:00
Alex Plate
121f130893 Ignore toString localization 2020-11-19 05:55:35 +03:00
Alex Plate
da9a0a414a [i18n] Extract more starting for localization 2020-11-19 00:26:17 +03:00
Alex Plate
e0732e041b Start with UI tests in the project 2020-11-18 22:03:46 +03:00
Alex Plate
6fc5b3189a [i18n] More extracted strings 2020-11-18 18:49:55 +03:00
Alex Plate
2a0c9cb749 [i18n] Add another file template 2020-11-18 14:22:39 +03:00
Alex Plate
7ad131b448 [i18n] More extracted strings 2020-11-18 13:03:35 +03:00
Alex Plate
3e7186bd60 [i18n] Update @Nls annotations 2020-11-18 13:03:35 +03:00
Alex Plate
132f8cce53 [i18n] Extract more strings 2020-11-18 13:03:35 +03:00
Alex Plate
5c07f42afb Disable unstable api usage inspection 2020-11-18 12:33:51 +03:00
Alex Plate
1f7a2594c8 Convert some strings to properties 2020-11-18 11:26:03 +03:00
Alex Plate
ec64d19a36 Update JetBrains annotations 2020-11-18 11:25:39 +03:00
Alex Plate
5cb67470d5 Enable hardcoded string inspection 2020-11-18 11:25:39 +03:00
Alex Plate
c03f01a96a Update MessageHelper.kt 2020-11-18 03:09:57 +03:00
Alex Plate
26b67a43ae Fix formatting 2020-11-17 19:55:09 +03:00
Alex Plate
a93ca3a205 Support mapclear commands 2020-11-17 19:47:56 +03:00
Alex Plate
dcc647ba3c [VIM-1491] Support unmap 2020-11-17 19:38:22 +03:00
Alex Plate
fe44f59239 Refactor map handler 2020-11-17 19:36:41 +03:00
Alex Plate
80dba0babf Move MapHandler into the separate package 2020-11-17 19:36:17 +03:00
Yaohui Wang
34acf9a91e Add CJK characterType for delimiting caret movement
- Add unicode CJK Unified Ideographs type
- Stop caret movement for CJK chars (as VIM does). Similar to VIM-58
2020-11-17 10:21:49 +03:00
Alex Plate
cb859dbb7b Add Grzegorz Antoniak to the contributors list 2020-11-17 10:16:13 +03:00
Alex Plate
20c04b1e62 Add Brandon Conway to the contributors list 2020-11-17 10:09:21 +03:00
Alex Plate
69eaf8ed0c Jan Palus was already on the list 2020-11-17 10:09:21 +03:00
Grzegorz Antoniak
62c828d722 VIM-1475: Respect the "use block caret" when in insert mode
The "use block caret" IDEA option controls the style of the cursor: when
it's enabled, the cursor is drawn as a block, and when it's disabled,
it's drawn as a bar. However, after installing IdeaVIM, this option
isn't respected; the plugin always uses a block cursor when in command
mode, and always uses a bar cursor when in insert mode.

This commit changes the behavior so that when the "use block caret"
option is enabled, IdeaVIM's insert mode uses a block cursor instead of
a bar cursor. The cursor in normal mode is always drawn as a block
cursor. If the "use block caret" option is disabled, the behavior is the
same as in previous versions of IdeaVIM (block cursor in normal mode,
bar cursor in insert mode).

Fixes VIM-1475 (on YouTrack)
2020-11-17 10:06:32 +03:00
Brandon Conway
251e8e8ff4 Fix typo 2020-11-16 11:20:07 +03:00
Alex Plate
acc41ec72e Add warning to the info about ideavimrc file 2020-11-13 15:15:28 +03:00
Alex Plate
f9ebcf2ee4 Update readme section 2020-11-13 15:13:01 +03:00
Alex Plate
f3df231dc2 Update changes 2020-11-13 12:35:21 +03:00
Jan Palus
75a1bb7cc5 VIM-2114 Do not override registers when deleting empty range 2020-11-13 12:34:04 +03:00
Alex Plate
7d6a64315a Add Jan Palus to contributors list 2020-11-13 11:58:10 +03:00
Jan Palus
2758071d31 VIM-2113 Increase tag range only in visual mode 2020-11-13 11:54:08 +03:00
Alex Plate
3881b905be Update CHANGES.md 2020-11-12 10:51:57 +03:00
Alex Plate
073c62f868 Update version on TeamCity 2020-11-12 10:50:25 +03:00
Alex Plate
d8e0f26bea Revert "Unmute falling tests"
This reverts commit 0296cae7
2020-11-12 10:48:54 +03:00
Alex Plate
04c24ab5d0 Run manual tests 2020-11-12 10:38:02 +03:00
Alex Plate
bfb0ba1ab9 Update emulated plugins 2020-11-12 10:31:11 +03:00
Alex Plate
fb7d48af1f Update IDE actions section in README 2020-11-12 10:11:33 +03:00
Alex Plate
559989ce4b Remove all To Be Released labels 2020-11-12 10:03:56 +03:00
Alex Plate
81f59e3c18 Update plugin.xml 2020-11-12 10:03:04 +03:00
Alex Plate
b6adf9f7a9 Rename ideaenabledbufs to ideavimsupport 2020-11-12 09:45:29 +03:00
Alex Plate
b972a01cf0 Add merged PR to changes 2020-11-12 09:41:57 +03:00
Alex Plate
0296cae712 Unmute falling tests 2020-11-12 09:39:09 +03:00
Matt Ellis
c38b18e16b Prevent inlays causing scrolling to stick 2020-11-12 09:37:55 +03:00
Matt Ellis
8d65c3ed26 Limit how much of an inlay is shown when scrolling 2020-11-12 09:37:55 +03:00
Matt Ellis
995bb966ad Reposition cursor when scrolloff changes 2020-11-12 09:37:55 +03:00
Matt Ellis
dbda1a76ca [VIM-2158] Fix scrolling when scrolloff is greater than half screen height, but less than full screen height 2020-11-12 09:37:55 +03:00
Alex Plate
ed6f990d9a Remove some qodana inspections 2020-11-06 21:39:37 +03:00
Alex Plate
4f86d9cc77 Correct visual mode exiting when after line end 2020-11-06 20:26:26 +03:00
Alex Plate
d55774abab Use vimForEachCaret 2020-11-06 20:21:05 +03:00
Alex Plate
d5591ba08d Update qodana profile 2020-11-06 20:00:21 +03:00
Alex Plate
f67d483c4e Exclude next method from property based testing 2020-11-06 19:48:33 +03:00
Alex Plate
f26ddd4a27 Fix exception in aW 2020-11-06 19:12:40 +03:00
Alex Plate
dbbea642bc Add inspection profiles to the repository 2020-11-06 18:40:02 +03:00
Alex Plate
0539e39977 Fix <C-\><C-N> 2020-11-06 11:16:13 +03:00
Alex Plate
65235d32a1 Fix exception for ]b command 2020-11-06 10:36:03 +03:00
Alex Plate
ecfcdf5a8c Update intellij gradle plugin 2020-11-06 10:06:25 +03:00
Alex Plate
cf127ba7f9 Update plugin description 2020-11-06 09:37:01 +03:00
Alex Plate
1fba77d925 Update getName description 2020-11-05 11:31:41 +03:00
Alex Plate
5752b116f6 Fix plugin.xml 2020-11-04 13:04:36 +03:00
192 changed files with 4064 additions and 1701 deletions

View File

@@ -9,6 +9,7 @@
<ID>ComplexMethod:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID> <ID>ComplexMethod:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID>
<ID>ComplexMethod:PutGroup.kt$PutGroup$private fun prepareDocumentAndGetStartOffsets(editor: Editor, caret: Caret, typeInRegister: SelectionType, data: PutData, additionalData: Map&lt;String, Any&gt;): List&lt;Int&gt;</ID> <ID>ComplexMethod:PutGroup.kt$PutGroup$private fun prepareDocumentAndGetStartOffsets(editor: Editor, caret: Caret, typeInRegister: SelectionType, data: PutData, additionalData: Map&lt;String, Any&gt;): List&lt;Int&gt;</ID>
<ID>ComplexMethod:SearchHelperKt.kt$// bounds are considered inside corresponding quotes fun checkInString(chars: CharSequence, currentPos: Int, str: Boolean): Boolean</ID> <ID>ComplexMethod:SearchHelperKt.kt$// bounds are considered inside corresponding quotes fun checkInString(chars: CharSequence, currentPos: Int, str: Boolean): Boolean</ID>
<ID>ComplexMethod:SearchHighlightsHelper.kt$ private fun updateSearchHighlights( pattern: String?, shouldIgnoreSmartCase: Boolean, showHighlights: Boolean, initialOffset: Int, searchRange: LineRange?, forwards: Boolean, forceUpdate: Boolean ): Int</ID>
<ID>ComplexMethod:TabCloseHandler.kt$TabCloseHandler$ private fun getTabIndexToClose(arg: String, current: Int, last: Int): Int?</ID> <ID>ComplexMethod:TabCloseHandler.kt$TabCloseHandler$ private fun getTabIndexToClose(arg: String, current: Int, last: Int): Int?</ID>
<ID>ComplexMethod:VimExchangeExtension.kt$VimExchangeExtension.Operator$private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult</ID> <ID>ComplexMethod:VimExchangeExtension.kt$VimExchangeExtension.Operator$private fun compareExchanges(x: Exchange, y: Exchange): ExchangeCompareResult</ID>
<ID>ComplexMethod:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.NextOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID> <ID>ComplexMethod:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.NextOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID>
@@ -16,6 +17,8 @@
<ID>LongMethod:CmdHandler.kt$CmdHandler$private fun addAlias(cmd: ExCommand, editor: Editor?): Boolean</ID> <ID>LongMethod:CmdHandler.kt$CmdHandler$private fun addAlias(cmd: ExCommand, editor: Editor?): Boolean</ID>
<ID>LongMethod:HistoryHandler.kt$HistoryHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID> <ID>LongMethod:HistoryHandler.kt$HistoryHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>LongMethod:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID> <ID>LongMethod:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID>
<ID>LongMethod:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.NextOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID>
<ID>LoopWithTooManyJumpStatements:SearchHighlightsHelper.kt$for (project in projectManager.openProjects) { val current = FileEditorManager.getInstance(project).selectedTextEditor ?: continue // [VERSION UPDATE] 202+ Use editors val editors = EditorFactory.getInstance().getEditors(current.document, project) ?: continue for (editor in editors) { // Try to keep existing highlights if possible. Update if hlsearch has changed or if the pattern has changed. // Force update for the situations where the text is the same, but the ignore case values have changed. // E.g. Use `*` to search for a word (which ignores smartcase), then use `/&lt;Up&gt;` to search for the same pattern, // which will match smartcase. Or changing the smartcase/ignorecase settings if (shouldRemoveSearchHighlights(editor, pattern, showHighlights) || forceUpdate) { removeSearchHighlights(editor) } if (pattern == null) continue if (shouldAddAllSearchHighlights(editor, pattern, showHighlights)) { // hlsearch (+ incsearch/noincsearch) val startLine = searchRange?.startLine ?: 0 val endLine = searchRange?.endLine ?: -1 val results = SearchGroup.findAll(editor, pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase)) if (results.isNotEmpty()) { currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards) highlightSearchResults(editor, pattern, results, currentMatchOffset) } editor.vimLastSearch = pattern } else if (shouldAddCurrentMatchSearchHighlight(pattern, showHighlights, initialOffset)) { // nohlsearch + incsearch val searchOptions = EnumSet.of(SearchOptions.WHOLE_FILE) if (wrapscan.isSet) searchOptions.add(SearchOptions.WRAP) if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE) if (!forwards) searchOptions.add(SearchOptions.BACKWARDS) val result = SearchGroup.findIt(editor, pattern, initialOffset, 1, searchOptions) if (result != null) { currentMatchOffset = result.startOffset val results = listOf(result) highlightSearchResults(editor, pattern, results, currentMatchOffset) } } else if (shouldMaintainCurrentMatchOffset(pattern, initialOffset)) { // incsearch. If nothing has changed (e.g. we've edited offset values in `/foo/e+2`) make sure we return the // current match offset so the caret remains at the current incsarch match val offset = editor.vimIncsearchCurrentMatchOffset if (offset != null) { currentMatchOffset = offset } } } }</ID>
<ID>MagicNumber:ActionListHandler.kt$ActionListHandler$50</ID> <ID>MagicNumber:ActionListHandler.kt$ActionListHandler$50</ID>
<ID>MagicNumber:AddBlockInlaysAction.kt$AddBlockInlaysAction$0.9f</ID> <ID>MagicNumber:AddBlockInlaysAction.kt$AddBlockInlaysAction$0.9f</ID>
<ID>MagicNumber:AddBlockInlaysAction.kt$AddBlockInlaysAction$1.75f</ID> <ID>MagicNumber:AddBlockInlaysAction.kt$AddBlockInlaysAction$1.75f</ID>
@@ -50,6 +53,7 @@
<ID>MagicNumber:OptionsManager.kt$OptionsManager$19</ID> <ID>MagicNumber:OptionsManager.kt$OptionsManager$19</ID>
<ID>MagicNumber:OptionsManager.kt$OptionsManager$20</ID> <ID>MagicNumber:OptionsManager.kt$OptionsManager$20</ID>
<ID>MagicNumber:OptionsManager.kt$OptionsManager$3</ID> <ID>MagicNumber:OptionsManager.kt$OptionsManager$3</ID>
<ID>MagicNumber:OptionsManager.kt$OptionsManager$50</ID>
<ID>MagicNumber:OptionsManager.kt$OptionsManager$80</ID> <ID>MagicNumber:OptionsManager.kt$OptionsManager$80</ID>
<ID>MagicNumber:ProcessExEntryAction.kt$ProcessExEntryAction$0x0a</ID> <ID>MagicNumber:ProcessExEntryAction.kt$ProcessExEntryAction$0x0a</ID>
<ID>MagicNumber:RegistersHandler.kt$RegistersHandler$200</ID> <ID>MagicNumber:RegistersHandler.kt$RegistersHandler$200</ID>
@@ -61,6 +65,7 @@
<ID>MagicNumber:VimHighlightedYank.kt$VimHighlightedYank.HighlightHandler$3</ID> <ID>MagicNumber:VimHighlightedYank.kt$VimHighlightedYank.HighlightHandler$3</ID>
<ID>MagicNumber:VimHighlightedYank.kt$VimHighlightedYank.HighlightHandler$4</ID> <ID>MagicNumber:VimHighlightedYank.kt$VimHighlightedYank.HighlightHandler$4</ID>
<ID>MatchingDeclarationName:CommandDefinition.kt$CommandName</ID> <ID>MatchingDeclarationName:CommandDefinition.kt$CommandName</ID>
<ID>MatchingDeclarationName:SearchHelperKt.kt$Direction</ID>
<ID>MaxLineLength:ExBeanClass.kt$ExBeanClass$logger&lt;ExBeanClass&gt;().error("IdeaVim doesn't accept contributions to `vimActions` extension points. Please create a plugin using `VimExtension`. Plugin to blame: $pluginId")</ID> <ID>MaxLineLength:ExBeanClass.kt$ExBeanClass$logger&lt;ExBeanClass&gt;().error("IdeaVim doesn't accept contributions to `vimActions` extension points. Please create a plugin using `VimExtension`. Plugin to blame: $pluginId")</ID>
<ID>MaxLineLength:ExRanges.kt$SearchRange$override</ID> <ID>MaxLineLength:ExRanges.kt$SearchRange$override</ID>
<ID>MaxLineLength:NotificationService.kt$NotificationService$notification.addAction(AppendToIdeaVimRcAction(notification, "set clipboard+=ideaput", "ideaput") { OptionsManager.clipboard.append(ClipboardOptionsData.ideaput) })</ID> <ID>MaxLineLength:NotificationService.kt$NotificationService$notification.addAction(AppendToIdeaVimRcAction(notification, "set clipboard+=ideaput", "ideaput") { OptionsManager.clipboard.append(ClipboardOptionsData.ideaput) })</ID>
@@ -79,67 +84,18 @@
<ID>MaxLineLength:VimShortcutKeyAction.kt$VimShortcutKeyAction.Companion$ImmutableSet.builder&lt;KeyStroke&gt;().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)).addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0)) .addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))</ID> <ID>MaxLineLength:VimShortcutKeyAction.kt$VimShortcutKeyAction.Companion$ImmutableSet.builder&lt;KeyStroke&gt;().addAll(getKeyStrokes(KeyEvent.VK_ENTER, 0)).addAll(getKeyStrokes(KeyEvent.VK_ESCAPE, 0)) .addAll(getKeyStrokes(KeyEvent.VK_TAB, 0)).addAll(getKeyStrokes(KeyEvent.VK_BACK_SPACE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_INSERT, 0)).addAll(getKeyStrokes(KeyEvent.VK_DELETE, 0, InputEvent.CTRL_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_UP, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)).addAll(getKeyStrokes(KeyEvent.VK_DOWN, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_LEFT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_RIGHT, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)) .addAll(getKeyStrokes(KeyEvent.VK_HOME, 0, InputEvent.CTRL_DOWN_MASK, InputEvent.SHIFT_DOWN_MASK, InputEvent.CTRL_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK))</ID>
<ID>MemberNameEqualsClassName:Ranges.kt$Ranges$private val ranges: MutableList&lt;Range&gt; = mutableListOf()</ID> <ID>MemberNameEqualsClassName:Ranges.kt$Ranges$private val ranges: MutableList&lt;Range&gt; = mutableListOf()</ID>
<ID>NestedBlockDepth:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID> <ID>NestedBlockDepth:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID>
<ID>ReturnCount:ActionBeanClass.kt$ActionBeanClass$fun getParsedModes(): Set&lt;MappingMode&gt;?</ID>
<ID>ReturnCount:Alias.kt$Alias$fun getCommand(input: String, count: Int): String</ID>
<ID>ReturnCount:CmdFilterHandler.kt$CmdFilterHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:CmdHandler.kt$CmdHandler$private fun addAlias(cmd: ExCommand, editor: Editor?): Boolean</ID> <ID>ReturnCount:CmdHandler.kt$CmdHandler$private fun addAlias(cmd: ExCommand, editor: Editor?): Boolean</ID>
<ID>ReturnCount:CommandGroup.kt$CommandGroup$fun isAlias(command: String): Boolean</ID>
<ID>ReturnCount:DeleteJoinLinesAction.kt$DeleteJoinLinesAction$override fun execute(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean</ID>
<ID>ReturnCount:DeleteJoinLinesSpacesAction.kt$DeleteJoinLinesSpacesAction$override fun execute(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean</ID>
<ID>ReturnCount:DeleteJoinVisualLinesAction.kt$DeleteJoinVisualLinesAction$override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map&lt;Caret, VimSelection&gt;): Boolean</ID>
<ID>ReturnCount:DeleteJoinVisualLinesSpacesAction.kt$DeleteJoinVisualLinesSpacesAction$override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map&lt;Caret, VimSelection&gt;): Boolean</ID>
<ID>ReturnCount:DeleteMotionAction.kt$DeleteMotionAction$override fun execute(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Boolean</ID>
<ID>ReturnCount:EditFileHandler.kt$EditFileHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:EditorHelper.kt$ fun Editor.isPrimaryEditor(): Boolean</ID>
<ID>ReturnCount:ExRanges.kt$Range.Companion$ @JvmStatic fun createRange(str: String, offset: Int, move: Boolean): Array&lt;Range&gt;?</ID> <ID>ReturnCount:ExRanges.kt$Range.Companion$ @JvmStatic fun createRange(str: String, offset: Int, move: Boolean): Array&lt;Range&gt;?</ID>
<ID>ReturnCount:FilterMotionAction.kt$FilterMotionAction$override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean</ID>
<ID>ReturnCount:GotoCharacterHandler.kt$GotoCharacterHandler$override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:Helper.kt$@Suppress("IncorrectParentDisposable") fun Editor.isTemplateActive(): Boolean</ID>
<ID>ReturnCount:HistoryHandler.kt$HistoryHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:IdeaSelectionControl.kt$IdeaSelectionControl$ fun predictMode(editor: Editor, selectionSource: VimListenerManager.SelectionSource): CommandState.Mode</ID>
<ID>ReturnCount:MapHandler.kt$MapHandler$@Throws(ExException::class) private fun executeCommand(cmd: ExCommand, editor: Editor?): Boolean</ID>
<ID>ReturnCount:Marks.kt$IntellijMark$private fun getProject(): Project?</ID>
<ID>ReturnCount:Marks.kt$VimMark.Companion$@JvmStatic fun create(key: Char?, logicalLine: Int?, col: Int?, filename: String?, protocol: String?): VimMark?</ID>
<ID>ReturnCount:ModalEntry.kt$ModalEntry.&lt;no name provided&gt;$override fun dispatchKeyEvent(e: KeyEvent): Boolean</ID>
<ID>ReturnCount:MotionScrollLastScreenLinePageStartAction.kt$MotionScrollLastScreenLinePageStartAction$override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean</ID>
<ID>ReturnCount:OperatorAction.kt$OperatorAction$override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean</ID>
<ID>ReturnCount:OptionsManager.kt$OptionsManager$ fun parseOptionLine(editor: Editor?, args: String, failOnBad: Boolean): Boolean</ID>
<ID>ReturnCount:PutGroup.kt$PutGroup$private fun getProviderForPasteViaIde(context: DataContext, typeInRegister: SelectionType, data: PutData): PasteProvider?</ID>
<ID>ReturnCount:PutGroup.kt$PutGroup$private fun prepareDocumentAndGetStartOffsets(editor: Editor, caret: Caret, typeInRegister: SelectionType, data: PutData, additionalData: Map&lt;String, Any&gt;): List&lt;Int&gt;</ID>
<ID>ReturnCount:PutLinesHandler.kt$PutLinesHandler$override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:ReloadVimRc.kt$VimRcFileState$fun equalTo(document: Document): Boolean</ID>
<ID>ReturnCount:RepeatHandler.kt$RepeatHandler$@Throws(ExException::class) override fun execute(editor: Editor, caret: Caret, context: DataContext, cmd: ExCommand): Boolean</ID>
<ID>ReturnCount:SelectMotionRightAction.kt$SelectMotionRightAction$override fun getOffset(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): Int</ID>
<ID>ReturnCount:ShowCmd.kt$ShowCmd$fun getFullText(editor: Editor?): String</ID>
<ID>ReturnCount:TabCloseHandler.kt$TabCloseHandler$ private fun getTabIndexToClose(arg: String, current: Int, last: Int): Int?</ID>
<ID>ReturnCount:TextObjectActionHandler.kt$TextObjectActionHandler$ final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean</ID>
<ID>ReturnCount:UndoRedoHelper.kt$UndoRedoHelper$fun redo(context: DataContext): Boolean</ID>
<ID>ReturnCount:UndoRedoHelper.kt$UndoRedoHelper$fun undo(context: DataContext): Boolean</ID>
<ID>ReturnCount:VimExchangeExtension.kt$VimExchangeExtension.Operator$override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean</ID>
<ID>ReturnCount:VimExtensionFacade.kt$VimExtensionFacade$ @JvmStatic fun inputString(editor: Editor, prompt: String, finishOn: Char?): String</ID>
<ID>ReturnCount:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.AllOccurrencesHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID> <ID>ReturnCount:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.AllOccurrencesHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID>
<ID>ReturnCount:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.NextOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID> <ID>ReturnCount:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.NextOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID>
<ID>ReturnCount:VimMultipleCursorsExtension.kt$VimMultipleCursorsExtension.SkipOccurrenceHandler$override fun executeInWriteAction(editor: Editor, context: DataContext)</ID>
<ID>ReturnCount:VimScriptParser.kt$VimScriptParser$@Throws(ExException::class) fun evaluate(expression: String, globals: Map&lt;String?, Any?&gt;): Any</ID>
<ID>ReturnCount:VimScriptParser.kt$VimScriptParser$fun findOrCreateIdeaVimRc(): File?</ID>
<ID>ReturnCount:VimShortcutKeyAction.kt$VimShortcutKeyAction$private fun getKeyStroke(e: AnActionEvent): KeyStroke?</ID>
<ID>ReturnCount:VimShortcutKeyAction.kt$VimShortcutKeyAction$private fun isEnabled(e: AnActionEvent): Boolean</ID> <ID>ReturnCount:VimShortcutKeyAction.kt$VimShortcutKeyAction$private fun isEnabled(e: AnActionEvent): Boolean</ID>
<ID>ReturnCount:VimSurroundExtension.kt$VimSurroundExtension.CSurroundHandler$override fun execute(editor: Editor, context: DataContext)</ID>
<ID>ReturnCount:VimSurroundExtension.kt$VimSurroundExtension.Operator$override fun apply(editor: Editor, context: DataContext, selectionType: SelectionType): Boolean</ID>
<ID>ReturnCount:VisualBlockAppendAction.kt$VisualBlockAppendAction$override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map&lt;Caret, VimSelection&gt;): Boolean</ID>
<ID>ReturnCount:VisualBlockInsertAction.kt$VisualBlockInsertAction$override fun executeForAllCarets(editor: Editor, context: DataContext, cmd: Command, caretsAndSelections: Map&lt;Caret, VimSelection&gt;): Boolean</ID>
<ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$ fun toggleVisual(editor: Editor, count: Int, rawCount: Int, subMode: CommandState.SubMode): Boolean</ID>
<ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$fun autodetectVisualSubmode(editor: Editor): CommandState.SubMode</ID>
<ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$fun selectPreviousVisualMode(editor: Editor): Boolean</ID>
<ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$fun swapVisualSelections(editor: Editor): Boolean</ID>
<ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$private fun seemsLikeBlockMode(editor: Editor): Boolean</ID> <ID>ReturnCount:VisualMotionGroup.kt$VisualMotionGroup$private fun seemsLikeBlockMode(editor: Editor): Boolean</ID>
<ID>ReturnCount:VisualOperatorActionHandler.kt$VisualOperatorActionHandler$final override fun baseExecute(editor: Editor, caret: Caret, context: DataContext, cmd: Command): Boolean</ID>
<ID>ReturnCount:YankGroup.kt$YankGroup$ fun yankMotion(editor: Editor, context: DataContext, count: Int, rawCount: Int, argument: Argument): Boolean</ID>
<ID>ThrowsCount:CommandHandler.kt$CommandHandler$private fun checkArgs(cmd: ExCommand)</ID> <ID>ThrowsCount:CommandHandler.kt$CommandHandler$private fun checkArgs(cmd: ExCommand)</ID>
<ID>TooManyFunctions:CommandBuilder.kt$CommandBuilder</ID> <ID>TooManyFunctions:CommandBuilder.kt$CommandBuilder</ID>
<ID>TooManyFunctions:CommandState.kt$CommandState</ID> <ID>TooManyFunctions:CommandState.kt$CommandState</ID>
<ID>TooManyFunctions:PutGroup.kt$PutGroup</ID> <ID>TooManyFunctions:PutGroup.kt$PutGroup</ID>
<ID>TooManyFunctions:Ranges.kt$Ranges</ID> <ID>TooManyFunctions:Ranges.kt$Ranges</ID>
<ID>TooManyFunctions:SearchHighlightsHelper.kt$com.maddyhome.idea.vim.helper.SearchHighlightsHelper.kt</ID>
<ID>TooManyFunctions:VisualGroup.kt$com.maddyhome.idea.vim.group.visual.VisualGroup.kt</ID> <ID>TooManyFunctions:VisualGroup.kt$com.maddyhome.idea.vim.group.visual.VisualGroup.kt</ID>
<ID>TooManyFunctions:VisualMotionGroup.kt$VisualMotionGroup</ID> <ID>TooManyFunctions:VisualMotionGroup.kt$VisualMotionGroup</ID>
</CurrentIssues> </CurrentIssues>

3
.gitignore vendored
View File

@@ -5,6 +5,9 @@
!/.idea/scopes !/.idea/scopes
!/.idea/copyright !/.idea/copyright
!/.idea/icon.png !/.idea/icon.png
!/.idea/inspectionProfiles
!/.idea/fileTemplates
!/.idea/runConfigurations
/build/ /build/
/out/ /out/

View File

@@ -0,0 +1 @@
com.maddyhome.idea.vim.helper.MessageHelper.message("${PROPERTY_KEY}", ${PARAMETERS})

View File

@@ -0,0 +1 @@
com.maddyhome.idea.vim.helper.MessageHelper.message("${PROPERTY_KEY}")

View File

@@ -0,0 +1,34 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HardCodedStringLiteral" enabled="false" level="WARNING" enabled_by_default="false">
<scope name="Production" level="WARNING" enabled="false">
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="true" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</scope>
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="false" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</inspection_tool>
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_requireAnnotationsFirst" value="true" />
</inspection_tool>
<inspection_tool class="PluginXmlI18n" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

47
.idea/inspectionProfiles/Qodana.xml generated Normal file
View File

@@ -0,0 +1,47 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<description>Inspections profile for Qodana</description>
<option name="myName" value="Qodana" />
<inspection_tool class="CanBeFinal" enabled="false" level="WARNING" enabled_by_default="false">
<option name="REPORT_CLASSES" value="false" />
<option name="REPORT_METHODS" value="false" />
<option name="REPORT_FIELDS" value="true" />
</inspection_tool>
<inspection_tool class="GrUnresolvedAccess" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="HardCodedStringLiteral" enabled="false" level="WARNING" enabled_by_default="false">
<scope name="Production" level="WARNING" enabled="false">
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="true" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</scope>
<option name="ignoreForAssertStatements" value="true" />
<option name="ignoreForExceptionConstructors" value="true" />
<option name="ignoreForSpecifiedExceptionConstructors" value="" />
<option name="ignoreForJUnitAsserts" value="true" />
<option name="ignoreForClassReferences" value="true" />
<option name="ignoreForPropertyKeyReferences" value="true" />
<option name="ignoreForNonAlpha" value="true" />
<option name="ignoreAssignedToConstants" value="false" />
<option name="ignoreToString" value="false" />
<option name="nonNlsCommentPattern" value="NON-NLS" />
</inspection_tool>
<inspection_tool class="MissortedModifiers" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_requireAnnotationsFirst" value="true" />
</inspection_tool>
<inspection_tool class="SameReturnValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -13,6 +13,7 @@ import _Self.buildTypes.TestsForIntelliJ20192
import _Self.buildTypes.TestsForIntelliJ20193 import _Self.buildTypes.TestsForIntelliJ20193
import _Self.buildTypes.TestsForIntelliJ20201 import _Self.buildTypes.TestsForIntelliJ20201
import _Self.buildTypes.TestsForIntelliJ20202 import _Self.buildTypes.TestsForIntelliJ20202
import _Self.buildTypes.TestsForIntelliJ20203
import _Self.buildTypes.TestsForIntelliJEAP import _Self.buildTypes.TestsForIntelliJEAP
import _Self.vcsRoots.Branch_181 import _Self.vcsRoots.Branch_181
import _Self.vcsRoots.Branch_183 import _Self.vcsRoots.Branch_183
@@ -29,19 +30,24 @@ object Project : Project({
vcsRoot(Branch_191_193) vcsRoot(Branch_191_193)
buildType(GitHubPullRequests) buildType(GitHubPullRequests)
buildType(Release) buildType(Release)
buildType(ReleaseEap)
buildType(TestsForIntelliJ20201) buildType(TestsForIntelliJ20201)
buildType(TestsForIntelliJ20202)
buildType(TestsForIntelliJ20203)
buildType(TestsForIntelliJEAP)
buildType(Nvim)
buildType(PluginVerifier)
buildType(TestsForIntelliJ20191) buildType(TestsForIntelliJ20191)
buildType(TestsForIntelliJ20181) buildType(TestsForIntelliJ20181)
buildType(TestsForIntelliJ20192) buildType(TestsForIntelliJ20192)
buildType(TestsForIntelliJ20182) buildType(TestsForIntelliJ20182)
buildType(TestsForIntelliJ20193) buildType(TestsForIntelliJ20193)
buildType(TestsForIntelliJ20183) buildType(TestsForIntelliJ20183)
buildType(Nvim)
buildType(ReleaseEap)
buildType(TestsForIntelliJ20202)
buildType(TestsForIntelliJEAP)
buildType(PluginVerifier)
features { features {
feature { feature {

View File

@@ -41,5 +41,6 @@ sealed class ActiveTests(buildName: String, ijVersion: String) : BuildType({
}) })
object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT") object TestsForIntelliJEAP : ActiveTests("Tests for IntelliJ Latest EAP", "LATEST-EAP-SNAPSHOT")
object TestsForIntelliJ20203 : ActiveTests("Tests for IntelliJ 2020.3", "2020.3")
object TestsForIntelliJ20202 : ActiveTests("Tests for IntelliJ 2020.2", "2020.2") object TestsForIntelliJ20202 : ActiveTests("Tests for IntelliJ 2020.2", "2020.2")
object TestsForIntelliJ20201 : ActiveTests("Tests for IntelliJ 2020.1", "2020.1") object TestsForIntelliJ20201 : ActiveTests("Tests for IntelliJ 2020.1", "2020.1")

View File

@@ -16,7 +16,7 @@ object GitHubPullRequests : BuildType({
params { params {
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false") param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.1") param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.3")
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false") param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
} }

View File

@@ -10,7 +10,7 @@ object Release : BuildType({
description = "Build and publish IdeaVim plugin" description = "Build and publish IdeaVim plugin"
artifactRules = "build/distributions/*" artifactRules = "build/distributions/*"
buildNumberPattern = "0.60" buildNumberPattern = "0.62"
params { params {
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2") param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2")

View File

@@ -13,7 +13,7 @@ object ReleaseEap : BuildType({
description = "Build and publish EAP of IdeaVim plugin" description = "Build and publish EAP of IdeaVim plugin"
artifactRules = "build/distributions/*" artifactRules = "build/distributions/*"
buildNumberPattern = "0.60.%build.counter%" buildNumberPattern = "0.62.%build.counter%"
params { params {
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2") param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2")

View File

@@ -323,6 +323,18 @@ Contributors:
[![icon][github]](https://github.com/i-e-b) [![icon][github]](https://github.com/i-e-b)
&nbsp; &nbsp;
Iain Ballard Iain Ballard
* [![icon][mail]](mailto:brandoncc@gmail.com)
[![icon][github]](https://github.com/brandoncc)
&nbsp;
Brandon Conway
* [![icon][mail]](mailto:ga@anadoxin.org)
[![icon][github]](https://github.com/antekone)
&nbsp;
Grzegorz Antoniak
* [![icon][mail]](mailto:shaunpatterson@gmail.com)
[![icon][github]](https://github.com/shaunpatterson)
&nbsp;
Shaun Patterson
If you are a contributor and your name is not listed here, feel free to If you are a contributor and your name is not listed here, feel free to
contact the maintainers. contact the maintainers.

View File

@@ -22,7 +22,35 @@ It is important to distinguish EAP from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even Please note that the quality of EAP versions may at times be way below even
usual beta standards. usual beta standards.
## To Be Released ## 0.62, 2020-12-15
### Features:
* Support `unmap` and `mapclear` commands [VIM-1491](https://youtrack.jetbrains.com/issue/VIM-1491)
* Support mappings in ex panel (`cmap`) [VIM-1227](https://youtrack.jetbrains.com/issue/VIM-1227)
### Changes:
* `octal` is now disabled by default for `nrformats`. [VIM-2181](https://youtrack.jetbrains.com/issue/VIM-2181)
### Fixes:
* [VIM-2113](https://youtrack.jetbrains.com/issue/VIM-2113) Fix `cit` for empty tags
* [VIM-2114](https://youtrack.jetbrains.com/issue/VIM-2114) Unnamed register isn't changed after deleting empty tag
* [VIM-1475](https://youtrack.jetbrains.com/issue/VIM-1475) Enable block caret to be used in insert mode.
* [VIM-2170](https://youtrack.jetbrains.com/issue/VIM-2170) Fix an alternative range format for `s` command
* [VIM-1913](https://youtrack.jetbrains.com/issue/VIM-1913)
[VIM-2154](https://youtrack.jetbrains.com/issue/VIM-2154) Several fixes for AppCode templates
* [VIM-1756](https://youtrack.jetbrains.com/issue/VIM-1756) Fix startsel from insert mode
### Merged PRs:
* [249](https://github.com/JetBrains/ideavim/pull/249) by [Jan Palus](https://github.com/jpalus): VIM-2113 Increase tag range only in visual mode
* [250](https://github.com/JetBrains/ideavim/pull/250) by [Jan Palus](https://github.com/jpalus): VIM-2114 Do not override registers when deleting empty range
* [256](https://github.com/JetBrains/ideavim/pull/256) by [Brandon Conway](https://github.com/brandoncc): Fix typo
* [254](https://github.com/JetBrains/ideavim/pull/254) by [Grzegorz Antoniak](https://github.com/antekone): VIM-1475: Add an option to use block caret in insert mode
* [225](https://github.com/JetBrains/ideavim/pull/225) by [sumoooru2](https://github.com/sumoooru2): Implement cmap
* [258](https://github.com/JetBrains/ideavim/pull/258) by [Matt Ellis](https://github.com/citizenmatt): Show the correct handler class in :map
* [257](https://github.com/JetBrains/ideavim/pull/257) by [Matt Ellis](https://github.com/citizenmatt): Extract SearchHighlightsHelper from SearchGroup
* [251](https://github.com/JetBrains/ideavim/pull/251) by [Shaun Patterson](https://github.com/shaunpatterson): VIM-1756: startSel works in insert mode
## 0.61, 2020-11-12
### Features: ### Features:
* Ability to map IDE actions via the `<Action>` keyword. E.g. `map <C-K> <Action>(CommentByLineComment)`. * Ability to map IDE actions via the `<Action>` keyword. E.g. `map <C-K> <Action>(CommentByLineComment)`.
@@ -44,14 +72,18 @@ usual beta standards.
### Changes: ### Changes:
* Fix `<Esc>` for dialogs. Now `<Esc>` will exit insert / visual mode and close the dialog from normal mode. * Fix `<Esc>` for dialogs. Now `<Esc>` will exit insert / visual mode and close the dialog from normal mode.
* Add option to disable IdeaVim in dialogs / single line editors. [VIM-765](https://youtrack.jetbrains.com/issue/VIM-765) * Add option to disable IdeaVim in dialogs / single line editors. [VIM-765](https://youtrack.jetbrains.com/issue/VIM-765)
Use `set ideaenabledbufs=` to disable IdeaVim in dialog editors. Use `set ideavimsupport=` to disable IdeaVim in dialog editors.
_Note for EAP users: the option name can be changed for the stable release_ * Reposition cursor when `scrolloff` changes
### Fixes: ### Fixes:
* [VIM-2150](https://youtrack.jetbrains.com/issue/VIM-2150) `Shift-D` should not delete an empty line * [VIM-2150](https://youtrack.jetbrains.com/issue/VIM-2150) `Shift-D` should not delete an empty line
* [VIM-2157](https://youtrack.jetbrains.com/issue/VIM-2157) Fix tab with an active template * [VIM-2157](https://youtrack.jetbrains.com/issue/VIM-2157) Fix tab with an active template
* [VIM-2156](https://youtrack.jetbrains.com/issue/VIM-2156) Correct up/down motions with inlays * [VIM-2156](https://youtrack.jetbrains.com/issue/VIM-2156) Correct up/down motions with inlays
* [VIM-2144](https://youtrack.jetbrains.com/issue/VIM-2144) Correct text position after block insert with inlays * [VIM-2144](https://youtrack.jetbrains.com/issue/VIM-2144) Correct text position after block insert with inlays
* [VIM-2158](https://youtrack.jetbrains.com/issue/VIM-2158) Fix scrolling when `scrolloff` is over half screen height, but less than full height
### Merged PRs:
* [255](https://github.com/JetBrains/ideavim/pull/255) by [Matt Ellis](https://github.com/citizenmatt): Fix various scrolling issues
## 0.60, 2020-10-09 ## 0.60, 2020-10-09

105
README.md
View File

@@ -11,7 +11,7 @@ IdeaVim
[![Gitter][gitter-svg]][gitter] [![Gitter][gitter-svg]][gitter]
[![Twitter][twitter-svg]][twitter] [![Twitter][twitter-svg]][twitter]
IdeaVim is a Vim emulation plugin for IDEs based on the IntelliJ Platform. IdeaVim is a Vim emulation plugin for IntelliJ Platform-based IDEs.
##### Contact maintainers: ##### Contact maintainers:
* [Bug tracker](https://youtrack.jetbrains.com/issues/VIM) * [Bug tracker](https://youtrack.jetbrains.com/issues/VIM)
@@ -144,28 +144,45 @@ set idearefactormode=keep
map <leader>f <Plug>(easymotion-s) map <leader>f <Plug>(easymotion-s)
map <leader>e <Plug>(easymotion-f) map <leader>e <Plug>(easymotion-f)
map <leader>d :action Debug<CR> map <leader>d <Action>(Debug)
map <leader>r :action RenameElement<CR> map <leader>r <Action>(RenameElement)
map <leader>c :action Stop<CR> map <leader>c <Action>(Stop)
map <leader>z :action ToggleDistractionFreeMode<CR> map <leader>z <Action>(ToggleDistractionFreeMode)
map <leader>s :action SelectInProjectView<CR> map <leader>s <Action>(SelectInProjectView)
map <leader>a :action Annotate<CR> map <leader>a <Action>(Annotate)
map <leader>h :action Vcs.ShowTabbedFileHistory<CR> map <leader>h <Action>(Vcs.ShowTabbedFileHistory)
map <S-Space> :action GotoNextError<CR> map <S-Space> <Action>(GotoNextError)
map <leader>b :action ToggleLineBreakpoint<CR> map <leader>b <Action>(ToggleLineBreakpoint)
map <leader>o :action FileStructurePopup<CR> map <leader>o <Action>(FileStructurePopup)
``` ```
</details> </details>
<details>
<summary><strong>Suggested options</strong> (click to see)</summary>
Here is also a list of the suggested options from [defaults.vim](https://github.com/vim/vim/blob/master/runtime/defaults.vim)
```vim
" Show a few lines of context around the cursor. Note that this makes the
" text scroll if you mouse-click near the start or end of the window.
set scrolloff=5
" Do incremental searching
set incsearch
" Don't use Ex mode, use Q for formatting.
map Q gq
```
</details>
You can read your `~/.vimrc` file from `~/.ideavimrc` with this command: You can read your `~/.vimrc` file from `~/.ideavimrc` with this command:
source ~/.vimrc source ~/.vimrc
Please note that IdeaVim currently parses `~/.ideavimrc` & `~/.vimrc` files via simple pattern-matching. > :warning: Please note that IdeaVim currently parses `~/.ideavimrc` & `~/.vimrc` files via simple pattern-matching.
See [VIM-669](https://youtrack.jetbrains.com/issue/VIM-669) for proper parsing See [VIM-669](https://youtrack.jetbrains.com/issue/VIM-669) for proper parsing
of VimL files. of VimL files.
@@ -183,67 +200,43 @@ Emulated Vim Plugins
See [doc/emulated-plugins.md](doc/emulated-plugins.md) See [doc/emulated-plugins.md](doc/emulated-plugins.md)
Changes to the IDE Executing IDE Actions
------------------ ---------------------
### Executing IDE Actions IdeaVim adds various commands for listing and executing arbitrary IDE actions as
IdeaVim adds two commands for listing and executing arbitrary IDE actions as
Ex commands or via `:map` command mappings: Ex commands or via `:map` command mappings:
**Executing actions:**
* `:action {action_id}`
* Execute an action by id. Works from Ex command line.
* `<Action>(*action_id*)`
* For the mappings you can use a special `<Action>` keyword. Don't forget the parentheses.
**Finding actions:**
* `:actionlist [pattern]` * `:actionlist [pattern]`
* Find IDE actions by name or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`) * Find IDE actions by id or keymap pattern (E.g. `:actionlist extract`, `:actionlist <C-D`)
* `:action {name}`
* Execute an action named `NAME`
In addition to `:actionlist` command, IdeaVim provides `IdeaVim: track action Ids` option to * In addition to `:actionlist` command, IdeaVim provides `IdeaVim: track action Ids` option to
extract the ids of executed command. This option can be found in "Search everywhere" (double `shift`). **[To Be Released]** extract the ids of executed command. This option can be found in "Search everywhere" (double `shift`).
<details> <details>
<summary><strong>"Track aciton Ids" Details</strong> (click to see)</summary> <summary><strong>"Track action Ids" Details</strong> (click to see)</summary>
<img src="resources/readme/track_action_id.gif" alt="track actioin ids"/> <img src="resources/readme/track_action_id.gif" alt="track action ids"/>
</details> </details>
For the mappings you can use a special `<Action>` keyword. Use `<Action>(*action_id*)` to map keys to some action. Don't
forget the parentheses. This keyword works for insert mode as well. **[To Be Released]**
Examples: Examples:
```vim ```vim
" Map \r to the Reformat Code action " Map \r to the Reformat Code action
:map \r :action ReformatCode<CR> :map \r <Action>(ReformatCode)
" or
:map \r <Action>(ReformatCode) " [To Be Released]
" Map <leader>d to start debug " Map <leader>d to start debug
:map <leader>d :action Debug<CR> :map <leader>d <Action>(Debug)
" or
:map <leader>d <Action>(Debug) " [To Be Released]
" Map \b to toggle the breakpoint on the current line " Map \b to toggle the breakpoint on the current line
:map \b :action ToggleLineBreakpoint<CR> :map \b <Action>(ToggleLineBreakpoint)
" or
:map \b <Action>(ToggleLineBreakpoint) " [To Be Released]
``` ```
### Undo/Redo
The IdeaVim plugin uses the undo/redo functionality of the IntelliJ Platform,
so the behavior of the `u` and `<C-R>` commands may differ from the original
Vim. Vim compatibility of undo/redo may be improved in future releases.
See also [unresolved undo issues](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+Help+topic%3A+u).
### Escape
Using `<Esc>` in dialog windows remains problematic. For most dialog windows,
the Vim emulator is put into insert mode with `<Esc>` not working. You
should use `<C-c>` or `<C-[>` instead. In some dialog windows, the normal mode is
switched by default. The usage of the Vim emulator in dialog windows is an area for
improvement.
See also [unresolved escape issues](https://youtrack.jetbrains.com/issues/VIM?q=%23Unresolved+Help+topic%3A+i_Esc).
:gem: Contributing :gem: Contributing
------------ ------------

View File

@@ -13,7 +13,7 @@ buildscript {
} }
plugins { plugins {
id 'org.jetbrains.intellij' version '0.6.1' id 'org.jetbrains.intellij' version '0.6.5'
id 'io.gitlab.arturbosch.detekt' version '1.14.1' id 'io.gitlab.arturbosch.detekt' version '1.14.1'
id "org.jetbrains.changelog" version "0.6.2" id "org.jetbrains.changelog" version "0.6.2"
} }
@@ -45,6 +45,8 @@ intellij {
intellijRepo = "https://www.jetbrains.com/intellij-repository" intellijRepo = "https://www.jetbrains.com/intellij-repository"
plugins = ['java'] plugins = ['java']
downloadRobotServerPlugin.version = "0.10.0"
publishPlugin { publishPlugin {
channels publishChannels.split(',') channels publishChannels.split(',')
username publishUsername username publishUsername
@@ -52,6 +54,10 @@ intellij {
} }
} }
runIdeForUiTests {
systemProperty "robot-server.port", "8082"
}
runPluginVerifier { runPluginVerifier {
ideVersions = ["IC-2020.1.4", "IC-2020.2.3"] ideVersions = ["IC-2020.1.4", "IC-2020.2.3"]
downloadDirectory = "${project.buildDir}/pluginVerifier/ides" downloadDirectory = "${project.buildDir}/pluginVerifier/ides"
@@ -61,14 +67,18 @@ runPluginVerifier {
repositories { repositories {
mavenCentral() mavenCentral()
jcenter() jcenter()
maven { url = "https://jetbrains.bintray.com/intellij-third-party-dependencies" }
} }
dependencies { dependencies {
compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" compileOnly "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
compileOnly "org.jetbrains:annotations:19.0.0" compileOnly "org.jetbrains:annotations:20.1.0"
// https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api // https://mvnrepository.com/artifact/com.ensarsarajcic.neovim.java/neovim-api
compile group: 'com.ensarsarajcic.neovim.java', name: 'neovim-api', version: '0.1.16' testImplementation("com.ensarsarajcic.neovim.java:neovim-api:0.1.16")
testImplementation("com.intellij.remoterobot:remote-robot:0.10.0")
testImplementation("com.intellij.remoterobot:remote-fixtures:1.1.18")
} }
compileKotlin { compileKotlin {
@@ -113,6 +123,14 @@ changelog {
// version = "0.60" // version = "0.60"
} }
task getUnreleasedChangelog() {
group = "changelog"
doLast {
def log = changelog.getUnreleased().toHTML()
println log
}
}
tasks.register("slackEapNotification") { tasks.register("slackEapNotification") {
doLast { doLast {
if (!slackUrl) return if (!slackUrl) return

View File

@@ -4,7 +4,15 @@ Emulated Vim Plugins
IdeaVim extensions emulate plugins of the original Vim. In order to use IdeaVim extensions emulate plugins of the original Vim. In order to use
IdeaVim extensions, you have to enable them via this command in your `~/.ideavimrc`: IdeaVim extensions, you have to enable them via this command in your `~/.ideavimrc`:
set <extension-name> ```
set <extension-name>
```
If you reuse your existing `.vimrc` file using `source ~/.vimrc`, IdeaVim can parse and enable plugins that are defined
using [vim-plug](https://github.com/junegunn/vim-plug) or [vundle](https://github.com/VundleVim/Vundle.vim).
No additional set commands in `~/.ideavimrc` are required.
If you'd like to disable some plugin that's enabled in `.vimrc`, you can use `set no<extension-name>`
in `~/.ideavimrc`.
Available extensions: Available extensions:
@@ -20,18 +28,52 @@ Available extensions:
## surround ## surround
* Setup: `set surround` * Setup: `set surround`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tpope/vim-surround'</code>
<br/>
<code>Plug 'tpope/vim-surround'</code>
<br/>
<code>Plug 'vim-surround'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1697'</code>
</details>
* Emulates [vim-surround](https://github.com/tpope/vim-surround) * Emulates [vim-surround](https://github.com/tpope/vim-surround)
* Commands: `ys`, `cs`, `ds`, `S` * Commands: `ys`, `cs`, `ds`, `S`
## multiple-cursors ## multiple-cursors
* Setup: `set multiple-cursors` * Setup: `set multiple-cursors`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/terryma/vim-multiple-cursors'</code>
<br/>
<code>Plug 'terryma/vim-multiple-cursors'</code>
<br/>
<code>Plug 'vim-multiple-cursors'</code>
</details>
* Emulates [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors) * Emulates [vim-multiple-cursors](https://github.com/terryma/vim-multiple-cursors)
* Commands: `<A-n>`, `<A-x>`, `<A-p>`, `g<A-n>` * Commands: `<A-n>`, `<A-x>`, `<A-p>`, `g<A-n>`
## commentary ## commentary
* Setup: `set commentary` * Setup: `set commentary`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tpope/vim-commentary'</code>
<br/>
<code>Plug 'tpope/vim-commentary'</code>
<br/>
<code>Plug 'vim-commentary'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=3695'</code>
<br/>
<code>Plug 'tomtom/tcomment_vim'</code>
<br/>
<code>Plug 'tcomment_vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=1173'</code>
</details>
* Emulates [commentary.vim](https://github.com/tpope/vim-commentary) * Emulates [commentary.vim](https://github.com/tpope/vim-commentary)
* Commands: `gcc`, `gc + motion`, `v_gc` * Commands: `gcc`, `gc + motion`, `v_gc`
* By [Daniel Leong](https://github.com/dhleong) * By [Daniel Leong](https://github.com/dhleong)
@@ -39,6 +81,22 @@ Available extensions:
## ReplaceWithRegister ## ReplaceWithRegister
* Setup: `set ReplaceWithRegister` * Setup: `set ReplaceWithRegister`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/vim-scripts/ReplaceWithRegister'</code>
<br/>
<code>Plug 'vim-scripts/ReplaceWithRegister'</code>
<br/>
<code>Plug 'ReplaceWithRegister'</code>
<br/>
<code>Plug 'https://github.com/inkarkat/vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'inkarkat/vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'vim-ReplaceWithRegister'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2703'</code>
</details>
* Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister) * Emulates [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister)
* Commands: `gr`, `grr` * Commands: `gr`, `grr`
* By [igrekster](https://github.com/igrekster) * By [igrekster](https://github.com/igrekster)
@@ -47,6 +105,16 @@ Available extensions:
* Setup: * Setup:
* `set argtextobj` * `set argtextobj`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'vim-scripts/argtextobj.vim'</code>
<br/>
<code>Plug 'argtextobj.vim'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2699'</code>
</details>
* By default, only the arguments inside parenthesis are considered. To extend the functionality * By default, only the arguments inside parenthesis are considered. To extend the functionality
to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated to other types of brackets, set `g:argtextobj_pairs` variable to a comma-separated
list of colon-separated pairs (same as VIM's `matchpairs` option), like list of colon-separated pairs (same as VIM's `matchpairs` option), like
@@ -59,6 +127,14 @@ Available extensions:
## exchange ## exchange
* Setup: `set exchange` * Setup: `set exchange`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'tommcdo/vim-exchange'</code>
<br/>
<code>Plug 'vim-exchange'</code>
</details>
* Emulates [vim-exchange](https://github.com/tommcdo/vim-exchange) * Emulates [vim-exchange](https://github.com/tommcdo/vim-exchange)
* Commands: `cx`, `cxx`, `X`, `cxc` * Commands: `cx`, `cxx`, `X`, `cxc`
* By [fan-tom](https://github.com/fan-tom) * By [fan-tom](https://github.com/fan-tom)
@@ -66,6 +142,16 @@ Available extensions:
## textobj-entire ## textobj-entire
* Setup: `set textobj-entire` * Setup: `set textobj-entire`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/kana/vim-textobj-entire'</code>
<br/>
<code>Plug 'kana/vim-textobj-entire'</code>
<br/>
<code>Plug 'vim-textobj-entire'</code>
<br/>
<code>Plug 'https://www.vim.org/scripts/script.php?script_id=2610'</code>
</details>
* Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire) * Emulates [vim-textobj-entire](https://github.com/kana/vim-textobj-entire)
* Additional text objects: `ae`, `ie` * Additional text objects: `ae`, `ie`
* By [Alexandre Grison](https://github.com/agrison) * By [Alexandre Grison](https://github.com/agrison)
@@ -74,6 +160,14 @@ Available extensions:
* Setup: * Setup:
* `set highlightedyank` * `set highlightedyank`
* <details>
<summary>Alternative vim-plug / vundle syntax</summary>
<code>Plug 'https://github.com/machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'machakann/vim-highlightedyank'</code>
<br/>
<code>Plug 'vim-highlightedyank'</code>
</details>
* if you want to optimize highlight duration, assign a time in milliseconds: * if you want to optimize highlight duration, assign a time in milliseconds:
`let g:highlightedyank_highlight_duration = "1000"` `let g:highlightedyank_highlight_duration = "1000"`
A negative number makes the highlight persistent. A negative number makes the highlight persistent.

View File

@@ -125,7 +125,7 @@ The following `:set` commands can appear in `~/.ideavimrc` or be set manually in
"<C-Down>", "<C-Up>", "<PageUp>", "<PageDown>", "<C-Down>", "<C-Up>", "<PageUp>", "<PageDown>",
"<C-J>", "<C-Q>" "<C-J>", "<C-Q>"
`ideaenabledbufs` `ideaenabledbufs` List of strings (default "dialog") [To Be Released] `ideavimsupport` `ideavimsupport` List of strings (default "dialog")
Define the list of additional buffers where IdeaVim is enabled. Define the list of additional buffers where IdeaVim is enabled.

View File

@@ -28,9 +28,9 @@
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightMatchCharAction" mappingModes="NXO" keys="f"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightMatchCharAction" mappingModes="NXO" keys="f"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightTillMatchCharAction" mappingModes="NXO" keys="t"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionRightTillMatchCharAction" mappingModes="NXO" keys="t"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionHomeAction" mappingModes="NV" keys="«Home»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionHomeAction" mappingModes="NV" keys="«Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftHomeAction" mappingModes="NV" keys="«S-Home»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftHomeAction" mappingModes="INV" keys="«S-Home»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionEndAction" mappingModes="NVO" keys="«End»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionEndAction" mappingModes="NVO" keys="«End»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftEndAction" mappingModes="NV" keys="«S-End»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftEndAction" mappingModes="INV" keys="«S-End»"/>
<!-- Up/Down --> <!-- Up/Down -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownAction" mappingModes="NXO" keys="j"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownAction" mappingModes="NXO" keys="j"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownCtrlNAction" mappingModes="NXO" keys="«C-N»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionDownCtrlNAction" mappingModes="NXO" keys="«C-N»"/>
@@ -152,10 +152,10 @@
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction" mappingModes="NXO" keys="zh,z«Left»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollColumnRightAction" mappingModes="NXO" keys="zh,z«Left»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthLeftAction" mappingModes="NXO" keys="zL"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthLeftAction" mappingModes="NXO" keys="zL"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthRightAction" mappingModes="NXO" keys="zH"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.scroll.MotionScrollHalfWidthRightAction" mappingModes="NXO" keys="zH"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction" mappingModes="NV" keys="«S-Down»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftDownAction" mappingModes="INV" keys="«S-Down»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction" mappingModes="NV" keys="«S-Up»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.updown.MotionShiftUpAction" mappingModes="INV" keys="«S-Up»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction" mappingModes="NV" keys="«S-Right»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftRightAction" mappingModes="INV" keys="«S-Right»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction" mappingModes="NV" keys="«S-Left»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.leftright.MotionShiftLeftAction" mappingModes="INV" keys="«S-Left»"/>
<!-- Visual --> <!-- Visual -->
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction" mappingModes="X" keys="«Esc»,«C-[»,«C-C»"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualExitModeAction" mappingModes="X" keys="«Esc»,«C-[»,«C-C»"/>
<vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualToggleCharacterModeAction" mappingModes="NX" keys="v"/> <vimAction implementation="com.maddyhome.idea.vim.action.motion.visual.VisualToggleCharacterModeAction" mappingModes="NX" keys="v"/>

View File

@@ -25,7 +25,9 @@
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.JoinLinesHandler" names="j[oin]"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.JoinLinesHandler" names="j[oin]"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.JumpsHandler" names="ju[mps]"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.JumpsHandler" names="ju[mps]"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.LetHandler" names="let"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.LetHandler" names="let"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MapHandler"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.mapping.MapHandler"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.mapping.UnMapHandler"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.mapping.MapClearHandler"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MarkHandler" names="ma[rk],k"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MarkHandler" names="ma[rk],k"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MarksHandler" names="marks"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MarksHandler" names="marks"/>
<vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MoveTextHandler" names="m[ove]"/> <vimExCommand implementation="com.maddyhome.idea.vim.ex.handler.MoveTextHandler" names="m[ove]"/>

View File

@@ -1,6 +1,6 @@
<idea-plugin> <idea-plugin>
<applicationListeners> <applicationListeners>
<listener class="com.maddyhome.idea.vim.ui.ExEntryPanel$LafListener" <listener class="com.maddyhome.idea.vim.ui.ex.ExEntryPanel$LafListener"
topic="com.intellij.ide.ui.LafManagerListener"/> topic="com.intellij.ide.ui.LafManagerListener"/>
<listener class="com.maddyhome.idea.vim.extension.highlightedyank.HighlightColorResetter" <listener class="com.maddyhome.idea.vim.extension.highlightedyank.HighlightColorResetter"
topic="com.intellij.ide.ui.LafManagerListener"/> topic="com.intellij.ide.ui.LafManagerListener"/>

View File

@@ -2,18 +2,72 @@
<name>IdeaVim</name> <name>IdeaVim</name>
<id>IdeaVIM</id> <id>IdeaVIM</id>
<change-notes><![CDATA[ <change-notes><![CDATA[
<ul> <h3>Features:</h3>
<li>Fix ESC in dialogs</li> <ul>
<li>Add option to disable IdeaVim in dialogs and single line editors</li> <li>Support <code>unmap</code> and <code>mapclear</code> commands <a
<li>Ability to map IDE actions via the &lt;Action&gt; keyword.</li> href="https://youtrack.jetbrains.com/issue/VIM-1491">VIM-1491</a></li>
<li>"IdeaVim: track action Ids" command to find action ids for the :action command. <li>Support mappings in ex panel (<code>cmap</code>) <a
<li>Add new option to register IdeaVim extensions. Please read the changelog for details</li> href="https://youtrack.jetbrains.com/issue/VIM-1227">VIM-1227</a></li>
</ul> </ul>
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
]]></change-notes> <h3>Changes:</h3>
<ul>
<li><code>octal</code> is now disabled by default for <code>nrformats</code>. <a
href="https://youtrack.jetbrains.com/issue/VIM-2181">VIM-2181</a></li>
</ul>
<h3>Fixes:</h3>
<ul>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-2113">VIM-2113</a> Fix <code>cit</code> for empty tags</li>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-2114">VIM-2114</a> Unnamed register isn't changed after deleting
empty tag
</li>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-1475">VIM-1475</a> Enable block caret to be used in insert mode.
</li>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-2170">VIM-2170</a> Fix an alternative range format for
<code>s</code> command
</li>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-1913">VIM-1913</a>
<a href="https://youtrack.jetbrains.com/issue/VIM-2154">VIM-2154</a> Several fixes for AppCode templates
</li>
<li><a href="https://youtrack.jetbrains.com/issue/VIM-1756">VIM-1756</a> Fix startsel from insert mode</li>
</ul>
<h3>Merged PRs:</h3>
<ul>
<li><a href="https://github.com/JetBrains/ideavim/pull/249">249</a> by <a href="https://github.com/jpalus">Jan
Palus</a>: VIM-2113 Increase tag range only in visual mode
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/250">250</a> by <a href="https://github.com/jpalus">Jan
Palus</a>: VIM-2114 Do not override registers when deleting empty range
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/256">256</a> by <a href="https://github.com/brandoncc">Brandon
Conway</a>: Fix typo
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/254">254</a> by <a href="https://github.com/antekone">Grzegorz
Antoniak</a>: VIM-1475: Add an option to use block caret in insert mode
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/225">225</a> by <a href="https://github.com/sumoooru2">sumoooru2</a>:
Implement cmap
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/258">258</a> by <a href="https://github.com/citizenmatt">Matt
Ellis</a>: Show the correct handler class in :map
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/257">257</a> by <a href="https://github.com/citizenmatt">Matt
Ellis</a>: Extract SearchHighlightsHelper from SearchGroup
</li>
<li><a href="https://github.com/JetBrains/ideavim/pull/251">251</a> by <a href="https://github.com/shaunpatterson">Shaun
Patterson</a>: VIM-1756: startSel works in insert mode
</li>
</ul>
]]>
</change-notes>
<description><![CDATA[ <description><![CDATA[
<p>Vim emulation plug-in for IDEs based on the IntelliJ platform.</p> <p>Vim emulation plugin for IntelliJ Platform-based IDEs.</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> <br/>
<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, Vim plugins, etc.</p>
<br/>
<p>See also:</p> <p>See also:</p>
<ul> <ul>
<li><a href="https://github.com/JetBrains/ideavim">GitHub repository</a>: documentation and contributing</li> <li><a href="https://github.com/JetBrains/ideavim">GitHub repository</a>: documentation and contributing</li>
@@ -25,11 +79,11 @@
<!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version --> <!-- Please search for "[VERSION UPDATE]" in project in case you update the since-build version -->
<!-- Check for [Version Update] tag in YouTrack as well --> <!-- Check for [Version Update] tag in YouTrack as well -->
<idea-version since-build="201.5985.32"/> <idea-version since-build="201.5985.41"/>
<!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform --> <!-- Mark the plugin as compatible with RubyMine and other products based on the IntelliJ platform -->
<depends>com.intellij.modules.lang</depends> <depends>com.intellij.modules.lang</depends>
<resource-bundle xmlns="">messages</resource-bundle> <resource-bundle>messages.IdeaVimBundle</resource-bundle>
<application-components> <application-components>
<component> <component>
@@ -59,7 +113,6 @@
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/> <statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.StatusBarIconFactory"/>
<statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/> <statusBarWidgetFactory implementation="com.maddyhome.idea.vim.ui.ShowCmdStatusBarWidgetFactory" order="first"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.config.VimLocalConfig"/>
<applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/> <applicationService serviceImplementation="com.maddyhome.idea.vim.VimPlugin"/>
<!-- Initialise as early as possible so that we're ready to edit quickly. This is especially important for Rider, <!-- Initialise as early as possible so that we're ready to edit quickly. This is especially important for Rider,
@@ -76,7 +129,7 @@
<xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimExtensions.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/includes/VimListeners.xml" xpointer="xpointer(/idea-plugin/*)"/>
<actions> <actions resource-bundle="messages.IdeaVimBundle">
<action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction"> <action id="VimPluginToggle" class="com.maddyhome.idea.vim.action.VimPluginToggleAction">
<add-to-group group-id="ToolsMenu" anchor="last"/> <add-to-group group-id="ToolsMenu" anchor="last"/>
</action> </action>
@@ -102,8 +155,6 @@
</action> </action>
</group> </group>
<action id="VimFindActionIdAction" <action id="VimFindActionIdAction" class="com.maddyhome.idea.vim.listener.FindActionIdAction"/>
class="com.maddyhome.idea.vim.listener.FindActionIdAction" text="IdeaVim: Track Action Ids"
description="Starts tracking ids of executed actions"/>
</actions> </actions>
</idea-plugin> </idea-plugin>

View File

@@ -72,8 +72,54 @@ E475=E475: Invalid argument: {0}
E774=E774: 'operatorfunc' is empty E774=E774: 'operatorfunc' is empty
action.VimPluginToggle.text=Vim Emulator action.VimPluginToggle.text=Vim Emulator
description.VimPluginToggle.description=Toggle the vim plugin On/off action.VimPluginToggle.description=Toggle the vim plugin On/Off
action.VimPluginToggle.enabled=Enabled
action.VimPluginToggle.enable=Enable
action.VimShortcutKeyAction.text=Shortcuts action.VimShortcutKeyAction.text=Shortcuts
action.VimActions.text=Vim Actions action.VimActions.text=Vim Actions
action.not.found.0=Action not found: {0}
action.VimFindActionIdAction.text=IdeaVim: Track Action Ids
action.VimFindActionIdAction.description=Starts tracking ids of executed actions
ex.show.all.actions.0.1=--- Actions ---{0}{1}
e471.argument.required=E471: Argument required
buffer.0.does.not.exist=Buffer {0} does not exist
no.matching.buffer.for.0=No matching buffer for {0}
no.write.since.last.change.add.to.override=No write since last change (add ! to override)
more.than.one.match.for.0=More than one match for {0}
e176.invalid.number.of.arguments=E176: Invalid number of arguments
e183.user.defined.commands.must.start.with.an.uppercase.letter=E183: User defined commands must start with an uppercase letter
e841.reserved.name.cannot.be.used.for.user.defined.command=E841: Reserved name, cannot be used for user defined command
e174.command.already.exists.add.to.replace.it=E174: Command already exists: add ! to replace it
recursion.detected.maximum.alias.depth.reached=Recursion detected, maximum alias depth reached.
show.mode.recording=recording
e184.no.such.user.defined.command.0=E184: No such user-defined command: {0}
unable.to.find.0=Unable to find {0}
more.ret.line.space.page.d.half.page.q.quit=-- MORE -- (RET: line, SPACE: page, d: half page, q: quit)
hit.enter.or.type.command.to.continue=Hit ENTER or type command to continue
ex.output.panel.more=-- MORE --
command.name.vim.macro.playback=Vim Macro Playback
action.no.changes.text=No Changes
action.reload.text=Reload
replace.with.0=Replace with {0} (y/n/a/q/l)?
error.invalid.command.argument=Error: invalid command argument
argtextobj.invalid.value.of.g.argtextobj.pairs.0=argtextobj: Invalid value of g:argtextobj_pairs -- {0}
highlightedyank.invalid.value.of.0.1=highlightedyank: Invalid value of {0} -- {1}
could.not.find.file.0=Could not find file: {0}
popup.advertisement.version=Version {0}
action.eap.choice.active.text=EAP{0, choice, 0# (Active)|1#}
action.about.eap.text=About EAP...
action.contacts.help.text=Contacts && Help
action.contact.on.twitter.text=Contact on Twitter
action.create.issue.text=Create an Issue
action.contribute.on.github.text=Contribute on GitHub
action.settings.text=Settings...
action.finish.eap.text=Finish EAP
action.subscribe.to.eap.text=Subscribe to EAP
configurable.name.vim.emulation=Vim Emulation
border.title.shortcut.conflicts.for.active.keymap=Shortcut Conflicts for Active Keymap
message.no.more.matches=No more matches

View File

@@ -140,6 +140,6 @@ public class EventFacade {
} }
private @NotNull TypedAction getTypedAction() { private @NotNull TypedAction getTypedAction() {
return EditorActionManager.getInstance().getTypedAction(); return TypedAction.getInstance();
} }
} }

View File

@@ -26,10 +26,8 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.UndoConfirmationPolicy;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actionSystem.ActionPlan; import com.intellij.openapi.editor.actionSystem.ActionPlan;
import com.intellij.openapi.editor.actionSystem.CaretSpecificDataContext;
import com.intellij.openapi.editor.actionSystem.DocCommandGroupId; import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
import com.intellij.openapi.editor.actionSystem.TypedActionHandler; import com.intellij.openapi.editor.actionSystem.TypedActionHandler;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
@@ -40,17 +38,14 @@ import com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction;
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction; import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction;
import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction; import com.maddyhome.idea.vim.action.macro.ToggleRecordingAction;
import com.maddyhome.idea.vim.command.*; import com.maddyhome.idea.vim.command.*;
import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.group.ChangeGroup; import com.maddyhome.idea.vim.group.ChangeGroup;
import com.maddyhome.idea.vim.group.RegisterGroup; import com.maddyhome.idea.vim.group.RegisterGroup;
import com.maddyhome.idea.vim.group.visual.VimSelection;
import com.maddyhome.idea.vim.group.visual.VisualGroupKt; import com.maddyhome.idea.vim.group.visual.VisualGroupKt;
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase; import com.maddyhome.idea.vim.handler.EditorActionHandlerBase;
import com.maddyhome.idea.vim.helper.*; import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.key.*; import com.maddyhome.idea.vim.key.*;
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.ui.ShowCmd; import com.maddyhome.idea.vim.ui.ShowCmd;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NonNls;
@@ -59,13 +54,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.intellij.openapi.actionSystem.CommonDataKeys.*; import static com.intellij.openapi.actionSystem.CommonDataKeys.*;
import static com.intellij.openapi.actionSystem.PlatformDataKeys.PROJECT_FILE_DIRECTORY; import static com.intellij.openapi.actionSystem.PlatformDataKeys.PROJECT_FILE_DIRECTORY;
@@ -599,6 +593,22 @@ public class KeyHandler {
} }
DigraphResult res = editorState.processDigraphKey(key, editor); DigraphResult res = editorState.processDigraphKey(key, editor);
if (ExEntryPanel.getInstance().isActive()) {
switch (res.getResult()) {
case DigraphResult.RES_HANDLED:
setPromptCharacterEx(commandBuilder.isPuttingLiteral() ? '^' : key.getKeyChar());
break;
case DigraphResult.RES_DONE:
case DigraphResult.RES_BAD:
if (key.getKeyCode() == KeyEvent.VK_C && (key.getModifiers() & InputEvent.CTRL_DOWN_MASK) != 0) {
return false;
} else {
ExEntryPanel.getInstance().getEntry().clearCurrentAction();
}
break;
}
}
switch (res.getResult()) { switch (res.getResult()) {
case DigraphResult.RES_HANDLED: case DigraphResult.RES_HANDLED:
editorState.getCommandBuilder().addKey(key); editorState.getCommandBuilder().addKey(key);
@@ -753,8 +763,10 @@ public class KeyHandler {
// the key handler when it's complete. // the key handler when it's complete.
if (action instanceof InsertCompletedDigraphAction) { if (action instanceof InsertCompletedDigraphAction) {
editorState.startDigraphSequence(); editorState.startDigraphSequence();
setPromptCharacterEx('?');
} else if (action instanceof InsertCompletedLiteralAction) { } else if (action instanceof InsertCompletedLiteralAction) {
editorState.startLiteralSequence(); editorState.startLiteralSequence();
setPromptCharacterEx('^');
} }
break; break;
case EX_STRING: case EX_STRING:
@@ -777,7 +789,7 @@ public class KeyHandler {
* @param name The name of the action to execute * @param name The name of the action to execute
* @param context The context to run it in * @param context The context to run it in
*/ */
public static boolean executeAction(@NotNull String name, @NotNull DataContext context) { public static boolean executeAction(@NotNull @NonNls String name, @NotNull DataContext context) {
ActionManager aMgr = ActionManager.getInstance(); ActionManager aMgr = ActionManager.getInstance();
AnAction action = aMgr.getAction(name); AnAction action = aMgr.getAction(name);
return action != null && executeAction(action, context); return action != null && executeAction(action, context);
@@ -849,6 +861,13 @@ public class KeyHandler {
} }
private void setPromptCharacterEx(final char promptCharacter) {
final ExEntryPanel exEntryPanel = ExEntryPanel.getInstance();
if (exEntryPanel.isActive()) {
exEntryPanel.getEntry().setCurrentActionPromptCharacter(promptCharacter);
}
}
// This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext // This class is copied from com.intellij.openapi.editor.actionSystem.DialogAwareDataContext.DialogAwareDataContext
private static final class DialogAwareDataContext implements DataContext { private static final class DialogAwareDataContext implements DataContext {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@@ -915,9 +934,12 @@ public class KeyHandler {
if (editorState.getSubMode() == CommandState.SubMode.SINGLE_COMMAND && if (editorState.getSubMode() == CommandState.SubMode.SINGLE_COMMAND &&
(!cmd.getFlags().contains(CommandFlags.FLAG_EXPECT_MORE))) { (!cmd.getFlags().contains(CommandFlags.FLAG_EXPECT_MORE))) {
editorState.popModes(); editorState.popModes();
VisualGroupKt.resetShape(CommandStateHelper.getMode(editor), editor);
} }
KeyHandler.getInstance().reset(editor); if (editorState.getCommandBuilder().isDone()) {
KeyHandler.getInstance().reset(editor);
}
} }
private final Editor editor; private final Editor editor;

View File

@@ -19,12 +19,16 @@ package com.maddyhome.idea.vim;
import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.IdeaPluginDescriptor;
import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.PluginManager;
import com.intellij.ide.plugins.PluginManagerCore;
import com.intellij.notification.Notification; import com.intellij.notification.Notification;
import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationListener;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application; import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*; import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.extensions.PluginId;
import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.Keymap;
@@ -37,7 +41,6 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.WindowManager;
import com.maddyhome.idea.vim.config.VimLocalConfig;
import com.maddyhome.idea.vim.config.VimState; import com.maddyhome.idea.vim.config.VimState;
import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator; import com.maddyhome.idea.vim.config.migration.ApplicationConfigurationMigrator;
import com.maddyhome.idea.vim.ex.CommandParser; import com.maddyhome.idea.vim.ex.CommandParser;
@@ -50,7 +53,7 @@ import com.maddyhome.idea.vim.group.visual.VisualMotionGroup;
import com.maddyhome.idea.vim.helper.MacKeyRepeat; import com.maddyhome.idea.vim.helper.MacKeyRepeat;
import com.maddyhome.idea.vim.listener.VimListenerManager; import com.maddyhome.idea.vim.listener.VimListenerManager;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.ui.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import com.maddyhome.idea.vim.ui.StatusBarIconFactory; import com.maddyhome.idea.vim.ui.StatusBarIconFactory;
import com.maddyhome.idea.vim.ui.VimEmulationConfigurable; import com.maddyhome.idea.vim.ui.VimEmulationConfigurable;
import com.maddyhome.idea.vim.ui.VimRcFileState; import com.maddyhome.idea.vim.ui.VimRcFileState;
@@ -102,11 +105,6 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
public void initialize() { public void initialize() {
LOG.debug("initComponent"); LOG.debug("initComponent");
// Initialize a legacy local config.
if (previousStateVersion == 5) {
//noinspection deprecation
VimLocalConfig.Companion.initialize();
}
if (enabled) { if (enabled) {
Application application = ApplicationManager.getApplication(); Application application = ApplicationManager.getApplication();
if (application.isUnitTestMode()) { if (application.isUnitTestMode()) {
@@ -165,7 +163,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
} }
public static @Nullable RegisterGroup getRegisterIfCreated() { public static @Nullable RegisterGroup getRegisterIfCreated() {
return ServiceManager.getServiceIfCreated(RegisterGroup.class); return ApplicationManager.getApplication().getServiceIfCreated(RegisterGroup.class);
} }
public static @NotNull FileGroup getFile() { public static @NotNull FileGroup getFile() {
@@ -177,7 +175,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
} }
public static @Nullable SearchGroup getSearchIfCreated() { public static @Nullable SearchGroup getSearchIfCreated() {
return ServiceManager.getServiceIfCreated(SearchGroup.class); return ApplicationManager.getApplication().getServiceIfCreated(SearchGroup.class);
} }
public static @NotNull ProcessGroup getProcess() { public static @NotNull ProcessGroup getProcess() {
@@ -201,7 +199,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
} }
public static @Nullable KeyGroup getKeyIfCreated() { public static @Nullable KeyGroup getKeyIfCreated() {
return ServiceManager.getServiceIfCreated(KeyGroup.class); return ApplicationManager.getApplication().getServiceIfCreated(KeyGroup.class);
} }
public static @NotNull WindowGroup getWindow() { public static @NotNull WindowGroup getWindow() {
@@ -213,7 +211,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
} }
public static @Nullable EditorGroup getEditorIfCreated() { public static @Nullable EditorGroup getEditorIfCreated() {
return ServiceManager.getServiceIfCreated(EditorGroup.class); return ApplicationManager.getApplication().getServiceIfCreated(EditorGroup.class);
} }
public static @NotNull VisualMotionGroup getVisualMotion() { public static @NotNull VisualMotionGroup getVisualMotion() {
@@ -260,7 +258,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
} }
public static @NotNull String getVersion() { public static @NotNull String getVersion() {
final IdeaPluginDescriptor plugin = PluginManager.getPlugin(getPluginId()); final IdeaPluginDescriptor plugin = PluginManagerCore.getPlugin(getPluginId());
if (!ApplicationManager.getApplication().isInternal()) { if (!ApplicationManager.getApplication().isInternal()) {
return plugin != null ? plugin.getVersion() : "SNAPSHOT"; return plugin != null ? plugin.getVersion() : "SNAPSHOT";
} }
@@ -324,7 +322,7 @@ public class VimPlugin implements PersistentStateComponent<Element>, Disposable
showMessage(msg); showMessage(msg);
} }
public static void showMessage(@Nls @Nullable String msg) { public static void showMessage(@Nls(capitalization = Nls.Capitalization.Sentence) @Nullable String msg) {
if (ApplicationManager.getApplication().isUnitTestMode()) { if (ApplicationManager.getApplication().isUnitTestMode()) {
getInstance().message = msg; getInstance().message = msg;
} }

View File

@@ -20,15 +20,29 @@ package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.mode
import com.maddyhome.idea.vim.helper.vimForEachCaret
class ResetModeAction : VimActionHandler.SingleExecution() { class ResetModeAction : VimActionHandler.SingleExecution() {
override val type: Command.Type = Command.Type.OTHER_WRITABLE override val type: Command.Type = Command.Type.OTHER_WRITABLE
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean { override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
val modeBeforeReset = editor.mode
KeyHandler.getInstance().fullReset(editor.getTopLevelEditor()) KeyHandler.getInstance().fullReset(editor.getTopLevelEditor())
if (modeBeforeReset == CommandState.Mode.INSERT) {
editor.vimForEachCaret { caret ->
val position = VimPlugin.getMotion().getOffsetOfHorizontalMotion(editor, caret, -1, false)
MotionGroup.moveCaret(editor, caret, position)
}
}
return true return true
} }
} }

View File

@@ -21,6 +21,7 @@ package com.maddyhome.idea.vim.action
import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.project.DumbAwareToggleAction import com.intellij.openapi.project.DumbAwareToggleAction
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.ui.VimActions import com.maddyhome.idea.vim.ui.VimActions
/** /**
@@ -38,7 +39,7 @@ class VimPluginToggleAction : DumbAwareToggleAction() {
super.update(e) super.update(e)
e.presentation.text = if (VimActions.actionPlace == e.place) { e.presentation.text = if (VimActions.actionPlace == e.place) {
if (VimPlugin.isEnabled()) "Enabled" else "Enable" if (VimPlugin.isEnabled()) MessageHelper.message("action.VimPluginToggle.enabled") else MessageHelper.message("action.VimPluginToggle.enable")
} else "Vim Emulator" } else MessageHelper.message("action.VimPluginToggle.text")
} }
} }

View File

@@ -41,6 +41,7 @@ import com.maddyhome.idea.vim.helper.isIdeaVimDisabledHere
import com.maddyhome.idea.vim.helper.isPrimaryEditor import com.maddyhome.idea.vim.helper.isPrimaryEditor
import com.maddyhome.idea.vim.helper.isTemplateActive import com.maddyhome.idea.vim.helper.isTemplateActive
import com.maddyhome.idea.vim.key.ShortcutOwner import com.maddyhome.idea.vim.key.ShortcutOwner
import com.maddyhome.idea.vim.listener.IdeaSpecifics.AppCodeTemplates.appCodeTemplateCaptured
import com.maddyhome.idea.vim.listener.IdeaSpecifics.aceJumpActive import com.maddyhome.idea.vim.listener.IdeaSpecifics.aceJumpActive
import com.maddyhome.idea.vim.option.OptionsManager import com.maddyhome.idea.vim.option.OptionsManager
import java.awt.event.InputEvent import java.awt.event.InputEvent
@@ -99,6 +100,8 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false if (keyCode == KeyEvent.VK_TAB && editor.isTemplateActive()) return false
if ((keyCode == KeyEvent.VK_TAB || keyCode == KeyEvent.VK_ENTER) && editor.appCodeTemplateCaptured()) return false
if (editor.inInsertMode) { // XXX: <Tab> won't be recorded in macros if (editor.inInsertMode) { // XXX: <Tab> won't be recorded in macros
if (keyCode == KeyEvent.VK_TAB) { if (keyCode == KeyEvent.VK_TAB) {
VimPlugin.getChange().tabAction = true VimPlugin.getChange().tabAction = true
@@ -128,7 +131,7 @@ class VimShortcutKeyAction : AnAction(), DumbAware {
private fun isEnabledForEscape(editor: Editor): Boolean { private fun isEnabledForEscape(editor: Editor): Boolean {
return editor.isPrimaryEditor() return editor.isPrimaryEditor()
|| EditorHelper.isFileEditor(editor) && !editor.inNormalMode || EditorHelper.isFileEditor(editor) && !editor.inNormalMode
|| OptionsManager.ideaenabledbufs.contains("dialog") && !editor.inNormalMode || OptionsManager.ideavimsupport.contains("dialog") && !editor.inNormalMode
} }
private fun isShortcutConflict(keyStroke: KeyStroke): Boolean { private fun isShortcutConflict(keyStroke: KeyStroke): Boolean {

View File

@@ -26,6 +26,7 @@ import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.VisualPosition import com.intellij.openapi.editor.VisualPosition
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.VimNlsSafe
import java.util.* import java.util.*
import kotlin.math.max import kotlin.math.max
@@ -43,7 +44,10 @@ class AddInlineInlaysAction : AnAction() {
val lineLength = EditorHelper.getLineLength(editor, EditorHelper.visualLineToLogicalLine(editor, currentVisualLine)) val lineLength = EditorHelper.getLineLength(editor, EditorHelper.visualLineToLogicalLine(editor, currentVisualLine))
while (i < lineLength) { while (i < lineLength) {
val relatesToPrecedingText = random.nextInt(10) > 7 val relatesToPrecedingText = random.nextInt(10) > 7
@VimNlsSafe
val text = "a".repeat(max(1, random.nextInt(7))) val text = "a".repeat(max(1, random.nextInt(7)))
val offset = EditorHelper.visualPositionToOffset(editor, VisualPosition(currentVisualLine, i)) val offset = EditorHelper.visualPositionToOffset(editor, VisualPosition(currentVisualLine, i))
// We don't need a custom renderer, just use the standard parameter hint renderer // We don't need a custom renderer, just use the standard parameter hint renderer
inlayModel.addInlineElement(offset, relatesToPrecedingText, HintRenderer(if (relatesToPrecedingText) ":$text" else "$text:")) inlayModel.addInlineElement(offset, relatesToPrecedingText, HintRenderer(if (relatesToPrecedingText) ":$text" else "$text:"))

View File

@@ -27,6 +27,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.ex.CommandParser import com.maddyhome.idea.vim.ex.CommandParser
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.group.RegisterGroup
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
class PlaybackRegisterAction : VimActionHandler.SingleExecution() { class PlaybackRegisterAction : VimActionHandler.SingleExecution() {
@@ -44,7 +45,7 @@ class PlaybackRegisterAction : VimActionHandler.SingleExecution() {
'@' -> { '@' -> {
application.runWriteAction { res.set(VimPlugin.getMacro().playbackLastRegister(editor, context, project, cmd.count)) } application.runWriteAction { res.set(VimPlugin.getMacro().playbackLastRegister(editor, context, project, cmd.count)) }
} }
':' -> { // No write action RegisterGroup.LAST_COMMAND_REGISTER -> { // No write action
try { try {
res.set(CommandParser.getInstance().processLastCommand(editor, context, cmd.count)) res.set(CommandParser.getInstance().processLastCommand(editor, context, cmd.count))
} catch (e: ExException) { } catch (e: ExException) {

View File

@@ -47,9 +47,7 @@ class MotionScrollPageDownAction : VimActionHandler.SingleExecution() {
class MotionScrollPageDownInsertModeAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction { class MotionScrollPageDownInsertModeAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf( override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0))
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, KeyEvent.SHIFT_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_DOWN, KeyEvent.SHIFT_DOWN_MASK))
) )
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY

View File

@@ -47,9 +47,7 @@ class MotionScrollPageUpAction : VimActionHandler.SingleExecution() {
class MotionScrollPageUpInsertModeAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction { class MotionScrollPageUpInsertModeAction : VimActionHandler.SingleExecution(), ComplicatedKeysAction {
override val keyStrokesSet: Set<List<KeyStroke>> = setOf( override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0))
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_UP, KeyEvent.SHIFT_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_UP, KeyEvent.SHIFT_DOWN_MASK))
) )
override val type: Command.Type = Command.Type.OTHER_READONLY override val type: Command.Type = Command.Type.OTHER_READONLY

View File

@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.Direction
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.* import java.util.*
@@ -37,7 +38,7 @@ class SearchWholeWordBackwardAction : MotionActionHandler.ForEachCaret() {
count: Int, count: Int,
rawCount: Int, rawCount: Int,
argument: Argument?): Int { argument: Argument?): Int {
return VimPlugin.getSearch().searchWord(editor, caret, count, true, -1) return VimPlugin.getSearch().searchWord(editor, caret, count, true, Direction.BACKWARDS)
} }
override val motionType: MotionType = MotionType.EXCLUSIVE override val motionType: MotionType = MotionType.EXCLUSIVE

View File

@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.Direction
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.* import java.util.*
@@ -37,7 +38,7 @@ class SearchWholeWordForwardAction : MotionActionHandler.ForEachCaret() {
count: Int, count: Int,
rawCount: Int, rawCount: Int,
argument: Argument?): Int { argument: Argument?): Int {
return VimPlugin.getSearch().searchWord(editor, caret, count, true, 1) return VimPlugin.getSearch().searchWord(editor, caret, count, true, Direction.FORWARDS)
} }
override val motionType: MotionType = MotionType.EXCLUSIVE override val motionType: MotionType = MotionType.EXCLUSIVE

View File

@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.Direction
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.* import java.util.*
@@ -37,7 +38,7 @@ class SearchWordBackwardAction : MotionActionHandler.ForEachCaret() {
count: Int, count: Int,
rawCount: Int, rawCount: Int,
argument: Argument?): Int { argument: Argument?): Int {
return VimPlugin.getSearch().searchWord(editor, caret, count, false, -1) return VimPlugin.getSearch().searchWord(editor, caret, count, false, Direction.BACKWARDS)
} }
override val motionType: MotionType = MotionType.EXCLUSIVE override val motionType: MotionType = MotionType.EXCLUSIVE

View File

@@ -25,6 +25,7 @@ import com.maddyhome.idea.vim.command.Argument
import com.maddyhome.idea.vim.command.CommandFlags import com.maddyhome.idea.vim.command.CommandFlags
import com.maddyhome.idea.vim.command.MotionType import com.maddyhome.idea.vim.command.MotionType
import com.maddyhome.idea.vim.handler.MotionActionHandler import com.maddyhome.idea.vim.handler.MotionActionHandler
import com.maddyhome.idea.vim.helper.Direction
import com.maddyhome.idea.vim.helper.enumSetOf import com.maddyhome.idea.vim.helper.enumSetOf
import java.util.* import java.util.*
@@ -37,7 +38,7 @@ class SearchWordForwardAction : MotionActionHandler.ForEachCaret() {
count: Int, count: Int,
rawCount: Int, rawCount: Int,
argument: Argument?): Int { argument: Argument?): Int {
return VimPlugin.getSearch().searchWord(editor, caret, count, false, 1) return VimPlugin.getSearch().searchWord(editor, caret, count, false, Direction.FORWARDS)
} }
override val motionType: MotionType = MotionType.EXCLUSIVE override val motionType: MotionType = MotionType.EXCLUSIVE

View File

@@ -23,10 +23,10 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.vimLastColumn import com.maddyhome.idea.vim.helper.vimLastColumn
/** /**

View File

@@ -23,10 +23,10 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently import com.maddyhome.idea.vim.group.visual.vimSetSystemSelectionSilently
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.vimLastColumn import com.maddyhome.idea.vim.helper.vimLastColumn
/** /**

View File

@@ -23,10 +23,10 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.group.visual.updateCaretState import com.maddyhome.idea.vim.group.visual.updateCaretState
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.commandState import com.maddyhome.idea.vim.helper.commandState
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
/** /**
* @author Alex Plate * @author Alex Plate

View File

@@ -51,9 +51,7 @@ class MotionWordLeftInsertAction : MotionActionHandler.ForEachCaret(), Complicat
override val keyStrokesSet: Set<List<KeyStroke>> = setOf( override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_DOWN_MASK)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.CTRL_DOWN_MASK))
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, KeyEvent.SHIFT_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_LEFT, KeyEvent.SHIFT_DOWN_MASK))
) )
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)

View File

@@ -51,9 +51,7 @@ class MotionWordRightInsertAction : MotionActionHandler.ForEachCaret(), Complica
override val keyStrokesSet: Set<List<KeyStroke>> = setOf( override val keyStrokesSet: Set<List<KeyStroke>> = setOf(
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.CTRL_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_DOWN_MASK)), listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.CTRL_DOWN_MASK))
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, KeyEvent.SHIFT_DOWN_MASK)),
listOf(KeyStroke.getKeyStroke(KeyEvent.VK_KP_RIGHT, KeyEvent.SHIFT_DOWN_MASK))
) )
override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE) override val flags: EnumSet<CommandFlags> = enumSetOf(CommandFlags.FLAG_SAVE_STROKE)

View File

@@ -19,10 +19,14 @@ package com.maddyhome.idea.vim.action.motion.visual
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.Command import com.maddyhome.idea.vim.command.Command
import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.handler.VimActionHandler import com.maddyhome.idea.vim.handler.VimActionHandler
import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.exitVisualMode import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.vimForEachCaret
/** /**
* @author vlan * @author vlan
@@ -32,6 +36,16 @@ class VisualExitModeAction : VimActionHandler.SingleExecution() {
override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean { override fun execute(editor: Editor, context: DataContext, cmd: Command): Boolean {
editor.getTopLevelEditor().exitVisualMode() editor.getTopLevelEditor().exitVisualMode()
// Should it be in [exitVisualMode]?
editor.vimForEachCaret { caret ->
val lineEnd = EditorHelper.getLineEndForOffset(editor, caret.offset)
if (lineEnd == caret.offset) {
val position = VimPlugin.getMotion().getOffsetOfHorizontalMotion(editor, caret, -1, false)
MotionGroup.moveCaret(editor, caret, position)
}
}
return true return true
} }
} }

View File

@@ -1,6 +1,9 @@
package com.maddyhome.idea.vim.command package com.maddyhome.idea.vim.command
import com.maddyhome.idea.vim.action.DuplicableOperatorAction import com.maddyhome.idea.vim.action.DuplicableOperatorAction
import com.maddyhome.idea.vim.action.ResetModeAction
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedDigraphAction
import com.maddyhome.idea.vim.action.change.insert.InsertCompletedLiteralAction
import com.maddyhome.idea.vim.handler.EditorActionHandlerBase import com.maddyhome.idea.vim.handler.EditorActionHandlerBase
import com.maddyhome.idea.vim.key.CommandPartNode import com.maddyhome.idea.vim.key.CommandPartNode
import com.maddyhome.idea.vim.key.Node import com.maddyhome.idea.vim.key.Node
@@ -24,6 +27,9 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
var expectedArgumentType: Argument.Type? = null var expectedArgumentType: Argument.Type? = null
private set private set
var prevExpectedArgumentType: Argument.Type? = null
private set
val isReady get() = commandState == CurrentCommandState.READY val isReady get() = commandState == CurrentCommandState.READY
val isBad get() = commandState == CurrentCommandState.BAD_COMMAND val isBad get() = commandState == CurrentCommandState.BAD_COMMAND
val isEmpty get() = commandParts.isEmpty() val isEmpty get() = commandParts.isEmpty()
@@ -38,6 +44,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
fun pushCommandPart(action: EditorActionHandlerBase) { fun pushCommandPart(action: EditorActionHandlerBase) {
commandParts.add(Command(count, action, action.type, action.flags)) commandParts.add(Command(count, action, action.type, action.flags))
prevExpectedArgumentType = expectedArgumentType
expectedArgumentType = action.argumentType expectedArgumentType = action.argumentType
count = 0 count = 0
} }
@@ -106,6 +113,14 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
return currentCommandPartNode !is RootNode return currentCommandPartNode !is RootNode
} }
fun isPuttingLiteral(): Boolean {
return !commandParts.isEmpty() && commandParts.last.action is InsertCompletedLiteralAction
}
fun isDone(): Boolean {
return commandParts.isEmpty()
}
fun completeCommandPart(argument: Argument) { fun completeCommandPart(argument: Argument) {
commandParts.peekLast().argument = argument commandParts.peekLast().argument = argument
commandState = CurrentCommandState.READY commandState = CurrentCommandState.READY
@@ -121,6 +136,11 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
} }
fun buildCommand(): Command { fun buildCommand(): Command {
if (commandParts.last.action is InsertCompletedDigraphAction || commandParts.last.action is ResetModeAction) {
expectedArgumentType = prevExpectedArgumentType
prevExpectedArgumentType = null
return commandParts.removeLast()
}
var command: Command = commandParts.removeFirst() var command: Command = commandParts.removeFirst()
while (commandParts.size > 0) { while (commandParts.size > 0) {
@@ -147,6 +167,7 @@ class CommandBuilder(private var currentCommandPartNode: CommandPartNode) {
commandParts.clear() commandParts.clear()
keyList.clear() keyList.clear()
expectedArgumentType = null expectedArgumentType = null
prevExpectedArgumentType = null
} }
fun resetInProgressCommandPart(commandPartNode: CommandPartNode) { fun resetInProgressCommandPart(commandPartNode: CommandPartNode) {

View File

@@ -23,6 +23,8 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.helper.DigraphResult import com.maddyhome.idea.vim.helper.DigraphResult
import com.maddyhome.idea.vim.helper.DigraphSequence import com.maddyhome.idea.vim.helper.DigraphSequence
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.VimNlsSafe
import com.maddyhome.idea.vim.helper.noneOfEnum import com.maddyhome.idea.vim.helper.noneOfEnum
import com.maddyhome.idea.vim.helper.vimCommandState import com.maddyhome.idea.vim.helper.vimCommandState
import com.maddyhome.idea.vim.key.CommandPartNode import com.maddyhome.idea.vim.key.CommandPartNode
@@ -178,7 +180,7 @@ class CommandState private constructor() {
executingCommand = null executingCommand = null
resetModes() resetModes()
commandBuilder.resetInProgressCommandPart(getKeyRootNode(mappingState.mappingMode)) commandBuilder.resetInProgressCommandPart(getKeyRootNode(mappingState.mappingMode))
startDigraphSequence() digraphSequence.reset()
updateStatus() updateStatus()
} }
@@ -232,6 +234,7 @@ class CommandState private constructor() {
* be added. It's better not to compare the whole string but only * be added. It's better not to compare the whole string but only
* the leading character(s). * the leading character(s).
*/ */
@VimNlsSafe
fun toVimNotation(): String { fun toVimNotation(): String {
return when (mode) { return when (mode) {
Mode.COMMAND -> "n" Mode.COMMAND -> "n"
@@ -266,7 +269,7 @@ class CommandState private constructor() {
if (msg.isNotEmpty()) { if (msg.isNotEmpty()) {
msg.append(" - ") msg.append(" - ")
} }
msg.append("recording") msg.append(MessageHelper.message("show.mode.recording"))
} }
VimPlugin.showMode(msg.toString()) VimPlugin.showMode(msg.toString())
} }

View File

@@ -19,6 +19,8 @@
package com.maddyhome.idea.vim.common package com.maddyhome.idea.vim.common
import com.maddyhome.idea.vim.VimPlugin import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.helper.MessageHelper
import org.jetbrains.annotations.NonNls
/** /**
* @author Elliot Courant * @author Elliot Courant
@@ -39,10 +41,10 @@ class Alias(
} }
private companion object { private companion object {
const val LessThan = "<lt>" @NonNls const val LessThan = "<lt>"
const val Count = "<count>" @NonNls const val Count = "<count>"
const val Arguments = "<args>" @NonNls const val Arguments = "<args>"
const val QuotedArguments = "<q-args>" @NonNls const val QuotedArguments = "<q-args>"
} }
fun getCommand(input: String, count: Int): String { fun getCommand(input: String, count: Int): String {
@@ -52,7 +54,7 @@ class Alias(
var compiledCommand = this.command var compiledCommand = this.command
val cleanedInput = input.trim().removePrefix(name).trim() val cleanedInput = input.trim().removePrefix(name).trim()
if (minimumNumberOfArguments > 0 && cleanedInput.isEmpty()) { if (minimumNumberOfArguments > 0 && cleanedInput.isEmpty()) {
VimPlugin.showMessage("E471: Argument required") VimPlugin.showMessage(MessageHelper.message("e471.argument.required"))
VimPlugin.indicateError() VimPlugin.indicateError()
return "" return ""
} }

View File

@@ -22,6 +22,7 @@ import com.intellij.ide.bookmarks.Bookmark
import com.intellij.ide.bookmarks.BookmarkManager import com.intellij.ide.bookmarks.BookmarkManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.maddyhome.idea.vim.group.MarkGroup import com.maddyhome.idea.vim.group.MarkGroup
import org.jetbrains.annotations.NonNls
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
interface Mark { interface Mark {
@@ -35,7 +36,7 @@ interface Mark {
fun clear() fun clear()
object KeySorter : Comparator<Mark> { object KeySorter : Comparator<Mark> {
private const val ORDER = "'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"[]^.<>" @NonNls private const val ORDER = "'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\"[]^.<>"
override fun compare(o1: Mark, o2: Mark): Int { override fun compare(o1: Mark, o2: Mark): Int {
return ORDER.indexOf(o1.key) - ORDER.indexOf(o2.key) return ORDER.indexOf(o1.key) - ORDER.indexOf(o2.key)

View File

@@ -20,8 +20,8 @@ package com.maddyhome.idea.vim.common
import com.intellij.codeInsight.editorActions.TextBlockTransferableData import com.intellij.codeInsight.editorActions.TextBlockTransferableData
import com.maddyhome.idea.vim.command.SelectionType import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.helper.StringHelper import com.maddyhome.idea.vim.helper.StringHelper
import org.jetbrains.annotations.NonNls
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
class Register { class Register {
@@ -81,7 +81,7 @@ class Register {
} }
object KeySorter : Comparator<Register> { object KeySorter : Comparator<Register> {
private const val ORDER = "\"0123456789abcdefghijklmnopqrstuvwxyz-*+.:%#/=" @NonNls private const val ORDER = "\"0123456789abcdefghijklmnopqrstuvwxyz-*+.:%#/="
override fun compare(o1: Register, o2: Register): Int { override fun compare(o1: Register, o2: Register): Int {
return ORDER.indexOf(o1.name.toLowerCase()) - ORDER.indexOf(o2.name.toLowerCase()) return ORDER.indexOf(o1.name.toLowerCase()) - ORDER.indexOf(o2.name.toLowerCase())

View File

@@ -18,6 +18,7 @@
package com.maddyhome.idea.vim.common package com.maddyhome.idea.vim.common
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import org.jetbrains.annotations.NonNls
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -85,7 +86,7 @@ class TextRange(val startOffsets: IntArray, val endOffsets: IntArray) {
operator fun contains(offset: Int): Boolean = if (isMultiple) false else offset in startOffset until endOffset operator fun contains(offset: Int): Boolean = if (isMultiple) false else offset in startOffset until endOffset
override fun toString(): String { override fun toString(): String {
val sb = StringBuilder() @NonNls val sb = StringBuilder()
sb.append("TextRange") sb.append("TextRange")
sb.append("{starts=") sb.append("{starts=")

View File

@@ -1,57 +0,0 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.config
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.maddyhome.idea.vim.VimPlugin
import org.jdom.Element
/**
* @author Alex Plate
*/
@State(name = "VimLocalSettings", storages = [
Storage("\$APP_CONFIG$$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true),
Storage("\$APP_CONFIG$/vim_local_settings.xml", roamingType = RoamingType.DISABLED, deprecated = true)
])
@Deprecated("The data from this class will be stored in vim_settings")
class VimLocalConfig : PersistentStateComponent<Element> {
override fun getState(): Element? = null
override fun loadState(state: Element) {
// This is initialization of state from the legacy configuration structure.
// This code should be performed only once on settings migration.
// After the migration is done, the file with settings gets removed and this method won't be called again.
VimPlugin.getMark().readData(state)
VimPlugin.getRegister().readData(state)
VimPlugin.getSearch().readData(state)
VimPlugin.getHistory().readData(state)
}
companion object {
fun initialize() {
@Suppress("DEPRECATION")
ServiceManager.getService(VimLocalConfig::class.java)
}
}
}

View File

@@ -60,7 +60,7 @@ class MigrationComponents(
@Service @Service
class ApplicationConfigurationMigrator(migrationComponents: MigrationComponents) { class ApplicationConfigurationMigrator(migrationComponents: MigrationComponents) {
@Suppress("unused") @Suppress("unused", "HardCodedStringLiteral")
constructor() : this(productionMigrationComponents) constructor() : this(productionMigrationComponents)
private val migrators = migrationComponents.groupedMigrators private val migrators = migrationComponents.groupedMigrators

View File

@@ -21,6 +21,7 @@
package com.maddyhome.idea.vim.config.migration package com.maddyhome.idea.vim.config.migration
import com.intellij.openapi.application.PathManager import com.intellij.openapi.application.PathManager
import org.jetbrains.annotations.NonNls
interface VersionDetector { interface VersionDetector {
fun extractVersion(): Int? fun extractVersion(): Int?
@@ -28,7 +29,7 @@ interface VersionDetector {
object `Detect versions 3, 4, 5, 6` : VersionDetector { object `Detect versions 3, 4, 5, 6` : VersionDetector {
val pattern = "state version=\"(.)\"".toRegex() @NonNls val pattern = "state version=\"(.)\"".toRegex()
override fun extractVersion(): Int? { override fun extractVersion(): Int? {
val configFile = PathManager.getOptionsFile("vim_settings") val configFile = PathManager.getOptionsFile("vim_settings")

View File

@@ -18,8 +18,6 @@
package com.maddyhome.idea.vim.ex package com.maddyhome.idea.vim.ex
import java.util.*
class CommandNode(command: ExBeanClass? = null) { class CommandNode(command: ExBeanClass? = null) {
var commandHandler: ExBeanClass? = command var commandHandler: ExBeanClass? = command

View File

@@ -26,11 +26,11 @@ import com.intellij.openapi.util.ThrowableComputable;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.command.SelectionType; import com.maddyhome.idea.vim.command.SelectionType;
import com.maddyhome.idea.vim.common.Register; import com.maddyhome.idea.vim.common.Register;
import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.ex.handler.GotoLineHandler; import com.maddyhome.idea.vim.ex.handler.GotoLineHandler;
import com.maddyhome.idea.vim.ex.ranges.Range; import com.maddyhome.idea.vim.ex.ranges.Range;
import com.maddyhome.idea.vim.ex.ranges.Ranges; import com.maddyhome.idea.vim.ex.ranges.Ranges;
import com.maddyhome.idea.vim.group.HistoryGroup; import com.maddyhome.idea.vim.group.HistoryGroup;
import com.maddyhome.idea.vim.group.RegisterGroup;
import com.maddyhome.idea.vim.helper.MessageHelper; import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.Msg; import com.maddyhome.idea.vim.helper.Msg;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -150,7 +150,7 @@ public class CommandParser {
} }
processCommand(editor, context, commandAlias, count, aliasCountdown - 1); processCommand(editor, context, commandAlias, count, aliasCountdown - 1);
} else { } else {
VimPlugin.showMessage("Recursion detected, maximum alias depth reached."); VimPlugin.showMessage(MessageHelper.message("recursion.detected.maximum.alias.depth.reached"));
VimPlugin.indicateError(); VimPlugin.indicateError();
logger.warn("Recursion detected, maximum alias depth reached. "); logger.warn("Recursion detected, maximum alias depth reached. ");
} }
@@ -177,8 +177,7 @@ public class CommandParser {
ThrowableComputable<Object, ExException> runCommand = () -> { ThrowableComputable<Object, ExException> runCommand = () -> {
boolean ok = handler.process(editor, context, command, count); boolean ok = handler.process(editor, context, command, count);
if (ok && !handler.getArgFlags().getFlags().contains(CommandHandler.Flag.DONT_SAVE_LAST)) { if (ok && !handler.getArgFlags().getFlags().contains(CommandHandler.Flag.DONT_SAVE_LAST)) {
VimPlugin.getRegister().storeTextInternal(editor, new TextRange(-1, -1), cmd, VimPlugin.getRegister().storeTextSpecial(RegisterGroup.LAST_COMMAND_REGISTER, cmd);
SelectionType.CHARACTER_WISE, ':', false);
} }
return null; return null;
}; };
@@ -307,6 +306,10 @@ public class CommandParser {
state = State.RANGE_SHORT_PATTERN; state = State.RANGE_SHORT_PATTERN;
reprocess = false; reprocess = false;
} }
else if (ch == ',') {
location.append('.');
state = State.RANGE_MAYBE_DONE;
}
else if (ch == '/' || ch == '?') { else if (ch == '/' || ch == '?') {
location.append(ch); location.append(ch);
patternType = ch; patternType = ch;

View File

@@ -33,6 +33,7 @@ import com.maddyhome.idea.vim.ex.CommandHandler.RangeFlag.RANGE_OPTIONAL
import com.maddyhome.idea.vim.ex.CommandHandlerFlags import com.maddyhome.idea.vim.ex.CommandHandlerFlags
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.runAfterGotFocus import com.maddyhome.idea.vim.helper.runAfterGotFocus
/** /**
@@ -45,7 +46,7 @@ class ActionHandler : CommandHandler.SingleExecution() {
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean { override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
val actionName = cmd.argument.trim() val actionName = cmd.argument.trim()
val action = ActionManager.getInstance().getAction(actionName) ?: run { val action = ActionManager.getInstance().getAction(actionName) ?: run {
VimPlugin.showMessage("Action not found: $actionName") VimPlugin.showMessage(MessageHelper.message("action.not.found.0", actionName))
return false return false
} }
val application = ApplicationManager.getApplication() val application = ApplicationManager.getApplication()

View File

@@ -30,6 +30,7 @@ import com.maddyhome.idea.vim.ex.CommandHandlerFlags
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.StringHelper import com.maddyhome.idea.vim.helper.StringHelper
/** /**
@@ -55,7 +56,7 @@ class ActionListHandler : CommandHandler.SingleExecution() {
.joinToString(lineSeparator) .joinToString(lineSeparator)
ExOutputModel.getInstance(editor).output("--- Actions ---$lineSeparator$actions") ExOutputModel.getInstance(editor).output(MessageHelper.message("ex.show.all.actions.0.1", lineSeparator, actions))
return true return true
} }
} }

View File

@@ -27,6 +27,7 @@ import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.MessageHelper
/** /**
* Handles buffer, buf, bu, b. * Handles buffer, buf, bu, b.
@@ -47,7 +48,7 @@ class BufferHandler : CommandHandler.SingleExecution() {
val bufNum = buffer.toInt() - 1 val bufNum = buffer.toInt() - 1
if (!VimPlugin.getFile().selectFile(bufNum, context)) { if (!VimPlugin.getFile().selectFile(bufNum, context)) {
VimPlugin.showMessage("Buffer $bufNum does not exist") VimPlugin.showMessage(MessageHelper.message("buffer.0.does.not.exist", bufNum))
result = false result = false
} }
} else { } else {
@@ -55,12 +56,12 @@ class BufferHandler : CommandHandler.SingleExecution() {
when(editors.size) { when(editors.size) {
0 -> { 0 -> {
VimPlugin.showMessage("No matching buffer for $buffer") VimPlugin.showMessage(MessageHelper.message("no.matching.buffer.for.0", buffer))
result = false result = false
} }
1 -> { 1 -> {
if (EditorHelper.hasUnsavedChanges(editor) && !overrideModified) { if (EditorHelper.hasUnsavedChanges(editor) && !overrideModified) {
VimPlugin.showMessage("No write since last change (add ! to override)") VimPlugin.showMessage(MessageHelper.message("no.write.since.last.change.add.to.override"))
result = false result = false
} }
else { else {
@@ -68,7 +69,7 @@ class BufferHandler : CommandHandler.SingleExecution() {
} }
} }
else -> { else -> {
VimPlugin.showMessage("More than one match for $buffer") VimPlugin.showMessage(MessageHelper.message("more.than.one.match.for.0", buffer))
result = false result = false
} }
} }

View File

@@ -31,6 +31,7 @@ import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.ExOutputModel import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import org.jetbrains.annotations.NonNls
import java.io.File import java.io.File
/** /**
@@ -115,7 +116,7 @@ class BufferListHandler : CommandHandler.SingleExecution() {
} }
private fun getBufferStatus(editor: Editor, file: VirtualFile, currentFile: VirtualFile, previousFile: VirtualFile?): String { private fun getBufferStatus(editor: Editor, file: VirtualFile, currentFile: VirtualFile, previousFile: VirtualFile?): String {
val bufStatus = StringBuilder() @NonNls val bufStatus = StringBuilder()
when(file) { when(file) {
currentFile -> bufStatus.append("%a ") currentFile -> bufStatus.append("%a ")

View File

@@ -32,6 +32,8 @@ import com.maddyhome.idea.vim.ex.ExOutputModel
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
import com.maddyhome.idea.vim.group.CommandGroup.Companion.BLACKLISTED_ALIASES import com.maddyhome.idea.vim.group.CommandGroup.Companion.BLACKLISTED_ALIASES
import com.maddyhome.idea.vim.helper.MessageHelper
import org.jetbrains.annotations.NonNls
/** /**
* @author Elliot Courant * @author Elliot Courant
@@ -42,16 +44,13 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// Static definitions needed for aliases. // Static definitions needed for aliases.
private companion object { private companion object {
const val overridePrefix = "!" const val overridePrefix = "!"
const val argsPrefix = "-nargs"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls const val argsPrefix = "-nargs"
const val anyNumberOfArguments = "*" const val anyNumberOfArguments = "*"
const val zeroOrOneArguments = "?" const val zeroOrOneArguments = "?"
const val moreThanZeroArguments = "+" const val moreThanZeroArguments = "+"
const val errorInvalidNumberOfArguments = "E176: Invalid number of arguments"
const val errorCannotStartWithLowercase = "E183: User defined commands must start with an uppercase letter"
const val errorReservedName = "E841: Reserved name, cannot be used for user defined command"
const val errorCommandAlreadyExists = "E174: Command already exists: add ! to replace it"
} }
override fun execute(cmd: ExCommand) { override fun execute(cmd: ExCommand) {
@@ -95,7 +94,7 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// in the actual alias being created, and we don't want to parse that one. // in the actual alias being created, and we don't want to parse that one.
val trimmedInput = argument.takeWhile { it != ' ' } val trimmedInput = argument.takeWhile { it != ' ' }
val pattern = Regex("(?>-nargs=((|[-])\\d+|[?]|[+]|[*]))").find(trimmedInput) ?: run { val pattern = Regex("(?>-nargs=((|[-])\\d+|[?]|[+]|[*]))").find(trimmedInput) ?: run {
VimPlugin.showMessage(errorInvalidNumberOfArguments) VimPlugin.showMessage(MessageHelper.message("e176.invalid.number.of.arguments"))
return false return false
} }
val nargForTrim = pattern.groupValues[0] val nargForTrim = pattern.groupValues[0]
@@ -118,7 +117,7 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// I missed something, since the regex limits the value to be ? + * or // I missed something, since the regex limits the value to be ? + * or
// a valid number, its not possible (as far as I know) to have another value // a valid number, its not possible (as far as I know) to have another value
// that regex would accept that is not valid. // that regex would accept that is not valid.
VimPlugin.showMessage(errorInvalidNumberOfArguments) VimPlugin.showMessage(MessageHelper.message("e176.invalid.number.of.arguments"))
return false return false
} }
} }
@@ -126,7 +125,7 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// Not sure why this isn't documented, but if you try to create a command in vim // Not sure why this isn't documented, but if you try to create a command in vim
// with an explicit number of arguments greater than 1 it returns this error. // with an explicit number of arguments greater than 1 it returns this error.
if (argNum > 1 || argNum < 0) { if (argNum > 1 || argNum < 0) {
VimPlugin.showMessage(errorInvalidNumberOfArguments) VimPlugin.showMessage(MessageHelper.message("e176.invalid.number.of.arguments"))
return false return false
} }
minNumberOfArgs = argNum minNumberOfArgs = argNum
@@ -148,12 +147,12 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// User-aliases need to begin with an uppercase character. // User-aliases need to begin with an uppercase character.
if (!alias[0].isUpperCase()) { if (!alias[0].isUpperCase()) {
VimPlugin.showMessage(errorCannotStartWithLowercase) VimPlugin.showMessage(MessageHelper.message("e183.user.defined.commands.must.start.with.an.uppercase.letter"))
return false return false
} }
if (alias in BLACKLISTED_ALIASES) { if (alias in BLACKLISTED_ALIASES) {
VimPlugin.showMessage(errorReservedName) VimPlugin.showMessage(MessageHelper.message("e841.reserved.name.cannot.be.used.for.user.defined.command"))
return false return false
} }
@@ -169,7 +168,7 @@ class CmdHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler {
// If we are not over-writing existing aliases, and an alias with the same command // If we are not over-writing existing aliases, and an alias with the same command
// already exists then we want to do nothing. // already exists then we want to do nothing.
if (!overrideAlias && VimPlugin.getCommand().hasAlias(alias)) { if (!overrideAlias && VimPlugin.getCommand().hasAlias(alias)) {
VimPlugin.showMessage(errorCommandAlreadyExists) VimPlugin.showMessage(MessageHelper.message("e174.command.already.exists.add.to.replace.it"))
return false return false
} }

View File

@@ -24,12 +24,13 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.ex.CommandHandler import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.MessageHelper
class DelCmdHandler : CommandHandler.SingleExecution() { class DelCmdHandler : CommandHandler.SingleExecution() {
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_REQUIRED, Access.READ_ONLY) override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_REQUIRED, Access.READ_ONLY)
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean { override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
if (!VimPlugin.getCommand().hasAlias(cmd.argument)) { if (!VimPlugin.getCommand().hasAlias(cmd.argument)) {
VimPlugin.showMessage("E184: No such user-defined command: ${cmd.argument}") VimPlugin.showMessage(MessageHelper.message("e184.no.such.user.defined.command.0", cmd.argument))
return false return false
} }

View File

@@ -24,6 +24,7 @@ import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.ex.CommandHandler import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import org.jetbrains.annotations.NonNls
import java.io.UnsupportedEncodingException import java.io.UnsupportedEncodingException
import java.net.URLEncoder import java.net.URLEncoder
@@ -37,6 +38,7 @@ class HelpHandler : CommandHandler.SingleExecution() {
return true return true
} }
@NonNls
private fun helpTopicUrl(topic: String): String { private fun helpTopicUrl(topic: String): String {
if (topic.isBlank()) return HELP_ROOT_URL if (topic.isBlank()) return HELP_ROOT_URL

View File

@@ -36,7 +36,6 @@ import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.MessageHelper import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.Msg import com.maddyhome.idea.vim.helper.Msg
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import java.util.*
import kotlin.math.min import kotlin.math.min
class MoveTextHandler : CommandHandler.SingleExecution() { class MoveTextHandler : CommandHandler.SingleExecution() {

View File

@@ -28,9 +28,8 @@ import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.ex.ranges.LineRange import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.inBlockSubMode import com.maddyhome.idea.vim.helper.inBlockSubMode
import java.util.* import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
/** /**
* @author Alex Selesse * @author Alex Selesse

View File

@@ -24,11 +24,14 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.ex.CommandHandler import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import org.jetbrains.annotations.NonNls
class SplitHandler : CommandHandler.SingleExecution() { class SplitHandler : CommandHandler.SingleExecution() {
override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY) override val argFlags = flags(RangeFlag.RANGE_FORBIDDEN, ArgumentFlag.ARGUMENT_OPTIONAL, Access.READ_ONLY)
@NonNls private val visualSplitPrefix = "v"
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean { override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean {
if (cmd.command.startsWith("v")) { if (cmd.command.startsWith(visualSplitPrefix)) {
VimPlugin.getWindow().splitWindowVertical(context, cmd.argument) VimPlugin.getWindow().splitWindowVertical(context, cmd.argument)
} else { } else {
VimPlugin.getWindow().splitWindowHorizontal(context, cmd.argument) VimPlugin.getWindow().splitWindowHorizontal(context, cmd.argument)

View File

@@ -26,6 +26,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.ex.CommandHandler import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.ExCommand import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.helper.MessageHelper
/** /**
* @author Rieon Ke * @author Rieon Ke
@@ -51,7 +52,7 @@ class TabCloseHandler : CommandHandler.SingleExecution() {
val select = if (index == current) index + 1 else current val select = if (index == current) index + 1 else current
tabbedPane.removeTabAt(index, select) tabbedPane.removeTabAt(index, select)
} else { } else {
VimPlugin.showMessage("Error: invalid command argument") VimPlugin.showMessage(MessageHelper.message("error.invalid.command.argument"))
} }
return true return true

View File

@@ -0,0 +1,31 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.ex.handler.mapping
import com.maddyhome.idea.vim.command.MappingMode
import org.jetbrains.annotations.NonNls
internal class CommandInfo(
@NonNls val prefix: String,
@NonNls suffix: String,
val mappingModes: Set<MappingMode>,
val isRecursive: Boolean
) {
val command = if (suffix.isBlank()) prefix else "$prefix[$suffix]"
}

View File

@@ -0,0 +1,69 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.ex.handler.mapping
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.CommandHandler.Access.READ_ONLY
import com.maddyhome.idea.vim.ex.CommandHandler.ArgumentFlag.ARGUMENT_FORBIDDEN
import com.maddyhome.idea.vim.ex.CommandHandler.RangeFlag.RANGE_FORBIDDEN
import com.maddyhome.idea.vim.ex.CommandHandlerFlags
import com.maddyhome.idea.vim.ex.CommandName
import com.maddyhome.idea.vim.ex.ComplicatedNameExCommand
import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.commands
import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
class MapClearHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, ComplicatedNameExCommand {
override val argFlags: CommandHandlerFlags = flags(RANGE_FORBIDDEN, ARGUMENT_FORBIDDEN, READ_ONLY)
override val names: Array<CommandName> = COMMAND_NAMES
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean = executeCommand(cmd)
override fun execute(cmd: ExCommand) {
executeCommand(cmd)
}
private fun executeCommand(cmd: ExCommand): Boolean {
val commandInfo = COMMAND_INFOS.find { cmd.command.startsWith(it.prefix) } ?: return false
VimPlugin.getKey().removeKeyMapping(commandInfo.mappingModes)
return true
}
companion object {
private val COMMAND_INFOS = arrayOf(
CommandInfo("mapc", "lear", MappingMode.NVO, false),
CommandInfo("nmapc", "lear", MappingMode.N, false),
CommandInfo("vmapc", "lear", MappingMode.V, false),
CommandInfo("xmapc", "lear", MappingMode.X, false),
CommandInfo("smapc", "lear", MappingMode.S, false),
CommandInfo("omapc", "lear", MappingMode.O, false),
CommandInfo("imapc", "lear", MappingMode.I, false),
CommandInfo("cmapc", "lear", MappingMode.C, false)
)
val COMMAND_NAMES = commands(*COMMAND_INFOS.map { it.command }.toTypedArray())
}
}

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.maddyhome.idea.vim.ex.handler package com.maddyhome.idea.vim.ex.handler.mapping
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
@@ -30,11 +30,12 @@ import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ex.commands import com.maddyhome.idea.vim.ex.commands
import com.maddyhome.idea.vim.ex.flags import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.EXPR import com.maddyhome.idea.vim.ex.handler.mapping.MapHandler.SpecialArgument.EXPR
import com.maddyhome.idea.vim.ex.handler.MapHandler.SpecialArgument.SCRIPT import com.maddyhome.idea.vim.ex.handler.mapping.MapHandler.SpecialArgument.SCRIPT
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import org.jetbrains.annotations.NonNls
import java.util.* import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
@@ -57,34 +58,32 @@ class MapHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, Co
@Throws(ExException::class) @Throws(ExException::class)
private fun executeCommand(cmd: ExCommand, editor: Editor?): Boolean { private fun executeCommand(cmd: ExCommand, editor: Editor?): Boolean {
val commandInfo = COMMAND_INFOS.find { cmd.command.startsWith(it.prefix) } val commandInfo = COMMAND_INFOS.find { cmd.command.startsWith(it.prefix) } ?: return false
if (commandInfo != null) { val argument = cmd.argument
val argument = cmd.argument val modes = commandInfo.mappingModes
val modes = commandInfo.mappingModes
if (argument.isEmpty()) {
return editor != null && VimPlugin.getKey().showKeyMappings(modes, editor)
} else {
val arguments = try {
parseCommandArguments(argument)
} catch (ignored: IllegalArgumentException) {
return false
}
if (arguments != null) { if (argument.isEmpty()) return editor != null && VimPlugin.getKey().showKeyMappings(modes, editor)
for (unsupportedArgument in UNSUPPORTED_SPECIAL_ARGUMENTS) {
if (unsupportedArgument in arguments.specialArguments) { val arguments = try {
throw ExException("Unsupported map argument: $unsupportedArgument") parseCommandArguments(argument) ?: return false
} } catch (ignored: IllegalArgumentException) {
} return false
VimPlugin.getKey().putKeyMapping(modes, arguments.fromKeys, MappingOwner.IdeaVim, arguments.toKeys, commandInfo.isRecursive) }
return true
} for (unsupportedArgument in UNSUPPORTED_SPECIAL_ARGUMENTS) {
if (unsupportedArgument in arguments.specialArguments) {
throw ExException("Unsupported map argument: $unsupportedArgument")
} }
} }
return false
VimPlugin.getKey()
.putKeyMapping(modes, arguments.fromKeys, MappingOwner.IdeaVim, arguments.toKeys, commandInfo.isRecursive)
return true
} }
@Suppress("unused") @Suppress("unused")
@NonNls
private enum class SpecialArgument(val myName: String) { private enum class SpecialArgument(val myName: String) {
BUFFER("<buffer>"), BUFFER("<buffer>"),
NOWAIT("<nowait>"), NOWAIT("<nowait>"),
@@ -113,10 +112,6 @@ class MapHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, Co
private class CommandArguments(val specialArguments: Set<SpecialArgument>, val fromKeys: List<KeyStroke>, private class CommandArguments(val specialArguments: Set<SpecialArgument>, val fromKeys: List<KeyStroke>,
val toKeys: List<KeyStroke>) val toKeys: List<KeyStroke>)
private class CommandInfo(val prefix: String, suffix: String, val mappingModes: Set<MappingMode>, val isRecursive: Boolean) {
val command = if (suffix.isBlank()) prefix else "$prefix[$suffix]"
}
companion object { companion object {
private const val CTRL_V = '\u0016' private const val CTRL_V = '\u0016'
private val COMMAND_INFOS = arrayOf( private val COMMAND_INFOS = arrayOf(

View File

@@ -0,0 +1,75 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.maddyhome.idea.vim.ex.handler.mapping
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.MappingMode
import com.maddyhome.idea.vim.ex.CommandHandler
import com.maddyhome.idea.vim.ex.CommandHandler.Access.READ_ONLY
import com.maddyhome.idea.vim.ex.CommandHandler.ArgumentFlag.ARGUMENT_REQUIRED
import com.maddyhome.idea.vim.ex.CommandHandler.RangeFlag.RANGE_FORBIDDEN
import com.maddyhome.idea.vim.ex.CommandHandlerFlags
import com.maddyhome.idea.vim.ex.CommandName
import com.maddyhome.idea.vim.ex.ComplicatedNameExCommand
import com.maddyhome.idea.vim.ex.ExCommand
import com.maddyhome.idea.vim.ex.commands
import com.maddyhome.idea.vim.ex.flags
import com.maddyhome.idea.vim.ex.vimscript.VimScriptCommandHandler
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
class UnMapHandler : CommandHandler.SingleExecution(), VimScriptCommandHandler, ComplicatedNameExCommand {
override val argFlags: CommandHandlerFlags = flags(RANGE_FORBIDDEN, ARGUMENT_REQUIRED, READ_ONLY)
override val names: Array<CommandName> = COMMAND_NAMES
override fun execute(editor: Editor, context: DataContext, cmd: ExCommand): Boolean = executeCommand(cmd)
override fun execute(cmd: ExCommand) {
executeCommand(cmd)
}
private fun executeCommand(cmd: ExCommand): Boolean {
val commandInfo = COMMAND_INFOS.find { cmd.command.startsWith(it.prefix) } ?: return false
val argument = cmd.argument
if (argument.isEmpty()) return false
val parsedKeys = parseKeys(argument.trimStart())
VimPlugin.getKey().removeKeyMapping(commandInfo.mappingModes, parsedKeys)
return true
}
companion object {
private val COMMAND_INFOS = arrayOf(
CommandInfo("unm", "ap", MappingMode.NVO, false),
CommandInfo("nun", "map", MappingMode.N, false),
CommandInfo("vu", "nmap", MappingMode.V, false),
CommandInfo("xu", "nmap", MappingMode.X, false),
CommandInfo("sunm", "ap", MappingMode.S, false),
CommandInfo("ou", "nmap", MappingMode.O, false),
CommandInfo("iu", "nmap", MappingMode.I, false),
CommandInfo("cu", "nmap", MappingMode.C, false)
)
val COMMAND_NAMES = commands(*COMMAND_INFOS.map { it.command }.toTypedArray())
}
}

View File

@@ -24,6 +24,7 @@ import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import org.jetbrains.annotations.NonNls
import kotlin.math.min import kotlin.math.min
/** /**
@@ -179,6 +180,7 @@ class Ranges {
count = 0 count = 0
} }
@NonNls
override fun toString(): String = "Ranges[ranges=$ranges]" override fun toString(): String = "Ranges[ranges=$ranges]"
private var startLine = 0 private var startLine = 0

View File

@@ -20,6 +20,7 @@ package com.maddyhome.idea.vim.ex.vimscript
import com.maddyhome.idea.vim.ex.CommandParser import com.maddyhome.idea.vim.ex.CommandParser
import com.maddyhome.idea.vim.ex.ExException import com.maddyhome.idea.vim.ex.ExException
import com.maddyhome.idea.vim.ui.VimRcFileState import com.maddyhome.idea.vim.ui.VimRcFileState
import org.jetbrains.annotations.NonNls
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.file.Paths import java.nio.file.Paths
@@ -30,8 +31,14 @@ import java.util.regex.Pattern
* @author vlan * @author vlan
*/ */
object VimScriptParser { object VimScriptParser {
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val VIMRC_FILE_NAME = "ideavimrc" private const val VIMRC_FILE_NAME = "ideavimrc"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private val HOME_VIMRC_PATHS = arrayOf(".$VIMRC_FILE_NAME", "_$VIMRC_FILE_NAME") private val HOME_VIMRC_PATHS = arrayOf(".$VIMRC_FILE_NAME", "_$VIMRC_FILE_NAME")
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private val XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME private val XDG_VIMRC_PATH = "ideavim" + File.separator + VIMRC_FILE_NAME
private val DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"") private val DOUBLE_QUOTED_STRING = Pattern.compile("\"([^\"]*)\"")
private val SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'") private val SINGLE_QUOTED_STRING = Pattern.compile("'([^']*)'")

View File

@@ -21,7 +21,6 @@ package com.maddyhome.idea.vim.extension
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.AbstractExtensionPointBean import com.intellij.openapi.extensions.AbstractExtensionPointBean
import com.intellij.util.xmlb.annotations.Attribute import com.intellij.util.xmlb.annotations.Attribute
import com.intellij.util.xmlb.annotations.CollectionBean
import com.intellij.util.xmlb.annotations.Tag import com.intellij.util.xmlb.annotations.Tag
import com.intellij.util.xmlb.annotations.XCollection import com.intellij.util.xmlb.annotations.XCollection
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean

View File

@@ -20,6 +20,7 @@ package com.maddyhome.idea.vim.extension;
import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.ExtensionPointName;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.helper.VimNlsSafe;
import com.maddyhome.idea.vim.key.MappingOwner; import com.maddyhome.idea.vim.key.MappingOwner;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -31,10 +32,12 @@ public interface VimExtension {
@NotNull ExtensionPointName<ExtensionBeanClass> EP_NAME = ExtensionPointName.create("IdeaVIM.vimExtension"); @NotNull ExtensionPointName<ExtensionBeanClass> EP_NAME = ExtensionPointName.create("IdeaVIM.vimExtension");
/** /**
* @deprecated Please set extension name in <vimExtension> tag * @deprecated This property is not used anymore, but we'll remove it much later to keep the compatibility of IdeaVim
* extensions with previous versions of IdeaVim.
*/ */
@Deprecated @Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "0.63") @ApiStatus.ScheduledForRemoval(inVersion = "0.65")
@VimNlsSafe
@NotNull String getName(); @NotNull String getName();
default MappingOwner getOwner() { default MappingOwner getOwner() {

View File

@@ -30,7 +30,7 @@ import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.commandState import com.maddyhome.idea.vim.helper.commandState
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import com.maddyhome.idea.vim.ui.ExEntryPanel import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import com.maddyhome.idea.vim.ui.ModalEntry import com.maddyhome.idea.vim.ui.ModalEntry
import java.awt.event.KeyEvent import java.awt.event.KeyEvent
import javax.swing.KeyStroke import javax.swing.KeyStroke

View File

@@ -12,12 +12,16 @@ import com.maddyhome.idea.vim.extension.VimExtension;
import com.maddyhome.idea.vim.extension.VimExtensionHandler; import com.maddyhome.idea.vim.extension.VimExtensionHandler;
import com.maddyhome.idea.vim.handler.TextObjectActionHandler; import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
import com.maddyhome.idea.vim.helper.InlayHelperKt; import com.maddyhome.idea.vim.helper.InlayHelperKt;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor; import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor;
import com.maddyhome.idea.vim.listener.VimListenerSuppressor; import com.maddyhome.idea.vim.listener.VimListenerSuppressor;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.ArrayDeque;
import java.util.Deque;
import java.util.EnumSet;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putExtensionHandlerMapping;
import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping; import static com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping;
@@ -183,7 +187,8 @@ public class VimArgTextObjExtension implements VimExtension {
try { try {
bracketPairs = BracketPairs.fromBracketPairList(bracketPairsVar); bracketPairs = BracketPairs.fromBracketPairList(bracketPairsVar);
} catch (BracketPairs.ParseException parseException) { } catch (BracketPairs.ParseException parseException) {
VimPlugin.showMessage("argtextobj: Invalid value of g:argtextobj_pairs -- " + parseException.getMessage()); VimPlugin.showMessage(
MessageHelper.message("argtextobj.invalid.value.of.g.argtextobj.pairs.0", parseException.getMessage()));
VimPlugin.indicateError(); VimPlugin.indicateError();
return null; return null;
} }
@@ -259,7 +264,7 @@ public class VimArgTextObjExtension implements VimExtension {
private int rightBound = Integer.MIN_VALUE; private int rightBound = Integer.MIN_VALUE;
private int leftBracket; private int leftBracket;
private int rightBracket; private int rightBracket;
private String error = null; private @Nls String error = null;
private static final String QUOTES = "\"'"; private static final String QUOTES = "\"'";
private static final int MAX_SEARCH_LINES = 10; private static final int MAX_SEARCH_LINES = 10;

View File

@@ -51,6 +51,7 @@ import com.maddyhome.idea.vim.helper.moveToInlayAwareLogicalPosition
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.subMode import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import org.jetbrains.annotations.NonNls
/** /**
* This emulation misses: * This emulation misses:
@@ -80,8 +81,11 @@ class VimExchangeExtension : VimExtension {
} }
companion object { companion object {
@NonNls
const val EXCHANGE_CMD = "<Plug>(Exchange)" const val EXCHANGE_CMD = "<Plug>(Exchange)"
@NonNls
const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)" const val EXCHANGE_CLEAR_CMD = "<Plug>(ExchangeClear)"
@NonNls
const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)" const val EXCHANGE_LINE_CMD = "<Plug>(ExchangeLine)"
val EXCHANGE_KEY = Key<Exchange>("exchange") val EXCHANGE_KEY = Key<Exchange>("exchange")

View File

@@ -35,16 +35,21 @@ import com.maddyhome.idea.vim.VimProjectService
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment import com.maddyhome.idea.vim.ex.vimscript.VimScriptGlobalEnvironment
import com.maddyhome.idea.vim.extension.VimExtension import com.maddyhome.idea.vim.extension.VimExtension
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.listener.VimInsertListener import com.maddyhome.idea.vim.listener.VimInsertListener
import com.maddyhome.idea.vim.listener.VimYankListener import com.maddyhome.idea.vim.listener.VimYankListener
import com.maddyhome.idea.vim.option.StrictMode import com.maddyhome.idea.vim.option.StrictMode
import org.jetbrains.annotations.NonNls
import java.awt.Color import java.awt.Color
import java.awt.Font import java.awt.Font
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
const val DEFAULT_HIGHLIGHT_DURATION: Long = 300 const val DEFAULT_HIGHLIGHT_DURATION: Long = 300
@NonNls
private const val HIGHLIGHT_DURATION_VARIABLE_NAME = "g:highlightedyank_highlight_duration" private const val HIGHLIGHT_DURATION_VARIABLE_NAME = "g:highlightedyank_highlight_duration"
@NonNls
private const val HIGHLIGHT_COLOR_VARIABLE_NAME = "g:highlightedyank_highlight_color" private const val HIGHLIGHT_COLOR_VARIABLE_NAME = "g:highlightedyank_highlight_color"
private var defaultHighlightTextColor: Color? = null private var defaultHighlightTextColor: Color? = null
@@ -197,7 +202,13 @@ class VimHighlightedYank: VimExtension, VimYankListener, VimInsertListener {
extractFun(value) extractFun(value)
} }
catch (e: Exception){ catch (e: Exception){
VimPlugin.showMessage("highlightedyank: Invalid value of $variableName -- ${e.message}") VimPlugin.showMessage(
MessageHelper.message(
"highlightedyank.invalid.value.of.0.1",
variableName,
e.message ?: ""
)
)
default default
} }

View File

@@ -33,22 +33,41 @@ import com.maddyhome.idea.vim.extension.VimExtensionFacade.putKeyMapping
import com.maddyhome.idea.vim.extension.VimExtensionHandler import com.maddyhome.idea.vim.extension.VimExtensionHandler
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.visual.vimSetSelection import com.maddyhome.idea.vim.group.visual.vimSetSelection
import com.maddyhome.idea.vim.helper.Direction
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.MessageHelper
import com.maddyhome.idea.vim.helper.SearchHelper.findWordUnderCursor import com.maddyhome.idea.vim.helper.SearchHelper.findWordUnderCursor
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.endOffsetInclusive import com.maddyhome.idea.vim.helper.endOffsetInclusive
import com.maddyhome.idea.vim.helper.exitVisualMode import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.inVisualMode import com.maddyhome.idea.vim.helper.inVisualMode
import com.maddyhome.idea.vim.option.OptionsManager import com.maddyhome.idea.vim.option.OptionsManager
import org.jetbrains.annotations.NonNls
import java.lang.Integer.min import java.lang.Integer.min
import java.util.* import java.util.*
import kotlin.Comparator
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val NEXT_WHOLE_OCCURRENCE = "<Plug>NextWholeOccurrence" private const val NEXT_WHOLE_OCCURRENCE = "<Plug>NextWholeOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val NEXT_OCCURRENCE = "<Plug>NextOccurrence" private const val NEXT_OCCURRENCE = "<Plug>NextOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val SKIP_OCCURRENCE = "<Plug>SkipOccurrence" private const val SKIP_OCCURRENCE = "<Plug>SkipOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val REMOVE_OCCURRENCE = "<Plug>RemoveOccurrence" private const val REMOVE_OCCURRENCE = "<Plug>RemoveOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val ALL_WHOLE_OCCURRENCES = "<Plug>AllWholeOccurrences" private const val ALL_WHOLE_OCCURRENCES = "<Plug>AllWholeOccurrences"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls
private const val ALL_OCCURRENCES = "<Plug>AllOccurrences" private const val ALL_OCCURRENCES = "<Plug>AllOccurrences"
/** /**
@@ -62,11 +81,23 @@ class VimMultipleCursorsExtension : VimExtension {
override fun init() { override fun init() {
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), owner, NextOccurrenceHandler(), false) putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_WHOLE_OCCURRENCE), owner, NextOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(NEXT_OCCURRENCE), owner, NextOccurrenceHandler(whole = false), false) putExtensionHandlerMapping(
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), owner, AllOccurrencesHandler(), false ) MappingMode.NXO,
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_OCCURRENCES), owner, AllOccurrencesHandler(whole = false), false ) parseKeys(NEXT_OCCURRENCE),
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), owner, SkipOccurrenceHandler(), false ) owner,
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), owner, RemoveOccurrenceHandler(), false ) NextOccurrenceHandler(whole = false),
false
)
putExtensionHandlerMapping(MappingMode.NXO, parseKeys(ALL_WHOLE_OCCURRENCES), owner, AllOccurrencesHandler(), false)
putExtensionHandlerMapping(
MappingMode.NXO,
parseKeys(ALL_OCCURRENCES),
owner,
AllOccurrencesHandler(whole = false),
false
)
putExtensionHandlerMapping(MappingMode.X, parseKeys(SKIP_OCCURRENCE), owner, SkipOccurrenceHandler(), false)
putExtensionHandlerMapping(MappingMode.X, parseKeys(REMOVE_OCCURRENCE), owner, RemoveOccurrenceHandler(), false)
putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), owner, parseKeys(NEXT_WHOLE_OCCURRENCE), true) putKeyMapping(MappingMode.NXO, parseKeys("<A-n>"), owner, parseKeys(NEXT_WHOLE_OCCURRENCE), true)
putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), owner, parseKeys(NEXT_OCCURRENCE), true) putKeyMapping(MappingMode.NXO, parseKeys("g<A-n>"), owner, parseKeys(NEXT_OCCURRENCE), true)
@@ -87,7 +118,8 @@ class VimMultipleCursorsExtension : VimExtension {
inner class NextOccurrenceHandler(val whole: Boolean = true) : WriteActionHandler() { inner class NextOccurrenceHandler(val whole: Boolean = true) : WriteActionHandler() {
override fun executeInWriteAction(editor: Editor, context: DataContext) { override fun executeInWriteAction(editor: Editor, context: DataContext) {
val caretModel = editor.caretModel val caretModel = editor.caretModel
val patternComparator = if (OptionsManager.ignorecase.isSet) String.CASE_INSENSITIVE_ORDER else Comparator(String::compareTo) val patternComparator =
if (OptionsManager.ignorecase.isSet) String.CASE_INSENSITIVE_ORDER else Comparator(String::compareTo)
if (!editor.inVisualMode) { if (!editor.inVisualMode) {
if (caretModel.caretCount > 1) return if (caretModel.caretCount > 1) return
@@ -97,7 +129,7 @@ class VimMultipleCursorsExtension : VimExtension {
if (range.startOffset > caret.offset) return if (range.startOffset > caret.offset) return
val nextOffset = findNextOccurrence(editor, caret, range, whole) val nextOffset = findNextOccurrence(editor, caret, range, whole)
if (nextOffset == caret.selectionStart) VimPlugin.showMessage("No more matches") if (nextOffset == caret.selectionStart) VimPlugin.showMessage(MessageHelper.message("message.no.more.matches"))
} else { } else {
val newPositions = arrayListOf<VisualPosition>() val newPositions = arrayListOf<VisualPosition>()
val patterns = sortedSetOf(patternComparator) val patterns = sortedSetOf(patternComparator)
@@ -125,10 +157,18 @@ class VimMultipleCursorsExtension : VimExtension {
val primaryCaret = editor.caretModel.primaryCaret val primaryCaret = editor.caretModel.primaryCaret
val nextOffset = VimPlugin.getSearch().searchNextFromOffset(editor, primaryCaret.offset + 1, 1) val nextOffset = VimPlugin.getSearch().searchNextFromOffset(editor, primaryCaret.offset + 1, 1)
val pattern = patterns.first() val pattern = patterns.first()
if (nextOffset == -1 || patternComparator.compare(EditorHelper.getText(editor, nextOffset, nextOffset + pattern.length), pattern) != 0) { if (nextOffset == -1 || patternComparator.compare(
EditorHelper.getText(
editor,
nextOffset,
nextOffset + pattern.length
), pattern
) != 0
) {
if (caretModel.caretCount > 1) return if (caretModel.caretCount > 1) return
val newNextOffset = VimPlugin.getSearch().search(editor, pattern, 1, EnumSet.of(CommandFlags.FLAG_SEARCH_FWD), false) val newNextOffset =
VimPlugin.getSearch().search(editor, pattern, 1, EnumSet.of(CommandFlags.FLAG_SEARCH_FWD), false)
if (newNextOffset != -1) { if (newNextOffset != -1) {
val caret = editor.caretModel.addCaret(editor.offsetToVisualPosition(newNextOffset)) ?: return val caret = editor.caretModel.addCaret(editor.offsetToVisualPosition(newNextOffset)) ?: return
@@ -140,7 +180,7 @@ class VimMultipleCursorsExtension : VimExtension {
caretModel.allCarets.forEach { caretModel.allCarets.forEach {
if (it.selectionStart == nextOffset) { if (it.selectionStart == nextOffset) {
VimPlugin.showMessage("No more matches") VimPlugin.showMessage(MessageHelper.message("message.no.more.matches"))
return return
} }
} }
@@ -159,7 +199,8 @@ class VimMultipleCursorsExtension : VimExtension {
val primaryCaret = caretModel.primaryCaret val primaryCaret = caretModel.primaryCaret
var nextOffset = if (editor.inVisualMode) { var nextOffset = if (editor.inVisualMode) {
val selectedText = primaryCaret.selectedText ?: return val selectedText = primaryCaret.selectedText ?: return
val nextOffset = VimPlugin.getSearch().search(editor, selectedText, 1, EnumSet.of(CommandFlags.FLAG_SEARCH_FWD), false) val nextOffset =
VimPlugin.getSearch().search(editor, selectedText, 1, EnumSet.of(CommandFlags.FLAG_SEARCH_FWD), false)
nextOffset nextOffset
} else { } else {
val range = findWordUnderCursor(editor, primaryCaret) ?: return val range = findWordUnderCursor(editor, primaryCaret) ?: return
@@ -194,7 +235,7 @@ class VimMultipleCursorsExtension : VimExtension {
editor.caretModel.allCarets.forEach { editor.caretModel.allCarets.forEach {
if (it.selectionStart == nextOffset) { if (it.selectionStart == nextOffset) {
VimPlugin.showMessage("No more matches") VimPlugin.showMessage(MessageHelper.message("message.no.more.matches"))
return return
} }
} }
@@ -229,7 +270,7 @@ class VimMultipleCursorsExtension : VimExtension {
val wordRange = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false) val wordRange = VimPlugin.getMotion().getWordRange(editor, caret, 1, false, false)
caret.vimSetSelection(wordRange.startOffset, wordRange.endOffsetInclusive, true) caret.vimSetSelection(wordRange.startOffset, wordRange.endOffsetInclusive, true)
val offset = VimPlugin.getSearch().searchWord(editor, caret, 1, whole, 1) val offset = VimPlugin.getSearch().searchWord(editor, caret, 1, whole, Direction.FORWARDS)
MotionGroup.moveCaret(editor, caret, range.endOffset - 1) MotionGroup.moveCaret(editor, caret, range.endOffset - 1)
return offset return offset

View File

@@ -42,6 +42,7 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
import com.maddyhome.idea.vim.helper.subMode import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.helper.vimForEachCaret import com.maddyhome.idea.vim.helper.vimForEachCaret
import com.maddyhome.idea.vim.key.OperatorFunction import com.maddyhome.idea.vim.key.OperatorFunction
import org.jetbrains.annotations.NonNls
class ReplaceWithRegister : VimExtension { class ReplaceWithRegister : VimExtension {
@@ -123,8 +124,11 @@ class ReplaceWithRegister : VimExtension {
} }
companion object { companion object {
@NonNls
private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator" private const val RWR_OPERATOR = "<Plug>ReplaceWithRegisterOperator"
@NonNls
private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine" private const val RWR_LINE = "<Plug>ReplaceWithRegisterLine"
@NonNls
private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual" private const val RWR_VISUAL = "<Plug>ReplaceWithRegisterVisual"
private fun doReplace(editor: Editor, visualSelection: PutData.VisualSelection) { private fun doReplace(editor: Editor, visualSelection: PutData.VisualSelection) {

View File

@@ -69,6 +69,7 @@ import com.maddyhome.idea.vim.option.BoundListOption;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import kotlin.Pair; import kotlin.Pair;
import kotlin.text.StringsKt; import kotlin.text.StringsKt;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -93,6 +94,9 @@ public class ChangeGroup {
private static final ImmutableSet<String> wordMotions = private static final ImmutableSet<String> wordMotions =
ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT); ImmutableSet.of(VIM_MOTION_WORD_RIGHT, VIM_MOTION_BIG_WORD_RIGHT, VIM_MOTION_CAMEL_RIGHT);
@NonNls private static final String HEX_START = "0x";
@NonNls private static final String MAX_HEX_INTEGER = "ffffffffffffffff";
private @Nullable Command lastInsert; private @Nullable Command lastInsert;
private List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private List<VimInsertListener> insertListeners = ContainerUtil.createLockFreeCopyOnWriteList();
@@ -1940,7 +1944,7 @@ public class ChangeGroup {
char ch = text.charAt(0); char ch = text.charAt(0);
if (hex && SearchHelper.NumberType.HEX.equals(numberType)) { if (hex && SearchHelper.NumberType.HEX.equals(numberType)) {
if (!text.toLowerCase().startsWith("0x")) throw new RuntimeException("Hex number should start with 0x: " + text); if (!text.toLowerCase().startsWith(HEX_START)) throw new RuntimeException("Hex number should start with 0x: " + text);
for (int i = text.length() - 1; i >= 2; i--) { for (int i = text.length() - 1; i >= 2; i--) {
int index = "abcdefABCDEF".indexOf(text.charAt(i)); int index = "abcdefABCDEF".indexOf(text.charAt(i));
if (index >= 0) { if (index >= 0) {
@@ -1952,7 +1956,7 @@ public class ChangeGroup {
BigInteger num = new BigInteger(text.substring(2), 16); BigInteger num = new BigInteger(text.substring(2), 16);
num = num.add(BigInteger.valueOf(count)); num = num.add(BigInteger.valueOf(count));
if (num.compareTo(BigInteger.ZERO) < 0) { if (num.compareTo(BigInteger.ZERO) < 0) {
num = new BigInteger("ffffffffffffffff", 16).add(BigInteger.ONE).add(num); num = new BigInteger(MAX_HEX_INTEGER, 16).add(BigInteger.ONE).add(num);
} }
number = num.toString(16); number = num.toString(16);
number = StringsKt.padStart(number, text.length() - 2, '0'); number = StringsKt.padStart(number, text.length() - 2, '0');

View File

@@ -19,13 +19,14 @@
package com.maddyhome.idea.vim.group package com.maddyhome.idea.vim.group
import com.maddyhome.idea.vim.common.Alias import com.maddyhome.idea.vim.common.Alias
import org.jetbrains.annotations.NonNls
/** /**
* @author Elliot Courant * @author Elliot Courant
*/ */
class CommandGroup { class CommandGroup {
companion object { companion object {
val BLACKLISTED_ALIASES = arrayOf("X", "Next", "Print") @NonNls val BLACKLISTED_ALIASES = arrayOf("X", "Next", "Print")
private const val overridePrefix = "!" private const val overridePrefix = "!"
} }

View File

@@ -27,14 +27,17 @@ import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.event.CaretEvent; import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.editor.event.CaretListener; import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.maddyhome.idea.vim.KeyHandler; import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.group.visual.VisualGroupKt;
import com.maddyhome.idea.vim.helper.*; import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.option.OptionChangeListener; import com.maddyhome.idea.vim.option.OptionChangeListener;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -44,7 +47,7 @@ import org.jetbrains.annotations.Nullable;
@State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")}) @State(name = "VimEditorSettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
public class EditorGroup implements PersistentStateComponent<Element> { public class EditorGroup implements PersistentStateComponent<Element> {
private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true; private static final boolean REFRAIN_FROM_SCROLLING_VIM_VALUE = true;
public static final String EDITOR_STORE_ELEMENT = "editor"; public static final @NonNls String EDITOR_STORE_ELEMENT = "editor";
private boolean isBlockCursor = false; private boolean isBlockCursor = false;
private boolean isRefrainFromScrolling = false; private boolean isRefrainFromScrolling = false;
@@ -203,6 +206,10 @@ public class EditorGroup implements PersistentStateComponent<Element> {
} }
} }
public boolean isBarCursorSettings() {
return !EditorSettingsExternalizable.getInstance().isBlockCursor();
}
public void editorCreated(@NotNull Editor editor) { public void editorCreated(@NotNull Editor editor) {
isBlockCursor = editor.getSettings().isBlockCursor(); isBlockCursor = editor.getSettings().isBlockCursor();
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling(); isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
@@ -217,7 +224,7 @@ public class EditorGroup implements PersistentStateComponent<Element> {
VimPlugin.getChange().insertBeforeCursor(editor, new EditorDataContext(editor, null)); VimPlugin.getChange().insertBeforeCursor(editor, new EditorDataContext(editor, null));
KeyHandler.getInstance().reset(editor); KeyHandler.getInstance().reset(editor);
} }
editor.getSettings().setBlockCursor(!CommandStateHelper.inInsertMode(editor)); VisualGroupKt.resetShape(CommandStateHelper.getMode(editor), editor);
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
} }

View File

@@ -44,6 +44,7 @@ import com.maddyhome.idea.vim.command.CommandState;
import com.maddyhome.idea.vim.common.TextRange; import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.helper.EditorHelper; import com.maddyhome.idea.vim.helper.EditorHelper;
import com.maddyhome.idea.vim.helper.EditorHelperRt; import com.maddyhome.idea.vim.helper.EditorHelperRt;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.SearchHelper; import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.option.IdeaWriteData; import com.maddyhome.idea.vim.option.IdeaWriteData;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
@@ -85,7 +86,7 @@ public class FileGroup {
} }
} }
else { else {
VimPlugin.showMessage("Unable to find " + filename); VimPlugin.showMessage(MessageHelper.message("unable.to.find.0", filename));
return false; return false;
} }

View File

@@ -27,6 +27,7 @@ import com.maddyhome.idea.vim.helper.StringHelper;
import com.maddyhome.idea.vim.option.NumberOption; import com.maddyhome.idea.vim.option.NumberOption;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -39,10 +40,10 @@ import java.util.Map;
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
}) })
public class HistoryGroup implements PersistentStateComponent<Element> { public class HistoryGroup implements PersistentStateComponent<Element> {
public static final String SEARCH = "search"; public static final @NonNls String SEARCH = "search";
public static final String COMMAND = "cmd"; public static final @NonNls String COMMAND = "cmd";
public static final String EXPRESSION = "expr"; public static final @NonNls String EXPRESSION = "expr";
public static final String INPUT = "input"; public static final @NonNls String INPUT = "input";
public void addEntry(String key, @NotNull String text) { public void addEntry(String key, @NotNull String text) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {

View File

@@ -47,6 +47,7 @@ import com.maddyhome.idea.vim.key.*;
import kotlin.Pair; import kotlin.Pair;
import kotlin.text.StringsKt; import kotlin.text.StringsKt;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -65,9 +66,9 @@ import static java.util.stream.Collectors.toList;
*/ */
@State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")}) @State(name = "VimKeySettings", storages = {@Storage(value = "$APP_CONFIG$/vim_settings.xml")})
public class KeyGroup implements PersistentStateComponent<Element> { public class KeyGroup implements PersistentStateComponent<Element> {
public static final String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts"; public static final @NonNls String SHORTCUT_CONFLICTS_ELEMENT = "shortcut-conflicts";
private static final String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict"; private static final @NonNls String SHORTCUT_CONFLICT_ELEMENT = "shortcut-conflict";
private static final String OWNER_ATTRIBUTE = "owner"; private static final @NonNls String OWNER_ATTRIBUTE = "owner";
private static final String TEXT_ELEMENT = "text"; private static final String TEXT_ELEMENT = "text";
private static final Logger logger = Logger.getInstance(KeyGroup.class); private static final Logger logger = Logger.getInstance(KeyGroup.class);
@@ -117,6 +118,14 @@ public class KeyGroup implements PersistentStateComponent<Element> {
unregisterKeyMapping(owner); unregisterKeyMapping(owner);
} }
public void removeKeyMapping(@NotNull Set<MappingMode> modes, @NotNull List<KeyStroke> keys) {
modes.stream().map(this::getKeyMapping).forEach(o -> o.delete(keys));
}
public void removeKeyMapping(@NotNull Set<MappingMode> modes) {
modes.stream().map(this::getKeyMapping).forEach(KeyMapping::delete);
}
public void putKeyMapping(@NotNull Set<MappingMode> modes, public void putKeyMapping(@NotNull Set<MappingMode> modes,
@NotNull List<KeyStroke> fromKeys, @NotNull List<KeyStroke> fromKeys,
@NotNull MappingOwner owner, @NotNull MappingOwner owner,
@@ -467,7 +476,7 @@ public class KeyGroup implements PersistentStateComponent<Element> {
return rows; return rows;
} }
private static @NotNull String getModesStringCode(@NotNull Set<MappingMode> modes) { private static @NotNull @NonNls String getModesStringCode(@NotNull Set<MappingMode> modes) {
if (modes.equals(MappingMode.NVO)) { if (modes.equals(MappingMode.NVO)) {
return ""; return "";
} }
@@ -525,7 +534,7 @@ public class KeyGroup implements PersistentStateComponent<Element> {
@Nullable @Nullable
@Override @Override
public Element getState() { public Element getState() {
Element element = new Element("key"); @NonNls Element element = new Element("key");
saveData(element); saveData(element);
return element; return element;
} }

View File

@@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project;
import com.maddyhome.idea.vim.KeyHandler; import com.maddyhome.idea.vim.KeyHandler;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.common.Register; import com.maddyhome.idea.vim.common.Register;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.StringHelper; import com.maddyhome.idea.vim.helper.StringHelper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -130,7 +131,8 @@ public class MacroGroup {
}; };
ApplicationManager.getApplication().invokeLater( ApplicationManager.getApplication().invokeLater(
() -> CommandProcessor.getInstance().executeCommand(project, run, "Vim Macro Playback", keys.get(pos))); () -> CommandProcessor.getInstance()
.executeCommand(project, run, MessageHelper.message("command.name.vim.macro.playback"), keys.get(pos)));
} }
public void postKey(@NotNull KeyStroke stroke, @NotNull Editor editor) { public void postKey(@NotNull KeyStroke stroke, @NotNull Editor editor) {

View File

@@ -48,6 +48,7 @@ import com.maddyhome.idea.vim.helper.SearchHelper;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -811,7 +812,7 @@ public class MarkGroup implements PersistentStateComponent<Element> {
private static final int SAVE_JUMP_COUNT = 100; private static final int SAVE_JUMP_COUNT = 100;
public static final String WR_GLOBAL_MARKS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final String WR_GLOBAL_MARKS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String WR_REGULAR_FILE_MARKS = "abcdefghijklmnopqrstuvwxyz"; public static final @NonNls String WR_REGULAR_FILE_MARKS = "abcdefghijklmnopqrstuvwxyz";
/** Marks: abcdefghijklmnopqrstuvwxyz' */ /** Marks: abcdefghijklmnopqrstuvwxyz' */
private static final String WR_FILE_MARKS = WR_REGULAR_FILE_MARKS + "'"; private static final String WR_FILE_MARKS = WR_REGULAR_FILE_MARKS + "'";

View File

@@ -41,11 +41,14 @@ import com.maddyhome.idea.vim.group.visual.VisualGroupKt;
import com.maddyhome.idea.vim.handler.MotionActionHandler; import com.maddyhome.idea.vim.handler.MotionActionHandler;
import com.maddyhome.idea.vim.handler.TextObjectActionHandler; import com.maddyhome.idea.vim.handler.TextObjectActionHandler;
import com.maddyhome.idea.vim.helper.*; import com.maddyhome.idea.vim.helper.*;
import com.maddyhome.idea.vim.listener.IdeaSpecifics;
import com.maddyhome.idea.vim.option.NumberOption; import com.maddyhome.idea.vim.option.NumberOption;
import com.maddyhome.idea.vim.option.OptionChangeListener;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.ui.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import kotlin.Pair; import kotlin.Pair;
import kotlin.ranges.IntProgression; import kotlin.ranges.IntProgression;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -184,7 +187,7 @@ public class MotionGroup {
if (caretVisualLine < topVisualLine + scrollOffset) { if (caretVisualLine < topVisualLine + scrollOffset) {
newVisualLine = normalizeVisualLine(editor, topVisualLine + scrollOffset); newVisualLine = normalizeVisualLine(editor, topVisualLine + scrollOffset);
} }
else if (caretVisualLine >= bottomVisualLine - scrollOffset) { else if (caretVisualLine > bottomVisualLine - scrollOffset) {
newVisualLine = normalizeVisualLine(editor, bottomVisualLine - scrollOffset); newVisualLine = normalizeVisualLine(editor, bottomVisualLine - scrollOffset);
} }
else { else {
@@ -316,6 +319,8 @@ public class MotionGroup {
else { else {
ModeHelper.exitVisualMode(editor); ModeHelper.exitVisualMode(editor);
} }
IdeaSpecifics.AppCodeTemplates.onMovement(editor, caret, oldOffset < offset);
} }
private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) { private @Nullable Editor selectEditor(@NotNull Editor editor, @NotNull Mark mark) {
@@ -631,10 +636,10 @@ public class MotionGroup {
private static void scrollCaretIntoViewVertically(@NotNull Editor editor, final int caretLine) { private static void scrollCaretIntoViewVertically(@NotNull Editor editor, final int caretLine) {
// TODO: Make this work with soft wraps // TODO: Make this work with soft wraps
// Vim's algorithm works counts line heights for wrapped lines. We're using visual lines, which handles collapsed // Vim's algorithm works by counting line heights for wrapped lines. We're using visual lines, which handles
// folds, but treats soft wrapped lines as individual lines. // collapsed folds, but treats soft wrapped lines as individual lines.
// Ironically, after figuring out how Vim's algorithm works (although not *why*), it looks likely to be rewritten as // Ironically, after figuring out how Vim's algorithm works (although not *why*) and reimplementing, it looks likely
// a dumb line for line reimplementation. // that this needs to be replaced as a more or less dumb line for line rewrite.
final int topLine = getVisualLineAtTopOfScreen(editor); final int topLine = getVisualLineAtTopOfScreen(editor);
final int bottomLine = getVisualLineAtBottomOfScreen(editor); final int bottomLine = getVisualLineAtBottomOfScreen(editor);
@@ -642,7 +647,7 @@ public class MotionGroup {
// We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred // We need the non-normalised value here, so we can handle cases such as so=999 to keep the current line centred
final int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value(); final int scrollOffset = OptionsManager.INSTANCE.getScrolloff().value();
final int topBound = topLine + scrollOffset; final int topBound = topLine + scrollOffset;
final int bottomBound = Math.max(topBound + 1, bottomLine - scrollOffset); final int bottomBound = Math.max(topBound, bottomLine - scrollOffset);
// If we need to scroll the current line more than half a screen worth of lines then we just centre the new // If we need to scroll the current line more than half a screen worth of lines then we just centre the new
// current line. This mimics vim behavior of e.g. 100G in a 300 line file with a screen size of 25 centering line // current line. This mimics vim behavior of e.g. 100G in a 300 line file with a screen size of 25 centering line
@@ -650,7 +655,6 @@ public class MotionGroup {
// Note that block inlays means that the pixel height we are scrolling can be larger than half the screen, even if // Note that block inlays means that the pixel height we are scrolling can be larger than half the screen, even if
// the number of lines is less. I'm not sure what impact this has. // the number of lines is less. I'm not sure what impact this has.
final int height = bottomLine - topLine + 1; final int height = bottomLine - topLine + 1;
final int halfHeight = Math.max(2, (height / 2) - 1);
// Scrolljump isn't handled as you might expect. It is the minimal number of lines to scroll, but that doesn't mean // Scrolljump isn't handled as you might expect. It is the minimal number of lines to scroll, but that doesn't mean
// newLine = caretLine +/- MAX(sj, so) // newLine = caretLine +/- MAX(sj, so)
@@ -663,7 +667,7 @@ public class MotionGroup {
// (See move.c:scroll_cursor_top) // (See move.c:scroll_cursor_top)
// //
// When scrolling down (`j` - scrolling window down in the buffer; more lines are visible at the bottom), Vim again // When scrolling down (`j` - scrolling window down in the buffer; more lines are visible at the bottom), Vim again
// expands lines above and below the new bottom line, but calcualtes things a little differently. The total number // expands lines above and below the new bottom line, but calculates things a little differently. The total number
// of lines expanded is at least scrolljump and there must be at least scrolloff lines below. // of lines expanded is at least scrolljump and there must be at least scrolloff lines below.
// Since the lines are advancing simultaneously, it is only possible to get scrolljump/2 above the new cursor line. // Since the lines are advancing simultaneously, it is only possible to get scrolljump/2 above the new cursor line.
// If there are fewer than scrolljump/2 lines between the current bottom line and the new cursor line, the extra // If there are fewer than scrolljump/2 lines between the current bottom line and the new cursor line, the extra
@@ -678,10 +682,26 @@ public class MotionGroup {
// out correct scroll locations // out correct scroll locations
final int scrollJump = getScrollJump(editor, height); final int scrollJump = getScrollJump(editor, height);
if (caretLine < topBound) { // Unavoidable fudge value. Multiline rendered doc comments can mean we have very few actual lines, and scrolling
// can get stuck in a loop as we re-centre the cursor instead of actually moving it. But if we ignore all inlays
// and use the approximate screen height instead of the actual screen height (in lines), we make incorrect
// assumptions about the top/bottom line numbers and can scroll to the wrong location. E.g. if there are enough doc
// comments (String.java) it's possible to get 12 lines of actual code on screen. Given scrolloff=5, it's very easy
// to hit problems, and have (scrolloffset > height / 2) and scroll to the middle of the screen. We'll use this
// fudge value to make sure we're working with sensible values. Note that this problem doesn't affect code without
// block inlays as positioning the cursor in the middle of the screen always positions it in a deterministic manner,
// relative to other text in the file.
final int inlayAwareMinHeightFudge = getApproximateScreenHeight(editor) / 2;
// Note that while these calculations do the same thing that Vim does, it processes them differently. E.g. it
// optionally checks and moves the top line, then optionally checks the bottom line. This gives us the same results
// via the tests.
if (height > inlayAwareMinHeightFudge && scrollOffset > height / 2) {
scrollVisualLineToMiddleOfScreen(editor, caretLine);
} else if (caretLine < topBound) {
// Scrolling up, put the cursor at the top of the window (minus scrolloff) // Scrolling up, put the cursor at the top of the window (minus scrolloff)
// Initial approximation in move.c:update_topline // Initial approximation in move.c:update_topline (including same calculation for halfHeight)
if (topLine + scrollOffset - caretLine >= halfHeight) { if (topLine + scrollOffset - caretLine >= Math.max(2, (height / 2) - 1)) {
scrollVisualLineToMiddleOfScreen(editor, caretLine); scrollVisualLineToMiddleOfScreen(editor, caretLine);
} }
else { else {
@@ -692,9 +712,12 @@ public class MotionGroup {
final int scrollOffsetTopLine = Math.max(0, caretLine - scrollOffset); final int scrollOffsetTopLine = Math.max(0, caretLine - scrollOffset);
final int newTopLine = Math.min(scrollOffsetTopLine, scrollJumpTopLine); final int newTopLine = Math.min(scrollOffsetTopLine, scrollJumpTopLine);
// Used is set to the line height of caretLine, and then incremented by line height of the lines above and // Used is set to the line height of caretLine (1 or how many lines soft wraps take up), and then incremented by
// below caretLine (up to scrolloff or end of file) // the line heights of the lines above and below caretLine (up to scrolloff or end of file).
final int used = 1 + (newTopLine - topLine) + Math.min(scrollOffset, getVisualLineCount(editor) - topLine); // Our implementation ignores soft wrap line heights. Folds already have a line height of 1.
final int usedAbove = caretLine - newTopLine;
final int usedBelow = Math.min(scrollOffset, getVisualLineCount(editor) - caretLine);
final int used = 1 + usedAbove + usedBelow;
if (used > height) { if (used > height) {
scrollVisualLineToMiddleOfScreen(editor, caretLine); scrollVisualLineToMiddleOfScreen(editor, caretLine);
} }
@@ -1374,4 +1397,21 @@ public class MotionGroup {
return moveCaretToLineEnd(editor, visualLineToLogicalLine(editor, line), allowPastEnd); return moveCaretToLineEnd(editor, visualLineToLogicalLine(editor, line), allowPastEnd);
} }
} }
public static class ScrollOptionsChangeListener implements OptionChangeListener<String> {
public static ScrollOptionsChangeListener INSTANCE = new ScrollOptionsChangeListener();
@Contract(pure = true)
private ScrollOptionsChangeListener() {
}
@Override
public void valueChange(String oldValue, String newValue) {
for (Editor editor : EditorFactory.getInstance().getAllEditors()) {
if (UserDataManager.getVimEditorGroup(editor)) {
MotionGroup.scrollCaretIntoView(editor);
}
}
}
}
} }

View File

@@ -31,7 +31,7 @@ import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.ex.CommandParser; import com.maddyhome.idea.vim.ex.CommandParser;
import com.maddyhome.idea.vim.ex.ExException; import com.maddyhome.idea.vim.ex.ExException;
import com.maddyhome.idea.vim.helper.UiHelper; import com.maddyhome.idea.vim.helper.UiHelper;
import com.maddyhome.idea.vim.ui.ExEntryPanel; import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;

View File

@@ -18,7 +18,6 @@
package com.maddyhome.idea.vim.group; package com.maddyhome.idea.vim.group;
import com.google.common.collect.ImmutableList;
import com.intellij.codeInsight.editorActions.CopyPastePostProcessor; import com.intellij.codeInsight.editorActions.CopyPastePostProcessor;
import com.intellij.codeInsight.editorActions.CopyPastePreProcessor; import com.intellij.codeInsight.editorActions.CopyPastePreProcessor;
import com.intellij.codeInsight.editorActions.TextBlockTransferable; import com.intellij.codeInsight.editorActions.TextBlockTransferable;
@@ -62,6 +61,7 @@ import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.ui.ClipboardHandler; import com.maddyhome.idea.vim.ui.ClipboardHandler;
import kotlin.Pair; import kotlin.Pair;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -80,18 +80,33 @@ import java.util.stream.Collectors;
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
}) })
public class RegisterGroup implements PersistentStateComponent<Element> { public class RegisterGroup implements PersistentStateComponent<Element> {
private static final String WRITABLE_REGISTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-*+_/\""; public static final char UNNAMED_REGISTER = '"';
private static final String READONLY_REGISTERS = ":.%#=/"; public static final char LAST_SEARCH_REGISTER = '/'; // IdeaVim does not supporting writing to this register
private static final String RECORDABLE_REGISTER = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static final char LAST_COMMAND_REGISTER = ':';
private static final String PLAYBACK_REGISTER = RECORDABLE_REGISTER + "\".*+"; private static final char LAST_INSERTED_TEXT_REGISTER = '.';
public static final char SMALL_DELETION_REGISTER = '-';
private static final char BLACK_HOLE_REGISTER = '_';
private static final char ALTERNATE_BUFFER_REGISTER = '#'; // Not supported
private static final char EXPRESSION_BUFFER_REGISTER = '='; // Not supported
private static final char CURRENT_FILENAME_REGISTER = '%'; // Not supported
private static final @NonNls String CLIPBOARD_REGISTERS = "*+";
private static final @NonNls String NUMBERED_REGISTERS = "0123456789";
private static final @NonNls String NAMED_REGISTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final @NonNls String WRITABLE_REGISTERS = NUMBERED_REGISTERS + NAMED_REGISTERS + CLIPBOARD_REGISTERS
+ SMALL_DELETION_REGISTER + BLACK_HOLE_REGISTER + UNNAMED_REGISTER + LAST_SEARCH_REGISTER;
private static final String READONLY_REGISTERS = ""
+ CURRENT_FILENAME_REGISTER + LAST_COMMAND_REGISTER + LAST_INSERTED_TEXT_REGISTER + ALTERNATE_BUFFER_REGISTER
+ EXPRESSION_BUFFER_REGISTER; // Expression buffer is not actually readonly
private static final @NonNls String RECORDABLE_REGISTERS = NUMBERED_REGISTERS + NAMED_REGISTERS;
private static final String PLAYBACK_REGISTERS = RECORDABLE_REGISTERS + UNNAMED_REGISTER + CLIPBOARD_REGISTERS + LAST_INSERTED_TEXT_REGISTER;
private static final String VALID_REGISTERS = WRITABLE_REGISTERS + READONLY_REGISTERS; 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 static final Logger logger = Logger.getInstance(RegisterGroup.class.getName());
public static final char UNNAMED_REGISTER = '"'; private final @NotNull HashMap<Character, Register> registers = new HashMap<>();
private char defaultRegister = UNNAMED_REGISTER; private char defaultRegister = UNNAMED_REGISTER;
private char lastRegister = defaultRegister; private char lastRegister = defaultRegister;
private final @NotNull HashMap<Character, Register> registers = new HashMap<>();
private char recordRegister = 0; private char recordRegister = 0;
private @Nullable List<KeyStroke> recordList = null; private @Nullable List<KeyStroke> recordList = null;
@@ -172,13 +187,43 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
return false; return false;
} }
public boolean storeTextInternal(@NotNull Editor editor, @NotNull TextRange range, @NotNull String text, /**
@NotNull SelectionType type, char register, boolean isDelete) { * Stores text, character wise, in the given special register
// Null register doesn't get saved *
if (lastRegister == '_') return true; * <p>This method is intended to support writing to registers when the text cannot be yanked from an editor. This is
* expected to only be used to update the search and command registers. It will not update named registers.</p>
*
* <p>While this method allows setting the unnamed register, this should only be done from tests, and only when it's
* not possible to yank or cut from the fixture editor. This method will skip additional text processing, and won't
* update other registers such as the small delete register or reorder the numbered registers. It is much more
* preferable to yank from the fixture editor.</p>
*
* @param register The register to use for storing the text. Cannot be a normal text register
* @param text The text to store, without further processing
* @return True if the text is stored, false if the passed register is not supported
*/
public boolean storeTextSpecial(char register, @NotNull String text) {
if (READONLY_REGISTERS.indexOf(register) == -1 && register != LAST_SEARCH_REGISTER
&& register != UNNAMED_REGISTER) {
return false;
}
registers.put(register, new Register(register, SelectionType.CHARACTER_WISE, text, new ArrayList<>()));
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + text + "\"");
return true;
}
private boolean storeTextInternal(@NotNull Editor editor, @NotNull TextRange range, @NotNull String text,
@NotNull SelectionType type, char register, boolean isDelete) {
// Null register doesn't get saved, but acts like it was
if (lastRegister == BLACK_HOLE_REGISTER) return true;
int start = range.getStartOffset(); int start = range.getStartOffset();
int end = range.getEndOffset(); int end = range.getEndOffset();
if (isDelete && start == end) {
return true;
}
// Normalize the start and end // Normalize the start and end
if (start > end) { if (start > end) {
int t = start; int t = start;
@@ -213,7 +258,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\""); if (logger.isDebugEnabled()) logger.debug("register '" + register + "' contains: \"" + processedText + "\"");
} }
if (CLIPBOARD_REGISTERS.contains(register)) { if (CLIPBOARD_REGISTERS.indexOf(register) >= 0) {
ClipboardHandler.setClipboardText(processedText, new ArrayList<>(transferableData), text); ClipboardHandler.setClipboardText(processedText, new ArrayList<>(transferableData), text);
} }
@@ -242,7 +287,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
// Deletes smaller than one line and without specified register go the the "-" register // Deletes smaller than one line and without specified register go the the "-" register
if (smallInlineDeletion && register == defaultRegister) { if (smallInlineDeletion && register == defaultRegister) {
registers.put('-', new Register('-', type, processedText, new ArrayList<>(transferableData))); registers.put(SMALL_DELETION_REGISTER, new Register(SMALL_DELETION_REGISTER, type, processedText, new ArrayList<>(transferableData)));
} }
} }
// Yanks also go to register 0 if the default register was used // Yanks also go to register 0 if the default register was used
@@ -332,7 +377,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
} }
public @Nullable Register getPlaybackRegister(char r) { public @Nullable Register getPlaybackRegister(char r) {
if (PLAYBACK_REGISTER.indexOf(r) != 0) { if (PLAYBACK_REGISTERS.indexOf(r) != 0) {
return getRegister(r); return getRegister(r);
} }
else { else {
@@ -345,7 +390,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
if (Character.isUpperCase(r)) { if (Character.isUpperCase(r)) {
r = Character.toLowerCase(r); r = Character.toLowerCase(r);
} }
return CLIPBOARD_REGISTERS.contains(r) ? refreshClipboardRegister(r) : registers.get(r); return CLIPBOARD_REGISTERS.indexOf(r) >= 0 ? refreshClipboardRegister(r) : registers.get(r);
} }
public void saveRegister(char r, Register register) { public void saveRegister(char r, Register register) {
@@ -353,7 +398,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
if (Character.isUpperCase(r)) { if (Character.isUpperCase(r)) {
r = Character.toLowerCase(r); r = Character.toLowerCase(r);
} }
if (CLIPBOARD_REGISTERS.contains(r)) { if (CLIPBOARD_REGISTERS.indexOf(r) >= 0) {
ClipboardHandler.setClipboardText(register.getText(), new ArrayList<>(register.getTransferableData()), register.getRawText()); ClipboardHandler.setClipboardText(register.getText(), new ArrayList<>(register.getTransferableData()), register.getRawText());
} }
registers.put(r, register); registers.put(r, register);
@@ -377,7 +422,8 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
public @NotNull List<Register> getRegisters() { public @NotNull List<Register> getRegisters() {
final List<Register> res = new ArrayList<>(registers.values()); final List<Register> res = new ArrayList<>(registers.values());
for (Character r : CLIPBOARD_REGISTERS) { for (int i = 0; i < CLIPBOARD_REGISTERS.length(); i++) {
final char r = CLIPBOARD_REGISTERS.charAt(i);
final Register register = refreshClipboardRegister(r); final Register register = refreshClipboardRegister(r);
if (register != null) { if (register != null) {
res.add(register); res.add(register);
@@ -388,7 +434,7 @@ public class RegisterGroup implements PersistentStateComponent<Element> {
} }
public boolean startRecording(Editor editor, char register) { public boolean startRecording(Editor editor, char register) {
if (RECORDABLE_REGISTER.indexOf(register) != -1) { if (RECORDABLE_REGISTERS.indexOf(register) != -1) {
CommandState.getInstance(editor).setRecording(true); CommandState.getInstance(editor).setRecording(true);
recordRegister = register; recordRegister = register;
recordList = new ArrayList<>(); recordList = new ArrayList<>();

View File

@@ -25,20 +25,15 @@ import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage; import com.intellij.openapi.components.Storage;
import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.*;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.editor.markup.*;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.FileEditorManagerEvent; import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Ref;
import com.intellij.ui.ColorUtil;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.command.CommandFlags; import com.maddyhome.idea.vim.command.CommandFlags;
import com.maddyhome.idea.vim.command.SelectionType;
import com.maddyhome.idea.vim.common.CharacterPosition; import com.maddyhome.idea.vim.common.CharacterPosition;
import com.maddyhome.idea.vim.common.TextRange; import com.maddyhome.idea.vim.common.TextRange;
import com.maddyhome.idea.vim.ex.ranges.LineRange; import com.maddyhome.idea.vim.ex.ranges.LineRange;
@@ -49,20 +44,24 @@ import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.regexp.CharPointer; import com.maddyhome.idea.vim.regexp.CharPointer;
import com.maddyhome.idea.vim.regexp.CharacterClasses; import com.maddyhome.idea.vim.regexp.CharacterClasses;
import com.maddyhome.idea.vim.regexp.RegExp; import com.maddyhome.idea.vim.regexp.RegExp;
import com.maddyhome.idea.vim.ui.ExEntryPanel;
import com.maddyhome.idea.vim.ui.ModalEntry; import com.maddyhome.idea.vim.ui.ModalEntry;
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel;
import kotlin.jvm.functions.Function1; import kotlin.jvm.functions.Function1;
import org.jdom.Element; import org.jdom.Element;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.swing.*; import javax.swing.*;
import java.awt.*;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.*;
import static com.maddyhome.idea.vim.helper.SearchHelperKtKt.shouldIgnoreCase;
@State(name = "VimSearchSettings", storages = { @State(name = "VimSearchSettings", storages = {
@Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED) @Storage(value = "$APP_CONFIG$/vim_settings_local.xml", roamingType = RoamingType.DISABLED)
@@ -81,9 +80,6 @@ public class SearchGroup implements PersistentStateComponent<Element> {
} }
}; };
options.getIgnorecase().addOptionChangeListener(updateHighlightsIfVisible); options.getIgnorecase().addOptionChangeListener(updateHighlightsIfVisible);
// It appears that when changing smartcase, Vim only redraws the highlights when the screen is redrawn. We can't
// reliably copy that, so do the most intuitive thing
options.getSmartcase().addOptionChangeListener(updateHighlightsIfVisible); options.getSmartcase().addOptionChangeListener(updateHighlightsIfVisible);
} }
@@ -104,7 +100,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
// This method is used in AceJump integration plugin // This method is used in AceJump integration plugin
@SuppressWarnings("unused") @SuppressWarnings("unused")
public int getLastDir() { public int getLastDir() {
return lastDir; return lastDir.toInt();
} }
public @Nullable String getLastPattern() { public @Nullable String getLastPattern() {
@@ -114,15 +110,13 @@ public class SearchGroup implements PersistentStateComponent<Element> {
public void resetState() { public void resetState() {
lastSearch = lastPattern = lastSubstitute = lastReplace = lastOffset = null; lastSearch = lastPattern = lastSubstitute = lastReplace = lastOffset = null;
lastIgnoreSmartCase = false; lastIgnoreSmartCase = false;
lastDir = 0; lastDir = Direction.UNSET;
resetShowSearchHighlight(); resetShowSearchHighlight();
} }
private void setLastPattern(@NotNull Editor editor, @NotNull String lastPattern) { private void setLastPattern(@NotNull String lastPattern) {
this.lastPattern = lastPattern; this.lastPattern = lastPattern;
VimPlugin.getRegister().storeTextInternal(editor, new TextRange(-1, -1), VimPlugin.getRegister().storeTextSpecial(RegisterGroup.LAST_SEARCH_REGISTER, lastPattern);
lastPattern, SelectionType.CHARACTER_WISE, '/', false);
VimPlugin.getHistory().addEntry(HistoryGroup.SEARCH, lastPattern); VimPlugin.getHistory().addEntry(HistoryGroup.SEARCH, lastPattern);
} }
@@ -209,7 +203,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
else { else {
// XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method // XXX: The Ex entry panel is used only for UI here, its logic might be inappropriate for this method
final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts(); final ExEntryPanel exEntryPanel = ExEntryPanel.getInstanceWithoutShortcuts();
exEntryPanel.activate(editor, new EditorDataContext(editor, null), "Replace with " + match + " (y/n/a/q/l)?", "", 1); exEntryPanel.activate(editor, new EditorDataContext(editor, null), MessageHelper.message("replace.with.0", match), "", 1);
MotionGroup.moveCaret(editor, caret, startoff); MotionGroup.moveCaret(editor, caret, startoff);
ModalEntry.INSTANCE.activate(keyStrokeProcessor); ModalEntry.INSTANCE.activate(keyStrokeProcessor);
exEntryPanel.deactivate(true, false); exEntryPanel.deactivate(true, false);
@@ -217,13 +211,6 @@ public class SearchGroup implements PersistentStateComponent<Element> {
return result.get(); return result.get();
} }
private static boolean shouldIgnoreCase(@NotNull String pattern, boolean ignoreSmartCase) {
boolean sc = !ignoreSmartCase && OptionsManager.INSTANCE.getSmartcase().isSet();
boolean ic = OptionsManager.INSTANCE.getIgnorecase().isSet();
return ic && !(sc && StringHelper.containsUpperCase(pattern));
}
public int search(@NotNull Editor editor, @NotNull String command, int count, EnumSet<CommandFlags> flags, boolean moveCursor) { public int search(@NotNull Editor editor, @NotNull String command, int count, EnumSet<CommandFlags> flags, boolean moveCursor) {
return search(editor, editor.getCaretModel().getPrimaryCaret(), command, count, flags, moveCursor); return search(editor, editor.getCaretModel().getPrimaryCaret(), command, count, flags, moveCursor);
} }
@@ -241,12 +228,12 @@ public class SearchGroup implements PersistentStateComponent<Element> {
} }
public int search(@NotNull Editor editor, @NotNull String command, int startOffset, int count, @NotNull EnumSet<CommandFlags> flags) { public int search(@NotNull Editor editor, @NotNull String command, int startOffset, int count, @NotNull EnumSet<CommandFlags> flags) {
int dir = DIR_FORWARDS; Direction dir = Direction.FORWARDS;
char type = '/'; char type = '/';
String pattern = lastSearch; String pattern = lastSearch;
String offset = lastOffset; String offset = lastOffset;
if (flags.contains(CommandFlags.FLAG_SEARCH_REV)) { if (flags.contains(CommandFlags.FLAG_SEARCH_REV)) {
dir = DIR_BACKWARDS; dir = Direction.BACKWARDS;
type = '?'; type = '?';
} }
@@ -283,7 +270,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
lastSearch = pattern; lastSearch = pattern;
lastIgnoreSmartCase = false; lastIgnoreSmartCase = false;
if (pattern != null) { if (pattern != null) {
setLastPattern(editor, pattern); setLastPattern(pattern);
} }
lastOffset = offset; lastOffset = offset;
lastDir = dir; lastDir = dir;
@@ -300,7 +287,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
return findItOffset(editor, startOffset, count, lastDir); return findItOffset(editor, startOffset, count, lastDir);
} }
public int searchWord(@NotNull Editor editor, @NotNull Caret caret, int count, boolean whole, int dir) { public int searchWord(@NotNull Editor editor, @NotNull Caret caret, int count, boolean whole, Direction dir) {
TextRange range = SearchHelper.findWordUnderCursor(editor, caret); TextRange range = SearchHelper.findWordUnderCursor(editor, caret);
if (range == null) { if (range == null) {
logger.warn("No range was found"); logger.warn("No range was found");
@@ -320,7 +307,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
lastSearch = pattern.toString(); lastSearch = pattern.toString();
lastIgnoreSmartCase = true; lastIgnoreSmartCase = true;
setLastPattern(editor, lastSearch); setLastPattern(lastSearch);
lastOffset = ""; lastOffset = "";
lastDir = dir; lastDir = dir;
@@ -335,16 +322,16 @@ public class SearchGroup implements PersistentStateComponent<Element> {
} }
public int searchPrevious(@NotNull Editor editor, @NotNull Caret caret, int count) { public int searchPrevious(@NotNull Editor editor, @NotNull Caret caret, int count) {
return searchNextWithDirection(editor, caret, count, -lastDir); return searchNextWithDirection(editor, caret, count, lastDir.reverse());
} }
public int searchNextFromOffset(@NotNull Editor editor, int offset, int count) { public int searchNextFromOffset(@NotNull Editor editor, int offset, int count) {
resetShowSearchHighlight(); resetShowSearchHighlight();
updateSearchHighlights(); updateSearchHighlights();
return findItOffset(editor, offset, count, 1); return findItOffset(editor, offset, count, Direction.FORWARDS);
} }
private int searchNextWithDirection(@NotNull Editor editor, @NotNull Caret caret, int count, int dir) { private int searchNextWithDirection(@NotNull Editor editor, @NotNull Caret caret, int count, Direction dir) {
resetShowSearchHighlight(); resetShowSearchHighlight();
updateSearchHighlights(); updateSearchHighlights();
final int startOffset = caret.getOffset(); final int startOffset = caret.getOffset();
@@ -368,163 +355,25 @@ public class SearchGroup implements PersistentStateComponent<Element> {
} }
private void forceUpdateSearchHighlights() { private void forceUpdateSearchHighlights() {
updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, true); SearchHighlightsHelper.updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, true);
} }
private void updateSearchHighlights() { private void updateSearchHighlights() {
updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, false); SearchHighlightsHelper.updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, false);
} }
public void resetIncsearchHighlights() { public void resetIncsearchHighlights() {
updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, true); SearchHighlightsHelper.updateSearchHighlights(lastSearch, lastIgnoreSmartCase, showSearchHighlight, true);
}
public int updateIncsearchHighlights(@NotNull Editor editor, @NotNull String pattern, boolean forwards, int caretOffset, @Nullable LineRange searchRange) {
final int searchStartOffset = searchRange != null ? EditorHelper.getLineStartOffset(editor, searchRange.startLine) : caretOffset;
final boolean showHighlights = OptionsManager.INSTANCE.getHlsearch().isSet();
return updateSearchHighlights(pattern, false, showHighlights, searchStartOffset, searchRange, forwards, false);
}
private void updateSearchHighlights(@Nullable String pattern, boolean shouldIgnoreSmartCase, boolean showHighlights, boolean forceUpdate) {
updateSearchHighlights(pattern, shouldIgnoreSmartCase, showHighlights, -1, null, true, forceUpdate);
}
/**
* Refreshes current search highlights for all editors of currently active text editor/document
*/
private int updateSearchHighlights(@Nullable String pattern, boolean shouldIgnoreSmartCase, boolean showHighlights,
int initialOffset, @Nullable LineRange searchRange, boolean forwards, boolean forceUpdate) {
int currentMatchOffset = -1;
ProjectManager projectManager = ProjectManager.getInstanceIfCreated();
if (projectManager == null) return currentMatchOffset;
Project[] projects = projectManager.getOpenProjects();
for (Project project : projects) {
Editor current = FileEditorManager.getInstance(project).getSelectedTextEditor();
Editor[] editors = current == null ? null : EditorFactory.getInstance().getEditors(current.getDocument(), project);
if (editors == null) {
continue;
}
for (final Editor editor : editors) {
// Force update for the situations where the text is the same, but the ignore case values have changed.
// E.g. Use `*` to search for a word (which ignores smartcase), then use `/<Up>` to search for the same pattern,
// which will match smartcase. Or changing the smartcase/ignorecase settings
if (shouldRemoveSearchHighlight(editor, pattern, showHighlights) || forceUpdate) {
removeSearchHighlight(editor);
}
if (shouldAddAllSearchHighlights(editor, pattern, showHighlights)) {
final int startLine = searchRange == null ? 0 : searchRange.startLine;
final int endLine = searchRange == null ? -1 : searchRange.endLine;
List<TextRange> results = findAll(editor, pattern, startLine, endLine, shouldIgnoreCase(pattern, shouldIgnoreSmartCase));
if (!results.isEmpty()) {
currentMatchOffset = findClosestMatch(editor, results, initialOffset, forwards);
highlightSearchResults(editor, pattern, results, currentMatchOffset);
}
UserDataManager.setVimLastSearch(editor, pattern);
}
else if (shouldAddCurrentMatchSearchHighlight(pattern, showHighlights, initialOffset)) {
final boolean wrap = OptionsManager.INSTANCE.getWrapscan().isSet();
final EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.WHOLE_FILE);
if (wrap) searchOptions.add(SearchOptions.WRAP);
if (shouldIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE);
if (!forwards) searchOptions.add(SearchOptions.BACKWARDS);
final TextRange result = findIt(editor, pattern, initialOffset, 1, searchOptions);
if (result != null && pattern != null) {
currentMatchOffset = result.getStartOffset();
final List<TextRange> results = Collections.singletonList(result);
highlightSearchResults(editor, pattern, results, currentMatchOffset);
}
}
else if (shouldMaintainCurrentMatchOffset(pattern, initialOffset)) {
final Integer offset = UserDataManager.getVimIncsearchCurrentMatchOffset(editor);
if (offset != null) {
currentMatchOffset = offset;
}
}
}
}
return currentMatchOffset;
}
/**
* Remove current search highlights if hlSearch is false, or if the pattern is changed
*/
@Contract("_, _, false -> true; _, null, true -> false")
private boolean shouldRemoveSearchHighlight(@NotNull Editor editor, String newPattern, boolean hlSearch) {
return !hlSearch || (newPattern != null && !newPattern.equals(UserDataManager.getVimLastSearch(editor)));
}
/**
* Add search highlights if hlSearch is true and the pattern is changed
*/
@Contract("_, _, false -> false; _, null, true -> false")
private boolean shouldAddAllSearchHighlights(@NotNull Editor editor, @Nullable String newPattern, boolean hlSearch) {
return hlSearch && newPattern != null && !newPattern.equals(UserDataManager.getVimLastSearch(editor)) && !Objects.equals(newPattern, "");
}
/**
* Add search highlight for current match if hlsearch is false and we're performing incsearch highlights
*/
@Contract("_, true, _ -> false")
private boolean shouldAddCurrentMatchSearchHighlight(@Nullable String pattern, boolean hlSearch, int initialOffset) {
return !hlSearch && isIncrementalSearchHighlights(initialOffset) && pattern != null && pattern.length() > 0;
}
/**
* Keep the current match offset if the pattern is still valid and we're performing incremental search highlights
* This will keep the caret position when editing the offset in e.g. `/foo/e+1`
*/
@Contract("null, _ -> false")
private boolean shouldMaintainCurrentMatchOffset(@Nullable String pattern, int initialOffset) {
return pattern != null && pattern.length() > 0 && isIncrementalSearchHighlights(initialOffset);
}
/**
* initialOffset is only valid if we're highlighting incsearch
*/
@Contract(pure = true)
private boolean isIncrementalSearchHighlights(int initialOffset) {
return initialOffset != -1;
} }
private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) { private void highlightSearchLines(@NotNull Editor editor, int startLine, int endLine) {
if (lastSearch != null) { if (lastSearch != null) {
final List<TextRange> results = findAll(editor, lastSearch, startLine, endLine, shouldIgnoreCase(lastSearch, lastIgnoreSmartCase)); final List<TextRange> results = findAll(editor, lastSearch, startLine, endLine,
highlightSearchResults(editor, lastSearch, results, -1); shouldIgnoreCase(lastSearch, lastIgnoreSmartCase));
SearchHighlightsHelper.highlightSearchResults(editor, lastSearch, results, -1);
} }
} }
private int findClosestMatch(@NotNull Editor editor, @NotNull List<TextRange> results, int initialOffset, boolean forwards) {
if (results.isEmpty() || initialOffset == -1) {
return -1;
}
final int size = EditorHelperRt.getFileSize(editor);
final TextRange max = Collections.max(results, (r1, r2) -> {
final int d1 = distance(r1, initialOffset, forwards, size);
final int d2 = distance(r2, initialOffset, forwards, size);
if (d1 < 0 && d2 >= 0) {
return Integer.MAX_VALUE;
}
return d2 - d1;
});
if (!OptionsManager.INSTANCE.getWrapscan().isSet()) {
final int start = max.getStartOffset();
if (forwards && start < initialOffset) {
return -1;
}
else if (start >= initialOffset) {
return -1;
}
}
return max.getStartOffset();
}
public @Nullable TextRange getNextSearchRange(@NotNull Editor editor, int count, boolean forwards) { public @Nullable TextRange getNextSearchRange(@NotNull Editor editor, int count, boolean forwards) {
editor.getCaretModel().removeSecondaryCarets(); editor.getCaretModel().removeSecondaryCarets();
TextRange current = findUnderCaret(editor); TextRange current = findUnderCaret(editor);
@@ -574,23 +423,13 @@ public class SearchGroup implements PersistentStateComponent<Element> {
return findIt(editor, lastSearch, startOffset, 1, searchOptions); return findIt(editor, lastSearch, startOffset, 1, searchOptions);
} }
private static int distance(@NotNull TextRange range, int pos, boolean forwards, int size) { public static TextRange findIt(@NotNull Editor editor, @Nullable String pattern, int startOffset, int count, EnumSet<SearchOptions> searchOptions) {
final int start = range.getStartOffset();
if (start <= pos) {
return forwards ? size - pos + start : pos - start;
}
else {
return forwards ? start - pos : pos + size - start;
}
}
private static TextRange findIt(@NotNull Editor editor, @Nullable String pattern, int startOffset, int count, EnumSet<SearchOptions> searchOptions) {
if (pattern == null || pattern.length() == 0) { if (pattern == null || pattern.length() == 0) {
logger.warn("Pattern is null or empty. Cannot perform search"); logger.warn("Pattern is null or empty. Cannot perform search");
return null; return null;
} }
int dir = searchOptions.contains(SearchOptions.BACKWARDS) ? DIR_BACKWARDS : DIR_FORWARDS; Direction dir = searchOptions.contains(SearchOptions.BACKWARDS) ? Direction.BACKWARDS : Direction.FORWARDS;
//RE sp; //RE sp;
RegExp sp; RegExp sp;
@@ -628,7 +467,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
int loop; int loop;
RegExp.lpos_T start_pos; RegExp.lpos_T start_pos;
boolean at_first_line; boolean at_first_line;
int extra_col = dir == DIR_FORWARDS ? 1 : 0; int extra_col = dir == Direction.FORWARDS ? 1 : 0;
boolean match_ok; boolean match_ok;
long nmatched; long nmatched;
//int submatch = 0; //int submatch = 0;
@@ -652,7 +491,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
* Start searching in current line, unless searching backwards and * Start searching in current line, unless searching backwards and
* we're in column 0. * we're in column 0.
*/ */
if (dir == DIR_BACKWARDS && start_pos.col == 0) { if (dir == Direction.BACKWARDS && start_pos.col == 0) {
lnum = pos.lnum - 1; lnum = pos.lnum - 1;
at_first_line = false; at_first_line = false;
} }
@@ -666,7 +505,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
startLine = lnum; startLine = lnum;
endLine = lnum + 1; endLine = lnum + 1;
} }
for (; lnum >= startLine && lnum < endLine; lnum += dir, at_first_line = false) { for (; lnum >= startLine && lnum < endLine; lnum += dir.toInt(), at_first_line = false) {
/* /*
* Look for a match somewhere in the line. * Look for a match somewhere in the line.
*/ */
@@ -683,7 +522,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
* the start position. If not, continue at the end of the * the start position. If not, continue at the end of the
* match (this is vi compatible) or on the next char. * match (this is vi compatible) or on the next char.
*/ */
if (dir == DIR_FORWARDS && at_first_line) { if (dir == Direction.FORWARDS && at_first_line) {
match_ok = true; match_ok = true;
/* /*
* When match lands on a NUL the cursor will be put * When match lands on a NUL the cursor will be put
@@ -721,7 +560,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
continue; continue;
} }
} }
if (dir == DIR_BACKWARDS) { if (dir == Direction.BACKWARDS) {
/* /*
* Now, if there are multiple matches on this line, * Now, if there are multiple matches on this line,
* we have to get the last one. Or the last one before * we have to get the last one. Or the last one before
@@ -815,7 +654,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
* is redrawn. The keep_msg is cleared whenever another message is * is redrawn. The keep_msg is cleared whenever another message is
* written. * written.
*/ */
if (dir == DIR_BACKWARDS) /* start second loop at the other end */ { if (dir == Direction.BACKWARDS) /* start second loop at the other end */ {
lnum = lineCount - 1; lnum = lineCount - 1;
//if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG)) //if (!shortmess(SHM_SEARCH) && (options & SEARCH_MSG))
// give_warning((char_u *)_(top_bot_msg), TRUE); // give_warning((char_u *)_(top_bot_msg), TRUE);
@@ -855,24 +694,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
new CharacterPosition(endpos.lnum, endpos.col).toOffset(editor)); new CharacterPosition(endpos.lnum, endpos.col).toOffset(editor));
} }
private static void highlightSearchResults(@NotNull Editor editor, @NotNull String pattern, List<TextRange> results, private int findItOffset(@NotNull Editor editor, int startOffset, int count, Direction dir) {
int currentMatchOffset) {
Collection<RangeHighlighter> highlighters = UserDataManager.getVimLastHighlighters(editor);
if (highlighters == null) {
highlighters = new ArrayList<>();
UserDataManager.setVimLastHighlighters(editor, highlighters);
}
for (TextRange range : results) {
final boolean current = range.getStartOffset() == currentMatchOffset;
final RangeHighlighter highlighter = highlightMatch(editor, range.getStartOffset(), range.getEndOffset(), current, pattern);
highlighters.add(highlighter);
}
UserDataManager.setVimIncsearchCurrentMatchOffset(editor, currentMatchOffset);
}
private int findItOffset(@NotNull Editor editor, int startOffset, int count, int dir) {
boolean wrap = OptionsManager.INSTANCE.getWrapscan().isSet(); boolean wrap = OptionsManager.INSTANCE.getWrapscan().isSet();
logger.debug("Perform search. Direction: " + dir + " wrap: " + wrap); logger.debug("Perform search. Direction: " + dir + " wrap: " + wrap);
@@ -935,7 +757,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
} }
EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.SHOW_MESSAGES, SearchOptions.WHOLE_FILE); EnumSet<SearchOptions> searchOptions = EnumSet.of(SearchOptions.SHOW_MESSAGES, SearchOptions.WHOLE_FILE);
if (dir == DIR_BACKWARDS) searchOptions.add(SearchOptions.BACKWARDS); if (dir == Direction.BACKWARDS) searchOptions.add(SearchOptions.BACKWARDS);
if (lastIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE); if (lastIgnoreSmartCase) searchOptions.add(SearchOptions.IGNORE_SMARTCASE);
if (wrap) searchOptions.add(SearchOptions.WRAP); if (wrap) searchOptions.add(SearchOptions.WRAP);
if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS); if (hasEndOffset) searchOptions.add(SearchOptions.WANT_ENDPOS);
@@ -983,7 +805,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
@RWLockLabel.SelfSynchronized @RWLockLabel.SelfSynchronized
public boolean searchAndReplace(@NotNull Editor editor, @NotNull Caret caret, @NotNull LineRange range, public boolean searchAndReplace(@NotNull Editor editor, @NotNull Caret caret, @NotNull LineRange range,
@NotNull String excmd, String exarg) { @NotNull @NonNls String excmd, @NonNls String exarg) {
// Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match. // Explicitly exit visual mode here, so that visual mode marks don't change when we move the cursor to a match.
if (CommandStateHelper.inVisualMode(editor)) { if (CommandStateHelper.inVisualMode(editor)) {
ModeHelper.exitVisualMode(editor); ModeHelper.exitVisualMode(editor);
@@ -1164,7 +986,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
lastSubstitute = pattern; lastSubstitute = pattern;
lastSearch = pattern; lastSearch = pattern;
if (pattern != null) { if (pattern != null) {
setLastPattern(editor, pattern); setLastPattern(pattern);
} }
int start = editor.getDocument().getLineStartOffset(line1); int start = editor.getDocument().getLineStartOffset(line1);
@@ -1247,7 +1069,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
if (do_all || line != lastLine) { if (do_all || line != lastLine) {
boolean doReplace = true; boolean doReplace = true;
if (do_ask) { if (do_ask) {
RangeHighlighter hl = highlightConfirm(editor, startoff, endoff); RangeHighlighter hl = SearchHighlightsHelper.addSubstitutionConfirmationHighlight(editor, startoff, endoff);
final ReplaceConfirmationChoice choice = confirmChoice(editor, match, caret, startoff); final ReplaceConfirmationChoice choice = confirmChoice(editor, match, caret, startoff);
editor.getMarkupModel().removeHighlighter(hl); editor.getMarkupModel().removeHighlighter(hl);
switch (choice) { switch (choice) {
@@ -1318,65 +1140,6 @@ public class SearchGroup implements PersistentStateComponent<Element> {
return true; return true;
} }
private @NotNull RangeHighlighter highlightConfirm(@NotNull Editor editor, int start, int end) {
TextAttributes color = new TextAttributes(
editor.getColorsScheme().getColor(EditorColors.SELECTION_FOREGROUND_COLOR),
editor.getColorsScheme().getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
editor.getColorsScheme().getColor(EditorColors.CARET_COLOR),
EffectType.ROUNDED_BOX, Font.PLAIN
);
return editor.getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION,
color, HighlighterTargetArea.EXACT_RANGE);
}
private static @NotNull RangeHighlighter highlightMatch(@NotNull Editor editor, int start, int end, boolean current, String tooltip) {
TextAttributes attributes = editor.getColorsScheme().getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES);
if (current) {
// This mimics what IntelliJ does with the Find live preview
attributes = attributes.clone();
attributes.setEffectType(EffectType.ROUNDED_BOX);
attributes.setEffectColor(editor.getColorsScheme().getColor(EditorColors.CARET_COLOR));
}
if (attributes.getErrorStripeColor() == null) {
attributes.setErrorStripeColor(getFallbackErrorStripeColor(attributes, editor.getColorsScheme()));
}
final RangeHighlighter highlighter = editor.getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION - 1,
attributes, HighlighterTargetArea.EXACT_RANGE);
highlighter.setErrorStripeTooltip(tooltip);
return highlighter;
}
/**
* Return a valid error stripe colour based on editor background
*
* Based on HighlightManager#addRangeHighlight behaviour, which we can't use because it will hide highlights when
* hitting Escape
*/
private static @Nullable Color getFallbackErrorStripeColor(TextAttributes attributes, EditorColorsScheme colorsScheme) {
if (attributes.getBackgroundColor() != null) {
boolean isDark = ColorUtil.isDark(colorsScheme.getDefaultBackground());
return isDark ? attributes.getBackgroundColor().brighter() : attributes.getBackgroundColor().darker();
}
return null;
}
private static void removeSearchHighlight(@NotNull Editor editor) {
UserDataManager.setVimLastSearch(editor, null);
Collection<RangeHighlighter> ehl = UserDataManager.getVimLastHighlighters(editor);
if (ehl == null) {
return;
}
for (RangeHighlighter rh : ehl) {
editor.getMarkupModel().removeHighlighter(rh);
}
ehl.clear();
UserDataManager.setVimLastHighlighters(editor, null);
}
public void saveData(@NotNull Element element) { public void saveData(@NotNull Element element) {
logger.debug("saveData"); logger.debug("saveData");
Element search = new Element("search"); Element search = new Element("search");
@@ -1396,7 +1159,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
search.addContent(createElementWithText("last-substitute", lastSubstitute)); search.addContent(createElementWithText("last-substitute", lastSubstitute));
} }
Element text = new Element("last-dir"); Element text = new Element("last-dir");
text.addContent(Integer.toString(lastDir)); text.addContent(Integer.toString(lastDir.toInt()));
search.addContent(text); search.addContent(text);
text = new Element("show-last"); text = new Element("show-last");
@@ -1425,7 +1188,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
lastSubstitute = getSafeChildText(search, "last-substitute"); lastSubstitute = getSafeChildText(search, "last-substitute");
Element dir = search.getChild("last-dir"); Element dir = search.getChild("last-dir");
lastDir = Integer.parseInt(dir.getText()); lastDir = Direction.Companion.fromInt(Integer.parseInt(dir.getText()));
Element show = search.getChild("show-last"); Element show = search.getChild("show-last");
final ListOption vimInfo = OptionsManager.INSTANCE.getViminfo(); final ListOption vimInfo = OptionsManager.INSTANCE.getViminfo();
@@ -1522,7 +1285,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
SUBSTITUTE_ALL, SUBSTITUTE_ALL,
} }
private enum SearchOptions { public enum SearchOptions {
BACKWARDS, BACKWARDS,
WANT_ENDPOS, WANT_ENDPOS,
WRAP, WRAP,
@@ -1537,7 +1300,7 @@ public class SearchGroup implements PersistentStateComponent<Element> {
private @Nullable String lastReplace; private @Nullable String lastReplace;
private @Nullable String lastOffset; private @Nullable String lastOffset;
private boolean lastIgnoreSmartCase; private boolean lastIgnoreSmartCase;
private int lastDir; private @NotNull Direction lastDir = Direction.FORWARDS;
private boolean showSearchHighlight = OptionsManager.INSTANCE.getHlsearch().isSet(); private boolean showSearchHighlight = OptionsManager.INSTANCE.getHlsearch().isSet();
private boolean do_all = false; /* do multiple substitutions per line */ private boolean do_all = false; /* do multiple substitutions per line */
@@ -1550,8 +1313,5 @@ public class SearchGroup implements PersistentStateComponent<Element> {
private static final int RE_SEARCH = 2; private static final int RE_SEARCH = 2;
private static final int RE_SUBST = 3; private static final int RE_SUBST = 3;
private static final int DIR_FORWARDS = 1;
private static final int DIR_BACKWARDS = -1;
private static final Logger logger = Logger.getInstance(SearchGroup.class.getName()); private static final Logger logger = Logger.getInstance(SearchGroup.class.getName());
} }

View File

@@ -26,6 +26,7 @@ import com.intellij.openapi.fileEditor.impl.EditorWithProviderComposite;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.maddyhome.idea.vim.VimPlugin; import com.maddyhome.idea.vim.VimPlugin;
import com.maddyhome.idea.vim.helper.MessageHelper;
import com.maddyhome.idea.vim.helper.RWLockLabel; import com.maddyhome.idea.vim.helper.RWLockLabel;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -158,7 +159,7 @@ public class WindowGroup {
if (filename.length() > 0) { if (filename.length() > 0) {
virtualFile = VimPlugin.getFile().findFile(filename, project); virtualFile = VimPlugin.getFile().findFile(filename, project);
if (virtualFile == null) { if (virtualFile == null) {
VimPlugin.showMessage("Could not find file: " + filename); VimPlugin.showMessage(MessageHelper.message("could.not.find.file.0", filename));
return; return;
} }
} }

View File

@@ -46,9 +46,9 @@ import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.MarkGroup import com.maddyhome.idea.vim.group.MarkGroup
import com.maddyhome.idea.vim.group.MotionGroup import com.maddyhome.idea.vim.group.MotionGroup
import com.maddyhome.idea.vim.group.visual.VimSelection import com.maddyhome.idea.vim.group.visual.VimSelection
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.helper.moveToInlayAwareOffset
import com.maddyhome.idea.vim.option.ClipboardOptionsData import com.maddyhome.idea.vim.option.ClipboardOptionsData
import com.maddyhome.idea.vim.option.OptionsManager import com.maddyhome.idea.vim.option.OptionsManager
import java.util.* import java.util.*

View File

@@ -32,7 +32,6 @@ import com.maddyhome.idea.vim.helper.EditorHelper
import com.maddyhome.idea.vim.helper.fileSize import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.listener.VimYankListener import com.maddyhome.idea.vim.listener.VimYankListener
import org.jetbrains.annotations.Contract import org.jetbrains.annotations.Contract
import java.util.*
import kotlin.math.min import kotlin.math.min
class YankGroup { class YankGroup {

View File

@@ -27,6 +27,7 @@ import com.maddyhome.idea.vim.command.SelectionType.CHARACTER_WISE
import com.maddyhome.idea.vim.command.SelectionType.LINE_WISE import com.maddyhome.idea.vim.command.SelectionType.LINE_WISE
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.helper.EditorHelper import com.maddyhome.idea.vim.helper.EditorHelper
import org.jetbrains.annotations.NonNls
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -68,6 +69,7 @@ sealed class VimSelection {
} }
} }
@NonNls
override fun toString(): String { override fun toString(): String {
val startLogPosition = editor.offsetToLogicalPosition(vimStart) val startLogPosition = editor.offsetToLogicalPosition(vimStart)
val endLogPosition = editor.offsetToLogicalPosition(vimEnd) val endLogPosition = editor.offsetToLogicalPosition(vimEnd)

View File

@@ -163,7 +163,7 @@ fun updateCaretState(editor: Editor) {
fun CommandState.Mode.resetShape(editor: Editor) = when (this) { fun CommandState.Mode.resetShape(editor: Editor) = when (this) {
CommandState.Mode.COMMAND, CommandState.Mode.VISUAL, CommandState.Mode.REPLACE -> ChangeGroup.resetCaret(editor, false) CommandState.Mode.COMMAND, CommandState.Mode.VISUAL, CommandState.Mode.REPLACE -> ChangeGroup.resetCaret(editor, false)
CommandState.Mode.SELECT, CommandState.Mode.INSERT -> ChangeGroup.resetCaret(editor, true) CommandState.Mode.SELECT, CommandState.Mode.INSERT -> ChangeGroup.resetCaret(editor, VimPlugin.getEditor().isBarCursorSettings)
CommandState.Mode.CMD_LINE, CommandState.Mode.OP_PENDING -> Unit CommandState.Mode.CMD_LINE, CommandState.Mode.OP_PENDING -> Unit
} }

View File

@@ -32,6 +32,7 @@ import com.maddyhome.idea.vim.helper.StringHelper
import com.maddyhome.idea.vim.helper.commandState import com.maddyhome.idea.vim.helper.commandState
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.noneOfEnum import com.maddyhome.idea.vim.helper.noneOfEnum
import org.jetbrains.annotations.NonNls
import java.util.* import java.util.*
import javax.swing.KeyStroke import javax.swing.KeyStroke
@@ -103,14 +104,17 @@ abstract class EditorActionHandlerBase(private val myRunForEachCaret: Boolean) {
fun parseKeysSet(keyStrings: List<String>) = keyStrings.map { StringHelper.parseKeys(it) }.toSet() fun parseKeysSet(keyStrings: List<String>) = keyStrings.map { StringHelper.parseKeys(it) }.toSet()
@JvmStatic @JvmStatic
fun parseKeysSet(vararg keyStrings: String): Set<List<KeyStroke>> = List(keyStrings.size) { fun parseKeysSet(@NonNls vararg keyStrings: String): Set<List<KeyStroke>> = List(keyStrings.size) {
StringHelper.parseKeys(keyStrings[it]) StringHelper.parseKeys(keyStrings[it])
}.toSet() }.toSet()
@NonNls private const val VimActionPrefix = "Vim"
@NonNls
fun getActionId(classFullName: String): String { fun getActionId(classFullName: String): String {
return classFullName return classFullName
.takeLastWhile { it != '.' } .takeLastWhile { it != '.' }
.let { if (it.startsWith("Vim", true)) it else "Vim$it" } .let { if (it.startsWith(VimActionPrefix, true)) it else "$VimActionPrefix$it" }
} }
} }
} }

View File

@@ -50,6 +50,8 @@ object CharacterHelper {
CharacterType.KATAKANA CharacterType.KATAKANA
} else if (isHalfWidthKatakanaLetter(ch)) { } else if (isHalfWidthKatakanaLetter(ch)) {
CharacterType.HALF_WIDTH_KATAKANA CharacterType.HALF_WIDTH_KATAKANA
} else if (block == UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS) {
CharacterType.CJK_UNIFIED_IDEOGRAPHS
} else if (punctuationAsLetters || iskeyword.isKeyword(ch)) { } else if (punctuationAsLetters || iskeyword.isKeyword(ch)) {
CharacterType.KEYWORD CharacterType.KEYWORD
} else { } else {
@@ -89,6 +91,6 @@ object CharacterHelper {
} }
enum class CharacterType { enum class CharacterType {
KEYWORD, HIRAGANA, KATAKANA, HALF_WIDTH_KATAKANA, PUNCTUATION, WHITESPACE KEYWORD, HIRAGANA, KATAKANA, HALF_WIDTH_KATAKANA, CJK_UNIFIED_IDEOGRAPHS, PUNCTUATION, WHITESPACE
} }
} }

Some files were not shown because too many files have changed in this diff Show More