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

Compare commits

...

45 Commits

Author SHA1 Message Date
Alex Plate
c8c42cc4b2 Update plugin verifier versions 2020-12-16 13:00:32 +03:00
Alex Plate
0b22b454e6 Prepare to 0.63 release 2020-12-16 12:56:25 +03:00
vladimir.petrenko
f4fe03c582 CWM-927 disable vim for client's hidden editor on host
to provide compatibility with Code With Me plugin
2020-12-16 12:27:34 +03:00
Alex Plate
964e87fd6a Update minimap IJ version to 202 2020-12-16 11:39:49 +03:00
Alex Plate
ec4c0a0d0d Update teamcity configurations to deprecate 201 branch 2020-12-16 11:38:52 +03:00
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
60 changed files with 964 additions and 633 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>

View File

@@ -29,7 +29,6 @@
<option name="m_requireAnnotationsFirst" value="true" /> <option name="m_requireAnnotationsFirst" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="PluginXmlI18n" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="PluginXmlI18n" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StaticMethodOnlyUsedInOneClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" />
</profile> </profile>
</component> </component>

View File

@@ -41,7 +41,6 @@
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />
<option name="processComments" value="true" /> <option name="processComments" value="true" />
</inspection_tool> </inspection_tool>
<inspection_tool class="StaticMethodOnlyUsedInOneClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="SuperTearDownInFinally" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="UnstableApiUsage" enabled="false" level="WARNING" enabled_by_default="false" />
</profile> </profile>

View File

