mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-04-21 16:15:46 +02:00
Support gn text objects
This commit is contained in:
parent
e919a026b7
commit
778ddb0e75
resources/META-INF
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/action/motion/gn
@ -419,5 +419,7 @@
|
|||||||
<!-- Visual Selection of last Search Pattern (gn) -->
|
<!-- Visual Selection of last Search Pattern (gn) -->
|
||||||
<action id="VimVisualSelectNextSearch" class="com.maddyhome.idea.vim.action.motion.gn.VisualSelectNextSearch" text="Visual Select Next Search"/>
|
<action id="VimVisualSelectNextSearch" class="com.maddyhome.idea.vim.action.motion.gn.VisualSelectNextSearch" text="Visual Select Next Search"/>
|
||||||
<action id="VimVisualSelectPreviousSearch" class="com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch" text="Visual Select Previous Search"/>
|
<action id="VimVisualSelectPreviousSearch" class="com.maddyhome.idea.vim.action.motion.gn.VisualSelectPreviousSearch" text="Visual Select Previous Search"/>
|
||||||
|
<action id="VimGnNextTextObject" class="com.maddyhome.idea.vim.action.motion.gn.GnNextTextObject" text="Gn Next Text Object"/>
|
||||||
|
<action id="VimGnPreviousTextObject" class="com.maddyhome.idea.vim.action.motion.gn.GnPreviousTextObject" text="Gn Previous Text Object"/>
|
||||||
</actions>
|
</actions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
@ -179,6 +179,14 @@ class RegisterActions {
|
|||||||
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE, CommandFlags.FLAG_MOT_INCLUSIVE, CommandFlags.FLAG_TEXT_BLOCK),
|
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE, CommandFlags.FLAG_MOT_INCLUSIVE, CommandFlags.FLAG_TEXT_BLOCK),
|
||||||
new Shortcut[]{new Shortcut("at")}
|
new Shortcut[]{new Shortcut("at")}
|
||||||
);
|
);
|
||||||
|
parser.registerAction(MappingMode.O, "VimGnNextTextObject", Command.Type.MOTION,
|
||||||
|
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE, CommandFlags.FLAG_MOT_INCLUSIVE),
|
||||||
|
new Shortcut[]{new Shortcut("gn")}
|
||||||
|
);
|
||||||
|
parser.registerAction(MappingMode.O, "VimGnPreviousTextObject", Command.Type.MOTION,
|
||||||
|
EnumSet.of(CommandFlags.FLAG_MOT_CHARACTERWISE, CommandFlags.FLAG_MOT_INCLUSIVE),
|
||||||
|
new Shortcut[]{new Shortcut("gN")}
|
||||||
|
);
|
||||||
parser.registerAction(MappingMode.NO, "VimResetMode", Command.Type.RESET, new Shortcut(new KeyStroke[]{
|
parser.registerAction(MappingMode.NO, "VimResetMode", Command.Type.RESET, new Shortcut(new KeyStroke[]{
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SLASH, KeyEvent.CTRL_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SLASH, KeyEvent.CTRL_MASK),
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK)
|
KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK)
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||||
|
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.action.motion.gn
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.editor.Caret
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.action.motion.TextObjectAction
|
||||||
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Alex Plate
|
||||||
|
*/
|
||||||
|
|
||||||
|
private object GnNextTextObjectHandler : TextObjectActionHandler() {
|
||||||
|
override fun getRange(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): TextRange? {
|
||||||
|
val range = VimPlugin.getSearch().getNextSearchRange(editor, count, true)
|
||||||
|
val adj = VimPlugin.getVisualMotion().selectionAdj
|
||||||
|
return range?.let { TextRange(it.startOffset, it.endOffset - adj) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GnNextTextObject : TextObjectAction(GnNextTextObjectHandler)
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||||
|
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.maddyhome.idea.vim.action.motion.gn
|
||||||
|
|
||||||
|
import com.intellij.openapi.actionSystem.DataContext
|
||||||
|
import com.intellij.openapi.editor.Caret
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.action.motion.TextObjectAction
|
||||||
|
import com.maddyhome.idea.vim.command.Argument
|
||||||
|
import com.maddyhome.idea.vim.common.TextRange
|
||||||
|
import com.maddyhome.idea.vim.handler.TextObjectActionHandler
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Alex Plate
|
||||||
|
*/
|
||||||
|
|
||||||
|
private object GnPreviousTextObjectHandler : TextObjectActionHandler() {
|
||||||
|
override fun getRange(editor: Editor, caret: Caret, context: DataContext, count: Int, rawCount: Int, argument: Argument?): TextRange? {
|
||||||
|
val range = VimPlugin.getSearch().getNextSearchRange(editor, count, false)
|
||||||
|
val adj = VimPlugin.getVisualMotion().selectionAdj
|
||||||
|
return range?.let { TextRange(it.startOffset, it.endOffset - adj) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GnPreviousTextObject : TextObjectAction(GnPreviousTextObjectHandler)
|
@ -1352,44 +1352,16 @@ public class MotionGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int selectNextSearch(@NotNull Editor editor, int count, boolean forwards) {
|
public int selectNextSearch(@NotNull Editor editor, int count, boolean forwards) {
|
||||||
editor.getCaretModel().removeSecondaryCarets();
|
|
||||||
TextRange current = VimPlugin.getSearch().findUnderCaret(editor);
|
|
||||||
final Caret caret = editor.getCaretModel().getPrimaryCaret();
|
final Caret caret = editor.getCaretModel().getPrimaryCaret();
|
||||||
|
final TextRange range = VimPlugin.getSearch().getNextSearchRange(editor, count, forwards);
|
||||||
if (current == null || CommandStateHelper.inVisualMode(editor) && atEdgeOfGnRange(current, editor, forwards)) {
|
if (range == null) return -1;
|
||||||
current = VimPlugin.getSearch().findNextSearchForGn(editor, count, forwards);
|
final int adj = VimPlugin.getVisualMotion().getSelectionAdj();
|
||||||
if (current == null) return -1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (count > 1) {
|
|
||||||
current = VimPlugin.getSearch().findNextSearchForGn(editor, count - 1, forwards);
|
|
||||||
if (current == null) return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CommandStateHelper.inVisualMode(editor)) {
|
if (!CommandStateHelper.inVisualMode(editor)) {
|
||||||
MotionGroup.moveCaret(editor, caret, gnStartOffset(current, forwards));
|
final int startOffset = forwards ? range.getStartOffset() : Math.max(range.getEndOffset() - adj, 0);
|
||||||
|
MotionGroup.moveCaret(editor, caret, startOffset);
|
||||||
VimPlugin.getVisualMotion().enterVisualMode(editor, CommandState.SubMode.VISUAL_CHARACTER);
|
VimPlugin.getVisualMotion().enterVisualMode(editor, CommandState.SubMode.VISUAL_CHARACTER);
|
||||||
}
|
}
|
||||||
return gnEndOffset(current, forwards);
|
return forwards ? Math.max(range.getEndOffset() - adj, 0) : range.getStartOffset();
|
||||||
}
|
|
||||||
|
|
||||||
private boolean atEdgeOfGnRange(@NotNull TextRange nextRange, @NotNull Editor editor, boolean forwards) {
|
|
||||||
int currentPosition = editor.getCaretModel().getOffset();
|
|
||||||
if (forwards) {
|
|
||||||
return nextRange.getEndOffset() - 1 == currentPosition;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return nextRange.getStartOffset() == currentPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int gnEndOffset(@NotNull TextRange nextRange, boolean forwards) {
|
|
||||||
return forwards ? Math.max(nextRange.getEndOffset() - 1, 0) : nextRange.getStartOffset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int gnStartOffset(@NotNull TextRange range, boolean forwards) {
|
|
||||||
return forwards ? range.getStartOffset() : Math.max(range.getEndOffset() - 1, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int lastFTCmd = 0;
|
private int lastFTCmd = 0;
|
||||||
|
@ -648,7 +648,31 @@ public class SearchGroup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public TextRange findNextSearchForGn(@NotNull Editor editor, int count, boolean forwards) {
|
public TextRange getNextSearchRange(@NotNull Editor editor, int count, boolean forwards) {
|
||||||
|
editor.getCaretModel().removeSecondaryCarets();
|
||||||
|
TextRange current = findUnderCaret(editor);
|
||||||
|
|
||||||
|
if (current == null || CommandStateHelper.inVisualMode(editor) && atEdgeOfGnRange(current, editor, forwards)) {
|
||||||
|
current = findNextSearchForGn(editor, count, forwards);
|
||||||
|
}
|
||||||
|
else if (count > 1) {
|
||||||
|
current = findNextSearchForGn(editor, count - 1, forwards);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean atEdgeOfGnRange(@NotNull TextRange nextRange, @NotNull Editor editor, boolean forwards) {
|
||||||
|
int currentPosition = editor.getCaretModel().getOffset();
|
||||||
|
if (forwards) {
|
||||||
|
return nextRange.getEndOffset() - VimPlugin.getVisualMotion().getSelectionAdj() == currentPosition;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nextRange.getStartOffset() == currentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TextRange findNextSearchForGn(@NotNull Editor editor, int count, boolean forwards) {
|
||||||
if (forwards) {
|
if (forwards) {
|
||||||
return findIt(editor, editor.getCaretModel().getOffset(), count, 1, false, true, false, true);
|
return findIt(editor, editor.getCaretModel().getOffset(), count, 1, false, true, false, true);
|
||||||
} else {
|
} else {
|
||||||
@ -656,7 +680,8 @@ public class SearchGroup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextRange findUnderCaret(@NotNull Editor editor) {
|
@Nullable
|
||||||
|
private TextRange findUnderCaret(@NotNull Editor editor) {
|
||||||
final TextRange backSearch = searchBackward(editor, editor.getCaretModel().getOffset() + 1, 1);
|
final TextRange backSearch = searchBackward(editor, editor.getCaretModel().getOffset() + 1, 1);
|
||||||
if (backSearch == null) return null;
|
if (backSearch == null) return null;
|
||||||
return backSearch.contains(editor.getCaretModel().getOffset()) ? backSearch : null;
|
return backSearch.contains(editor.getCaretModel().getOffset()) ? backSearch : null;
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||||
|
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("RemoveCurlyBracesFromTemplate")
|
||||||
|
|
||||||
|
package org.jetbrains.plugins.ideavim.action.motion.gn
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
|
import com.maddyhome.idea.vim.command.CommandState
|
||||||
|
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||||
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
|
class GnNextTextObjectTest : VimTestCase() {
|
||||||
|
fun `test delete word`() {
|
||||||
|
doTestWithSearch(parseKeys("dgn"), """
|
||||||
|
Hello, ${c}this is a test here
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
Hello, this is a ${c} here
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `test delete second word`() {
|
||||||
|
doTestWithSearch(parseKeys("2dgn"), """
|
||||||
|
Hello, ${c}this is a test here
|
||||||
|
Hello, this is a test here
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
Hello, this is a test here
|
||||||
|
Hello, this is a ${c} here
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `test with repeat`() {
|
||||||
|
doTestWithSearch(parseKeys("cgnNewValue<ESC>..."), """
|
||||||
|
Hello, ${c}this is a test here
|
||||||
|
Hello, this is a test here
|
||||||
|
Hello, this is a test here
|
||||||
|
Hello, this is a test here
|
||||||
|
Hello, this is a test here
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
Hello, this is a NewValue here
|
||||||
|
Hello, this is a NewValue here
|
||||||
|
Hello, this is a NewValue here
|
||||||
|
Hello, this is a NewValu${c}e here
|
||||||
|
Hello, this is a test here
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestWithSearch(keys: List<KeyStroke>, before: String,
|
||||||
|
after: String) {
|
||||||
|
configureByText(before)
|
||||||
|
VimPlugin.getSearch().search(myFixture.editor, "test", 1, EnumSet.noneOf(CommandFlags::class.java), false)
|
||||||
|
typeText(keys)
|
||||||
|
myFixture.checkResult(after)
|
||||||
|
assertState(CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* IdeaVim - Vim emulator for IDEs based on the IntelliJ platform
|
||||||
|
* Copyright (C) 2003-2019 The IdeaVim authors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("RemoveCurlyBracesFromTemplate")
|
||||||
|
|
||||||
|
package org.jetbrains.plugins.ideavim.action.motion.gn
|
||||||
|
|
||||||
|
import com.maddyhome.idea.vim.VimPlugin
|
||||||
|
import com.maddyhome.idea.vim.command.CommandFlags
|
||||||
|
import com.maddyhome.idea.vim.command.CommandState
|
||||||
|
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||||
|
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||||
|
import java.util.*
|
||||||
|
import javax.swing.KeyStroke
|
||||||
|
|
||||||
|
class GnPreviousTextObjectTest : VimTestCase() {
|
||||||
|
fun `test delete word`() {
|
||||||
|
doTestWithSearch(parseKeys("dgN"), """
|
||||||
|
Hello, ${c}this is a test here
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
Hello, this is a ${c} here
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `test delete second word`() {
|
||||||
|
doTestWithSearch(parseKeys("2dgN"), """
|
||||||
|
Hello, this is a test here
|
||||||
|
Hello, this is a test ${c}here
|
||||||
|
""".trimIndent(),
|
||||||
|
"""
|
||||||
|
Hello, this is a ${c} here
|
||||||
|
Hello, this is a test here
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doTestWithSearch(keys: List<KeyStroke>, before: String,
|
||||||
|
after: String) {
|
||||||
|
configureByText(before)
|
||||||
|
VimPlugin.getSearch().search(myFixture.editor, "test", 1, EnumSet.noneOf(CommandFlags::class.java), false)
|
||||||
|
typeText(keys)
|
||||||
|
myFixture.checkResult(after)
|
||||||
|
assertState(CommandState.Mode.COMMAND, CommandState.SubMode.NONE)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user