mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-03-07 12:32:52 +01:00
Merge pull request #179 from Vvalter/master
Fix VIM-1090 and VIM-1100 tag motion with duplicate tags.
This commit is contained in:
commit
af79066c26
src/com/maddyhome/idea/vim/helper
test/org/jetbrains/plugins/ideavim/action
@ -18,7 +18,6 @@
|
||||
|
||||
package com.maddyhome.idea.vim.helper;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.lang.CodeDocumentationAwareCommenter;
|
||||
import com.intellij.lang.Commenter;
|
||||
import com.intellij.lang.Language;
|
||||
@ -27,7 +26,6 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@ -42,6 +40,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -432,80 +431,192 @@ public class SearchHelper {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** returns new position which ignore whitespaces at beginning of the line*/
|
||||
private static int ignoreWhitespaceAtLineStart(CharSequence seq, int lineStart, int pos) {
|
||||
if (seq.subSequence(lineStart, pos).chars().allMatch(Character::isWhitespace)) {
|
||||
while (pos < seq.length() && seq.charAt(pos) != '\n' && Character.isWhitespace(seq.charAt(pos))) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
public static TextRange findBlockTagRange(@NotNull Editor editor, @NotNull Caret caret, int count, boolean isOuter) {
|
||||
final int cursorOffset = caret.getOffset();
|
||||
int pos = cursorOffset;
|
||||
int currentCount = count;
|
||||
final int position = caret.getOffset();
|
||||
final CharSequence sequence = editor.getDocument().getCharsSequence();
|
||||
|
||||
final int selectionStart = caret.getSelectionStart();
|
||||
final int selectionEnd = caret.getSelectionEnd();
|
||||
|
||||
final boolean isRangeSelection = selectionEnd - selectionStart > 1;
|
||||
|
||||
int searchStartPosition;
|
||||
if (!isRangeSelection) {
|
||||
final int line = caret.getLogicalPosition().line;
|
||||
final int lineBegin = editor.getDocument().getLineStartOffset(line);
|
||||
searchStartPosition = ignoreWhitespaceAtLineStart(sequence, lineBegin, position);
|
||||
} else {
|
||||
searchStartPosition = selectionEnd;
|
||||
}
|
||||
|
||||
if (isInHTMLTag(sequence, searchStartPosition, false)) {
|
||||
// caret is inside opening tag. Move to closing '>'.
|
||||
while (searchStartPosition < sequence.length() && sequence.charAt(searchStartPosition) != '>') {
|
||||
searchStartPosition ++;
|
||||
}
|
||||
}
|
||||
else if (isInHTMLTag(sequence, searchStartPosition, true)) {
|
||||
// caret is inside closing tag. Move to starting '<'.
|
||||
while (searchStartPosition > 0 && sequence.charAt(searchStartPosition) != '<') {
|
||||
searchStartPosition --;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
final Pair<TextRange, String> closingTagResult = findClosingTag(sequence, pos);
|
||||
if (closingTagResult == null) {
|
||||
final Pair<TextRange, String> closingTag = findUnmatchedClosingTag(sequence, searchStartPosition, count);
|
||||
if (closingTag == null) {
|
||||
return null;
|
||||
}
|
||||
final TextRange closingTagTextRange = closingTagResult.getFirst();
|
||||
final String tagName = closingTagResult.getSecond();
|
||||
final TextRange openingTagTextRange = findOpeningTag(sequence, closingTagTextRange.getStartOffset(), tagName);
|
||||
if (openingTagTextRange != null && openingTagTextRange.getStartOffset() <= cursorOffset && --currentCount == 0) {
|
||||
if (isOuter) {
|
||||
return new TextRange(openingTagTextRange.getStartOffset(), closingTagTextRange.getEndOffset());
|
||||
final TextRange closingTagTextRange = closingTag.getFirst();
|
||||
final String tagName = closingTag.getSecond();
|
||||
|
||||
TextRange openingTag = findUnmatchedOpeningTag(sequence, closingTagTextRange.getStartOffset(), tagName);
|
||||
if (openingTag == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isRangeSelection && openingTag.getEndOffset() - 1 >= selectionStart) {
|
||||
// If there was already some text selected and the new selection would not extend further, we try again
|
||||
searchStartPosition = closingTagTextRange.getEndOffset();
|
||||
count = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int selectionEndWithoutNewline = selectionEnd;
|
||||
while (selectionEndWithoutNewline < sequence.length() && sequence.charAt(selectionEndWithoutNewline) == '\n') {
|
||||
selectionEndWithoutNewline ++;
|
||||
}
|
||||
|
||||
if (closingTagTextRange.getStartOffset() == selectionEndWithoutNewline && openingTag.getEndOffset() == selectionStart) {
|
||||
// Special case: if the inner tag is already selected we should like isOuter is active
|
||||
// Note that we need to ignore newlines, because their selection is lost between multiple "it" invocations
|
||||
isOuter = true;
|
||||
} else
|
||||
if (openingTag.getEndOffset() == closingTagTextRange.getStartOffset() && selectionStart == openingTag.getEndOffset()) {
|
||||
// Special case: for an empty tag pair (e.g. <a></a>) the whole tag is selected if the caret is in the middle.
|
||||
isOuter = true;
|
||||
}
|
||||
|
||||
if (isOuter) {
|
||||
return new TextRange(openingTag.getStartOffset(), closingTagTextRange.getEndOffset() - 1);
|
||||
} else {
|
||||
return new TextRange(openingTag.getEndOffset(), Math.max(closingTagTextRange.getStartOffset() - 1, openingTag.getEndOffset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a html at the given position. Ignores tags with a trailing slash like <aaa/>.
|
||||
*/
|
||||
private static boolean isInHTMLTag(@NotNull final CharSequence sequence, final int position, final boolean isEndtag) {
|
||||
int openingBracket = -1;
|
||||
for (int i = position; i >= 0 && i < sequence.length(); i--) {
|
||||
if (sequence.charAt(i) == '<') {
|
||||
openingBracket = i;
|
||||
break;
|
||||
}
|
||||
if (sequence.charAt(i) == '>' && i != position) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (openingBracket == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasSlashAfterOpening = openingBracket + 1 < sequence.length() && sequence.charAt(openingBracket + 1) == '/';
|
||||
if ((isEndtag && !hasSlashAfterOpening) || (!isEndtag && hasSlashAfterOpening)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int closingBracket = -1;
|
||||
for (int i = openingBracket; i < sequence.length(); i++) {
|
||||
if (sequence.charAt(i) == '>') {
|
||||
closingBracket = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return closingBracket != -1 && sequence.charAt(closingBracket - 1) != '/';
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<TextRange,String> findUnmatchedClosingTag(@NotNull final CharSequence sequence, final int position, int count) {
|
||||
// The tag name may contain any characters except slashes, whitespace and '>'
|
||||
final String tagNamePattern = "([^/\\s>]+)";
|
||||
// An opening tag consists of '<' followed by a tag name, optionally some additional text after whitespace and a '>'
|
||||
final String openingTagPattern = String.format("<%s(?:\\s[^>]*)?>", tagNamePattern);
|
||||
final String closingTagPattern = String.format("</%s>", tagNamePattern);
|
||||
final Pattern tagPattern = Pattern.compile(String.format("(?:%s)|(?:%s)", openingTagPattern, closingTagPattern));
|
||||
final Matcher matcher = tagPattern.matcher(sequence.subSequence(position, sequence.length()));
|
||||
|
||||
final Stack<String> openTags = new Stack<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
boolean isClosingTag = matcher.group(1) == null;
|
||||
if (isClosingTag) {
|
||||
final String tagName = matcher.group(2);
|
||||
// Ignore unmatched open tags. Either the file is malformed or it might be a tag like <br> that does not need to be closed.
|
||||
while (!openTags.isEmpty() && !openTags.peek().equalsIgnoreCase(tagName)) {
|
||||
openTags.pop();
|
||||
}
|
||||
else {
|
||||
return new TextRange(openingTagTextRange.getEndOffset() + 1, closingTagTextRange.getStartOffset() - 1);
|
||||
if (openTags.isEmpty()) {
|
||||
if (count <= 1) {
|
||||
return Pair.create(new TextRange(position + matcher.start(), position + matcher.end()), tagName);
|
||||
} else {
|
||||
count--;
|
||||
}
|
||||
} else {
|
||||
openTags.pop();
|
||||
}
|
||||
} else {
|
||||
final String tagName = matcher.group(1);
|
||||
openTags.push(tagName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TextRange findUnmatchedOpeningTag(@NotNull CharSequence sequence, int position, @NotNull String tagName) {
|
||||
final String quotedTagName = Pattern.quote(tagName);
|
||||
final String patternString = "(</%s>)" // match closing tags
|
||||
+ "|(<%s" // or opening tags starting with tagName
|
||||
+ "(\\s([^>]*" // After at least one whitespace there might be additional text in the tag. E.g. <html lang="en">
|
||||
+ "[^/])?)?>)"; // Slash is not allowed as last character (this would be a self closing tag).
|
||||
final Pattern tagPattern = Pattern.compile(String.format(patternString, quotedTagName, quotedTagName), Pattern.CASE_INSENSITIVE);
|
||||
final Matcher matcher = tagPattern.matcher(sequence.subSequence(0, position+1));
|
||||
final Stack<TextRange> openTags = new Stack<>();
|
||||
|
||||
while (matcher.find()) {
|
||||
final TextRange match = new TextRange(matcher.start(), matcher.end());
|
||||
if (sequence.charAt(matcher.start() + 1) == '/') {
|
||||
if (!openTags.isEmpty()) {
|
||||
openTags.pop();
|
||||
}
|
||||
}
|
||||
else {
|
||||
pos = closingTagTextRange.getEndOffset() + 1;
|
||||
openTags.push(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TextRange findOpeningTag(@NotNull CharSequence sequence, int position, @NotNull String tagName) {
|
||||
final String tagBeginning = "<" + tagName;
|
||||
final Pattern pattern = Pattern.compile(Pattern.quote(tagBeginning), Pattern.CASE_INSENSITIVE);
|
||||
final Matcher matcher = pattern.matcher(sequence.subSequence(0, position));
|
||||
final List<Integer> possibleBeginnings = Lists.newArrayList();
|
||||
while (matcher.find()) {
|
||||
possibleBeginnings.add(matcher.start());
|
||||
if (openTags.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return openTags.pop();
|
||||
}
|
||||
final List<Integer> reversedBeginnings = Lists.reverse(possibleBeginnings);
|
||||
for (int openingTagPos : reversedBeginnings) {
|
||||
final int openingTagEndPos = openingTagPos + tagBeginning.length();
|
||||
final int closeBracketPos = StringUtil.indexOf(sequence, '>', openingTagEndPos);
|
||||
if (closeBracketPos > 0 && (closeBracketPos == openingTagEndPos || sequence.charAt(openingTagEndPos) == ' ')) {
|
||||
return new TextRange(openingTagPos, closeBracketPos);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Pair<TextRange, String> findClosingTag(@NotNull CharSequence sequence, int pos) {
|
||||
int closeBracketPos = pos;
|
||||
int openBracketPos;
|
||||
while (closeBracketPos < sequence.length()) {
|
||||
closeBracketPos = StringUtil.indexOf(sequence, '>', closeBracketPos);
|
||||
if (closeBracketPos < 0) {
|
||||
return null;
|
||||
}
|
||||
openBracketPos = closeBracketPos - 1;
|
||||
while (openBracketPos >= 0) {
|
||||
openBracketPos = StringUtil.lastIndexOf(sequence, '<', 0, openBracketPos);
|
||||
if (openBracketPos >= 0 &&
|
||||
openBracketPos + 1 < sequence.length() &&
|
||||
sequence.charAt(openBracketPos + 1) == '/') {
|
||||
final String tagName = String.valueOf(sequence.subSequence(openBracketPos + "</".length(), closeBracketPos));
|
||||
if (tagName.length() > 0 && tagName.charAt(0) != ' ') {
|
||||
TextRange textRange = new TextRange(openBracketPos, closeBracketPos);
|
||||
return Pair.create(textRange, tagName);
|
||||
}
|
||||
}
|
||||
openBracketPos--;
|
||||
}
|
||||
closeBracketPos++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
@ -383,350 +383,6 @@ public class MotionActionTest extends VimTestCase {
|
||||
myFixture.checkResult("foo = ;\n");
|
||||
}
|
||||
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockCaretInHtml() {
|
||||
typeTextInFile(parseKeys("dit"), "<template <caret>name=\"hello\">\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n");
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockCaretInHtmlUnclosedTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<template <caret>name=\"hello\">\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <br>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n");
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n");
|
||||
}
|
||||
|
||||
public void testDeleteInnerTagBlockCaretEdgeTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<template name=\"hello\"<caret>>\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <br>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n");
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBefore() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de<tag>fg</tag>hi");
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpen() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fg</tag>hi");
|
||||
myFixture.checkResult("abcde<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpenEndOfLine() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fg</tag>");
|
||||
myFixture.checkResult("abcde<tag></tag>");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpenStartOfLine() {
|
||||
typeTextInFile(parseKeys("dit"), "<ta<caret>g>fg</tag>hi");
|
||||
myFixture.checkResult("<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpenWithArgs() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g name = \"name\">fg</tag>hi");
|
||||
myFixture.checkResult("abcde<tag name = \"name\"></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetween() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcde<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenTagWithRegex() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<[abc]*>af<caret>gbc</[abc]*>hi");
|
||||
myFixture.checkResult("abcde<[abc]*></[abc]*>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenCamelCase() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tAg>f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcde<tAg></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenCaps() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>g</TAG>hi");
|
||||
myFixture.checkResult("abcde<tag></TAG>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenWithSpaceBeforeTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde< tag>f<caret>g</ tag>hi");
|
||||
myFixture.checkResult("abcde< tag>fg</ tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenWithSpaceAfterTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag >f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcde<tag ></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenWithArgs() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag name = \"name\">f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcde<tag name = \"name\"></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInClose() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>fg</ta<caret>g>hi");
|
||||
myFixture.checkResult("abcde<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockAfter() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>fg</tag>h<caret>i");
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInAlone() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fghi");
|
||||
myFixture.checkResult("abcde<tag>fghi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockWithoutTags() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de");
|
||||
myFixture.checkResult("abcde");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBeforeWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>defg</tag>hi");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInCloseWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg</ta<caret>g>hi");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockAfterWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg</tag>h<caret>i");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBeforeWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>defg<tag>hi");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpenWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg<ta<caret>g>hi");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockAfterWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg<tag>h<caret>i");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBeforeWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de</tag>fg<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInOpenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</ta<caret>g>fg<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBetweenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>f<caret>g<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockInCloseWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>fg<ta<caret>g>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockTwoTagsWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "<foo><html>t<caret>ext</foo></html>");
|
||||
myFixture.checkResult("<foo></foo></html>");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockTwoTagsWrongOrderInClosingTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<foo><html>text</foo></htm<caret>l>");
|
||||
myFixture.checkResult("<foo><html></html>");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockAfterWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>fg<tag>h<caret>i");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBracketInside() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret><>g</tag>hi");
|
||||
myFixture.checkResult("abcde<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
public void testDeleteInnerTagBlockBracketInsideString() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>\"<>\"g</tag>hi");
|
||||
myFixture.checkResult("abcde<tag></tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBefore() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de<tag>fg</tag>hi");
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInOpen() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g>fg</tag>hi");
|
||||
myFixture.checkResult("abcdehi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInOpenWithArgs() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g name = \"name\">fg</tag>hi");
|
||||
myFixture.checkResult("abcdehi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBetween() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcdehi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBetweenWithArgs() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag name = \"name\">f<caret>g</tag>hi");
|
||||
myFixture.checkResult("abcdehi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInClose() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>fg</ta<caret>g>hi");
|
||||
myFixture.checkResult("abcdehi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockAfter() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>fg</tag>h<caret>i");
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInAlone() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g>fghi");
|
||||
myFixture.checkResult("abcde<tag>fghi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockWithoutTags() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de");
|
||||
myFixture.checkResult("abcde");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBeforeWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>defg</tag>hi");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInCloseWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg</ta<caret>g>hi");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockAfterWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg</tag>h<caret>i");
|
||||
myFixture.checkResult("abcdefg</tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBeforeWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>defg<tag>hi");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInOpenWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg<ta<caret>g>hi");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockAfterWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg<tag>h<caret>i");
|
||||
myFixture.checkResult("abcdefg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBeforeWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de</tag>fg<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInOpenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</ta<caret>g>fg<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockBetweenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>f<caret>g<tag>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockInCloseWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>fg<ta<caret>g>hi");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
public void testDeleteOuterTagBlockAfterWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>fg<tag>h<caret>i");
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi");
|
||||
}
|
||||
|
||||
// |v_it|
|
||||
public void testFileStartsWithSlash() {
|
||||
configureByText("/*hello\n" +
|
||||
"<caret>foo\n" +
|
||||
"bar>baz\n");
|
||||
typeText(parseKeys("vit"));
|
||||
assertPluginError(true);
|
||||
}
|
||||
|
||||
// VIM-1427
|
||||
public void testDeleteOuterTagWithCount() {
|
||||
typeTextInFile(parseKeys("d2at"),"<a><b><c><caret></c></b></a>");
|
||||
|
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* 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 org.jetbrains.plugins.ideavim.action.motion.`object`
|
||||
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class MotionInnerBlockTagActionTest : VimTestCase() {
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockCaretInHtml() {
|
||||
typeTextInFile(parseKeys("dit"), "<template <caret>name=\"hello\">\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n")
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockCaretInHtmlUnclosedTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<template <caret>name=\"hello\">\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <br>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n")
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n")
|
||||
}
|
||||
|
||||
fun testDeleteInnerTagBlockCaretEdgeTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<template name=\"hello\"<caret>>\n" +
|
||||
" <button>Click Me</button>\n" +
|
||||
" <br>\n" +
|
||||
" <p>You've pressed the button {{counter}} times.</p>\n" +
|
||||
"</template>\n")
|
||||
myFixture.checkResult("<template name=\"hello\"></template>\n")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBefore() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de<tag>fg</tag>hi")
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpen() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fg</tag>hi")
|
||||
myFixture.checkResult("abcde<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpenEndOfLine() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fg</tag>")
|
||||
myFixture.checkResult("abcde<tag></tag>")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpenStartOfLine() {
|
||||
typeTextInFile(parseKeys("dit"), "<ta<caret>g>fg</tag>hi")
|
||||
myFixture.checkResult("<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpenWithArgs() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g name = \"name\">fg</tag>hi")
|
||||
myFixture.checkResult("abcde<tag name = \"name\"></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetween() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcde<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenTagWithRegex() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<[abc]*>af<caret>gbc</[abc]*>hi")
|
||||
myFixture.checkResult("abcde<[abc]*></[abc]*>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenCamelCase() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tAg>f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcde<tAg></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenCaps() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>g</TAG>hi")
|
||||
myFixture.checkResult("abcde<tag></TAG>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenWithSpaceBeforeTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde< tag>f<caret>g</ tag>hi")
|
||||
myFixture.checkResult("abcde< tag>fg</ tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenWithSpaceAfterTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag >f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcde<tag ></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenWithArgs() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag name = \"name\">f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcde<tag name = \"name\"></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInClose() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>fg</ta<caret>g>hi")
|
||||
myFixture.checkResult("abcde<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockAfter() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>fg</tag>h<caret>i")
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInAlone() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<ta<caret>g>fghi")
|
||||
myFixture.checkResult("abcde<tag>fghi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockWithoutTags() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de")
|
||||
myFixture.checkResult("abcde")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBeforeWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>defg</tag>hi")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInCloseWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg</ta<caret>g>hi")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockAfterWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg</tag>h<caret>i")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBeforeWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>defg<tag>hi")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpenWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg<ta<caret>g>hi")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockAfterWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dit"), "abcdefg<tag>h<caret>i")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBeforeWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abc<caret>de</tag>fg<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInOpenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</ta<caret>g>fg<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBetweenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>f<caret>g<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockInCloseWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>fg<ta<caret>g>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockTwoTagsWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "<foo><html>t<caret>ext</foo></html>")
|
||||
myFixture.checkResult("<foo></foo></html>")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockTwoTagsWrongOrderInClosingTag() {
|
||||
typeTextInFile(parseKeys("dit"), "<foo><html>text</foo></htm<caret>l>")
|
||||
myFixture.checkResult("<foo><html></html>")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockAfterWrongOrder() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde</tag>fg<tag>h<caret>i")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBracketInside() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret><>g</tag>hi")
|
||||
myFixture.checkResult("abcde<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagBlockBracketInsideString() {
|
||||
typeTextInFile(parseKeys("dit"), "abcde<tag>f<caret>\"<>\"g</tag>hi")
|
||||
myFixture.checkResult("abcde<tag></tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagIsCaseInsensitive() {
|
||||
typeTextInFile(parseKeys("dit"), "<a> <as<caret>df> </A>")
|
||||
myFixture.checkResult("<a></A>")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagSlashesInAttribute() {
|
||||
typeTextInFile(parseKeys("dit"), "<a href=\"http://isitchristmas.com\" class=\"button\">Bing <caret>Bing bing</a>")
|
||||
myFixture.checkResult("<a href=\"http://isitchristmas.com\" class=\"button\"></a>")
|
||||
}
|
||||
|
||||
// VIM-1090 |d| |v_it|
|
||||
// Adapted from vim source file "test_textobjects.vim"
|
||||
fun testDeleteInnerTagDuplicateTags() {
|
||||
typeTextInFile(parseKeys("dit"), "<b>as<caret>d<i>as<b />df</i>asdf</b>")
|
||||
myFixture.checkResult("<b></b>")
|
||||
}
|
||||
|
||||
// |v_it|
|
||||
fun testFileStartsWithSlash() {
|
||||
configureByText("/*hello\n" +
|
||||
"<caret>foo\n" +
|
||||
"bar>baz\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertPluginError(true)
|
||||
}
|
||||
|
||||
// |v_it|
|
||||
fun testSelectInnerTagEmptyTag() {
|
||||
configureByText("<a><caret></a>")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("<a></a>")
|
||||
}
|
||||
|
||||
fun `test single character`() {
|
||||
// The whole tag block is also selected if there is only a single character inside
|
||||
configureByText("<a><caret>a</a>")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("<a>a</a>")
|
||||
}
|
||||
|
||||
fun `test single character inside tag`() {
|
||||
configureByText("<a<caret>></a>")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("<")
|
||||
}
|
||||
|
||||
// VIM-1633 |v_it|
|
||||
fun testNestedInTagSelection() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("Inner")
|
||||
}
|
||||
|
||||
fun `test nested tag double motion`() {
|
||||
configureByText("<o>Outer\n" +
|
||||
" <caret> <t></t>\n" +
|
||||
"</o>\n")
|
||||
typeText(parseKeys("vitit"))
|
||||
assertSelection("<t></t>")
|
||||
}
|
||||
|
||||
fun `test in inner tag double motion`() {
|
||||
configureByText("<o><t><caret></t>\n</o>")
|
||||
typeText(parseKeys("vitit"))
|
||||
assertSelection("<o><t></t>\n</o>")
|
||||
}
|
||||
|
||||
fun `test nested tags between tags`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t>Inner</t> <caret> <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t> <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags number motion`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("v2it"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags double motion`() {
|
||||
configureByText("<o>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</o>\n")
|
||||
typeText(parseKeys("vitit"))
|
||||
assertSelection("<t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags triple motion`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vititit"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags in closing tag`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</<caret>t>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags in opening tag`() {
|
||||
configureByText("<<caret>t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags ouside tag`() {
|
||||
configureByText("<caret><t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("Outer\n" + " <t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test skip whitespace at start of line`() {
|
||||
configureByText("<o>Outer\n" +
|
||||
" <caret> <t></t>\n" +
|
||||
"</o>\n")
|
||||
typeText(parseKeys("vit"))
|
||||
assertSelection("<")
|
||||
}
|
||||
}
|
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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 org.jetbrains.plugins.ideavim.action.motion.`object`
|
||||
|
||||
import com.maddyhome.idea.vim.helper.StringHelper.parseKeys
|
||||
import org.jetbrains.plugins.ideavim.VimTestCase
|
||||
|
||||
class MotionOuterBlockTagActionTest : VimTestCase() {
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBefore() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de<tag>fg</tag>hi")
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInOpen() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g>fg</tag>hi")
|
||||
myFixture.checkResult("abcdehi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInOpenWithArgs() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g name = \"name\">fg</tag>hi")
|
||||
myFixture.checkResult("abcdehi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBetween() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcdehi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBetweenWithArgs() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag name = \"name\">f<caret>g</tag>hi")
|
||||
myFixture.checkResult("abcdehi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInClose() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>fg</ta<caret>g>hi")
|
||||
myFixture.checkResult("abcdehi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockAfter() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<tag>fg</tag>h<caret>i")
|
||||
myFixture.checkResult("abcde<tag>fg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInAlone() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde<ta<caret>g>fghi")
|
||||
myFixture.checkResult("abcde<tag>fghi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockWithoutTags() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de")
|
||||
myFixture.checkResult("abcde")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBeforeWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>defg</tag>hi")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInCloseWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg</ta<caret>g>hi")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockAfterWithoutOpenTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg</tag>h<caret>i")
|
||||
myFixture.checkResult("abcdefg</tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBeforeWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>defg<tag>hi")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInOpenWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg<ta<caret>g>hi")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockAfterWithoutCloseTag() {
|
||||
typeTextInFile(parseKeys("dat"), "abcdefg<tag>h<caret>i")
|
||||
myFixture.checkResult("abcdefg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBeforeWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abc<caret>de</tag>fg<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInOpenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</ta<caret>g>fg<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockBetweenWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>f<caret>g<tag>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockInCloseWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>fg<ta<caret>g>hi")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_at|
|
||||
fun testDeleteOuterTagBlockAfterWrongOrder() {
|
||||
typeTextInFile(parseKeys("dat"), "abcde</tag>fg<tag>h<caret>i")
|
||||
myFixture.checkResult("abcde</tag>fg<tag>hi")
|
||||
}
|
||||
|
||||
//|d| |v_it|
|
||||
fun testDeleteInnerTagAngleBrackets() {
|
||||
typeTextInFile(parseKeys("dit"), "<div <caret>hello=\"d > hsj < akl\"></div>")
|
||||
myFixture.checkResult("<div hello=\"d ></div>")
|
||||
}
|
||||
|
||||
// VIM-1090 |d| |v_at|
|
||||
fun testDeleteOuterTagDuplicateTags() {
|
||||
typeTextInFile(parseKeys("dat"), "<a><a></a></a<caret>>")
|
||||
myFixture.checkResult("")
|
||||
}
|
||||
|
||||
// |v_it| |v_at|
|
||||
fun testTagSelectionSkipsWhitespaceAtStartOfLine() {
|
||||
// Also skip tabs
|
||||
configureByText("<o>Outer\n" +
|
||||
" <caret> \t <t>Inner</t>\n" +
|
||||
"</o>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test skip new line`() {
|
||||
// Newline must not be skipped
|
||||
configureByText("<caret>\n" + " <t>asdf</t>")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection(null)
|
||||
}
|
||||
|
||||
fun `test whitespace skip`() {
|
||||
// Whitespace is only skipped if there is nothing else at the start of the line
|
||||
configureByText("<o>Outer\n" +
|
||||
"a <caret> <t>Inner</t>\n" +
|
||||
"</o>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<o>Outer\n" +
|
||||
"a <t>Inner</t>\n" +
|
||||
"</o>")
|
||||
}
|
||||
|
||||
// |v_at|
|
||||
fun testNestedTagSelection() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Inner</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags between tags`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t>Inner</t> <caret> <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t> <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags double motion`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vatat"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags number motion`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t><caret>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("v2at"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags on outer`() {
|
||||
configureByText("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</<caret>t>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags on outer start`() {
|
||||
configureByText("<<caret>t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
|
||||
fun `test nested tags outside outer`() {
|
||||
configureByText("<caret><t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>\n")
|
||||
typeText(parseKeys("vat"))
|
||||
assertSelection("<t>Outer\n" +
|
||||
" <t>Inner</t>\n" +
|
||||
"</t>")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user