Skip to content

Autocomplete: Reopen @-mention typeahead when editing inside ** - #2166

Open
MritunjayTiwari14 wants to merge 2 commits into
zulip:mainfrom
MritunjayTiwari14:issue_1967
Open

MritunjayTiwari14 wants to merge 2 commits into
zulip:mainfrom
MritunjayTiwari14:issue_1967

Conversation

@MritunjayTiwari14

Copy link
Copy Markdown
Contributor

Fixes #1967.

Changes
Before After
before.webm
after.webm

@MritunjayTiwari14

Copy link
Copy Markdown
Contributor Author

@chrisbobbe Ready for review.

@gnprice gnprice added the maintainer review PR ready for review by Zulip maintainers label Feb 27, 2026
@chrisbobbe chrisbobbe self-assigned this Feb 27, 2026

@Khader-1 Khader-1 left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @MritunjayTiwari14! I think this is close, a few notes bellow.

Comment thread lib/model/autocomplete.dart Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be bumped to be + 3. The longest prefix after @ is now _** (3 chars, for a silent mention like @_**foo). With a small maxChannelNameLength, the lookback limit can prevent the scanner from reaching the @ and then the intent would be missed.

For example:
@_**ab with maxChannelName: 1, lookback is 1+2+2=5 but the pattern is 6 chars.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does clearly make sense. This should be increased to 3 because of _**. Thanks for noticing!

Comment thread lib/model/autocomplete.dart Outdated
+ r'[^' + fullNameAndEmailCharExclusions + r']*'
+ r'(?:'
// Reject on whitespace and "**" right after "@" or "@_". Emails can't start
// with both, and full_name can't either (it's run through Python's `.strip()`).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit):

Suggested change
// with both, and full_name can't either (it's run through Python's `.strip()`).
// with either, and full_name can't either (it's run through Python's `.strip()`).

doTest('Ask @**Chris"Bobbe^', null);

