@@ -18,24 +18,6 @@ function mentionStart(src: string) {
1818 return src . indexOf ( '<' ) ;
1919}
2020
21- function mentionTokenizer ( this : any , src : string , options : MentionOptions = { } ) {
22- const trigger = options . triggerChar ?? '@' ;
23- // Build dynamic regex for `<@id>`, `<@id|label>`, `<@id|>`
24- // Added forward slash (/) to the character class for IDs
25- const re = new RegExp ( `^<\\${ trigger } ([\\w.\\-:/]+)(?:\\|([^>]*))?>` ) ;
26- const m = re . exec ( src ) ;
27- if ( ! m ) return ;
28-
29- const [ , id , label ] = m ;
30- return {
31- type : 'mention' ,
32- raw : m [ 0 ] ,
33- triggerChar : trigger ,
34- id,
35- label : label && label . length > 0 ? label : id
36- } ;
37- }
38-
3921function mentionRenderer ( token : any , options : MentionOptions = { } ) {
4022 const trigger = options . triggerChar ?? '@' ;
4123 const cls = options . className ?? 'mention' ;
@@ -55,12 +37,26 @@ function mentionRenderer(token: any, options: MentionOptions = {}) {
5537}
5638
5739export function mentionExtension ( opts : MentionOptions = { } ) {
40+ // Compile the regex once when the extension is created, not on every tokenizer call.
41+ // mentionStart fires on every '<' in the document, making the tokenizer a hot path.
42+ const trigger = opts . triggerChar ?? '@' ;
43+ const re = new RegExp ( `^<\\${ trigger } ([\\w.\\-:/]+)(?:\\|([^>]*))?>` ) ;
44+
5845 return {
5946 name : 'mention' ,
6047 level : 'inline' as const ,
6148 start : mentionStart ,
6249 tokenizer ( src : string ) {
63- return mentionTokenizer . call ( this , src , opts ) ;
50+ const m = re . exec ( src ) ;
51+ if ( ! m ) return ;
52+ const [ , id , label ] = m ;
53+ return {
54+ type : 'mention' ,
55+ raw : m [ 0 ] ,
56+ triggerChar : trigger ,
57+ id,
58+ label : label && label . length > 0 ? label : id
59+ } ;
6460 } ,
6561 renderer ( token : any ) {
6662 return mentionRenderer ( token , opts ) ;
0 commit comments