mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-05-22 01:34:04 +02:00
Fix incsearch not updating empty selection
As a by-product, this also fixes an off-by-one error where incsearch would effectively treat all Visual searches as exclusive
This commit is contained in:
parent
a969b93ba6
commit
e9e86b07fb
src
main/java/com/maddyhome/idea/vim/ui/ex
test/java/org/jetbrains/plugins/ideavim/group/search
vim-engine/src/main/kotlin/com/maddyhome/idea/vim
@ -308,6 +308,9 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
|
|||||||
protected void textChanged(@NotNull DocumentEvent e) {
|
protected void textChanged(@NotNull DocumentEvent e) {
|
||||||
try {
|
try {
|
||||||
final Editor editor = entry.getEditor();
|
final Editor editor = entry.getEditor();
|
||||||
|
if (editor == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final String labelText = label.getText(); // Either '/', '?' or ':'boolean searchCommand = false;
|
final String labelText = label.getText(); // Either '/', '?' or ':'boolean searchCommand = false;
|
||||||
|
|
||||||
@ -351,8 +354,8 @@ public class ExEntryPanel extends JPanel implements VimCommandLine {
|
|||||||
|
|
||||||
if (labelText.equals("/") || labelText.equals("?") || searchCommand) {
|
if (labelText.equals("/") || labelText.equals("?") || searchCommand) {
|
||||||
final boolean forwards = !labelText.equals("?"); // :s, :g, :v are treated as forwards
|
final boolean forwards = !labelText.equals("?"); // :s, :g, :v are treated as forwards
|
||||||
int pattenEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0);
|
int patternEnd = injector.getSearchGroup().findEndOfPattern(searchText, separator, 0);
|
||||||
final String pattern = searchText.substring(0, pattenEnd);
|
final String pattern = searchText.substring(0, patternEnd);
|
||||||
|
|
||||||
VimPlugin.getEditor().closeEditorSearchSession(editor);
|
VimPlugin.getEditor().closeEditorSearchSession(editor);
|
||||||
final int matchOffset =
|
final int matchOffset =
|
||||||
|
@ -563,7 +563,67 @@ class IncsearchTests : VimTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `test incsearch updates selection when started in Visual mode`() {
|
fun `test incsearch updates Visual selection`() {
|
||||||
|
doTest(
|
||||||
|
listOf("ve", "/dolor"),
|
||||||
|
"""
|
||||||
|
|Lorem ipsum dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
"""
|
||||||
|
|${s}Lorem ipsum ${c}d${se}olor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
Mode.CMD_LINE(Mode.VISUAL(SelectionType.CHARACTER_WISE))
|
||||||
|
) {
|
||||||
|
enterCommand("set hlsearch incsearch")
|
||||||
|
}
|
||||||
|
assertSearchHighlights("dolor",
|
||||||
|
"""
|
||||||
|
|Lorem ipsum ‷dolor‴ sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test incsearch updates empty Visual selection`() {
|
||||||
|
doTest(
|
||||||
|
"v/ipsum",
|
||||||
|
"""
|
||||||
|
|Lorem ipsum dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
"""
|
||||||
|
|${s}Lorem ${c}i${se}psum dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
Mode.CMD_LINE(Mode.VISUAL(SelectionType.CHARACTER_WISE))
|
||||||
|
) {
|
||||||
|
enterCommand("set hlsearch incsearch")
|
||||||
|
}
|
||||||
|
assertSearchHighlights("ipsum",
|
||||||
|
"""
|
||||||
|
|Lorem ‷ipsum‴ dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test incsearch updates exclusive Visual selection`() {
|
||||||
doTest(
|
doTest(
|
||||||
listOf("ve", "/dolor"),
|
listOf("ve", "/dolor"),
|
||||||
"""
|
"""
|
||||||
@ -580,8 +640,48 @@ class IncsearchTests : VimTestCase() {
|
|||||||
""".trimMargin(),
|
""".trimMargin(),
|
||||||
Mode.CMD_LINE(Mode.VISUAL(SelectionType.CHARACTER_WISE))
|
Mode.CMD_LINE(Mode.VISUAL(SelectionType.CHARACTER_WISE))
|
||||||
) {
|
) {
|
||||||
|
enterCommand("set selection=exclusive")
|
||||||
enterCommand("set hlsearch incsearch")
|
enterCommand("set hlsearch incsearch")
|
||||||
}
|
}
|
||||||
|
assertSearchHighlights("dolor",
|
||||||
|
"""
|
||||||
|
|Lorem ipsum ‷dolor‴ sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test incsearch updates empty exclusive Visual selection`() {
|
||||||
|
doTest(
|
||||||
|
"v/ipsum",
|
||||||
|
"""
|
||||||
|
|Lorem ipsum dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
"""
|
||||||
|
|${s}Lorem ${c}${se}ipsum dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin(),
|
||||||
|
Mode.CMD_LINE(Mode.VISUAL(SelectionType.CHARACTER_WISE))
|
||||||
|
) {
|
||||||
|
enterCommand("set selection=exclusive")
|
||||||
|
enterCommand("set hlsearch incsearch")
|
||||||
|
}
|
||||||
|
assertSearchHighlights("ipsum",
|
||||||
|
"""
|
||||||
|
|Lorem ‷ipsum‴ dolor sit amet,
|
||||||
|
|consectetur adipiscing elit
|
||||||
|
|Sed in orci mauris.
|
||||||
|
|Cras id tellus in ex imperdiet egestas.
|
||||||
|
""".trimMargin()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -20,7 +20,7 @@ import com.maddyhome.idea.vim.helper.exitVisualMode
|
|||||||
import com.maddyhome.idea.vim.register.Register
|
import com.maddyhome.idea.vim.register.Register
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||||
import com.maddyhome.idea.vim.state.mode.inCommandLineMode
|
import com.maddyhome.idea.vim.state.mode.inCommandLineModeWithVisual
|
||||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import javax.swing.KeyStroke
|
import javax.swing.KeyStroke
|
||||||
@ -98,27 +98,26 @@ per-caret marks.
|
|||||||
// Make sure to always reposition the caret, even if the offset hasn't changed. We might need to reposition due to
|
// Make sure to always reposition the caret, even if the offset hasn't changed. We might need to reposition due to
|
||||||
// changes in surrounding text, especially with inline inlays.
|
// changes in surrounding text, especially with inline inlays.
|
||||||
val oldOffset = this.offset
|
val oldOffset = this.offset
|
||||||
var caretAfterMove = moveToInlayAwareOffset(offset)
|
var updatedCaret = moveToInlayAwareOffset(offset)
|
||||||
|
|
||||||
// Similarly, always make sure the caret is positioned within the view. Adding or removing text could move the caret
|
// Similarly, always make sure the caret is positioned within the view. Adding or removing text could move the caret
|
||||||
// position relative to the view, without changing offset.
|
// position relative to the view, without changing offset.
|
||||||
if (this == editor.primaryCaret()) {
|
if (this == editor.primaryCaret()) {
|
||||||
injector.scroll.scrollCaretIntoView(editor)
|
injector.scroll.scrollCaretIntoView(editor)
|
||||||
}
|
}
|
||||||
caretAfterMove = if (editor.inVisualMode || editor.inSelectMode) {
|
|
||||||
|
// If we're in Visual or Select mode, update the selection. We also need to handle Command-line mode, with Visual
|
||||||
|
// pending, e.g. `v/foo` or `v:<C-U>normal 3j`
|
||||||
|
updatedCaret = if (editor.inVisualMode || editor.inSelectMode || editor.inCommandLineModeWithVisual) {
|
||||||
// Another inconsistency with immutable caret. This method should be called on the new caret instance.
|
// Another inconsistency with immutable caret. This method should be called on the new caret instance.
|
||||||
caretAfterMove.vimMoveSelectionToCaret(this.vimSelectionStart)
|
updatedCaret.vimMoveSelectionToCaret(this.vimSelectionStart)
|
||||||
editor.findLastVersionOfCaret(caretAfterMove) ?: caretAfterMove
|
editor.findLastVersionOfCaret(updatedCaret) ?: updatedCaret
|
||||||
} else if (editor.inCommandLineMode && caretAfterMove.hasSelection()) {
|
|
||||||
// If we're updating the caret in Command-line mode, it's most likely due to incsearch
|
|
||||||
caretAfterMove.setSelection(caretAfterMove.selectionStart, offset)
|
|
||||||
editor.findLastVersionOfCaret(caretAfterMove) ?: caretAfterMove
|
|
||||||
} else {
|
} else {
|
||||||
editor.exitVisualMode()
|
editor.exitVisualMode()
|
||||||
caretAfterMove
|
updatedCaret
|
||||||
}
|
}
|
||||||
injector.motion.onAppCodeMovement(editor, this, offset, oldOffset)
|
injector.motion.onAppCodeMovement(editor, this, offset, oldOffset)
|
||||||
return caretAfterMove
|
return updatedCaret
|
||||||
}
|
}
|
||||||
|
|
||||||
fun moveToOffsetNative(offset: Int)
|
fun moveToOffsetNative(offset: Int)
|
||||||
|
@ -20,6 +20,7 @@ import com.maddyhome.idea.vim.helper.RWLockLabel
|
|||||||
import com.maddyhome.idea.vim.state.mode.Mode
|
import com.maddyhome.idea.vim.state.mode.Mode
|
||||||
import com.maddyhome.idea.vim.state.mode.SelectionType
|
import com.maddyhome.idea.vim.state.mode.SelectionType
|
||||||
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
import com.maddyhome.idea.vim.state.mode.inBlockSelection
|
||||||
|
import com.maddyhome.idea.vim.state.mode.inCommandLineModeWithVisual
|
||||||
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
import com.maddyhome.idea.vim.state.mode.inSelectMode
|
||||||
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
import com.maddyhome.idea.vim.state.mode.inVisualMode
|
||||||
import com.maddyhome.idea.vim.state.mode.selectionType
|
import com.maddyhome.idea.vim.state.mode.selectionType
|
||||||
@ -122,7 +123,7 @@ fun vimMoveBlockSelectionToOffset(editor: VimEditor, offset: Int) {
|
|||||||
* @see vimMoveBlockSelectionToOffset for blockwise selection
|
* @see vimMoveBlockSelectionToOffset for blockwise selection
|
||||||
*/
|
*/
|
||||||
fun VimCaret.vimMoveSelectionToCaret(vimSelectionStart: Int = this.vimSelectionStart) {
|
fun VimCaret.vimMoveSelectionToCaret(vimSelectionStart: Int = this.vimSelectionStart) {
|
||||||
if (!editor.inVisualMode && !editor.inSelectMode) error("Attempt to extent selection in non-visual mode")
|
if (!editor.inVisualMode && !editor.inSelectMode && !editor.inCommandLineModeWithVisual) error("Attempt to extent selection in non-visual mode")
|
||||||
if (editor.inBlockSelection) error("Move caret with [vimMoveBlockSelectionToOffset]")
|
if (editor.inBlockSelection) error("Move caret with [vimMoveBlockSelectionToOffset]")
|
||||||
|
|
||||||
val startOffsetMark = vimSelectionStart
|
val startOffsetMark = vimSelectionStart
|
||||||
|
@ -156,6 +156,13 @@ sealed interface Mode {
|
|||||||
"CMD_LINE mode can be active only in NORMAL, OP_PENDING, VISUAL or INSERT modes, not ${returnTo.javaClass.simpleName}"
|
"CMD_LINE mode can be active only in NORMAL, OP_PENDING, VISUAL or INSERT modes, not ${returnTo.javaClass.simpleName}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if Visual mode is pending, by starting Command-line with a Visual selection
|
||||||
|
*
|
||||||
|
* For example, `v/foo` or `v:<C-U>normal 3j`
|
||||||
|
*/
|
||||||
|
val isVisualPending = returnTo is VISUAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,9 @@ val VimEditor.inSelectMode: Boolean
|
|||||||
val VimEditor.inCommandLineMode: Boolean
|
val VimEditor.inCommandLineMode: Boolean
|
||||||
get() = this.mode is Mode.CMD_LINE
|
get() = this.mode is Mode.CMD_LINE
|
||||||
|
|
||||||
|
val VimEditor.inCommandLineModeWithVisual: Boolean
|
||||||
|
get() = (this.mode as? Mode.CMD_LINE)?.isVisualPending == true
|
||||||
|
|
||||||
val VimEditor.singleModeActive: Boolean
|
val VimEditor.singleModeActive: Boolean
|
||||||
get() = this.mode.isSingleModeActive
|
get() = this.mode.isSingleModeActive
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user