Skip to content

Commit

Permalink
Trigger mention on whitespace removal rebased (#143)
Browse files Browse the repository at this point in the history
Editing the mention should trigger the typeahead again

- On removal of whitespace between control character and typed in string, it should trigger mention again
- Updated state machine, to update the flow to trigger mention
- When a control character is inserted before an already existing word, we would need the word to begin a mention with or replace the word with the selected mention
  • Loading branch information
aniyati authored and Binya Koatz committed Dec 9, 2019
1 parent 734c5c4 commit bee254a
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 70 deletions.
6 changes: 6 additions & 0 deletions Hakawai/Core/HKWTextView.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readonly) BOOL inSingleLineViewportMode;

/*!
String that saves the state of the text in the text view so that it can be accessed in the NSTextStorageDelegate, which will
already have deleted the character by the time it's trying to process said deletion
*/
@property (nonatomic, strong, readonly) NSString *textStateBeforeDeletion;

@end

NS_ASSUME_NONNULL_END
5 changes: 1 addition & 4 deletions Hakawai/Core/HKWTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@
@interface HKWTextView () <UITextViewDelegate, HKWAbstractionLayerDelegate, NSTextStorageDelegate>

@property (nonatomic) NSMutableDictionary *simplePluginsDictionary;
/*!
String that saves the state of the text in the text view so that it can be accessed in the NSTextStorageDelegate, which will
already have deleted the character by the time it's trying to process said deletion
*/

@property (nonatomic, strong, readwrite) NSString *textStateBeforeDeletion;

