@@ -299,45 +299,24 @@ - (void)manageMentionEditing {
299299 return ;
300300 }
301301
302- // get the current word if it exists
303- // we can be using current word only thanks to the fact that ongoing mentions are always one word (in contrast to ready, added mentions)
304- NSDictionary *currentWord = [WordsUtils getCurrentWord: _input->textView.textStorage.string range: _input->textView.selectedRange];
305- if (currentWord == nullptr ) {
302+ // get the text (and its range) that could be an editable mention
303+ NSArray *mentionCandidate = [self getMentionCandidate ];
304+ if (mentionCandidate == nullptr ) {
306305 [self removeActiveMentionRange ];
307306 return ;
308307 }
308+ NSString *candidateText = mentionCandidate[0 ];
309+ NSRange candidateRange = [(NSValue *)mentionCandidate[1 ] rangeValue ];
309310
310- // get word properties
311- NSString *wordText = (NSString *)[currentWord objectForKey: @" word" ];
312- NSValue *wordRangeValue = (NSValue *)[currentWord objectForKey: @" range" ];
313- if (wordText == nullptr || wordRangeValue == nullptr ) {
314- [self removeActiveMentionRange ];
315- return ;
316- }
317- NSRange wordRange = [wordRangeValue rangeValue ];
318-
319- // check for mentionIndicators - no sign of them means we shouldn't be editing a mention
320- unichar firstChar = [wordText characterAtIndex: 0 ];
321- if (![[_input->config mentionIndicators ] containsObject: @(firstChar)]) {
322- [self removeActiveMentionRange ];
323- return ;
324- }
325-
326- // check for existing mentions - we don't edit them
327- if ([self detectStyle: wordRange]) {
328- [self removeActiveMentionRange ];
329- return ;
330- }
331-
332- // get style classes that the mention shouldn't be recognized in
311+ // get style classes that the mention shouldn't be recognized in, together with other mentions
333312 NSArray *conflicts = _input->conflictingStyles [@([MentionStyle getStyleType ])];
334313 NSArray *blocks = _input->blockingStyles [@([MentionStyle getStyleType ])];
335- NSArray *allConflicts = [conflicts arrayByAddingObjectsFromArray: blocks];
314+ NSArray *allConflicts = [[ conflicts arrayByAddingObjectsFromArray: blocks] arrayByAddingObject: @([MentionStyle getStyleType ]) ];
336315 BOOL conflictingStyle = NO ;
337316
338317 for (NSNumber *styleType in allConflicts) {
339318 id <BaseStyleProtocol> styleClass = _input->stylesDict [styleType];
340- if (styleClass != nullptr && [styleClass anyOccurence: wordRange ]) {
319+ if (styleClass != nullptr && [styleClass anyOccurence: candidateRange ]) {
341320 conflictingStyle = YES ;
342321 break ;
343322 }
@@ -350,7 +329,7 @@ - (void)manageMentionEditing {
350329 }
351330
352331 // everything checks out - we are indeed editing a mention
353- [self setActiveMentionRange: wordRange text: wordText ];
332+ [self setActiveMentionRange: candidateRange text: candidateText ];
354333}
355334
356335// used to fix mentions' typing attributes
@@ -462,6 +441,116 @@ - (MentionStyleProps *)stylePropsWithParams:(MentionParams *)params {
462441 return [_input->config mentionStylePropsForIndicator: params.indicator];
463442}
464443
444+ // finds if any word/words around current selection are eligible to be edited as mentions
445+ // since we allow for a single space inside an edited mention, we have take both current and the previous word into account
446+ - (NSArray *)getMentionCandidate {
447+ NSDictionary *currentWord, *previousWord;
448+ NSString *currentWordText, *previousWordText, *finalText;
449+ NSValue *currentWordRange, *previousWordRange;
450+ NSRange finalRange;
451+
452+ // word at the current selection
453+ currentWord = [WordsUtils getCurrentWord: _input->textView.textStorage.string range: _input->textView.selectedRange];
454+ if (currentWord != nullptr ) {
455+ currentWordText = (NSString *)[currentWord objectForKey: @" word" ];
456+ currentWordRange = (NSValue *)[currentWord objectForKey: @" range" ];
457+ }
458+
459+ if (currentWord != nullptr ) {
460+ // current word exists
461+ unichar currentFirstChar = [currentWordText characterAtIndex: 0 ];
462+
463+ if ([[_input->config mentionIndicators ] containsObject: @(currentFirstChar)]) {
464+ // current word exists and has a mention indicator; no need to check for the previous word
465+ finalText = currentWordText;
466+ finalRange = [currentWordRange rangeValue ];
467+ } else {
468+ // current word exists but no traces of mention indicator; get the previous word
469+
470+ NSInteger previousWordSearchLocation = [currentWordRange rangeValue ].location - 1 ;
471+ if (previousWordSearchLocation < 0 ) {
472+ // previous word can't exist
473+ return nullptr ;
474+ }
475+
476+ unichar separatorChar = [_input->textView.textStorage.string characterAtIndex: previousWordSearchLocation];
477+ if (![[NSCharacterSet whitespaceCharacterSet ] characterIsMember: separatorChar]) {
478+ // we want to check for the previous word ONLY if the separating character was a space
479+ // newlines don't make it
480+ return nullptr ;
481+ }
482+
483+ previousWord = [WordsUtils getCurrentWord: _input->textView.textStorage.string range: NSMakeRange (previousWordSearchLocation, 0 )];
484+
485+ if (previousWord != nullptr ) {
486+ // previous word exists; get its properties
487+ previousWordText = (NSString *)[previousWord objectForKey: @" word" ];
488+ previousWordRange = (NSValue *)[previousWord objectForKey: @" range" ];
489+
490+ // check for the mention indicators in the previous word
491+ unichar previousFirstChar = [previousWordText characterAtIndex: 0 ];
492+
493+ if ([[_input->config mentionIndicators ] containsObject: @(previousFirstChar) ]) {
494+ // previous word has a proper mention indicator: treat both words as an editable mention
495+ finalText = [NSString stringWithFormat: @" %@ %@ " , previousWordText, currentWordText];
496+ // range length is both words' lengths + 1 for a space between them
497+ finalRange = NSMakeRange (
498+ [previousWordRange rangeValue ].location ,
499+ [previousWordRange rangeValue ].length + [currentWordRange rangeValue ].length + 1
500+ );
501+ } else {
502+ // neither current nor previous words have a mention indicator
503+ return nullptr ;
504+ }
505+ } else {
506+ // previous word doesn't exist and no mention indicators in the current word
507+ return nullptr ;
508+ }
509+ }
510+ } else {
511+ // current word doesn't exist; try getting the previous one
512+
513+ NSInteger previousWordSearchLocation = _input->textView .selectedRange .location - 1 ;
514+ if (previousWordSearchLocation < 0 ) {
515+ // previous word can't exist
516+ return nullptr ;
517+ }
518+
519+ unichar separatorChar = [_input->textView.textStorage.string characterAtIndex: previousWordSearchLocation];
520+ if (![[NSCharacterSet whitespaceCharacterSet ] characterIsMember: separatorChar]) {
521+ // we want to check for the previous word ONLY if the separating character was a space
522+ // newlines don't make it
523+ return nullptr ;
524+ }
525+
526+ previousWord = [WordsUtils getCurrentWord: _input->textView.textStorage.string range: NSMakeRange (previousWordSearchLocation, 0 )];
527+
528+ if (previousWord != nullptr ) {
529+ // previous word exists; get its properties
530+ previousWordText = (NSString *)[previousWord objectForKey: @" word" ];
531+ previousWordRange = (NSValue *)[previousWord objectForKey: @" range" ];
532+
533+ // check for the mention indicators in the previous word
534+ unichar previousFirstChar = [previousWordText characterAtIndex: 0 ];
535+
536+ if ([[_input->config mentionIndicators ] containsObject: @(previousFirstChar)]) {
537+ // previous word has a proper mention indicator; treat previous word + a space as a editable mention
538+ finalText = [NSString stringWithFormat: @" %@ " , previousWordText];
539+ // the range length is previous word length + 1 for a space
540+ finalRange = NSMakeRange ([previousWordRange rangeValue ].location , [previousWordRange rangeValue ].length + 1 );
541+ } else {
542+ // no current word, previous has no mention indicators
543+ return nullptr ;
544+ }
545+ } else {
546+ // no current word, no previous word
547+ return nullptr ;
548+ }
549+ }
550+
551+ return @[finalText, [NSValue valueWithRange: finalRange]];
552+ }
553+
465554// both used for setting the active mention range + indicator and fires proper onMention event
466555- (void )setActiveMentionRange : (NSRange )range text : (NSString *)text {
467556 NSString *indicatorString = [NSString stringWithFormat: @" %C " , [text characterAtIndex: 0 ]];
0 commit comments