@@ -13,10 +13,12 @@ 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
import _Self.vcsRoots.Branch_191_193 import _Self.vcsRoots.Branch_191_193
import _Self.vcsRoots.Branch_201
import _Self.vcsRoots.GitHubPullRequest import _Self.vcsRoots.GitHubPullRequest
import jetbrains.buildServer.configs.kotlin.v2019_2.Project import jetbrains.buildServer.configs.kotlin.v2019_2.Project
@@ -25,11 +27,22 @@ object Project : Project({
vcsRoot(Branch_183) vcsRoot(Branch_183)
vcsRoot(Branch_181) vcsRoot(Branch_181)
vcsRoot(GitHubPullRequest)
vcsRoot(Branch_191_193) vcsRoot(Branch_191_193)
vcsRoot(Branch_201)
vcsRoot(GitHubPullRequest)
buildType(GitHubPullRequests) buildType(GitHubPullRequests)
buildType(Release) buildType(Release)
buildType(ReleaseEap)
buildType(TestsForIntelliJ20202)
buildType(TestsForIntelliJ20203)
buildType(TestsForIntelliJEAP)
buildType(Nvim)
buildType(PluginVerifier)
buildType(TestsForIntelliJ20201) buildType(TestsForIntelliJ20201)
buildType(TestsForIntelliJ20191) buildType(TestsForIntelliJ20191)
buildType(TestsForIntelliJ20181) buildType(TestsForIntelliJ20181)
@@ -37,11 +50,6 @@ object Project : Project({
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,5 @@ 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")

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.61" buildNumberPattern = "0.63"
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.61.%build.counter%" buildNumberPattern = "0.63.%build.counter%"
params { params {
param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2") param("env.ORG_GRADLE_PROJECT_ideaVersion", "2020.2")

View File

@@ -0,0 +1,46 @@
@file:Suppress("ClassName")
package _Self.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.BuildType
import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode
import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.vcs
sealed class TestsForIntelliJ_201_branch(private val version: String) : BuildType({
name = "Tests for IntelliJ $version"
params {
param("env.ORG_GRADLE_PROJECT_downloadIdeaSources", "false")
param("env.ORG_GRADLE_PROJECT_ideaVersion", "IC-$version")
param("env.ORG_GRADLE_PROJECT_instrumentPluginCode", "false")
param("env.ORG_GRADLE_PROJECT_javaVersion", "1.8")
}
vcs {
root(_Self.vcsRoots.Branch_201)
checkoutMode = CheckoutMode.ON_SERVER
}
steps {
gradle {
tasks = "clean test"
buildFile = ""
enableStacktrace = true
param("org.jfrog.artifactory.selectedDeployableServer.defaultModuleVersionConfiguration", "GLOBAL")
}
}
triggers {
vcs {
branchFilter = ""
}
}
requirements {
noLessThanVer("teamcity.agent.jvm.version", "1.8")
}
})
object TestsForIntelliJ20201 : TestsForIntelliJ_201_branch("2020.1")

12
.teamcity/_Self/vcsRoots/Branch_201.kt vendored Normal file
View File

@@ -0,0 +1,12 @@
@file:Suppress("ClassName")
package _Self.vcsRoots
import jetbrains.buildServer.configs.kotlin.v2019_2.vcs.GitVcsRoot
object Branch_201 : GitVcsRoot({
name = "https://github.com/JetBrains/ideavim (branch 201)"
url = "https://github.com/JetBrains/ideavim.git"
branch = "201"
useMirrors = false
})

View File

@@ -331,6 +331,14 @@ Contributors:
[![icon][github]](https://github.com/antekone) [![icon][github]](https://github.com/antekone)
&nbsp; &nbsp;
Grzegorz Antoniak Grzegorz Antoniak
* [![icon][mail]](mailto:shaunpatterson@gmail.com)
[![icon][github]](https://github.com/shaunpatterson)
&nbsp;
Shaun Patterson
* [![icon][mail]](mailto:vladimir.petrenko@jetbrains.com)
[![icon][github]](https://github.com/vladimir-petrenko)
&nbsp;
Vladimir Petrenko
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,18 +22,34 @@ It is important to distinguish EAP from traditional pre-release software.
Please note that the quality of EAP versions may at times be way below even Please note that the quality of EAP versions may at times be way below even
usual beta standards. usual beta standards.
## To Be Released ## 0.63, 2020-12-16
### Changes:
* Update the minimal required IJ version: 2020.2+
### Fixes:
* [CWM-927](https://youtrack.jetbrains.com/issue/CWM-927) Fix typing for CodeWithMe and IdeaVim
### Merged PRs:
* [259](https://github.com/JetBrains/ideavim/pull/259) by [Vladimir Petrenko](https://github.com/vladimir-petrenko): CWM-927 disable vim for client's hidden editor on host
## 0.62, 2020-12-15
### Features: ### Features:
* Support `unmap` and `mapclear` commands [VIM-1491](https://youtrack.jetbrains.com/issue/VIM-1491) * 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) * 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: ### Fixes:
* [VIM-2113](https://youtrack.jetbrains.com/issue/VIM-2113) Fix `cit` for empty tags * [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-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-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-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: ### 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 * [249](https://github.com/JetBrains/ideavim/pull/249) by [Jan Palus](https://github.com/jpalus): VIM-2113 Increase tag range only in visual mode
@@ -41,6 +57,9 @@ usual beta standards.
* [256](https://github.com/JetBrains/ideavim/pull/256) by [Brandon Conway](https://github.com/brandoncc): Fix typo * [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 * [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 * [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 ## 0.61, 2020-11-12

View File

@@ -144,21 +144,38 @@ 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:

View File

@@ -59,7 +59,7 @@ runIdeForUiTests {
} }
runPluginVerifier { runPluginVerifier {
ideVersions = ["IC-2020.1.4", "IC-2020.2.3"] ideVersions = ["IC-2020.2.3", "IC-2020.3"]
downloadDirectory = "${project.buildDir}/pluginVerifier/ides" downloadDirectory = "${project.buildDir}/pluginVerifier/ides"
teamCityOutputFormat = true teamCityOutputFormat = true
} }

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

@@ -4,60 +4,62 @@
<change-notes><![CDATA[ <change-notes><![CDATA[
<h3>Features:</h3> <h3>Features:</h3>
<ul> <ul>
<li>Support <code>unmap</code> and <code>mapclear</code> commands <li>Support <code>unmap</code> and <code>mapclear</code> commands <a
<a href="https://youtrack.jetbrains.com/issue/VIM-1491">VIM-1491</a> href="https://youtrack.jetbrains.com/issue/VIM-1491">VIM-1491</a></li>
</li> <li>Support mappings in ex panel (<code>cmap</code>) <a
<li>Support mappings in ex panel (<code>cmap</code>) href="https://youtrack.jetbrains.com/issue/VIM-1227">VIM-1227</a></li>
<a href="https://youtrack.jetbrains.com/issue/VIM-1227">VIM-1227</a> </ul>
</li>
<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> </ul>
<h3>Fixes:</h3> <h3>Fixes:</h3>
<ul> <ul>
<li> <li><a href="https://youtrack.jetbrains.com/issue/VIM-2113">VIM-2113</a> Fix <code>cit</code> for empty tags</li>
<a href="https://youtrack.jetbrains.com/issue/VIM-2113">VIM-2113</a> <li><a href="https://youtrack.jetbrains.com/issue/VIM-2114">VIM-2114</a> Unnamed register isn't changed after deleting
Fix <code>cit</code> for empty tags empty tag
</li> </li>
<li> <li><a href="https://youtrack.jetbrains.com/issue/VIM-1475">VIM-1475</a> Enable block caret to be used in insert mode.
<a href="https://youtrack.jetbrains.com/issue/VIM-2114">VIM-2114</a> </li>
Unnamed register isn't changed after deleting empty tag <li><a href="https://youtrack.jetbrains.com/issue/VIM-2170">VIM-2170</a> Fix an alternative range format for
</li> <code>s</code> command
<li> </li>
<a href="https://youtrack.jetbrains.com/issue/VIM-1475">VIM-1475</a> <li><a href="https://youtrack.jetbrains.com/issue/VIM-1913">VIM-1913</a>
Enable block caret to be used in insert mode. <a href="https://youtrack.jetbrains.com/issue/VIM-2154">VIM-2154</a> Several fixes for AppCode templates
</li> </li>
<li> <li><a href="https://youtrack.jetbrains.com/issue/VIM-1756">VIM-1756</a> Fix startsel from insert mode</li>
<a href="https://youtrack.jetbrains.com/issue/VIM-2170">VIM-2170</a>
Fix an alternative range format for <code>s</code> command
</li>
</ul> </ul>
<h3>Merged PRs:</h3> <h3>Merged PRs:</h3>
<ul> <ul>
<li> <li><a href="https://github.com/JetBrains/ideavim/pull/249">249</a> by <a href="https://github.com/jpalus">Jan
<a href="https://github.com/JetBrains/ideavim/pull/249">249</a> Palus</a>: VIM-2113 Increase tag range only in visual mode
by <a href="https://github.com/jpalus">Jan Palus</a>: VIM-2113 Increase tag range only in visual mode </li>
</li> <li><a href="https://github.com/JetBrains/ideavim/pull/250">250</a> by <a href="https://github.com/jpalus">Jan
<li> Palus</a>: VIM-2114 Do not override registers when deleting empty range
<a href="https://github.com/JetBrains/ideavim/pull/250">250</a> </li>
by <a href="https://github.com/jpalus">Jan Palus</a>: VIM-2114 Do not override registers when deleting empty range <li><a href="https://github.com/JetBrains/ideavim/pull/256">256</a> by <a href="https://github.com/brandoncc">Brandon
</li> Conway</a>: Fix typo
<li> </li>
<a href="https://github.com/JetBrains/ideavim/pull/256">256</a> <li><a href="https://github.com/JetBrains/ideavim/pull/254">254</a> by <a href="https://github.com/antekone">Grzegorz
by <a href="https://github.com/brandoncc">Brandon Conway</a>: Fix typo Antoniak</a>: VIM-1475: Add an option to use block caret in insert mode
</li> </li>
<li> <li><a href="https://github.com/JetBrains/ideavim/pull/225">225</a> by <a href="https://github.com/sumoooru2">sumoooru2</a>:
<a href="https://github.com/JetBrains/ideavim/pull/254">254</a> Implement cmap
by <a href="https://github.com/antekone">Grzegorz Antoniak</a>: VIM-1475: Add an option to use block caret in insert </li>
mode <li><a href="https://github.com/JetBrains/ideavim/pull/258">258</a> by <a href="https://github.com/citizenmatt">Matt
</li> Ellis</a>: Show the correct handler class in :map
<li> </li>
<a href="https://github.com/JetBrains/ideavim/pull/225">225</a> <li><a href="https://github.com/JetBrains/ideavim/pull/257">257</a> by <a href="https://github.com/citizenmatt">Matt
by <a href="https://github.com/sumoooru2">sumoooru2</a>: Implement cmap Ellis</a>: Extract SearchHighlightsHelper from SearchGroup
</li> </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> </ul>
<br/>
<p>See also the complete <a href="https://github.com/JetBrains/ideavim/blob/master/CHANGES.md">changelog</a>.</p>
]]> ]]>
</change-notes> </change-notes>
<description><![CDATA[ <description><![CDATA[
@@ -77,7 +79,7 @@
<!-- 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.41"/> <idea-version since-build="202"/>
<!-- 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>
@@ -111,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,

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

@@ -319,7 +319,7 @@ public class KeyHandler {
} }
} }
reset(editor); reset(editor);
ChangeGroup.resetCaret(editor, VimPlugin.getEditor().isBarCursor()); ChangeGroup.resetCaret(editor, false);
} }
private boolean handleKeyMapping(final @NotNull Editor editor, private boolean handleKeyMapping(final @NotNull Editor editor,
@@ -934,6 +934,7 @@ 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);
} }
if (editorState.getCommandBuilder().isDone()) { if (editorState.getCommandBuilder().isDone()) {

View File

@@ -19,6 +19,7 @@ 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;
@@ -40,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;
@@ -105,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()) {
@@ -168,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() {
@@ -180,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() {
@@ -204,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() {
@@ -216,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() {
@@ -263,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";
} }

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

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

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

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

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

View File

@@ -33,6 +33,7 @@ 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.MessageHelper
import com.maddyhome.idea.vim.helper.SearchHelper.findWordUnderCursor import com.maddyhome.idea.vim.helper.SearchHelper.findWordUnderCursor
@@ -48,18 +49,23 @@ import java.util.*
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @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 // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @NonNls
private const val NEXT_OCCURRENCE = "<Plug>NextOccurrence" private const val NEXT_OCCURRENCE = "<Plug>NextOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @NonNls
private const val SKIP_OCCURRENCE = "<Plug>SkipOccurrence" private const val SKIP_OCCURRENCE = "<Plug>SkipOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @NonNls
private const val REMOVE_OCCURRENCE = "<Plug>RemoveOccurrence" private const val REMOVE_OCCURRENCE = "<Plug>RemoveOccurrence"
// [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @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 // [VERSION UPDATE] 203+ Annotation should be replaced with @NlsSafe
@NonNls @NonNls
private const val ALL_OCCURRENCES = "<Plug>AllOccurrences" private const val ALL_OCCURRENCES = "<Plug>AllOccurrences"
@@ -75,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)
@@ -100,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
@@ -138,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
@@ -172,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
@@ -242,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

@@ -27,9 +27,11 @@ 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;
@@ -204,8 +206,8 @@ public class EditorGroup implements PersistentStateComponent<Element> {
} }
} }
public boolean isBarCursor() { public boolean isBarCursorSettings() {
return !isBlockCursor; return !EditorSettingsExternalizable.getInstance().isBlockCursor();
} }
public void editorCreated(@NotNull Editor editor) { public void editorCreated(@NotNull Editor editor) {
@@ -222,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) || isBlockCursor); VisualGroupKt.resetShape(CommandStateHelper.getMode(editor), editor);
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
} }

View File

@@ -41,6 +41,7 @@ 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.OptionChangeListener;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
@@ -318,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) {

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;
@@ -81,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 @NonNls 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 @NonNls 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;
@@ -173,10 +187,35 @@ 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();
@@ -219,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);
} }
@@ -248,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
@@ -338,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 {
@@ -351,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) {
@@ -359,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);
@@ -383,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);
@@ -394,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,8 +44,8 @@ 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.ex.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;
@@ -59,11 +54,14 @@ 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)
@@ -82,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);
} }
@@ -105,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() {
@@ -115,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);
} }
@@ -218,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);
} }
@@ -242,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 = '?';
} }
@@ -284,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;
@@ -301,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");
@@ -321,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;
@@ -336,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();
@@ -369,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);
@@ -575,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;
@@ -629,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;
@@ -653,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;
} }
@@ -667,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.
*/ */
@@ -684,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
@@ -722,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
@@ -816,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);
@@ -856,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);
@@ -936,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);
@@ -1165,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);
@@ -1248,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) {
@@ -1319,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");
@@ -1397,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");
@@ -1426,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();
@@ -1523,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,
@@ -1538,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 */
@@ -1551,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

@@ -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, VimPlugin.getEditor().isBarCursor) 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

