Skip to content

Commit b71b7a0

Browse files
committed
Fix token detection for single letters
1 parent 8e85537 commit b71b7a0

File tree

7 files changed

+89
-14
lines changed

7 files changed

+89
-14
lines changed

example/src/main/java/com/tokenautocompleteexample/TagCompletionView.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.app.Activity;
44
import android.content.Context;
5+
import android.os.Parcelable;
56
import android.util.AttributeSet;
67
import android.view.LayoutInflater;
78
import android.view.View;
@@ -25,6 +26,10 @@ protected View getViewForObject(Tag object) {
2526

2627
@Override
2728
protected Tag defaultObject(String completionText) {
28-
return new Tag(completionText.charAt(0), completionText.substring(1, completionText.length()));
29+
if (completionText.length() == 1) {
30+
return null;
31+
} else {
32+
return new Tag(completionText.charAt(0), completionText.substring(1, completionText.length()));
33+
}
2934
}
3035
}

example/src/main/java/com/tokenautocompleteexample/TokenActivity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ protected void onCreate(Bundle savedInstanceState) {
3939

4040
completionView = (ContactsCompletionView)findViewById(R.id.searchView);
4141
completionView.setAdapter(adapter);
42+
completionView.setThreshold(1);
4243
completionView.setTokenListener(this);
4344
completionView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Select);
4445
completionView.addTextChangedListener(new TextWatcher() {
@@ -92,6 +93,7 @@ public void onClick(View v) {
9293
tagView.setTokenizer(new TagTokenizer(Arrays.asList('@', '#')));
9394
tagView.setAdapter(new TagAdapter(this, R.layout.tag_layout, Tag.sampleTags()));
9495
tagView.setTokenClickStyle(TokenCompleteTextView.TokenClickStyle.Select);
96+
tagView.setThreshold(1);
9597

9698
final TextView taggedContentPreview = findViewById(R.id.composedValue);
9799

library/src/main/java/com/tokenautocomplete/CharacterTokenizer.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public List<Range> findTokenRanges(CharSequence charSequence, int start, int end
5050
int tokenStart = start;
5151

5252
for (int cursor = start; cursor < end; ++cursor) {
53-
Character character = charSequence.charAt(cursor);
53+
char character = charSequence.charAt(cursor);
5454

5555
//Avoid including leading whitespace, tokenStart will match the cursor as long as we're at the start
5656
if (tokenStart == cursor && Character.isWhitespace(character)) {
@@ -59,7 +59,12 @@ public List<Range> findTokenRanges(CharSequence charSequence, int start, int end
5959

6060
//Either this is a split character, or we contain some content and are at the end of input
6161
if (splitChar.contains(character) || cursor == end - 1) {
62-
if (cursor > tokenStart) {
62+
boolean hasTokenContent =
63+
//There is token content befor the current character
64+
cursor > tokenStart ||
65+
//If the current single character is valid token content, not a split char or whitespace
66+
(cursor == tokenStart && !splitChar.contains(character));
67+
if (hasTokenContent) {
6368
//There is some token content
6469
//Add one to range end as the end of the ranges is not inclusive
6570
result.add(new Range(tokenStart, cursor + 1));

library/src/main/java/com/tokenautocomplete/TagTokenizer.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,13 @@ public List<Range> findTokenRanges(CharSequence charSequence, int start, int end
5252
int tokenStart = Integer.MAX_VALUE;
5353

5454
for (int cursor = start; cursor < end; ++cursor) {
55-
Character character = charSequence.charAt(cursor);
55+
char character = charSequence.charAt(cursor);
5656

5757
//Either this is a terminator, or we contain some content and are at the end of input
58-
if (isTokenTerminator(character) || cursor == end - 1) {
58+
if (isTokenTerminator(character)) {
5959
//Is there some token content? Might just be two terminators in a row
6060
if (cursor - 1 > tokenStart) {
61-
if (isTokenTerminator(character)) {
62-
result.add(new Range(tokenStart, cursor));
63-
} else {
64-
//If we're here and the character isn't a token terminator, make sure we include
65-
//the last character of the input
66-
result.add(new Range(tokenStart, cursor + 1));
67-
}
61+
result.add(new Range(tokenStart, cursor));
6862
}
6963

7064
//mark that we don't have a candidate token start any more
@@ -77,6 +71,11 @@ public List<Range> findTokenRanges(CharSequence charSequence, int start, int end
7771
}
7872
}
7973

74+
if (end > tokenStart) {
75+
//There was unterminated text after a start of token
76+
result.add(new Range(tokenStart, end));
77+
}
78+
8079
return result;
8180
}
8281

library/src/main/java/com/tokenautocomplete/TokenCompleteTextView.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ private Range getCurrentCandidateTokenRange() {
508508
candidateStringStart = spanEnd;
509509
}
510510
int spanStart = editable.getSpanStart(span);
511-
if (candidateStringEnd < spanStart && cursorEndPosition <= spanEnd) {
511+
if (candidateStringEnd > spanStart && cursorEndPosition <= spanEnd) {
512512
candidateStringEnd = spanStart;
513513
}
514514
}

library/src/test/java/com/tokenautocomplete/CharacterTokenizerTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.tokenautocomplete;
22

3+
import static org.junit.Assert.assertFalse;
34
import static org.junit.Assert.assertTrue;
45
import static org.junit.Assert.assertEquals;
56
import org.junit.Test;
67

8+
import java.util.ArrayList;
79
import java.util.Arrays;
810
import java.util.Collections;
911
import java.util.List;
12+
import java.util.RandomAccess;
1013

1114
/**
1215
* Make sure the tokenizer finds the right boundaries
@@ -31,6 +34,7 @@ public void handleWhiteSpaceWithCommaTokens() {
3134
assertEquals("ponies", text.subSequence(ranges.get(1).start, ranges.get(1).end));
3235

3336
ranges = tokenizer.findTokenRanges(text, 5, text.length());
37+
assertEquals(", ponies", text.substring(5));
3438
assertEquals(Collections.singletonList(new Range(7, 13)), ranges);
3539

3640
ranges = tokenizer.findTokenRanges(text, 1, text.length());
@@ -85,4 +89,40 @@ public void handleLotsOfWhitespaceWithWhitespaceTokenizer() {
8589
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
8690
assertEquals(Arrays.asList(new Range(0, 7), new Range(12, 19), new Range(23, 31)), ranges);
8791
}
92+
93+
@Test
94+
public void allowsOneCharacterCandidateRangeMatches() {
95+
CharacterTokenizer tokenizer = new CharacterTokenizer(Collections.singletonList(','), "");
96+
String text = "a";
97+
98+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
99+
assertEquals(Collections.singletonList(new Range(0,1)), ranges);
100+
}
101+
102+
@Test
103+
public void allowsOneCharacterCandidateRangeMatchesWithWhitespace() {
104+
CharacterTokenizer tokenizer = new CharacterTokenizer(Collections.singletonList(','), "");
105+
String text = " a";
106+
107+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
108+
assertEquals(Collections.singletonList(new Range(1,2)), ranges);
109+
}
110+
111+
@Test
112+
public void doesntMatchWhitespaceAsCandidateTokenRange() {
113+
CharacterTokenizer tokenizer = new CharacterTokenizer(Collections.singletonList(','), "");
114+
String text = "test, ";
115+
116+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
117+
assertEquals(Collections.singletonList(new Range(0, 5)), ranges);
118+
}
119+
120+
@Test
121+
public void matchesSingleLetterTokens() {
122+
CharacterTokenizer tokenizer = new CharacterTokenizer(Collections.singletonList(','), "");
123+
String text = "t,r, a,,b";
124+
125+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
126+
assertEquals(Arrays.asList(new Range(0, 2), new Range(2,4), new Range(5,7), new Range(8,9)), ranges);
127+
}
88128
}

library/src/test/java/com/tokenautocomplete/TagTokenizerTest.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public void testBasicTagDetection() {
3030
@Test
3131
public void testSequentialTagDetection() {
3232
String test = "@@bears#tokens#";
33-
assertEquals(Arrays.asList(new Range(1,7), new Range(7,14)),
33+
assertEquals(Arrays.asList(new Range(1,7), new Range(7,14), new Range(14, 15)),
3434
tokenizer.findTokenRanges(test, 0, test.length()));
3535
}
3636

@@ -47,4 +47,28 @@ public void testMissingTokenContentInput() {
4747
assertEquals(Arrays.asList(new Range(0,6), new Range(25, 36)),
4848
tokenizer.findTokenRanges(test, 0, test.length()));
4949
}
50+
51+
@Test
52+
public void allowsOneCharacterCandidateRangeMatches() {
53+
String text = "#";
54+
55+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
56+
assertEquals(Collections.singletonList(new Range(0,1)), ranges);
57+
}
58+
59+
@Test
60+
public void allowsOneCharacterCandidateRangeMatchesWithWhitespace() {
61+
String text = " #";
62+
63+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
64+
assertEquals(Collections.singletonList(new Range(1,2)), ranges);
65+
}
66+
67+
@Test
68+
public void matchesSingleLetterTokens() {
69+
String text = "#t#r #a##b";
70+
71+
List<Range> ranges = tokenizer.findTokenRanges(text, 0, text.length());
72+
assertEquals(Arrays.asList(new Range(0, 2), new Range(2,4), new Range(5,7), new Range(8,10)), ranges);
73+
}
5074
}

0 commit comments

Comments
 (0)