Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ios/EnrichedTextInputView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,12 @@ - (void)manageSelectionBasedChanges {
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
if(mentionStyleClass != nullptr) {
[mentionStyleClass manageMentionTypingAttributes];
[mentionStyleClass manageMentionEditing];

// mention editing runs if only a selection was done (no text change)
// otherwise we would double-emit with a second call in the anyTextMayHaveBeenModified method
if([_recentlyEmittedString isEqualToString:[textView.textStorage.string copy]]) {
[mentionStyleClass manageMentionEditing];
}
}

// typing attributes for empty lines selection reset
Expand Down Expand Up @@ -1112,10 +1117,11 @@ - (void)anyTextMayHaveBeenModified {
[h3Style handleImproperHeadings];
}

// mentions removal management
// mentions management: removal and editing
MentionStyle *mentionStyleClass = (MentionStyle *)stylesDict[@([MentionStyle getStyleType])];
if(mentionStyleClass != nullptr) {
[mentionStyleClass handleExistingMentions];
[mentionStyleClass manageMentionEditing];
}

// placholder management
Expand Down
149 changes: 119 additions & 30 deletions ios/styles/MentionStyle.mm
Original file line number Diff line number Diff line change
Expand Up @@ -299,45 +299,24 @@ - (void)manageMentionEditing {
return;
}

// get the current word if it exists
// we can be using current word only thanks to the fact that ongoing mentions are always one word (in contrast to ready, added mentions)
NSDictionary *currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
if(currentWord == nullptr) {
// get the text (and its range) that could be an editable mention
NSArray *mentionCandidate = [self getMentionCandidate];
if(mentionCandidate == nullptr) {
[self removeActiveMentionRange];
return;
}
NSString *candidateText = mentionCandidate[0];
NSRange candidateRange = [(NSValue *)mentionCandidate[1] rangeValue];

// get word properties
NSString *wordText = (NSString *)[currentWord objectForKey:@"word"];
NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey:@"range"];
if(wordText == nullptr || wordRangeValue == nullptr) {
[self removeActiveMentionRange];
return;
}
NSRange wordRange = [wordRangeValue rangeValue];

// check for mentionIndicators - no sign of them means we shouldn't be editing a mention
unichar firstChar = [wordText characterAtIndex:0];
if(![[_input->config mentionIndicators] containsObject: @(firstChar)]) {
[self removeActiveMentionRange];
return;
}

// check for existing mentions - we don't edit them
if([self detectStyle:wordRange]) {
[self removeActiveMentionRange];
return;
}

// get style classes that the mention shouldn't be recognized in
// get style classes that the mention shouldn't be recognized in, together with other mentions
NSArray *conflicts = _input->conflictingStyles[@([MentionStyle getStyleType])];
NSArray *blocks = _input->blockingStyles[@([MentionStyle getStyleType])];
NSArray *allConflicts = [conflicts arrayByAddingObjectsFromArray:blocks];
NSArray *allConflicts = [[conflicts arrayByAddingObjectsFromArray:blocks] arrayByAddingObject:@([MentionStyle getStyleType])];
BOOL conflictingStyle = NO;

for(NSNumber *styleType in allConflicts) {
id<BaseStyleProtocol> styleClass = _input->stylesDict[styleType];
if(styleClass != nullptr && [styleClass anyOccurence:wordRange]) {
if(styleClass != nullptr && [styleClass anyOccurence:candidateRange]) {
conflictingStyle = YES;
break;
}
Expand All @@ -350,7 +329,7 @@ - (void)manageMentionEditing {
}

// everything checks out - we are indeed editing a mention
[self setActiveMentionRange:wordRange text:wordText];
[self setActiveMentionRange:candidateRange text:candidateText];
}

// used to fix mentions' typing attributes
Expand Down Expand Up @@ -462,6 +441,116 @@ - (MentionStyleProps *)stylePropsWithParams:(MentionParams *)params {
return [_input->config mentionStylePropsForIndicator:params.indicator];
}

// finds if any word/words around current selection are eligible to be edited as mentions
// since we allow for a single space inside an edited mention, we have take both current and the previous word into account
- (NSArray *)getMentionCandidate {
NSDictionary *currentWord, *previousWord;
NSString *currentWordText, *previousWordText, *finalText;
NSValue *currentWordRange, *previousWordRange;
NSRange finalRange;

// word at the current selection
currentWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:_input->textView.selectedRange];
if(currentWord != nullptr ) {
currentWordText = (NSString *)[currentWord objectForKey:@"word"];
currentWordRange = (NSValue *)[currentWord objectForKey:@"range"];
}

if(currentWord != nullptr) {
// current word exists
unichar currentFirstChar = [currentWordText characterAtIndex:0];

if([[_input->config mentionIndicators] containsObject:@(currentFirstChar)]) {
// current word exists and has a mention indicator; no need to check for the previous word
finalText = currentWordText;
finalRange = [currentWordRange rangeValue];
} else {
// current word exists but no traces of mention indicator; get the previous word

NSInteger previousWordSearchLocation = [currentWordRange rangeValue].location - 1;
if(previousWordSearchLocation < 0) {
// previous word can't exist
return nullptr;
}

unichar separatorChar = [_input->textView.textStorage.string characterAtIndex:previousWordSearchLocation];
if(![[NSCharacterSet whitespaceCharacterSet] characterIsMember:separatorChar]) {
// we want to check for the previous word ONLY if the separating character was a space
// newlines don't make it
return nullptr;
}

previousWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:NSMakeRange(previousWordSearchLocation, 0)];

if(previousWord != nullptr) {
// previous word exists; get its properties
previousWordText = (NSString *)[previousWord objectForKey:@"word"];
previousWordRange = (NSValue *)[previousWord objectForKey:@"range"];

// check for the mention indicators in the previous word
unichar previousFirstChar = [previousWordText characterAtIndex:0];

if([[_input->config mentionIndicators] containsObject:@(previousFirstChar) ]) {
// previous word has a proper mention indicator: treat both words as an editable mention
finalText = [NSString stringWithFormat:@"%@ %@", previousWordText, currentWordText];
// range length is both words' lengths + 1 for a space between them
finalRange = NSMakeRange(
[previousWordRange rangeValue].location,
[previousWordRange rangeValue].length + [currentWordRange rangeValue].length + 1
);
} else {
// neither current nor previous words have a mention indicator
return nullptr;
}
} else {
// previous word doesn't exist and no mention indicators in the current word
return nullptr;
}
}
} else {
// current word doesn't exist; try getting the previous one

NSInteger previousWordSearchLocation = _input->textView.selectedRange.location - 1;
if(previousWordSearchLocation < 0) {
// previous word can't exist
return nullptr;
}

unichar separatorChar = [_input->textView.textStorage.string characterAtIndex:previousWordSearchLocation];
if(![[NSCharacterSet whitespaceCharacterSet] characterIsMember:separatorChar]) {
// we want to check for the previous word ONLY if the separating character was a space
// newlines don't make it
return nullptr;
}

previousWord = [WordsUtils getCurrentWord:_input->textView.textStorage.string range:NSMakeRange(previousWordSearchLocation, 0)];

if(previousWord != nullptr) {
// previous word exists; get its properties
previousWordText = (NSString *)[previousWord objectForKey:@"word"];
previousWordRange = (NSValue *)[previousWord objectForKey:@"range"];

// check for the mention indicators in the previous word
unichar previousFirstChar = [previousWordText characterAtIndex:0];

if([[_input->config mentionIndicators] containsObject:@(previousFirstChar)]) {
// previous word has a proper mention indicator; treat previous word + a space as a editable mention
finalText = [NSString stringWithFormat:@"%@ ", previousWordText];
// the range length is previous word length + 1 for a space
finalRange = NSMakeRange([previousWordRange rangeValue].location, [previousWordRange rangeValue].length + 1);
} else {
// no current word, previous has no mention indicators
return nullptr;
}
} else {
// no current word, no previous word
return nullptr;
}
}

return @[finalText, [NSValue valueWithRange:finalRange]];
}

// both used for setting the active mention range + indicator and fires proper onMention event
- (void)setActiveMentionRange:(NSRange)range text:(NSString *)text {
NSString *indicatorString = [NSString stringWithFormat:@"%C", [text characterAtIndex:0]];
Expand Down
Loading