@@ -21,6 +21,7 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.option.OptionsManager import com.maddyhome.idea.vim.option.OptionsManager
@@ -40,12 +41,23 @@ val CommandState.Mode.isEndAllowedIgnoringOnemore: Boolean
CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> false CommandState.Mode.COMMAND, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.OP_PENDING -> false
} }
val CommandState.Mode.isBlockCaret /**
* Should this caret behave like the block caret?
* Keep in mind that in insert mode the caret can have a block shape, but it doesn't behave like the block one
* If you're looking for a shape, check [isBlockCaretShape]
*/
val CommandState.Mode.isBlockCaretBehaviour
get() = when (this) { get() = when (this) {
CommandState.Mode.VISUAL, CommandState.Mode.COMMAND, CommandState.Mode.OP_PENDING -> true CommandState.Mode.VISUAL, CommandState.Mode.COMMAND, CommandState.Mode.OP_PENDING -> true
CommandState.Mode.INSERT, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.SELECT -> false CommandState.Mode.INSERT, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.SELECT -> false
} }
val CommandState.Mode.isBlockCaretShape
get() = when (this) {
CommandState.Mode.VISUAL, CommandState.Mode.COMMAND, CommandState.Mode.OP_PENDING -> true
CommandState.Mode.INSERT, CommandState.Mode.CMD_LINE, CommandState.Mode.REPLACE, CommandState.Mode.SELECT -> !VimPlugin.getEditor().isBarCursorSettings
}
val CommandState.Mode.hasVisualSelection val CommandState.Mode.hasVisualSelection
get() = when (this) { get() = when (this) {
CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true CommandState.Mode.VISUAL, CommandState.Mode.SELECT -> true

View File

@@ -20,7 +20,7 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.diagnostic.logger import com.intellij.codeWithMe.ClientId
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.ex.util.EditorUtil import com.intellij.openapi.editor.ex.util.EditorUtil
import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx
@@ -29,7 +29,6 @@ import com.maddyhome.idea.vim.option.OptionsManager
import java.awt.Component import java.awt.Component
import javax.swing.JComponent import javax.swing.JComponent
import javax.swing.JTable import javax.swing.JTable
import kotlin.system.measureTimeMillis
val Editor.fileSize: Int val Editor.fileSize: Int
get() = document.textLength get() = document.textLength
@@ -40,31 +39,14 @@ val Editor.fileSize: Int
*/ */
val Editor.isIdeaVimDisabledHere: Boolean val Editor.isIdeaVimDisabledHere: Boolean
get() { get() {
var res = true return disabledInDialog
val start = System.currentTimeMillis() || (!ClientId.isCurrentlyUnderLocalId) // CWM-927
val times = mutableListOf<Pair<Long, String>>() || (!OptionsManager.ideavimsupport.contains("singleline") && isDatabaseCell())
val timeForCalculation = measureTimeMillis { || (!OptionsManager.ideavimsupport.contains("singleline") && isOneLineMode)
res = (disabledInDialog.apply { times += System.currentTimeMillis() to "Disabled in dialog" }
|| (!OptionsManager.ideavimsupport.contains("singleline")
.apply { times += System.currentTimeMillis() to "first single line check" }
&& isDatabaseCell(times).apply { times += System.currentTimeMillis() to "is db cell" })
|| (!OptionsManager.ideavimsupport.contains("singleline")
.apply { times += System.currentTimeMillis() to "second single line check" }
&& isOneLineMode.apply { times += System.currentTimeMillis() to "is one line" })
)
}
if (timeForCalculation > 10) {
val timeDiffs = times.map { it.second + ": " + (it.first - start) }
val message = "Time for calculation of 'isIdeaVimDisabledHere' took $timeForCalculation ms. Time diff: $timeDiffs"
logger<Editor>().error(message)
}
return res
} }
private fun Editor.isDatabaseCell(times: MutableList<Pair<Long, String>>): Boolean { private fun Editor.isDatabaseCell(): Boolean {
return isTableCellEditor(this.component, times) return isTableCellEditor(this.component)
} }
private val Editor.disabledInDialog: Boolean private val Editor.disabledInDialog: Boolean
@@ -81,15 +63,10 @@ fun Editor.isPrimaryEditor(): Boolean {
} }
// Optimized clone of com.intellij.ide.ui.laf.darcula.DarculaUIUtil.isTableCellEditor // Optimized clone of com.intellij.ide.ui.laf.darcula.DarculaUIUtil.isTableCellEditor
private fun isTableCellEditor(c: Component, times: MutableList<Pair<Long, String>>): Boolean { private fun isTableCellEditor(c: Component): Boolean {
return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) return (java.lang.Boolean.TRUE == (c as JComponent).getClientProperty("JComboBox.isTableCellEditor")) ||
.apply { times += System.currentTimeMillis() to "is property tru" } || (findParentByCondition(c) { it is JTable } != null) &&
(findParentByCondition(c) { it is JTable } != null)
.apply { times += System.currentTimeMillis() to "is not null" } &&
(findParentByCondition(c) { it is JBTableRowEditor } == null) (findParentByCondition(c) { it is JBTableRowEditor } == null)
.apply { times += System.currentTimeMillis() to "is null" }
} }
private const val PARENT_BY_CONDITION_DEPTH = 10 private const val PARENT_BY_CONDITION_DEPTH = 10

View File