@end
Expand Down
21 changes: 21 additions & 0 deletions Hakawai/Mentions/HKWMentionsCreationStateMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,33 @@ - (void)stringDeleted:(NSString *)deleteString {

__strong __auto_type delegate = self.delegate;

/**
When mentions was originally triggered because the whitespace between a control character and a word was deleted,
the cursor is next to the control character (like "@|John", where '|' represents the cursor-state). If the user then deletes the control character,
the string buffer will not be empty (it will have "John" in it), but mentions has to stop, because control character is deleted.
We use the isControlCharacterDeleted flag to decide what to do in this case of control character deletion.
*/
BOOL isControlCharacterDeleted = NO;
if (deleteString.length == 1
&& [deleteString containsString:[NSString stringWithFormat:@"%C", self.explicitSearchControlCharacter]]
&& self.stringBuffer.length > 0
&& [self.stringBuffer characterAtIndex:self.stringBuffer.length - 1] != self.explicitSearchControlCharacter) {
isControlCharacterDeleted = YES;
}

// Switch on the overall state
switch (self.state) {
case HKWMentionsCreationStateQuiescent:
// User not creating a mention right now
return;
case HKWMentionsCreationStateCreatingMention:
if (isControlCharacterDeleted) {
// When user deletes control character during mention creation state, then end mention creation.
self.state = HKWMentionsCreationStateQuiescent;
[delegate cancelMentionFromStartingLocation:self.startingLocation];
return;
}

if (deleteStringIsTransient) {
// Delete was typed, but for some sort of transient state (e.g. keyboard suggestions); don't do anything
return;
Expand Down
34 changes: 27 additions & 7 deletions Hakawai/Mentions/HKWMentionsPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -845,15 +845,23 @@ - (BOOL)advanceStateForCharacterInsertion:(unichar)newChar
BOOL isSecondSpace = (location > 1) && (precedingChar == ' ' && newChar == ' ');
switch (self.state) {
case HKWMentionsStateQuiescent: {
// Word following typed character would be used to trigger matching mentions menu when possible.
NSString *wordFollowingTypedCharacter;
if (HKWTextView.enableKoreanMentionsFix) {
// Update the location of the selected range for this insertion here
// (since the text view will already be updated when utilizing the text storage delegate in the korean mentions fix)
// This should replace other settings of this range when the fix is ramped
parentTextView.selectedRange = NSMakeRange(location, parentTextView.selectedRange.length);
wordFollowingTypedCharacter = [HKWMentionsStartDetectionStateMachine wordAfterLocation:location text:parentTextView.textStateBeforeDeletion];
} else {
wordFollowingTypedCharacter = [HKWMentionsStartDetectionStateMachine wordAfterLocation:location text:parentTextView.text];
}
// Inform the start detection state machine that a character was inserted. Also, override the double space
// to period auto-substitution if the substitution would place a period right after a preceding mention.
[self.startDetectionStateMachine characterTyped:newChar asInsertedCharacter:NO previousCharacter:precedingChar];
[self.startDetectionStateMachine characterTyped:newChar
asInsertedCharacter:NO
previousCharacter:precedingChar
wordFollowingTypedCharacter:wordFollowingTypedCharacter];
NSRange r;
id mentionTwoPreceding = [self mentionAttributePrecedingLocation:(location-1) range:&r];
BOOL shouldSuppress = (mentionTwoPreceding != nil) && (r.location + r.length == location-1);
Expand All @@ -879,7 +887,10 @@ - (BOOL)advanceStateForCharacterInsertion:(unichar)newChar
// insert a new character and continue in the quiescent state. Do not allow auto-substitution.
self.state = HKWMentionsStateQuiescent;
[self resetCurrentMentionsData];
[self.startDetectionStateMachine characterTyped:newChar asInsertedCharacter:NO previousCharacter:precedingChar];
[self.startDetectionStateMachine characterTyped:newChar
asInsertedCharacter:NO
previousCharacter:precedingChar
wordFollowingTypedCharacter:nil];
if (isSecondSpace) {
[self manuallyInsertCharacter:newChar atLocation:location inTextView:parentTextView];
self.characterForAdvanceStateForCharacterInsertion = (unichar)0;
Expand All @@ -901,7 +912,7 @@ - (BOOL)advanceStateForCharacterInsertion:(unichar)newChar
[self resetCurrentMentionsData];
self.state = HKWMentionsStateQuiescent;
self.characterForAdvanceStateForCharacterInsertion = (unichar)0;
[self.startDetectionStateMachine characterTyped:newChar asInsertedCharacter:YES previousCharacter:precedingChar];
[self.startDetectionStateMachine characterTyped:newChar asInsertedCharacter:YES previousCharacter:precedingChar wordFollowingTypedCharacter:nil];
returnValue = NO;
break;
case HKWMentionsStateLosingFocus:
Expand Down Expand Up @@ -932,7 +943,9 @@ - (BOOL)advanceStateForCharacterDeletion:(unichar)precedingChar
switch (self.state) {
case HKWMentionsStateQuiescent: {
[self.startDetectionStateMachine deleteTypedCharacter:deletedChar
withCharacterNowPrecedingCursor:precedingChar];
withCharacterNowPrecedingCursor:precedingChar
location:location
textViewText:parentTextView.text];
self.nextSelectionChangeShouldBeIgnored = YES;
// Look for a mention
Expand Down Expand Up @@ -1144,8 +1157,10 @@ - (BOOL)advanceStateForStringInsertionAtRange:(NSRange)range text:(NSString *)te
self.previousSelectionRange = newSelectionRange;
self.previousTextLength = [[parentTextView text] length];
[self.startDetectionStateMachine characterTyped:[text characterAtIndex:0] asInsertedCharacter:YES previousCharacter:precedingChar];
[self.startDetectionStateMachine characterTyped:[text characterAtIndex:0]
asInsertedCharacter:YES
previousCharacter:precedingChar
wordFollowingTypedCharacter:nil];
// Manually notify external delegate that the textView changed
id<HKWTextViewDelegate> externalDelegate = parentTextView.externalDelegate;
if ([externalDelegate respondsToSelector:@selector(textViewDidChange:)]) {
Expand Down Expand Up @@ -1932,7 +1947,12 @@ - (void)createMention:(HKWMentionsAttribute *)mention startingLocation:(NSUInteg
UIColor *parentColor = parentTextView.textColorSetByApp;
NSAssert(self.mentionUnselectedAttributes != nil, @"Error! Mention attribute dictionaries should never be nil.");
NSDictionary *unselectedAttributes = self.mentionUnselectedAttributes;
NSRange rangeToTransform = NSMakeRange(location, currentLocation - location);
// When control character is inserted before word and user selects mention for that word,
// we want to replace word after control character with mention text.
// e.g "hey @|john" will be replaced as "hey John Doe". '|' indicates cursor.
NSString *const wordAfterCurrentLocation = [HKWMentionsStartDetectionStateMachine wordAfterLocation:currentLocation text:parentTextView.text];
NSRange rangeToTransform = NSMakeRange(location, currentLocation + wordAfterCurrentLocation.length - location);
/*
When the textview text that matches the mention text is not the first part of the mention text,
Expand Down
Loading

0 comments on commit bee254a

Please sign in to comment.