1
0
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:
Alex Plate 2019-06-11 13:06:36 +03:00
parent e919a026b7
commit 778ddb0e75
No known key found for this signature in database
GPG Key ID: 0B97153C8FFEC09F
8 changed files with 264 additions and 36 deletions
resources/META-INF
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/action/motion/gn

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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