mirror of
https://github.com/chylex/IntelliJ-IdeaVim.git
synced 2025-08-08 00:40:37 +02:00
VIM-331 Fixed word bounds in 'w' motion for extended latin letters
This commit is contained in:
parent
de318dc975
commit
c7855a98c4
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/action
@ -1334,7 +1334,7 @@ public class ChangeGroup extends AbstractActionGroup {
|
||||
int weoff = editor.logicalPositionToOffset(epos);
|
||||
int pos;
|
||||
for (pos = wsoff; pos <= weoff; pos++) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos), false) != CharacterHelper.TYPE_SPACE) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos), false) != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -19,99 +19,59 @@ package com.maddyhome.idea.vim.helper;
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This helper class is used when working with various character level operations
|
||||
*/
|
||||
public class CharacterHelper {
|
||||
|
||||
public static enum CharacterType {
|
||||
LETTER_OR_DIGIT,
|
||||
HIRAGANA,
|
||||
KATAKANA,
|
||||
HALF_WIDTH_KATAKANA,
|
||||
PUNCTUATION,
|
||||
WHITESPACE;
|
||||
}
|
||||
|
||||
public static final char CASE_TOGGLE = '~';
|
||||
public static final char CASE_UPPER = 'u';
|
||||
public static final char CASE_LOWER = 'l';
|
||||
|
||||
public static final int TYPE_CHAR = 1;
|
||||
public static final int TYPE_PUNC = 2;
|
||||
public static final int TYPE_SPACE = 3;
|
||||
public static final int TYPE_KANA = 4;
|
||||
public static final int TYPE_WIDE_ALPHANUM = 5;
|
||||
public static final int TYPE_WIDE_PUNC = 6;
|
||||
public static final int TYPE_WIDE_HIRAGANA = 7;
|
||||
public static final int TYPE_WIDE_KATAKANA = 8;
|
||||
public static final int TYPE_OTHER = 9;
|
||||
|
||||
private static char[] ALL_HALF_SYMBOL =
|
||||
"\u3001\u3002\uff0c\uff0e\uff1a\uff1b\uff1f\uff01\uff40\uff3e\uff3f\uff0f\uff5e\uff5c\u2018\u2019\u201c\u201d\uff08\uff09\uff3b\uff3d\uff5b\uff5d\uff0b\uff0d\uff1d\uff1c\uff1e\uffe5\uff04\uff05\uff03\uff06\uff0a\uff20"
|
||||
.toCharArray();
|
||||
private static char[] ALL_WIDE_SYMBOL = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~".toCharArray();
|
||||
private static char[] ALL_HALFKANA =
|
||||
"\uff61\uff62\uff63\uff64\uff65\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff70\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uff9e\uff9f"
|
||||
.toCharArray();
|
||||
|
||||
static {
|
||||
Arrays.sort(ALL_HALF_SYMBOL);
|
||||
Arrays.sort(ALL_WIDE_SYMBOL);
|
||||
Arrays.sort(ALL_HALFKANA);
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns the type of the supplied character. The logic is as follows:<br>
|
||||
* If the character is whitespace, <code>TYPE_SPACE</code> is returned.<br>
|
||||
* If the punctuation is being skipped or the character is a letter, digit, or underscore, <code>TYPE_CHAR</code>
|
||||
* If the character is whitespace, <code>WHITESPACE</code> is returned.<br>
|
||||
* If the punctuation is being skipped or the character is a letter, digit, or underscore, <code>LETTER_OR_DIGIT</code>
|
||||
* is returned.<br>
|
||||
* Otherwise <code>TYPE_PUNC</code> is returned.
|
||||
* Otherwise <code>PUNCTUATION</code> is returned.
|
||||
*
|
||||
* @param ch The character to analyze
|
||||
* @param punctuationAsChar True if punctuation is to be ignored, false if not
|
||||
* @param ch The character to analyze
|
||||
* @param punctuationAsLetters True if punctuation is to be ignored, false if not
|
||||
* @return The type of the character
|
||||
*/
|
||||
public static int charType(char ch, boolean punctuationAsChar) {
|
||||
public static CharacterType charType(char ch, boolean punctuationAsLetters) {
|
||||
final Character.UnicodeBlock block = Character.UnicodeBlock.of(ch);
|
||||
if (Character.isWhitespace(ch)) {
|
||||
return TYPE_SPACE;
|
||||
return CharacterType.WHITESPACE;
|
||||
}
|
||||
else if (punctuationAsChar || isHalfAlphaNum(ch) || ch == '_') {
|
||||
return TYPE_CHAR;
|
||||
else if (block == Character.UnicodeBlock.HIRAGANA) {
|
||||
return CharacterType.HIRAGANA;
|
||||
}
|
||||
else if (isHalfSymbol(ch)) {
|
||||
return TYPE_PUNC;
|
||||
else if (block == Character.UnicodeBlock.KATAKANA) {
|
||||
return CharacterType.KATAKANA;
|
||||
}
|
||||
else if (isHalfKana(ch)) {
|
||||
return TYPE_KANA;
|
||||
else if (isHalfWidthKatakanaLetter(ch)) {
|
||||
return CharacterType.HALF_WIDTH_KATAKANA;
|
||||
}
|
||||
else if (isWideAlphaNum(ch)) {
|
||||
return TYPE_WIDE_ALPHANUM;
|
||||
}
|
||||
else if (isWideSymbol(ch)) {
|
||||
return TYPE_WIDE_PUNC;
|
||||
}
|
||||
else if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.HIRAGANA) {
|
||||
return TYPE_WIDE_HIRAGANA;
|
||||
}
|
||||
else if (Character.UnicodeBlock.of(ch) == Character.UnicodeBlock.KATAKANA) {
|
||||
return TYPE_WIDE_KATAKANA;
|
||||
else if (punctuationAsLetters || Character.isLetterOrDigit(ch) || ch == '_') {
|
||||
return CharacterType.LETTER_OR_DIGIT;
|
||||
}
|
||||
else {
|
||||
return TYPE_OTHER;
|
||||
return CharacterType.PUNCTUATION;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isHalfAlphaNum(char ch) {
|
||||
return (0x30 <= ch && ch <= 0x39) || (0x41 <= ch && ch <= 0x5A) || (0x61 <= ch && ch <= 0x7A);
|
||||
}
|
||||
|
||||
private static boolean isWideAlphaNum(char ch) {
|
||||
return (0xFF10 <= ch && ch <= 0xFF19) || (0xFF21 <= ch && ch <= 0xFF3A) || (0xFF41 <= ch && ch <= 0xFF5A);
|
||||
}
|
||||
|
||||
private static boolean isHalfSymbol(char ch) {
|
||||
return Arrays.binarySearch(ALL_HALF_SYMBOL, ch) > 0;
|
||||
}
|
||||
|
||||
private static boolean isWideSymbol(char ch) {
|
||||
return Arrays.binarySearch(ALL_WIDE_SYMBOL, ch) > 0;
|
||||
}
|
||||
|
||||
private static boolean isHalfKana(char ch) {
|
||||
return Arrays.binarySearch(ALL_HALFKANA, ch) > 0;
|
||||
private static boolean isHalfWidthKatakanaLetter(char ch) {
|
||||
return ch >= '\uFF66' && ch <= '\uFF9F';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -424,7 +424,7 @@ public class SearchHelper {
|
||||
pos = pos < size ? pos : size - 1;
|
||||
// For back searches, skip any current whitespace so we start at the end of a word
|
||||
if (step < 0 && pos > 0) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.TYPE_SPACE && !spaceWords) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos - 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && !spaceWords) {
|
||||
pos = skipSpace(chars, pos - 1, step, size) + 1;
|
||||
}
|
||||
if (CharacterHelper.charType(chars.charAt(pos), bigWord) != CharacterHelper.charType(chars.charAt(pos - 1), bigWord)) {
|
||||
@ -436,16 +436,16 @@ public class SearchHelper {
|
||||
return pos;
|
||||
}
|
||||
|
||||
int type = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (type == CharacterHelper.TYPE_SPACE && step < 0 && pos > 0 && !spaceWords) {
|
||||
CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (type == CharacterHelper.CharacterType.WHITESPACE && step < 0 && pos > 0 && !spaceWords) {
|
||||
type = CharacterHelper.charType(chars.charAt(pos - 1), bigWord);
|
||||
}
|
||||
|
||||
pos += step;
|
||||
while (pos >= 0 && pos < size && !found) {
|
||||
int newType = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
CharacterHelper.CharacterType newType = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (newType != type) {
|
||||
if (newType == CharacterHelper.TYPE_SPACE && step >= 0 && !spaceWords) {
|
||||
if (newType == CharacterHelper.CharacterType.WHITESPACE && step >= 0 && !spaceWords) {
|
||||
pos = skipSpace(chars, pos, step, size);
|
||||
res = pos;
|
||||
}
|
||||
@ -621,10 +621,13 @@ public class SearchHelper {
|
||||
|
||||
int pos = editor.getCaretModel().getOffset();
|
||||
int start = pos;
|
||||
int[] types = new int[]{CharacterHelper.TYPE_CHAR, CharacterHelper.TYPE_PUNC};
|
||||
CharacterHelper.CharacterType[] types = new CharacterHelper.CharacterType[] {
|
||||
CharacterHelper.CharacterType.LETTER_OR_DIGIT,
|
||||
CharacterHelper.CharacterType.PUNCTUATION
|
||||
};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
start = pos;
|
||||
int type = CharacterHelper.charType(chars.charAt(start), false);
|
||||
CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(start), false);
|
||||
if (type == types[i]) {
|
||||
// Search back for start of word
|
||||
while (start > 0 && CharacterHelper.charType(chars.charAt(start - 1), false) == types[i]) {
|
||||
@ -649,7 +652,7 @@ public class SearchHelper {
|
||||
|
||||
int end;
|
||||
// Special case 1 character words because 'findNextWordEnd' returns one to many chars
|
||||
if (start < stop && CharacterHelper.charType(chars.charAt(start + 1), false) != CharacterHelper.TYPE_CHAR) {
|
||||
if (start < stop && CharacterHelper.charType(chars.charAt(start + 1), false) != CharacterHelper.CharacterType.LETTER_OR_DIGIT) {
|
||||
end = start + 1;
|
||||
}
|
||||
else {
|
||||
@ -680,7 +683,7 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
int pos = editor.getCaretModel().getOffset();
|
||||
boolean startSpace = CharacterHelper.charType(chars.charAt(pos), isBig) == CharacterHelper.TYPE_SPACE;
|
||||
boolean startSpace = CharacterHelper.charType(chars.charAt(pos), isBig) == CharacterHelper.CharacterType.WHITESPACE;
|
||||
// Find word start
|
||||
boolean onWordStart = pos == min ||
|
||||
CharacterHelper.charType(chars.charAt(pos - 1), isBig) != CharacterHelper.charType(chars.charAt(pos), isBig);
|
||||
@ -733,14 +736,14 @@ public class SearchHelper {
|
||||
firstEnd = findNextWordEnd(chars, pos, max, 1, isBig, true, false);
|
||||
}
|
||||
if (firstEnd < max) {
|
||||
if (CharacterHelper.charType(chars.charAt(firstEnd + 1), false) != CharacterHelper.TYPE_SPACE) {
|
||||
if (CharacterHelper.charType(chars.charAt(firstEnd + 1), false) != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
goBack = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dir == -1 && isOuter && startSpace) {
|
||||
if (pos > min) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos - 1), false) != CharacterHelper.TYPE_SPACE) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos - 1), false) != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
goBack = true;
|
||||
}
|
||||
}
|
||||
@ -753,7 +756,7 @@ public class SearchHelper {
|
||||
firstEnd = findNextWordEnd(chars, pos, max, 1, isBig, true, false);
|
||||
}
|
||||
if (firstEnd < max) {
|
||||
if (CharacterHelper.charType(chars.charAt(firstEnd + 1), false) != CharacterHelper.TYPE_SPACE) {
|
||||
if (CharacterHelper.charType(chars.charAt(firstEnd + 1), false) != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
goForward = true;
|
||||
}
|
||||
}
|
||||
@ -772,12 +775,12 @@ public class SearchHelper {
|
||||
}
|
||||
|
||||
if (goForward) {
|
||||
while (end < max && CharacterHelper.charType(chars.charAt(end + 1), false) == CharacterHelper.TYPE_SPACE) {
|
||||
while (end < max && CharacterHelper.charType(chars.charAt(end + 1), false) == CharacterHelper.CharacterType.WHITESPACE) {
|
||||
end++;
|
||||
}
|
||||
}
|
||||
if (goBack) {
|
||||
while (start > min && CharacterHelper.charType(chars.charAt(start - 1), false) == CharacterHelper.TYPE_SPACE) {
|
||||
while (start > min && CharacterHelper.charType(chars.charAt(start - 1), false) == CharacterHelper.CharacterType.WHITESPACE) {
|
||||
start--;
|
||||
}
|
||||
}
|
||||
@ -828,7 +831,7 @@ public class SearchHelper {
|
||||
// For forward searches, skip any current whitespace so we start at the start of a word
|
||||
if (step > 0 && pos < size - 1) {
|
||||
/*
|
||||
if (CharacterHelper.charType(chars[pos + step], false) == CharacterHelper.TYPE_SPACE)
|
||||
if (CharacterHelper.charType(chars[pos + step], false) == CharacterHelper.WHITESPACE)
|
||||
{
|
||||
if (!stayEnd)
|
||||
{
|
||||
@ -837,7 +840,7 @@ public class SearchHelper {
|
||||
pos = skipSpace(chars, pos, step, size);
|
||||
}
|
||||
*/
|
||||
if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.TYPE_SPACE && !spaceWords) {
|
||||
if (CharacterHelper.charType(chars.charAt(pos + 1), bigWord) == CharacterHelper.CharacterType.WHITESPACE && !spaceWords) {
|
||||
pos = skipSpace(chars, pos + 1, step, size) - 1;
|
||||
}
|
||||
if (pos < size - 1 && CharacterHelper.charType(chars.charAt(pos), bigWord) != CharacterHelper.charType(chars.charAt(pos + 1), bigWord)) {
|
||||
@ -848,19 +851,19 @@ public class SearchHelper {
|
||||
if (pos < 0 || pos >= size) {
|
||||
return pos;
|
||||
}
|
||||
int type = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (type == CharacterHelper.TYPE_SPACE && step >= 0 && pos < size - 1 && !spaceWords) {
|
||||
CharacterHelper.CharacterType type = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (type == CharacterHelper.CharacterType.WHITESPACE && step >= 0 && pos < size - 1 && !spaceWords) {
|
||||
type = CharacterHelper.charType(chars.charAt(pos + 1), bigWord);
|
||||
}
|
||||
|
||||
pos += step;
|
||||
while (pos >= 0 && pos < size && !found) {
|
||||
int newType = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
CharacterHelper.CharacterType newType = CharacterHelper.charType(chars.charAt(pos), bigWord);
|
||||
if (newType != type) {
|
||||
if (step >= 0) {
|
||||
res = pos - 1;
|
||||
}
|
||||
else if (newType == CharacterHelper.TYPE_SPACE && step < 0 && !spaceWords) {
|
||||
else if (newType == CharacterHelper.CharacterType.WHITESPACE && step < 0 && !spaceWords) {
|
||||
pos = skipSpace(chars, pos, step, size);
|
||||
res = pos;
|
||||
}
|
||||
@ -909,7 +912,7 @@ public class SearchHelper {
|
||||
if (c == '\n' && c == prev) {
|
||||
break;
|
||||
}
|
||||
if (CharacterHelper.charType(c, false) != CharacterHelper.TYPE_SPACE) {
|
||||
if (CharacterHelper.charType(c, false) != CharacterHelper.CharacterType.WHITESPACE) {
|
||||
break;
|
||||
}
|
||||
prev = c;
|
||||
|
@ -267,6 +267,13 @@ public class MotionActionTest extends VimTestCase {
|
||||
assertOffset(3);
|
||||
}
|
||||
|
||||
// VIM-58 |w|
|
||||
public void testHalftWidthKanaToLetters() {
|
||||
typeTextInFile(stringToKeys("w"),
|
||||
"ウウウAAA");
|
||||
assertOffset(3);
|
||||
}
|
||||
|
||||
// |w|
|
||||
public void testEmptyLineIsWord() {
|
||||
typeTextInFile(stringToKeys("w"),
|
||||
|
Loading…
Reference in New Issue
Block a user