@@ -103,7 +103,7 @@ public class SearchHelper {
int pos = caret.getOffset(); int pos = caret.getOffset();
int loc = blockChars.indexOf(type); int loc = blockChars.indexOf(type);
// What direction should we go now (-1 is backward, 1 is forward) // What direction should we go now (-1 is backward, 1 is forward)
Direction dir = loc % 2 == 0 ? Direction.BACK : Direction.FORWARD; Direction dir = loc % 2 == 0 ? Direction.BACKWARDS : Direction.FORWARDS;
// Which character did we find and which should we now search for // Which character did we find and which should we now search for
char match = blockChars.charAt(loc); char match = blockChars.charAt(loc);
char found = blockChars.charAt(loc - dir.toInt()); char found = blockChars.charAt(loc - dir.toInt());
@@ -163,10 +163,10 @@ public class SearchHelper {
int endOffset = quoteRange.getEndOffset(); int endOffset = quoteRange.getEndOffset();
CharSequence subSequence = chars.subSequence(startOffset, endOffset); CharSequence subSequence = chars.subSequence(startOffset, endOffset);
int inQuotePos = pos - startOffset; int inQuotePos = pos - startOffset;
int inQuoteStart = findBlockLocation(subSequence, close, type, Direction.BACK, inQuotePos, count, false); int inQuoteStart = findBlockLocation(subSequence, close, type, Direction.BACKWARDS, inQuotePos, count, false);
if (inQuoteStart != -1) { if (inQuoteStart != -1) {
startPosInStringFound = true; startPosInStringFound = true;
int inQuoteEnd = findBlockLocation(subSequence, type, close, Direction.FORWARD, inQuoteStart, 1, false); int inQuoteEnd = findBlockLocation(subSequence, type, close, Direction.FORWARDS, inQuoteStart, 1, false);
if (inQuoteEnd != -1) { if (inQuoteEnd != -1) {
bstart = inQuoteStart + startOffset; bstart = inQuoteStart + startOffset;
bend = inQuoteEnd + startOffset; bend = inQuoteEnd + startOffset;
@@ -176,9 +176,9 @@ public class SearchHelper {
} }
if (!startPosInStringFound) { if (!startPosInStringFound) {
bstart = findBlockLocation(chars, close, type, Direction.BACK, pos, count, false); bstart = findBlockLocation(chars, close, type, Direction.BACKWARDS, pos, count, false);
if (bstart != -1) { if (bstart != -1) {
bend = findBlockLocation(chars, type, close, Direction.FORWARD, bstart, 1, false); bend = findBlockLocation(chars, type, close, Direction.FORWARDS, bstart, 1, false);
} }
} }
@@ -294,7 +294,7 @@ public class SearchHelper {
// If we found one ... // If we found one ...
if (loc >= 0) { if (loc >= 0) {
// What direction should we go now (-1 is backward, 1 is forward) // What direction should we go now (-1 is backward, 1 is forward)
Direction dir = loc % 2 == 0 ? Direction.FORWARD : Direction.BACK; Direction dir = loc % 2 == 0 ? Direction.FORWARDS : Direction.BACKWARDS;
// Which character did we find and which should we now search for // Which character did we find and which should we now search for
char found = getPairChars().charAt(loc); char found = getPairChars().charAt(loc);
char match = getPairChars().charAt(loc + dir.toInt()); char match = getPairChars().charAt(loc + dir.toInt());
@@ -327,7 +327,7 @@ public class SearchHelper {
boolean allowInString) { boolean allowInString) {
int res = -1; int res = -1;
int initialPos = pos; int initialPos = pos;
Function<Integer, Integer> inCheckPosF = x -> dir == Direction.BACK && x > 0 ? x - 1 : x + 1; Function<Integer, Integer> inCheckPosF = x -> dir == Direction.BACKWARDS && x > 0 ? x - 1 : x + 1;
final int inCheckPos = inCheckPosF.apply(pos); final int inCheckPos = inCheckPosF.apply(pos);
boolean inString = checkInString(chars, inCheckPos, true); boolean inString = checkInString(chars, inCheckPos, true);
boolean initialInString = inString; boolean initialInString = inString;
@@ -391,30 +391,16 @@ public class SearchHelper {
return backslashCounter % 2 == 0; return backslashCounter % 2 == 0;
} }
public enum Direction {
BACK(-1), FORWARD(1);
private final int value;
Direction(int i) {
value = i;
}
public int toInt() {
return value;
}
}
public enum NumberType { public enum NumberType {
BIN, OCT, DEC, HEX, ALPHA BIN, OCT, DEC, HEX, ALPHA
} }
private static int findNextQuoteInLine(@NotNull CharSequence chars, int pos, char quote) { private static int findNextQuoteInLine(@NotNull CharSequence chars, int pos, char quote) {
return findQuoteInLine(chars, pos, quote, Direction.FORWARD); return findQuoteInLine(chars, pos, quote, Direction.FORWARDS);
} }
private static int findPreviousQuoteInLine(@NotNull CharSequence chars, int pos, char quote) { private static int findPreviousQuoteInLine(@NotNull CharSequence chars, int pos, char quote) {
return findQuoteInLine(chars, pos, quote, Direction.BACK); return findQuoteInLine(chars, pos, quote, Direction.BACKWARDS);
} }
private static int findFirstQuoteInLine(@NotNull Editor editor, int pos, char quote) { private static int findFirstQuoteInLine(@NotNull Editor editor, int pos, char quote) {
@@ -428,8 +414,8 @@ public class SearchHelper {
private static int countCharactersInLine(@NotNull CharSequence chars, int pos, char c) { private static int countCharactersInLine(@NotNull CharSequence chars, int pos, char c) {
int cnt = 0; int cnt = 0;
while (pos > 0 && (chars.charAt(pos + Direction.BACK.toInt()) != '\n')) { while (pos > 0 && (chars.charAt(pos + Direction.BACKWARDS.toInt()) != '\n')) {
pos = findCharacterPosition(chars, pos + Direction.BACK.toInt(), c, false, true, Direction.BACK); pos = findCharacterPosition(chars, pos + Direction.BACKWARDS.toInt(), c, false, true, Direction.BACKWARDS);
if (pos != -1) { if (pos != -1) {
cnt++; cnt++;
} }

View File

@@ -18,14 +18,34 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.maddyhome.idea.vim.helper.SearchHelper.Direction
import com.maddyhome.idea.vim.helper.SearchHelper.findPositionOfFirstCharacter import com.maddyhome.idea.vim.helper.SearchHelper.findPositionOfFirstCharacter
import com.maddyhome.idea.vim.option.OptionsManager.ignorecase
import com.maddyhome.idea.vim.option.OptionsManager.smartcase
enum class Direction(private val value: Int) {
BACKWARDS(-1), FORWARDS(1), UNSET(0);
fun toInt(): Int = value
fun reverse(): Direction = when (this) {
BACKWARDS -> FORWARDS
FORWARDS -> BACKWARDS
UNSET -> UNSET
}
companion object {
fun fromInt(value: Int) = when (value) {
BACKWARDS.value -> BACKWARDS
FORWARDS.value -> FORWARDS
else -> UNSET
}
}
}
private data class State(val position: Int, val trigger: Char, val inQuote: Boolean?, val lastOpenSingleQuotePos: Int) private data class State(val position: Int, val trigger: Char, val inQuote: Boolean?, val lastOpenSingleQuotePos: Int)
// bounds are considered inside corresponding quotes // bounds are considered inside corresponding quotes
fun checkInString(chars: CharSequence, currentPos: Int, str: Boolean): Boolean { fun checkInString(chars: CharSequence, currentPos: Int, str: Boolean): Boolean {
val begin = findPositionOfFirstCharacter(chars, currentPos, setOf('\n'), false, Direction.BACK)?.second ?: 0 val begin = findPositionOfFirstCharacter(chars, currentPos, setOf('\n'), false, Direction.BACKWARDS)?.second ?: 0
val changes = quoteChanges(chars, begin) val changes = quoteChanges(chars, begin)
// TODO: here we need to keep only the latest element in beforePos (if any) and // TODO: here we need to keep only the latest element in beforePos (if any) and
// don't need atAndAfterPos to be eagerly collected // don't need atAndAfterPos to be eagerly collected
@@ -105,7 +125,7 @@ private fun quoteChanges(chars: CharSequence, begin: Int) = sequence {
// in that situation it may be double quote inside single quotes, so we cannot threat it as double quote pair open/close // in that situation it may be double quote inside single quotes, so we cannot threat it as double quote pair open/close
var inQuote: Boolean? = false var inQuote: Boolean? = false
val charsToSearch = setOf('\'', '"', '\n') val charsToSearch = setOf('\'', '"', '\n')
var found = findPositionOfFirstCharacter(chars, begin, charsToSearch, false, Direction.FORWARD) var found = findPositionOfFirstCharacter(chars, begin, charsToSearch, false, Direction.FORWARDS)
while (found != null && found.first != '\n') { while (found != null && found.first != '\n') {
val i = found.second val i = found.second
@@ -158,6 +178,11 @@ private fun quoteChanges(chars: CharSequence, begin: Int) = sequence {
} }
} }
yield(State(i, c, inQuote, lastOpenSingleQuotePos)) yield(State(i, c, inQuote, lastOpenSingleQuotePos))
found = findPositionOfFirstCharacter(chars, i + Direction.FORWARD.toInt(), charsToSearch, false, Direction.FORWARD) found = findPositionOfFirstCharacter(chars, i + Direction.FORWARDS.toInt(), charsToSearch, false, Direction.FORWARDS)
} }
} }
fun shouldIgnoreCase(pattern: String, ignoreSmartCase: Boolean): Boolean {
val sc = smartcase.isSet && !ignoreSmartCase
return ignorecase.isSet && !(sc && StringHelper.containsUpperCase(pattern))
}

View File

@@ -0,0 +1,268 @@
/*
* 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/>.
*/
@file:JvmName("SearchHighlightsHelper")
package com.maddyhome.idea.vim.helper
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.colors.EditorColorsScheme
import com.intellij.openapi.editor.markup.EffectType
import com.intellij.openapi.editor.markup.HighlighterLayer
import com.intellij.openapi.editor.markup.HighlighterTargetArea
import com.intellij.openapi.editor.markup.RangeHighlighter
import com.intellij.openapi.editor.markup.TextAttributes
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.ProjectManager
import com.intellij.ui.ColorUtil
import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.ex.ranges.LineRange
import com.maddyhome.idea.vim.group.SearchGroup
import com.maddyhome.idea.vim.group.SearchGroup.SearchOptions
import com.maddyhome.idea.vim.option.OptionsManager.hlsearch
import com.maddyhome.idea.vim.option.OptionsManager.wrapscan
import org.jetbrains.annotations.Contract
import java.awt.Color
import java.awt.Font
import java.util.*
fun updateSearchHighlights(
pattern: String?,
shouldIgnoreSmartCase: Boolean,
showHighlights: Boolean,
forceUpdate: Boolean
) {
updateSearchHighlights(pattern, shouldIgnoreSmartCase, showHighlights, -1, null, true, forceUpdate)
}
fun updateIncsearchHighlights(
editor: Editor,
pattern: String,
forwards: Boolean,
caretOffset: Int,
searchRange: LineRange?
): Int {
val searchStartOffset =
if (searchRange != null) EditorHelper.getLineStartOffset(editor, searchRange.startLine) else caretOffset
val showHighlights = hlsearch.isSet
return updateSearchHighlights(pattern, false, showHighlights, searchStartOffset, searchRange, forwards, false)
}
fun addSubstitutionConfirmationHighlight(editor: Editor, start: Int, end: Int): RangeHighlighter {
val color = TextAttributes(
editor.colorsScheme.getColor(EditorColors.SELECTION_FOREGROUND_COLOR),
editor.colorsScheme.getColor(EditorColors.SELECTION_BACKGROUND_COLOR),
editor.colorsScheme.getColor(EditorColors.CARET_COLOR),
EffectType.ROUNDED_BOX, Font.PLAIN
)
return editor.markupModel.addRangeHighlighter(
start, end, HighlighterLayer.SELECTION,
color, HighlighterTargetArea.EXACT_RANGE
)
}
/**
* Refreshes current search highlights for all editors of currently active text editor/document
*/
private fun updateSearchHighlights(
pattern: String?, shouldIgnoreSmartCase: Boolean, showHighlights: Boolean,
initialOffset: Int, searchRange: LineRange?, forwards: Boolean, forceUpdate: Boolean
): Int {
var currentMatchOffset = -1
val projectManager = ProjectManager.getInstanceIfCreated() ?: return currentMatchOffset
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 `/<Up>` 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
}
}
}
}
return currentMatchOffset
}
/**
* Remove current search highlights if hlSearch is false, or if the pattern is changed
*/
@Contract("_, _, false -> true; _, null, true -> false")
private fun shouldRemoveSearchHighlights(editor: Editor, newPattern: String?, hlSearch: Boolean): Boolean {
return !hlSearch || newPattern != null && newPattern != editor.vimLastSearch
}
private fun removeSearchHighlights(editor: Editor) {
editor.vimLastSearch = null
val ehl = editor.vimLastHighlighters ?: return
for (rh in ehl) {
editor.markupModel.removeHighlighter(rh)
}
editor.vimLastHighlighters = null
}
/**
* Add search highlights if hlSearch is true and the pattern is changed
*/
@Contract("_, _, false -> false; _, null, true -> false")
private fun shouldAddAllSearchHighlights(editor: Editor, newPattern: String?, hlSearch: Boolean): Boolean {
return hlSearch && newPattern != null && newPattern != editor.vimLastSearch && newPattern != ""
}
private fun findClosestMatch(editor: Editor, results: List<TextRange>, initialOffset: Int, forwards: Boolean): Int {
if (results.isEmpty() || initialOffset == -1) {
return -1
}
val size = editor.fileSize
val max = Collections.max(results) { r1: TextRange, r2: TextRange ->
val d1 = distance(r1, initialOffset, forwards, size)
val d2 = distance(r2, initialOffset, forwards, size)
if (d1 < 0 && d2 >= 0) {
return@max Int.MAX_VALUE
}
d2 - d1
}
if (!wrapscan.isSet) {
val start = max.startOffset
if (forwards && start < initialOffset) {
return -1
} else if (start >= initialOffset) {
return -1
}
}
return max.startOffset
}
private fun distance(range: TextRange, pos: Int, forwards: Boolean, size: Int): Int {
val start = range.startOffset
return if (start <= pos) {
if (forwards) size - pos + start else pos - start
} else {
if (forwards) start - pos else pos + size - start
}
}
fun highlightSearchResults(editor: Editor, pattern: String, results: List<TextRange>, currentMatchOffset: Int) {
var highlighters = editor.vimLastHighlighters
if (highlighters == null) {
highlighters = mutableListOf()
editor.vimLastHighlighters = highlighters
}
for (range in results) {
val current = range.startOffset == currentMatchOffset
val highlighter = highlightMatch(editor, range.startOffset, range.endOffset, current, pattern)
highlighters.add(highlighter)
}
editor.vimIncsearchCurrentMatchOffset = currentMatchOffset
}
private fun highlightMatch(editor: Editor, start: Int, end: Int, current: Boolean, tooltip: String): RangeHighlighter {
var attributes = editor.colorsScheme.getAttributes(EditorColors.TEXT_SEARCH_RESULT_ATTRIBUTES)
if (current) {
// This mimics what IntelliJ does with the Find live preview
attributes = attributes.clone()
attributes.effectType = EffectType.ROUNDED_BOX
attributes.effectColor = editor.colorsScheme.getColor(EditorColors.CARET_COLOR)
}
if (attributes.errorStripeColor == null) {
attributes.errorStripeColor = getFallbackErrorStripeColor(attributes, editor.colorsScheme)
}
val highlighter = editor.markupModel.addRangeHighlighter(
start, end, HighlighterLayer.SELECTION - 1,
attributes, HighlighterTargetArea.EXACT_RANGE
)
highlighter.errorStripeTooltip = 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 fun getFallbackErrorStripeColor(attributes: TextAttributes, colorsScheme: EditorColorsScheme): Color? {
if (attributes.backgroundColor != null) {
val isDark = ColorUtil.isDark(colorsScheme.defaultBackground)
return if (isDark) attributes.backgroundColor.brighter() else attributes.backgroundColor.darker()
}
return null
}
/**
* Add search highlight for current match if hlsearch is false and we're performing incsearch highlights
*/
@Contract("_, true, _ -> false")
private fun shouldAddCurrentMatchSearchHighlight(pattern: String?, hlSearch: Boolean, initialOffset: Int): Boolean {
return !hlSearch && isIncrementalSearchHighlights(initialOffset) && pattern != null && pattern.isNotEmpty()
}
/**
* 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 fun shouldMaintainCurrentMatchOffset(pattern: String?, initialOffset: Int): Boolean {
return pattern != null && pattern.isNotEmpty() && isIncrementalSearchHighlights(initialOffset)
}
/**
* initialOffset is only valid if we're highlighting incsearch
*/
@Contract(pure = true)
private fun isIncrementalSearchHighlights(initialOffset: Int) = initialOffset != -1

View File

@@ -80,7 +80,7 @@ fun unInitializeEditor(editor: Editor) {
} }
var Editor.vimLastSearch: String? by userData() var Editor.vimLastSearch: String? by userData()
var Editor.vimLastHighlighters: Collection<RangeHighlighter>? by userData() var Editor.vimLastHighlighters: MutableCollection<RangeHighlighter>? by userData()
var Editor.vimIncsearchCurrentMatchOffset: Int? by userData() var Editor.vimIncsearchCurrentMatchOffset: Int? by userData()
/*** /***
* @see :help visualmode() * @see :help visualmode()

View File

@@ -107,7 +107,7 @@ class ToHandlerMappingInfo(
isRecursive: Boolean, isRecursive: Boolean,
owner: MappingOwner owner: MappingOwner
) : MappingInfo(fromKeys, isRecursive, owner) { ) : MappingInfo(fromKeys, isRecursive, owner) {
override fun getPresentableString(): String = "call ${this.javaClass.canonicalName}" override fun getPresentableString(): String = "call ${extensionHandler.javaClass.canonicalName}"
override fun execute(editor: Editor, context: DataContext) { override fun execute(editor: Editor, context: DataContext) {
val processor = CommandProcessor.getInstance() val processor = CommandProcessor.getInstance()

View File

@@ -32,12 +32,14 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.IdeActions import com.intellij.openapi.actionSystem.IdeActions
import com.intellij.openapi.actionSystem.ex.AnActionListener import com.intellij.openapi.actionSystem.ex.AnActionListener
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorActionManager import com.intellij.openapi.editor.actionSystem.EditorActionManager
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.project.DumbAwareToggleAction import com.intellij.openapi.project.DumbAwareToggleAction
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.util.PlatformUtils import com.intellij.util.PlatformUtils
import com.maddyhome.idea.vim.EventFacade import com.maddyhome.idea.vim.EventFacade
import com.maddyhome.idea.vim.KeyHandler import com.maddyhome.idea.vim.KeyHandler
@@ -47,10 +49,12 @@ import com.maddyhome.idea.vim.group.visual.IdeaSelectionControl
import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd import com.maddyhome.idea.vim.group.visual.moveCaretOneCharLeftFromSelectionEnd
import com.maddyhome.idea.vim.helper.EditorDataContext import com.maddyhome.idea.vim.helper.EditorDataContext
import com.maddyhome.idea.vim.helper.commandState import com.maddyhome.idea.vim.helper.commandState
import com.maddyhome.idea.vim.helper.fileSize
import com.maddyhome.idea.vim.helper.getTopLevelEditor import com.maddyhome.idea.vim.helper.getTopLevelEditor
import com.maddyhome.idea.vim.helper.inNormalMode import com.maddyhome.idea.vim.helper.inNormalMode
import com.maddyhome.idea.vim.option.IdeaRefactorMode import com.maddyhome.idea.vim.option.IdeaRefactorMode
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
import org.jetbrains.annotations.NotNull
import java.beans.PropertyChangeEvent import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener import java.beans.PropertyChangeListener
@@ -191,6 +195,92 @@ object IdeaSpecifics {
.javaClass.name.startsWith("org.acejump.") .javaClass.name.startsWith("org.acejump.")
} }
//endregion //endregion
//region AppCode templates
/**
* A collection of hacks to improve the interaction with fancy AppCode templates
*/
object AppCodeTemplates {
private val facedAppCodeTemplate = Key.create<IntRange>("FacedAppCodeTemplate")
private const val TEMPLATE_START = "<#T##"
private const val TEMPLATE_END = "#>"
@JvmStatic
fun onMovement(
editor: @NotNull Editor,
caret: @NotNull Caret,
toRight: Boolean
) {
if (!PlatformUtils.isAppCode()) return
val offset = caret.offset
val offsetRightEnd = offset + TEMPLATE_START.length
val offsetLeftEnd = offset - 1
val templateRange = caret.getUserData(facedAppCodeTemplate)
if (templateRange == null) {
if (offsetRightEnd < editor.fileSize
&& editor.document.charsSequence.subSequence(offset, offsetRightEnd).toString() == TEMPLATE_START) {
caret.shake()
val templateEnd = editor.findTemplateEnd(offset) ?: return
caret.putUserData(facedAppCodeTemplate, offset..templateEnd)
}
if (offsetLeftEnd >= 0
&& editor.document.charsSequence.subSequence(offsetLeftEnd, offset + 1).toString() == TEMPLATE_END) {
caret.shake()
val templateStart = editor.findTemplateStart(offsetLeftEnd) ?: return
caret.putUserData(facedAppCodeTemplate, templateStart..offset)
}
} else {
if (offset in templateRange) {
if (toRight) {
caret.moveToOffset(templateRange.last + 1)
} else {
caret.moveToOffset(templateRange.first)
}
}
caret.putUserData(facedAppCodeTemplate, null)
caret.shake()
}
}
fun Editor.appCodeTemplateCaptured(): Boolean {
if (!PlatformUtils.isAppCode()) return false
return this.caretModel.allCarets.any { it.getUserData(facedAppCodeTemplate) != null }
}
private fun Caret.shake() {
moveCaretRelatively(1, 0, false, false)
moveCaretRelatively(-1, 0, false, false)
}
private fun Editor.findTemplateEnd(start: Int): Int? {
val charSequence = this.document.charsSequence
val length = charSequence.length
for (i in start until length - 1) {
if (charSequence[i] == TEMPLATE_END[0] && charSequence[i+1] == TEMPLATE_END[1]) {
return i + 1
}
}
return null
}
private fun Editor.findTemplateStart(start: Int): Int? {
val charSequence = this.document.charsSequence
val templateLastIndex = TEMPLATE_START.length
for (i in start downTo templateLastIndex) {
if (charSequence.subSequence(i - templateLastIndex, i + 1).toString() == TEMPLATE_START) {
return i - templateLastIndex
}
}
return null
}
}
//endregion
} }
//region Find action ID //region Find action ID

View File

@@ -253,11 +253,11 @@ object VimListenerManager {
if (onLineEnd(caret)) { if (onLineEnd(caret)) {
// UX protection for case when user performs a small dragging while putting caret on line end // UX protection for case when user performs a small dragging while putting caret on line end
caret.removeSelection() caret.removeSelection()
ChangeGroup.resetCaret(e.editor, VimPlugin.getEditor().isBarCursor) ChangeGroup.resetCaret(e.editor, true)
} }
} }
if (mouseDragging && e.editor.caretModel.primaryCaret.hasSelection()) { if (mouseDragging && e.editor.caretModel.primaryCaret.hasSelection()) {
ChangeGroup.resetCaret(e.editor, VimPlugin.getEditor().isBarCursor) ChangeGroup.resetCaret(e.editor, true)
if (!cutOffFixed && ComponentMouseListener.cutOffEnd) { if (!cutOffFixed && ComponentMouseListener.cutOffEnd) {
cutOffFixed = true cutOffFixed = true

View File

@@ -32,7 +32,7 @@ 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.hasVisualSelection import com.maddyhome.idea.vim.helper.hasVisualSelection
import com.maddyhome.idea.vim.helper.isBlockCaret import com.maddyhome.idea.vim.helper.isBlockCaretBehaviour
import com.maddyhome.idea.vim.helper.mode import com.maddyhome.idea.vim.helper.mode
import com.maddyhome.idea.vim.helper.subMode import com.maddyhome.idea.vim.helper.subMode
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
@@ -52,7 +52,7 @@ object OptionsManager {
val clipboard = addOption(ListOption(ClipboardOptionsData.name, ClipboardOptionsData.abbr, arrayOf(ClipboardOptionsData.ideaput, "autoselect,exclude:cons\\|linux"), null)) val clipboard = addOption(ListOption(ClipboardOptionsData.name, ClipboardOptionsData.abbr, arrayOf(ClipboardOptionsData.ideaput, "autoselect,exclude:cons\\|linux"), null))
val digraph = addOption(ToggleOption("digraph", "dg", false)) val digraph = addOption(ToggleOption("digraph", "dg", false))
val gdefault = addOption(ToggleOption("gdefault", "gd", false)) val gdefault = addOption(ToggleOption("gdefault", "gd", false))
val history = addOption(NumberOption("history", "hi", 20, 1, Int.MAX_VALUE)) val history = addOption(NumberOption("history", "hi", 50, 1, Int.MAX_VALUE))
val hlsearch = addOption(ToggleOption("hlsearch", "hls", false)) val hlsearch = addOption(ToggleOption("hlsearch", "hls", false))
val ideamarks = addOption(IdeaMarkskOptionsData.option) val ideamarks = addOption(IdeaMarkskOptionsData.option)
val ignorecase = addOption(ToggleOption(IgnoreCaseOptionsData.name, IgnoreCaseOptionsData.abbr, false)) val ignorecase = addOption(ToggleOption(IgnoreCaseOptionsData.name, IgnoreCaseOptionsData.abbr, false))
@@ -62,7 +62,7 @@ object OptionsManager {
val lookupKeys = addOption(ListOption(LookupKeysData.name, LookupKeysData.name, LookupKeysData.defaultValues, null)) val lookupKeys = addOption(ListOption(LookupKeysData.name, LookupKeysData.name, LookupKeysData.defaultValues, null))
val matchpairs = addOption(ListOption("matchpairs", "mps", arrayOf("(:)", "{:}", "[:]"), ".:.")) val matchpairs = addOption(ListOption("matchpairs", "mps", arrayOf("(:)", "{:}", "[:]"), ".:."))
val more = addOption(ToggleOption("more", "more", true)) val more = addOption(ToggleOption("more", "more", true))
val nrformats = addOption(BoundListOption("nrformats", "nf", arrayOf("octal", "hex"), arrayOf("octal", "hex", "alpha"))) val nrformats = addOption(BoundListOption("nrformats", "nf", arrayOf("hex"), arrayOf("octal", "hex", "alpha"))) // Octal is disabled as in neovim
val number = addOption(ToggleOption("number", "nu", false)) val number = addOption(ToggleOption("number", "nu", false))
val relativenumber = addOption(ToggleOption("relativenumber", "rnu", false)) val relativenumber = addOption(ToggleOption("relativenumber", "rnu", false))
val scroll = addOption(NumberOption("scroll", "scr", 0)) val scroll = addOption(NumberOption("scroll", "scr", 0))
@@ -491,7 +491,7 @@ object IdeaRefactorMode {
} }
} }
if (editor.mode.isBlockCaret) { if (editor.mode.isBlockCaretBehaviour) {
TemplateManagerImpl.getTemplateState(editor)?.currentVariableRange?.let { segmentRange -> TemplateManagerImpl.getTemplateState(editor)?.currentVariableRange?.let { segmentRange ->
if (!segmentRange.isEmpty && segmentRange.endOffset == editor.caretModel.offset && editor.caretModel.offset != 0) { if (!segmentRange.isEmpty && segmentRange.endOffset == editor.caretModel.offset && editor.caretModel.offset != 0) {
editor.caretModel.moveToOffset(editor.caretModel.offset - 1) editor.caretModel.moveToOffset(editor.caretModel.offset - 1)

View File

@@ -669,7 +669,7 @@
* |c_<S-Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction} * |c_<S-Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpAction}
* |c_<Tab>| TO BE IMPLEMENTED * |c_<Tab>| TO BE IMPLEMENTED
* |c_<Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpFilterAction} * |c_<Up>| {@link com.maddyhome.idea.vim.ui.ex.HistoryUpFilterAction}
* |c_digraph| {char1} <BS> {char2} {@link com.maddyhome.idea.vim.ui.ex.StartDigraphAction} * |c_digraph| {char1} <BS> {char2}
* |c_wildchar| TO BE IMPLEMENTED * |c_wildchar| TO BE IMPLEMENTED
* |'cedit'| TO BE IMPLEMENTED * |'cedit'| TO BE IMPLEMENTED
* *

View File

@@ -33,6 +33,7 @@ import com.maddyhome.idea.vim.ex.CommandParser;
import com.maddyhome.idea.vim.ex.ExCommand; import com.maddyhome.idea.vim.ex.ExCommand;
import com.maddyhome.idea.vim.ex.ranges.LineRange; import com.maddyhome.idea.vim.ex.ranges.LineRange;
import com.maddyhome.idea.vim.group.MotionGroup; import com.maddyhome.idea.vim.group.MotionGroup;
import com.maddyhome.idea.vim.helper.SearchHighlightsHelper;
import com.maddyhome.idea.vim.helper.UiHelper; import com.maddyhome.idea.vim.helper.UiHelper;
import com.maddyhome.idea.vim.option.OptionsManager; import com.maddyhome.idea.vim.option.OptionsManager;
import com.maddyhome.idea.vim.regexp.CharPointer; import com.maddyhome.idea.vim.regexp.CharPointer;
@@ -279,6 +280,9 @@ public class ExEntryPanel extends JPanel {
searchText = argument.substring(1); searchText = argument.substring(1);
} }
if (searchText.length() == 0) { if (searchText.length() == 0) {
// Reset back to the original search highlights after deleting a search from a substitution command.
// E.g. Highlight `whatever`, type `:%s/foo` + highlight `foo`, delete back to `:%s/` and reset highlights
// back to `whatever`
VimPlugin.getSearch().resetIncsearchHighlights(); VimPlugin.getSearch().resetIncsearchHighlights();
return; return;
} }
@@ -294,7 +298,7 @@ public class ExEntryPanel extends JPanel {
pattern = p.substring(end.pointer() - p.pointer()); pattern = p.substring(end.pointer() - p.pointer());
VimPlugin.getEditor().closeEditorSearchSession(editor); VimPlugin.getEditor().closeEditorSearchSession(editor);
final int matchOffset = VimPlugin.getSearch().updateIncsearchHighlights(editor, pattern, forwards, caretOffset, searchRange); final int matchOffset = SearchHighlightsHelper.updateIncsearchHighlights(editor, pattern, forwards, caretOffset, searchRange);
if (matchOffset != -1) { if (matchOffset != -1) {
MotionGroup.moveCaret(editor, editor.getCaretModel().getPrimaryCaret(), matchOffset); MotionGroup.moveCaret(editor, editor.getCaretModel().getPrimaryCaret(), matchOffset);
} }

View File

@@ -37,6 +37,9 @@ class RegisterActionsTest : VimTestCase() {
} }
fun `test action in disabled plugin`() { fun `test action in disabled plugin`() {
setupChecks {
caretShape = false
}
val keys = StringHelper.parseKeys("jklwB") // just random keys val keys = StringHelper.parseKeys("jklwB") // just random keys
val before = "I ${c}found it in a legendary land" val before = "I ${c}found it in a legendary land"
val after = "I ${c}found it in a legendary land" val after = "I ${c}found it in a legendary land"

View File

@@ -55,6 +55,9 @@ import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.StringHelper.stringToKeys import com.maddyhome.idea.vim.helper.StringHelper.stringToKeys
import com.maddyhome.idea.vim.helper.TestInputModel import com.maddyhome.idea.vim.helper.TestInputModel
import com.maddyhome.idea.vim.helper.inBlockSubMode import com.maddyhome.idea.vim.helper.inBlockSubMode
import com.maddyhome.idea.vim.helper.isBlockCaretBehaviour
import com.maddyhome.idea.vim.helper.isBlockCaretShape
import com.maddyhome.idea.vim.helper.mode
import com.maddyhome.idea.vim.key.MappingOwner import com.maddyhome.idea.vim.key.MappingOwner
import com.maddyhome.idea.vim.key.ToKeysMappingInfo import com.maddyhome.idea.vim.key.ToKeysMappingInfo
import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor import com.maddyhome.idea.vim.listener.SelectionVimListenerSuppressor
@@ -65,7 +68,6 @@ import com.maddyhome.idea.vim.option.OptionsManager.resetAllOptions
import com.maddyhome.idea.vim.option.ToggleOption import com.maddyhome.idea.vim.option.ToggleOption
import com.maddyhome.idea.vim.ui.ex.ExEntryPanel import com.maddyhome.idea.vim.ui.ex.ExEntryPanel
import org.junit.Assert import org.junit.Assert
import java.util.*
import java.util.function.Consumer import java.util.function.Consumer
import javax.swing.KeyStroke import javax.swing.KeyStroke
@@ -93,6 +95,7 @@ abstract class VimTestCase : UsefulTestCase() {
VimPlugin.getSearch().resetState() VimPlugin.getSearch().resetState()
if (!VimPlugin.isEnabled()) VimPlugin.setEnabled(true) if (!VimPlugin.isEnabled()) VimPlugin.setEnabled(true)
ideastrictmode.set() ideastrictmode.set()
Checks.reset()
// Make sure the entry text field gets a bounds, or we won't be able to work out caret location // Make sure the entry text field gets a bounds, or we won't be able to work out caret location
ExEntryPanel.getInstance().entry.setBounds(0, 0, 100, 25) ExEntryPanel.getInstance().entry.setBounds(0, 0, 100, 25)
@@ -427,6 +430,7 @@ abstract class VimTestCase : UsefulTestCase() {
assertCaretsColour() assertCaretsColour()
assertMode(modeAfter) assertMode(modeAfter)
assertSubMode(subModeAfter) assertSubMode(subModeAfter)
if (Checks.caretShape) assertEquals(myFixture.editor.mode.isBlockCaretShape, myFixture.editor.settings.isBlockCursor)
} }
protected val fileManager: FileEditorManagerEx protected val fileManager: FileEditorManagerEx
@@ -440,6 +444,11 @@ abstract class VimTestCase : UsefulTestCase() {
return EditorTestUtil.addInlay(myFixture.editor, offset, relatesToPrecedingText, widthInColumns * columnWidth)!! return EditorTestUtil.addInlay(myFixture.editor, offset, relatesToPrecedingText, widthInColumns * columnWidth)!!
} }
// Disable or enable checks for the particular test
protected inline fun setupChecks(setup: Checks.() -> Unit) {
Checks.setup()
}
companion object { companion object {
const val c = EditorTestUtil.CARET_TAG const val c = EditorTestUtil.CARET_TAG
const val s = EditorTestUtil.SELECTION_START_TAG const val s = EditorTestUtil.SELECTION_START_TAG
@@ -480,4 +489,12 @@ abstract class VimTestCase : UsefulTestCase() {
fun String.dotToTab(): String = replace('.', '\t') fun String.dotToTab(): String = replace('.', '\t')
} }
object Checks {
var caretShape: Boolean = true
fun reset() {
caretShape = true
}
}
} }

View File

@@ -40,7 +40,7 @@ public class SpecialRegistersTest extends VimTestCase {
final RegisterGroup registerGroup = VimPlugin.getRegister(); final RegisterGroup registerGroup = VimPlugin.getRegister();
registerGroup.setKeys('a', stringToKeys(DUMMY_TEXT)); registerGroup.setKeys('a', stringToKeys(DUMMY_TEXT));
registerGroup.setKeys('-', stringToKeys(DUMMY_TEXT)); registerGroup.setKeys(RegisterGroup.SMALL_DELETION_REGISTER, stringToKeys(DUMMY_TEXT));
for (char c = '0'; c <= '9'; c++) { for (char c = '0'; c <= '9'; c++) {
registerGroup.setKeys(c, stringToKeys(DUMMY_TEXT)); registerGroup.setKeys(c, stringToKeys(DUMMY_TEXT));
} }
@@ -50,7 +50,7 @@ public class SpecialRegistersTest extends VimTestCase {
public void testSmallDelete() { public void testSmallDelete() {
typeTextInFile(parseKeys("de"), "one <caret>two three\n"); typeTextInFile(parseKeys("de"), "one <caret>two three\n");
assertEquals("two", getRegisterText('-')); assertEquals("two", getRegisterText(RegisterGroup.SMALL_DELETION_REGISTER));
// Text smaller than line doesn't go to numbered registers (except special cases) // Text smaller than line doesn't go to numbered registers (except special cases)
assertRegisterNotChanged('1'); assertRegisterNotChanged('1');
} }
@@ -60,7 +60,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d%"), "(one<caret> two) three\n"); typeTextInFile(parseKeys("d%"), "(one<caret> two) three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |(| Special case for small delete // |d| |(| Special case for small delete
@@ -68,7 +68,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d("), "One. Two<caret>. Three.\n"); typeTextInFile(parseKeys("d("), "One. Two<caret>. Three.\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |)| Special case for small delete // |d| |)| Special case for small delete
@@ -76,7 +76,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d)"), "One. <caret>Two. Three.\n"); typeTextInFile(parseKeys("d)"), "One. <caret>Two. Three.\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |`| Special case for small delete // |d| |`| Special case for small delete
@@ -84,7 +84,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("ma", "b", "d`a"), "one two<caret> three\n"); typeTextInFile(parseKeys("ma", "b", "d`a"), "one two<caret> three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |/| Special case for small delete // |d| |/| Special case for small delete
@@ -92,7 +92,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d/", "o", "<Enter>"), "one <caret>two three\n"); typeTextInFile(parseKeys("d/", "o", "<Enter>"), "one <caret>two three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |?| Special case for small delete // |d| |?| Special case for small delete
@@ -100,7 +100,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d?", "t", "<Enter>"), "one two<caret> three\n"); typeTextInFile(parseKeys("d?", "t", "<Enter>"), "one two<caret> three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |n| Special case for small delete // |d| |n| Special case for small delete
@@ -108,7 +108,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("/", "t", "<Enter>", "dn"), "<caret>one two three\n"); typeTextInFile(parseKeys("/", "t", "<Enter>", "dn"), "<caret>one two three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |N| Special case for small delete // |d| |N| Special case for small delete
@@ -116,7 +116,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("/", "t", "<Enter>", "dN"), "one tw<caret>o three\n"); typeTextInFile(parseKeys("/", "t", "<Enter>", "dN"), "one tw<caret>o three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |{| Special case for small delete // |d| |{| Special case for small delete
@@ -124,7 +124,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d{"), "one<caret> two three"); typeTextInFile(parseKeys("d{"), "one<caret> two three");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
// |d| |}| Special case for small delete // |d| |}| Special case for small delete
@@ -132,7 +132,7 @@ public class SpecialRegistersTest extends VimTestCase {
typeTextInFile(parseKeys("d}"), "one<caret> two three"); typeTextInFile(parseKeys("d}"), "one<caret> two three");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterChanged('-'); assertRegisterChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
public void testSmallDeleteInRegister() { public void testSmallDeleteInRegister() {
@@ -141,14 +141,14 @@ public class SpecialRegistersTest extends VimTestCase {
// Small deletes (less than a line) with register specified go to that register and to numbered registers // Small deletes (less than a line) with register specified go to that register and to numbered registers
assertRegisterChanged('a'); assertRegisterChanged('a');
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterNotChanged('-'); assertRegisterNotChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
public void testLineDelete() { public void testLineDelete() {
typeTextInFile(parseKeys("dd"), "one <caret>two three\n"); typeTextInFile(parseKeys("dd"), "one <caret>two three\n");
assertRegisterChanged('1'); assertRegisterChanged('1');
assertRegisterNotChanged('-'); assertRegisterNotChanged(RegisterGroup.SMALL_DELETION_REGISTER);
} }
public void testLineDeleteInRegister() { public void testLineDeleteInRegister() {
@@ -190,5 +190,4 @@ public class SpecialRegistersTest extends VimTestCase {
return register.getText(); return register.getText();
} }
} }

View File

@@ -0,0 +1,30 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.action.change.change.number
import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase
class ChangeNumberIncActionTest : VimTestCase() {
@VimBehaviorDiffers(originalVimAfter = "11X0")
fun `test inc fancy number`() {
doTest("<C-A>", "1${c}0X0", "10X1", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
}
}

View File

@@ -0,0 +1,40 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.action.change.insert
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
import com.maddyhome.idea.vim.command.CommandState
import org.jetbrains.plugins.ideavim.VimTestCase
class InsertBeforeCursorActionTest : VimTestCase() {
fun `test check caret shape`() {
doTest("i", "123", "123", CommandState.Mode.INSERT, CommandState.SubMode.NONE)
assertFalse(myFixture.editor.settings.isBlockCursor)
}
fun `test check caret shape for block caret`() {
EditorSettingsExternalizable.getInstance().isBlockCursor = true
try {
doTest("i", "123", "123", CommandState.Mode.INSERT, CommandState.SubMode.NONE)
assertTrue(myFixture.editor.settings.isBlockCursor)
} finally {
EditorSettingsExternalizable.getInstance().isBlockCursor = false
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
* Copyright (C) 2003-2020 The IdeaVim authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jetbrains.plugins.ideavim.action.change.insert
import com.maddyhome.idea.vim.command.CommandState
import org.jetbrains.plugins.ideavim.VimTestCase
class InsertSingleCommandActionTest : VimTestCase() {
fun `test enter visual`() {
doTest(
listOf("i", "<C-O>", "vlll", "<Esc>"),
"I found ${c}it in a legendary land",
"I found it ${c}in a legendary land",
CommandState.Mode.INSERT,
CommandState.SubMode.NONE
)
}
}

View File

@@ -21,8 +21,13 @@ package org.jetbrains.plugins.ideavim.action.motion.visual
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
class VisualExitModeAction : VimTestCase() { class VisualExitModeActionTest : VimTestCase() {
fun `test exit visual mode after line end`() { fun `test exit visual mode after line end`() {
doTest("vl<Esc>", "12${c}3", "12${c}3", CommandState.Mode.COMMAND, CommandState.SubMode.NONE) doTest("vl<Esc>", "12${c}3", "12${c}3", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
} }
fun `test double exit`() {
doTest("vl<Esc><Esc>", "12${c}3", "12${c}3", CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
assertTrue(myFixture.editor.settings.isBlockCursor)
}
} }

View File

@@ -65,6 +65,9 @@ class CommandParserTest : VimTestCase() {
} }
fun `test execute in disabled state`() { fun `test execute in disabled state`() {
setupChecks {
caretShape = false
}
val keys = commandToKeys(">>") val keys = commandToKeys(">>")
val before = "I ${c}found it in a legendary land" val before = "I ${c}found it in a legendary land"
val after = "I ${c}found it in a legendary land" val after = "I ${c}found it in a legendary land"

View File

@@ -21,8 +21,11 @@ package org.jetbrains.plugins.ideavim.ex.handler;
import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import com.maddyhome.idea.vim.ex.ExOutputModel; import com.maddyhome.idea.vim.ex.ExOutputModel;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.plugins.ideavim.VimTestCase; import org.jetbrains.plugins.ideavim.VimTestCase;
import java.util.List;
/** /**
* @author Naoto Ikeno * @author Naoto Ikeno
*/ */

View File

@@ -22,6 +22,7 @@ import com.maddyhome.idea.vim.VimPlugin
import com.maddyhome.idea.vim.command.CommandState import com.maddyhome.idea.vim.command.CommandState
import com.maddyhome.idea.vim.command.SelectionType import com.maddyhome.idea.vim.command.SelectionType
import com.maddyhome.idea.vim.common.TextRange import com.maddyhome.idea.vim.common.TextRange
import com.maddyhome.idea.vim.group.RegisterGroup
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.helper.VimBehaviorDiffers import com.maddyhome.idea.vim.helper.VimBehaviorDiffers
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
@@ -57,7 +58,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
val text = "" val text = ""
configureByText(text) configureByText(text)
VimPlugin.getRegister().storeTextInternal(myFixture.editor, TextRange(0, 0), "one", SelectionType.CHARACTER_WISE, '"', false) VimPlugin.getRegister().storeTextSpecial(RegisterGroup.UNNAMED_REGISTER, "one")
typeText(parseKeys("griw")) typeText(parseKeys("griw"))
myFixture.checkResult("on${c}e") myFixture.checkResult("on${c}e")
} }
@@ -66,7 +67,7 @@ class ReplaceWithRegisterTest : VimTestCase() {
val text = "${c}one" val text = "${c}one"
configureByText(text) configureByText(text)
VimPlugin.getRegister().storeText(myFixture.editor, "" rangeOf "", SelectionType.CHARACTER_WISE, false) VimPlugin.getRegister().storeTextSpecial(RegisterGroup.UNNAMED_REGISTER, "")
typeText(parseKeys("griw")) typeText(parseKeys("griw"))
myFixture.checkResult(c) myFixture.checkResult(c)
} }

View File

@@ -23,6 +23,7 @@ import com.intellij.openapi.editor.Editor
import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.PlatformTestUtil
import com.maddyhome.idea.vim.helper.StringHelper import com.maddyhome.idea.vim.helper.StringHelper
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
import com.maddyhome.idea.vim.option.OptionsManager
import org.jetbrains.jetCheck.Generator import org.jetbrains.jetCheck.Generator
import org.jetbrains.jetCheck.ImperativeCommand import org.jetbrains.jetCheck.ImperativeCommand
import org.jetbrains.jetCheck.PropertyChecker import org.jetbrains.jetCheck.PropertyChecker
@@ -30,6 +31,8 @@ import org.jetbrains.plugins.ideavim.NeovimTesting
import org.jetbrains.plugins.ideavim.SkipNeovimReason import org.jetbrains.plugins.ideavim.SkipNeovimReason
import org.jetbrains.plugins.ideavim.TestWithoutNeovim import org.jetbrains.plugins.ideavim.TestWithoutNeovim
import org.jetbrains.plugins.ideavim.VimTestCase import org.jetbrains.plugins.ideavim.VimTestCase
import kotlin.math.absoluteValue
import kotlin.math.sign
class IncrementDecrementTest : VimPropertyTest() { class IncrementDecrementTest : VimPropertyTest() {
@TestWithoutNeovim(SkipNeovimReason.DIFFERENT) @TestWithoutNeovim(SkipNeovimReason.DIFFERENT)
@@ -48,6 +51,7 @@ class IncrementDecrementTest : VimPropertyTest() {
} }
fun testPlayingWithNumbersGenerateNumber() { fun testPlayingWithNumbersGenerateNumber() {
OptionsManager.nrformats.append("octal")
PropertyChecker.checkScenarios { PropertyChecker.checkScenarios {
ImperativeCommand { env -> ImperativeCommand { env ->
val number = env.generateValue(testNumberGenerator, "Generate %s number") val number = env.generateValue(testNumberGenerator, "Generate %s number")
@@ -85,15 +89,10 @@ private class IncrementDecrementActions(private val editor: Editor, val test: Vi
val differentFormNumberGenerator = Generator.from { env -> val differentFormNumberGenerator = Generator.from { env ->
val form = env.generate(Generator.sampledFrom(/*2,*/ 8, 10, 16)) val form = env.generate(Generator.sampledFrom(/*2,*/ 8, 10, 16))
env.generate(Generator.integers().map { env.generate(Generator.integers().suchThat { it != Int.MIN_VALUE }.map {
val str = it.toString(form) val sign = it.sign
val prefix = when (form) { val stringNumber = it.absoluteValue.toString(form)
2 -> "0b" if (sign < 0) "-$stringNumber" else stringNumber
8 -> "0"
16 -> "0x"
else -> ""
}
if (str[0] == '-') "-$prefix${str.substring(1)}" else "$prefix$str"
}) })
} }