// "**" are not allowed inside the query.
doTest('Ask @**Chris**Bobbe^', null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add more edge cases here, maybe:

// Three asterisks: "@***" = prefix "@**" + query "*".
doTest('Ask ~@***^', mention('*'));
doTest('Ask ~@_***^', silentMention('*'));
doTest('~@_**ab^', silentMention('ab'), maxChannelName: 1);
doTest('~@**abc^', mention('abc'), maxChannelName: 1);
doTest('~@**ab^', mention('ab'), maxChannelName: 1);

Copilot AI review requested due to automatic review settings March 8, 2026 06:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the compose-box autocomplete behavior for @-mentions when a user edits inside the @**Full Name** syntax (e.g., after selecting a mention and then backspacing into the bolded name), so the typeahead reopens using the intended query text.

Changes:

  • Extend @-mention intent parsing to recognize @**…-prefixed mention text and exclude the leading ** from the query.
  • Adjust autocomplete lookback bounds to account for the longer optional prefix (@_**).
  • Expand and refine unit tests for @-mention and channel-link lookback/query parsing, including UTF-16 code-unit edge cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/model/autocomplete.dart Updates mention-intent regex and lookback length to support reopening autocomplete while editing inside @**... mention syntax.
test/model/autocomplete_test.dart Adds comprehensive tests for @**... edit/backspace scenarios and updates lookback-boundary tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/model/autocomplete_test.dart Outdated
doTest('Ask @****^', null);
doTest('Ask @_****^', null);

// " ' " (Aspotrophe) and "-" are allowed inside the query.

Copilot AI Mar 8, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling: "Aspotrophe" should be "Apostrophe" in this comment.

Suggested change
// " ' " (Aspotrophe) and "-" are allowed inside the query.
// " ' " (Apostrophe) and "-" are allowed inside the query.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@MritunjayTiwari14

Copy link
Copy Markdown
Contributor Author

@chrisbobbe Waiting for your review on this...

@chrisbobbe chrisbobbe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, and I'm sorry I've let this sit.

Comments below, including one that unlocks some simplification, I think: if we can continue prohibiting stars ("*") in the query, then I think we can neatly express three mutually exclusive branches in the regex:

  • no stars before or after the query
  • two stars before the query and no stars after
  • two stars before the query and one star after (for the case where you've backspaced through one star after finishing an autocomplete)

Would you give that a try?

Comment on lines 20 to 30

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reasoning in #2166 (comment) for bumping the 2 to a 3 doesn't quite hold, I think. The max length of a channel name isn't relevant when you're trying to do a silent @-mention, because in that case you're searching for users, not channels.

I think the complexity of trying to maintain an exactly correct number here isn't worth it, and we should just do the simple thing of hard-coding it to 30 or 60 or something. This came up in another context recently: #2137 (comment)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I disagree, maxlookback should be bumped from 2 to 3. I totally agree, channel name isn't relevant. But how the channel name is affected when the value is bumped from 2 to 3 is what I can't understand in your comment.

Those two are totally independent things, when we introduced silent mention. The min length would inflate from being **, i.e., 2, to _**, i.e., 3?

Comment thread lib/model/autocomplete.dart Outdated
@@ -73,7 +73,7 @@ extension ComposeContentAutocomplete on ComposeContentController {
if (charAtPos == '@') {
final match = _mentionIntentRegex.matchAsPrefix(textUntilCursor, pos);
if (match == null) continue;
query = MentionAutocompleteQuery(match[2]!, silent: match[1]! == '_');
query = MentionAutocompleteQuery(match[2] ?? match[3]!, silent: match[1]! == '_');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, I think this could be simplified by using duplicate named capture groups? I see an existing TODO mentioning that feature:

  // TODO(upstream): maybe use duplicate-named capture groups for better readability?
  //   https://github.com/dart-lang/sdk/issues/61337

and that Dart PR was merged recently! 🎉 Can we try that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure!

doTest('Ask ~@_***^', silentMention('*'));
doTest('~@_**ab^', silentMention('ab'), maxChannelName: 1);
doTest('~@**abc^', mention('abc'), maxChannelName: 1);
doTest('~@**ab^', mention('ab'), maxChannelName: 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to allow "*" in the query at all? We have a comment suggesting that no users will ever match if that character (or a handful of others) is in the query:

  // Characters that would defeat searches in full_name and emails, since
  // they're prohibited in both forms. These are all the characters prohibited
  // in full_name except "@", which appears in emails. (For the form of
  // full_name, find uses of UserProfile.NAME_INVALID_CHARS in zulip/zulip.)
  const fullNameAndEmailCharExclusions = r'\*`\\>"\p{Other}';

Fixes zulip#1967.

RegEx logic is now divide into 3 mutually exclusive case
where 1st case deals with query not starting with "**", while
the 2nd and 3rd case handles query starting with "**" and,

1. Not containing any "*" afterwards.
2. Ending with an "*".
…dcoding

Recently, dart added support for duplicate-named group.
Using this would improve readability.

Following this, we should use duplicate-named group instead of
access the same query in different cases using hardcoded indices
using former provides better readability.

This was implemented by typecasting match to RegExpMatch. This seems
safe since match was obtained by a regex operation and was required as only
RegExpMatch supports `namedGroup('name')` method to access capture group named 'name'.

notes:
1. Implemention of named capturing group was not done for capturing group
which were accessible by single definite index, since there is only one
capture group.

more details about namedGroup:
  https://api.flutter.dev/flutter/dart-core/RegExpMatch/namedGroup.html

dart upstream issue which was resolved:
  dart-lang/sdk#61337
@MritunjayTiwari14

MritunjayTiwari14 commented Apr 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks, @chrisbobbe, for the review. I have left a comment on your feedback. As well as addressing all of your feedback. PTAL.

Changes:

  • Used a 3 mutually exclusive case-wise breakdown to identify both normal and silent mentions. Please check the commit description for more details.
  • Added a post-refactor commit to replace hardcoded RegExp group capture using namedGroup().
  • I have not updated the maxlookback value since the last review; it's still (4 + 2*maxChannelName).

@MritunjayTiwari14
MritunjayTiwari14 marked this pull request as draft April 14, 2026 06:02
@MritunjayTiwari14
MritunjayTiwari14 marked this pull request as ready for review April 17, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer review PR ready for review by Zulip maintainers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

After choosing an @-mention option, backspacing should reopen autocomplete

5 participants