@@ -4250,6 +4250,47 @@ describe("vim scroll mapping", () => {
42504250} )
42514251
42524252describe ( "copy mode" , ( ) => {
4253+ function createRenderedCopyMode ( lines : string [ ] , gutter = 4 ) {
4254+ const child = {
4255+ id : "text-part" ,
4256+ y : 0 ,
4257+ height : lines . length ,
4258+ gutter : { calculateWidth : ( ) => gutter } ,
4259+ getChildren : ( ) => [
4260+ {
4261+ _y : 0 ,
4262+ plainText : lines . join ( "\n" ) ,
4263+ lineInfo : {
4264+ lineSources : lines . map ( ( _ , i ) => i ) ,
4265+ lineStartCols : lines . map ( ( ) => 0 ) ,
4266+ lineWidthCols : lines . map ( ( line ) => Bun . stringWidth ( line ) ) ,
4267+ lineWraps : lines . map ( ( ) => 0 ) ,
4268+ } ,
4269+ } ,
4270+ ] ,
4271+ }
4272+ const scroll = {
4273+ y : 0 ,
4274+ height : 10 ,
4275+ width : 120 ,
4276+ scrollHeight : lines . length ,
4277+ getChildren : ( ) => [ child ] ,
4278+ scrollBy ( ) { } ,
4279+ } as unknown as ScrollBoxRenderable
4280+ const cm = createCopyMode ( {
4281+ scroll : ( ) => scroll ,
4282+ messages : ( ) => [ { id : "message" , role : "assistant" } ] ,
4283+ parts : ( ) => [ { id : "part" , type : "text" , text : lines . join ( "\n" ) } ] as Part [ ] ,
4284+ thinking : ( ) => false ,
4285+ details : ( ) => false ,
4286+ session : ( ) => "session" ,
4287+ toBottom ( ) { } ,
4288+ } )
4289+ cm . prompt . enter ( )
4290+ cm . prompt . jump ( "top" )
4291+ return cm
4292+ }
4293+
42534294 test ( "highlights final wrapped row using its visual slice" , ( ) => {
42544295 const child = {
42554296 id : "text-part" ,
@@ -4289,6 +4330,44 @@ describe("copy mode", () => {
42894330 expect ( cm . prompt . yank ( ) ) . toEqual ( { text : "abcdefghij\nklmnopqrst\nuvwxyz" , linewise : false } )
42904331 } )
42914332
4333+ test ( "word motions use copy row minimum columns" , ( ) => {
4334+ const cm = createRenderedCopyMode ( [ "alpha beta" , " gamma delta" ] )
4335+
4336+ expect ( cm . state ( ) . col ) . toBe ( 7 )
4337+ expect ( cm . prompt . wordNext ( false ) ) . toBe ( true )
4338+ expect ( cm . state ( ) . col ) . toBe ( 13 )
4339+ cm . prompt . setCol ( 13 )
4340+ expect ( cm . prompt . wordPrev ( false ) ) . toBe ( true )
4341+ expect ( cm . state ( ) . col ) . toBe ( 7 )
4342+ expect ( cm . prompt . wordEnd ( false ) ) . toBe ( true )
4343+ expect ( cm . state ( ) . col ) . toBe ( 11 )
4344+ } )
4345+
4346+ test ( "word motions use target row minimum columns across rows" , ( ) => {
4347+ const cm = createRenderedCopyMode ( [ "alpha beta" , " gamma delta" ] )
4348+
4349+ cm . prompt . setCol ( 16 )
4350+ expect ( cm . prompt . wordNext ( false ) ) . toBe ( true )
4351+ expect ( cm . state ( ) . idx ) . toBe ( 1 )
4352+ expect ( cm . state ( ) . col ) . toBe ( 9 )
4353+
4354+ cm . prompt . jump ( "top" )
4355+ cm . prompt . setCol ( 16 )
4356+ expect ( cm . prompt . wordEnd ( false ) ) . toBe ( true )
4357+ expect ( cm . state ( ) . idx ) . toBe ( 1 )
4358+ expect ( cm . state ( ) . col ) . toBe ( 13 )
4359+ } )
4360+
4361+ test ( "b uses previous row minimum columns across rows" , ( ) => {
4362+ const cm = createRenderedCopyMode ( [ "alpha beta" , "gamma" ] )
4363+ cm . prompt . jump ( "bottom" )
4364+
4365+ expect ( cm . state ( ) . col ) . toBe ( 7 )
4366+ expect ( cm . prompt . wordPrev ( false ) ) . toBe ( true )
4367+ expect ( cm . state ( ) . idx ) . toBe ( 0 )
4368+ expect ( cm . state ( ) . col ) . toBe ( 13 )
4369+ } )
4370+
42924371 test ( "copyWordNext advances to next row when next word is on following line" , ( ) => {
42934372 const next = copyWordNext ( [ { col : 0 } , { col : 0 } ] , ( idx ) => [ "alpha" , "beta gamma" ] [ idx ] ! , 0 , 4 , false )
42944373 expect ( next ) . toEqual ( { idx : 1 , col : 5 } )
0 commit comments