@@ -8,6 +8,89 @@ describe('cursor persistence (state)', function()
88 state .set_cursor_position (' output' , nil )
99 end )
1010
11+ describe (' renderer.scroll_to_bottom' , function ()
12+ local renderer = require (' opencode.ui.renderer' )
13+ local buf , win
14+
15+ before_each (function ()
16+ config .setup ({})
17+ renderer .reset ()
18+
19+ buf = vim .api .nvim_create_buf (false , true )
20+ vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , {
21+ ' line 1' ,
22+ ' line 2' ,
23+ ' line 3' ,
24+ ' line 4' ,
25+ ' line 5' ,
26+ ' line 6' ,
27+ ' line 7' ,
28+ ' line 8' ,
29+ ' line 9' ,
30+ ' line 10' ,
31+ })
32+
33+ win = vim .api .nvim_open_win (buf , true , {
34+ relative = ' editor' ,
35+ width = 80 ,
36+ height = 10 ,
37+ row = 0 ,
38+ col = 0 ,
39+ })
40+
41+ state .windows = { output_win = win , output_buf = buf }
42+ vim .api .nvim_set_current_win (win )
43+ vim .api .nvim_win_set_cursor (win , { 10 , 0 })
44+ end )
45+
46+ after_each (function ()
47+ renderer .reset ()
48+ pcall (vim .api .nvim_win_close , win , true )
49+ pcall (vim .api .nvim_buf_delete , buf , { force = true })
50+ state .windows = nil
51+ end )
52+
53+ it (' auto-scrolls when cursor was at previous bottom and buffer grows' , function ()
54+ renderer .scroll_to_bottom ()
55+
56+ vim .api .nvim_buf_set_lines (buf , 10 , 10 , false , { ' line 11' , ' line 12' })
57+ renderer .scroll_to_bottom ()
58+
59+ local cursor = vim .api .nvim_win_get_cursor (win )
60+ assert .equals (12 , cursor [1 ])
61+ end )
62+
63+ it (' does not auto-scroll when user moved away from previous bottom before growth' , function ()
64+ renderer .scroll_to_bottom ()
65+
66+ vim .api .nvim_win_set_cursor (win , { 5 , 0 })
67+ vim .api .nvim_buf_set_lines (buf , 10 , 10 , false , { ' line 11' , ' line 12' })
68+ renderer .scroll_to_bottom ()
69+
70+ local cursor = vim .api .nvim_win_get_cursor (win )
71+ assert .equals (5 , cursor [1 ])
72+ end )
73+
74+ it (' auto-scrolls even when output window is unfocused if cursor was at previous bottom' , function ()
75+ renderer .scroll_to_bottom ()
76+
77+ local input_buf = vim .api .nvim_create_buf (false , true )
78+ vim .cmd (' vsplit' )
79+ local input_win = vim .api .nvim_get_current_win ()
80+ vim .api .nvim_win_set_buf (input_win , input_buf )
81+ vim .api .nvim_set_current_win (input_win )
82+
83+ vim .api .nvim_buf_set_lines (buf , 10 , 10 , false , { ' line 11' })
84+ renderer .scroll_to_bottom ()
85+
86+ local cursor = vim .api .nvim_win_get_cursor (win )
87+ assert .equals (11 , cursor [1 ])
88+
89+ pcall (vim .api .nvim_win_close , input_win , true )
90+ pcall (vim .api .nvim_buf_delete , input_buf , { force = true })
91+ end )
92+ end )
93+
1194 describe (' set/get round-trip' , function ()
1295 it (' stores and retrieves input cursor' , function ()
1396 state .set_cursor_position (' input' , { 5 , 3 })
@@ -83,7 +166,11 @@ describe('cursor persistence (state)', function()
83166 local buf = vim .api .nvim_create_buf (false , true )
84167 vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , { ' line1' , ' line2' , ' line3' })
85168 local win = vim .api .nvim_open_win (buf , true , {
86- relative = ' editor' , width = 40 , height = 10 , row = 0 , col = 0 ,
169+ relative = ' editor' ,
170+ width = 40 ,
171+ height = 10 ,
172+ row = 0 ,
173+ col = 0 ,
87174 })
88175 vim .api .nvim_win_set_cursor (win , { 2 , 3 })
89176
@@ -111,7 +198,11 @@ describe('output_window.is_at_bottom', function()
111198 vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , lines )
112199
113200 win = vim .api .nvim_open_win (buf , true , {
114- relative = ' editor' , width = 80 , height = 10 , row = 0 , col = 0 ,
201+ relative = ' editor' ,
202+ width = 80 ,
203+ height = 10 ,
204+ row = 0 ,
205+ col = 0 ,
115206 })
116207
117208 state .windows = { output_win = win , output_buf = buf }
@@ -162,7 +253,11 @@ describe('output_window.is_at_bottom', function()
162253 it (' returns true for empty buffer' , function ()
163254 local empty_buf = vim .api .nvim_create_buf (false , true )
164255 local empty_win = vim .api .nvim_open_win (empty_buf , true , {
165- relative = ' editor' , width = 40 , height = 5 , row = 0 , col = 0 ,
256+ relative = ' editor' ,
257+ width = 40 ,
258+ height = 5 ,
259+ row = 0 ,
260+ col = 0 ,
166261 })
167262 state .windows = { output_win = empty_win , output_buf = empty_buf }
168263
@@ -202,7 +297,11 @@ describe('renderer.scroll_to_bottom', function()
202297 vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , lines )
203298
204299 win = vim .api .nvim_open_win (buf , true , {
205- relative = ' editor' , width = 80 , height = 10 , row = 0 , col = 0 ,
300+ relative = ' editor' ,
301+ width = 80 ,
302+ height = 10 ,
303+ row = 0 ,
304+ col = 0 ,
206305 })
207306
208307 state .windows = { output_win = win , output_buf = buf }
@@ -251,10 +350,18 @@ describe('ui.focus_input', function()
251350 vim .api .nvim_buf_set_lines (output_buf , 0 , - 1 , false , { ' output' })
252351
253352 output_win = vim .api .nvim_open_win (output_buf , true , {
254- relative = ' editor' , width = 40 , height = 5 , row = 0 , col = 0 ,
353+ relative = ' editor' ,
354+ width = 40 ,
355+ height = 5 ,
356+ row = 0 ,
357+ col = 0 ,
255358 })
256359 input_win = vim .api .nvim_open_win (input_buf , true , {
257- relative = ' editor' , width = 40 , height = 5 , row = 6 , col = 0 ,
360+ relative = ' editor' ,
361+ width = 40 ,
362+ height = 5 ,
363+ row = 6 ,
364+ col = 0 ,
258365 })
259366
260367 state .windows = {
@@ -297,7 +404,11 @@ describe('renderer._add_message_to_buffer scrolling', function()
297404 vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , { ' existing line' })
298405
299406 win = vim .api .nvim_open_win (buf , true , {
300- relative = ' editor' , width = 80 , height = 10 , row = 0 , col = 0 ,
407+ relative = ' editor' ,
408+ width = 80 ,
409+ height = 10 ,
410+ row = 0 ,
411+ col = 0 ,
301412 })
302413
303414 state .windows = { output_win = win , output_buf = buf }
0 commit comments