@@ -12,6 +12,7 @@ import {
1212 getCaretPositionAfterPinnedReorder ,
1313 getCaretPositionAfterTokenRemoval ,
1414 mergeConsecutiveTextTokens ,
15+ removeTokenRange ,
1516 validateTrigger ,
1617} from '../core/token-utils' ;
1718import { PromptInputProps } from '../interfaces' ;
@@ -512,3 +513,83 @@ describe('getCaretPositionAfterTokenRemoval - trigger token handling', () => {
512513 expect ( getCaretPositionAfterTokenRemoval ( 16 , prev , next ) ) . toBe ( 15 ) ;
513514 } ) ;
514515} ) ;
516+
517+ describe ( 'removeTokenRange' , ( ) => {
518+ test ( 'returns copy when start >= end' , ( ) => {
519+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'hello' ) ] ;
520+ expect ( removeTokenRange ( tokens , 3 , 3 ) ) . toEqual ( [ text ( 'hello' ) ] ) ;
521+ expect ( removeTokenRange ( tokens , 5 , 2 ) ) . toEqual ( [ text ( 'hello' ) ] ) ;
522+ } ) ;
523+
524+ test ( 'removes entire text token when fully within range' , ( ) => {
525+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'aaa' ) , text ( 'bbb' ) , text ( 'ccc' ) ] ;
526+ // Remove "bbb" (positions 3-6)
527+ const result = removeTokenRange ( tokens , 3 , 6 ) ;
528+ expect ( result ) . toEqual ( [ text ( 'aaa' ) , text ( 'ccc' ) ] ) ;
529+ } ) ;
530+
531+ test ( 'trims text token at start of range' , ( ) => {
532+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'hello world' ) ] ;
533+ // Remove "world" (positions 6-11)
534+ const result = removeTokenRange ( tokens , 6 , 11 ) ;
535+ expect ( result ) . toEqual ( [ text ( 'hello ' ) ] ) ;
536+ } ) ;
537+
538+ test ( 'trims text token at end of range' , ( ) => {
539+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'hello world' ) ] ;
540+ // Remove "hello " (positions 0-6)
541+ const result = removeTokenRange ( tokens , 0 , 6 ) ;
542+ expect ( result ) . toEqual ( [ text ( 'world' ) ] ) ;
543+ } ) ;
544+
545+ test ( 'trims text token from both sides' , ( ) => {
546+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'hello world' ) ] ;
547+ // Remove "lo wo" (positions 3-8)
548+ const result = removeTokenRange ( tokens , 3 , 8 ) ;
549+ expect ( result ) . toEqual ( [ text ( 'helrld' ) ] ) ;
550+ } ) ;
551+
552+ test ( 'removes reference token when overlapping' , ( ) => {
553+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'hi ' ) , ref ( 'r1' , 'Alice' , 'alice' , 'mentions' ) , text ( ' bye' ) ] ;
554+ // Range spans the reference (positions 3-4)
555+ const result = removeTokenRange ( tokens , 3 , 4 ) ;
556+ expect ( result . find ( t => t . type === 'reference' ) ) . toBeUndefined ( ) ;
557+ expect ( result ) . toHaveLength ( 2 ) ;
558+ expect ( result [ 0 ] ) . toEqual ( text ( 'hi ' ) ) ;
559+ expect ( result [ 1 ] ) . toEqual ( text ( ' bye' ) ) ;
560+ } ) ;
561+
562+ test ( 'removes break token when overlapping' , ( ) => {
563+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'aaa' ) , { type : 'break' , value : '\n' } , text ( 'bbb' ) ] ;
564+ // Range spans the break (position 3-4)
565+ const result = removeTokenRange ( tokens , 3 , 4 ) ;
566+ expect ( result . find ( t => t . type === 'break' ) ) . toBeUndefined ( ) ;
567+ expect ( result ) . toHaveLength ( 2 ) ;
568+ expect ( result [ 0 ] ) . toEqual ( text ( 'aaa' ) ) ;
569+ expect ( result [ 1 ] ) . toEqual ( text ( 'bbb' ) ) ;
570+ } ) ;
571+
572+ test ( 'removes trigger token when overlapping' , ( ) => {
573+ const tokens : PromptInputProps . InputToken [ ] = [
574+ text ( 'hi ' ) ,
575+ { type : 'trigger' , value : 'bob' , triggerChar : '@' , id : 'trig-1' } ,
576+ ] ;
577+ // Range spans into the trigger (positions 3-7, trigger is @bob = 4 chars)
578+ const result = removeTokenRange ( tokens , 3 , 7 ) ;
579+ expect ( result . find ( t => t . type === 'trigger' ) ) . toBeUndefined ( ) ;
580+ expect ( result ) . toHaveLength ( 1 ) ;
581+ expect ( result [ 0 ] ) . toEqual ( text ( 'hi ' ) ) ;
582+ } ) ;
583+
584+ test ( 'removes everything when range covers all tokens' , ( ) => {
585+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'aaa' ) , { type : 'break' , value : '\n' } , text ( 'bbb' ) ] ;
586+ const result = removeTokenRange ( tokens , 0 , 7 ) ;
587+ expect ( result ) . toEqual ( [ ] ) ;
588+ } ) ;
589+
590+ test ( 'removes text token entirely when trimmed to empty' , ( ) => {
591+ const tokens : PromptInputProps . InputToken [ ] = [ text ( 'abc' ) ] ;
592+ const result = removeTokenRange ( tokens , 0 , 3 ) ;
593+ expect ( result ) . toEqual ( [ ] ) ;
594+ } ) ;
595+ } ) ;
0 commit comments