@@ -49,6 +49,9 @@ extension ComposeContentAutocomplete on ComposeContentController {
4949 } else if (charAtPos == ':' ) {
5050 final match = _emojiIntentRegex.matchAsPrefix (textUntilCursor, pos);
5151 if (match == null ) continue ;
52+ } else if (charAtPos == '#' ) {
53+ final match = _channelLinkIntentRegex.matchAsPrefix (textUntilCursor, pos);
54+ if (match == null ) continue ;
5255 } else {
5356 continue ;
5457 }
@@ -67,6 +70,10 @@ extension ComposeContentAutocomplete on ComposeContentController {
6770 final match = _emojiIntentRegex.matchAsPrefix (textUntilCursor, pos);
6871 if (match == null ) continue ;
6972 query = EmojiAutocompleteQuery (match[1 ]! );
73+ } else if (charAtPos == '#' ) {
74+ final match = _channelLinkIntentRegex.matchAsPrefix (textUntilCursor, pos);
75+ if (match == null ) continue ;
76+ query = ChannelLinkAutocompleteQuery (match[1 ] ?? match[2 ]! );
7077 } else {
7178 continue ;
7279 }
@@ -166,6 +173,32 @@ final RegExp _emojiIntentRegex = (() {
166173 + r')$' );
167174})();
168175
176+ final RegExp _channelLinkIntentRegex = () {
177+ // Similar reasoning as in _mentionIntentRegex.
178+ const before = r'(?<=^|\s|\p{Punctuation})' ;
179+
180+ // TODO(upstream): maybe use duplicate-named capture groups for better readability?
181+ // https://github.com/dart-lang/sdk/issues/61337
182+ return RegExp (unicode: true ,
183+ before
184+ + r'#'
185+ // As Web, match both '#channel' and '#**channel'. In both cases, the raw
186+ // query is going to be 'channel'. Matching the second case ('#**channel')
187+ // is useful when the user selects a channel from the autocomplete list, but
188+ // then starts pressing "backspace" to edit the query and choose another
189+ // option, instead of clearing the entire query and starting from scratch.
190+ + r'(?:'
191+ // Case '#channel': right after '#', reject whitespace as well as '**'.
192+ + r'(?!\s|\*\*)(.*)'
193+ + r'|'
194+ // Case '#**channel': right after '#**', reject whitespace.
195+ // Also, make sure that the remaining query doesn't contain '**',
196+ // otherwise '#**channel**' (which is a complete channel link syntax) and
197+ // any text followed by that will always match.
198+ + r'\*\*(?!\s)((?:(?!\*\*).)*)'
199+ + r')$' );
200+ }();
201+
169202/// The text controller's recognition that the user might want autocomplete UI.
170203class AutocompleteIntent <QueryT extends AutocompleteQuery > {
171204 AutocompleteIntent ({
0 commit comments