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

Compare commits

...

35 Commits

Author SHA1 Message Date
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
53 changed files with 745 additions and 576 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,6 +13,7 @@ import _Self.buildTypes.TestsForIntelliJ20192
import _Self.buildTypes.TestsForIntelliJ20193 import _Self.buildTypes.TestsForIntelliJ20193
import _Self.buildTypes.TestsForIntelliJ20201 import _Self.buildTypes.TestsForIntelliJ20201
import _Self.buildTypes.TestsForIntelliJ20202 import _Self.buildTypes.TestsForIntelliJ20202
import _Self.buildTypes.TestsForIntelliJ20203
import _Self.buildTypes.TestsForIntelliJEAP import _Self.buildTypes.TestsForIntelliJEAP
import _Self.vcsRoots.Branch_181 import _Self.vcsRoots.Branch_181
import _Self.vcsRoots.Branch_183 import _Self.vcsRoots.Branch_183
@@ -29,19 +30,24 @@ object Project : Project({
vcsRoot(Branch_191_193) vcsRoot(Branch_191_193)
buildType(GitHubPullRequests) buildType(GitHubPullRequests)
buildType(Release) buildType(Release)
buildType(ReleaseEap)
buildType(TestsForIntelliJ20201) buildType(TestsForIntelliJ20201)
buildType(TestsForIntelliJ20202)
buildType(TestsForIntelliJ20203)
buildType(TestsForIntelliJEAP)
buildType(Nvim)
buildType(PluginVerifier)
buildType(TestsForIntelliJ20191) buildType(TestsForIntelliJ20191)
buildType(TestsForIntelliJ20181) buildType(TestsForIntelliJ20181)
buildType(TestsForIntelliJ20192) buildType(TestsForIntelliJ20192)
buildType(TestsForIntelliJ20182) buildType(TestsForIntelliJ20182)
buildType(TestsForIntelliJ20193) buildType(TestsForIntelliJ20193)
buildType(TestsForIntelliJ20183) buildType(TestsForIntelliJ20183)
buildType(Nvim)
buildType(ReleaseEap)
buildType(TestsForIntelliJ20202)
buildType(TestsForIntelliJEAP)
buildType(PluginVerifier)
features { features {
feature { feature {

View File

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

View File

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

View File

@@ -331,6 +331,10 @@ 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
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

@@ -25,15 +25,20 @@ usual beta standards.
## To Be Released ## To Be Released
### 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 +46,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

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

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

@@ -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,6 +206,10 @@ public class EditorGroup implements PersistentStateComponent<Element> {
} }
} }
public boolean isBarCursorSettings() {
return !EditorSettingsExternalizable.getInstance().isBlockCursor();
}
public void editorCreated(@NotNull Editor editor) { public void editorCreated(@NotNull Editor editor) {
isBlockCursor = editor.getSettings().isBlockCursor(); isBlockCursor = editor.getSettings().isBlockCursor();
isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling(); isRefrainFromScrolling = editor.getSettings().isRefrainFromScrolling();
@@ -218,7 +224,7 @@ public class EditorGroup implements PersistentStateComponent<Element> {
VimPlugin.getChange().insertBeforeCursor(editor, new EditorDataContext(editor, null)); VimPlugin.getChange().insertBeforeCursor(editor, new EditorDataContext(editor, null));
KeyHandler.getInstance().reset(editor); KeyHandler.getInstance().reset(editor);
} }
editor.getSettings().setBlockCursor(!CommandStateHelper.inInsertMode(editor)); VisualGroupKt.resetShape(CommandStateHelper.getMode(editor), editor);
editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE); editor.getSettings().setRefrainFromScrolling(REFRAIN_FROM_SCROLLING_VIM_VALUE);
} }

View File

@@ -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 Direction lastDir;
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, true) CommandState.Mode.SELECT, CommandState.Mode.INSERT -> ChangeGroup.resetCaret(editor, VimPlugin.getEditor().isBarCursorSettings)
CommandState.Mode.CMD_LINE, CommandState.Mode.OP_PENDING -> Unit CommandState.Mode.CMD_LINE, CommandState.Mode.OP_PENDING -> Unit
} }

View File

@@ -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,6 @@
package com.maddyhome.idea.vim.helper package com.maddyhome.idea.vim.helper
import com.intellij.openapi.diagnostic.logger
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 +28,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 +38,13 @@ val Editor.fileSize: Int
*/ */
val Editor.isIdeaVimDisabledHere: Boolean val Editor.isIdeaVimDisabledHere: Boolean
get() { get() {
var res = true return disabledInDialog
val start = System.currentTimeMillis() || (!OptionsManager.ideavimsupport.contains("singleline") && isDatabaseCell())
val times = mutableListOf<Pair<Long, String>>() || (!OptionsManager.ideavimsupport.contains("singleline") && isOneLineMode)
val timeForCalculation = measureTimeMillis {
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 +61,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

@@ -248,6 +248,11 @@ object IdeaSpecifics {
} }
} }
fun Editor.appCodeTemplateCaptured(): Boolean {
if (!PlatformUtils.isAppCode()) return false
return this.caretModel.allCarets.any { it.getUserData(facedAppCodeTemplate) != null }
}
private fun Caret.shake() { private fun Caret.shake() {
moveCaretRelatively(1, 0, false, false) moveCaretRelatively(1, 0, false, false)
moveCaretRelatively(-1, 0, false, false) moveCaretRelatively(-1, 0, false, false)

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