1
0
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:
Andrey Vlasovskikh 2012-11-26 16:53:32 +04:00
parent de318dc975
commit c7855a98c4
4 changed files with 61 additions and 91 deletions
src/com/maddyhome/idea/vim
test/org/jetbrains/plugins/ideavim/action

View File

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

View File

@ -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';
}
/**

View File

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

View File

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