@@ -21,8 +21,16 @@ export type ComposerPromptSegment =
2121 context : TerminalContextDraft | null ;
2222 } ;
2323
24- const MENTION_TOKEN_REGEX = / ( ^ | \s ) @ ( [ ^ \s @ ] + ) (? = \s ) / g;
2524const SKILL_TOKEN_REGEX = / ( ^ | \s ) \$ ( [ a - z A - Z ] [ a - z A - Z 0 - 9 : _ - ] * ) (? = \s ) / g;
25+ const MENTION_TOKEN_REGEX = / ( ^ | \s ) @ (?: " ( (?: \\ .| [ ^ " \\ ] ) * ) " | ( [ ^ \s @ " ] + ) ) (? = \s ) / g;
26+ const SIMPLE_MENTION_PATH_REGEX = / ^ [ ^ \s @ " \\ ] + $ / ;
27+
28+ export function serializeComposerMentionPath ( path : string ) : string {
29+ if ( SIMPLE_MENTION_PATH_REGEX . test ( path ) ) {
30+ return path ;
31+ }
32+ return `"${ path . replaceAll ( "\\" , "\\\\" ) . replaceAll ( '"' , '\\"' ) } "` ;
33+ }
2634
2735function rangeIncludesIndex ( start : number , end : number , index : number ) : boolean {
2836 return start <= index && index < end ;
@@ -52,13 +60,18 @@ type InlineTokenMatch =
5260 end : number ;
5361 } ;
5462
55- function collectInlineTokenMatches ( text : string ) : InlineTokenMatch [ ] {
56- const matches : InlineTokenMatch [ ] = [ ] ;
63+ type MentionTokenMatch = Extract < InlineTokenMatch , { type : "mention" } > ;
64+
65+ function collectMentionTokenMatches ( text : string ) : MentionTokenMatch [ ] {
66+ const matches : MentionTokenMatch [ ] = [ ] ;
5767
5868 for ( const match of text . matchAll ( MENTION_TOKEN_REGEX ) ) {
5969 const fullMatch = match [ 0 ] ;
6070 const prefix = match [ 1 ] ?? "" ;
61- const path = match [ 2 ] ?? "" ;
71+ const quotedPath = match [ 2 ] ;
72+ const unquotedPath = match [ 3 ] ;
73+ const path =
74+ quotedPath !== undefined ? quotedPath . replace ( / \\ ( .) / g, "$1" ) : ( unquotedPath ?? "" ) ;
6275 const matchIndex = match . index ?? 0 ;
6376 const start = matchIndex + prefix . length ;
6477 const end = start + fullMatch . length - prefix . length ;
@@ -67,6 +80,12 @@ function collectInlineTokenMatches(text: string): InlineTokenMatch[] {
6780 }
6881 }
6982
83+ return matches ;
84+ }
85+
86+ function collectInlineTokenMatches ( text : string ) : InlineTokenMatch [ ] {
87+ const matches : InlineTokenMatch [ ] = collectMentionTokenMatches ( text ) ;
88+
7089 for ( const match of text . matchAll ( SKILL_TOKEN_REGEX ) ) {
7190 const fullMatch = match [ 0 ] ;
7291 const prefix = match [ 1 ] ?? "" ;
@@ -148,10 +167,10 @@ function forEachPromptTextSlice(
148167
149168function forEachMentionMatch (
150169 prompt : string ,
151- visitor : ( match : RegExpMatchArray , promptOffset : number ) => boolean | void ,
170+ visitor : ( match : MentionTokenMatch , promptOffset : number ) => boolean | void ,
152171) : boolean {
153172 return forEachPromptTextSlice ( prompt , ( text , promptOffset ) => {
154- for ( const match of text . matchAll ( MENTION_TOKEN_REGEX ) ) {
173+ for ( const match of collectMentionTokenMatches ( text ) ) {
155174 if ( visitor ( match , promptOffset ) === true ) {
156175 return true ;
157176 }
@@ -203,11 +222,8 @@ export function selectionTouchesMentionBoundary(
203222 }
204223
205224 return forEachMentionMatch ( prompt , ( match , promptOffset ) => {
206- const fullMatch = match [ 0 ] ;
207- const prefix = match [ 1 ] ?? "" ;
208- const matchIndex = match . index ?? 0 ;
209- const mentionStart = promptOffset + matchIndex + prefix . length ;
210- const mentionEnd = mentionStart + fullMatch . length - prefix . length ;
225+ const mentionStart = promptOffset + match . start ;
226+ const mentionEnd = promptOffset + match . end ;
211227 const beforeMentionIndex = mentionStart - 1 ;
212228 const afterMentionIndex = mentionEnd ;
213229
0 commit comments