@@ -2517,6 +2517,13 @@ function M:_expand_tool_call(call)
25172517 return
25182518 end
25192519
2520+ -- Save cursor position before expansion (if configured)
2521+ local saved_cursor = nil
2522+ local preserve_cursor = Utils .should_preserve_cursor ()
2523+ if preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2524+ saved_cursor = vim .api .nvim_win_get_cursor (chat .winid )
2525+ end
2526+
25202527 -- Reasoning ("Thinking") entries behave slightly differently: we never
25212528 -- wrap them in code fences and the streaming handler is responsible for
25222529 -- keeping the body up to date. Here we just insert the current body once.
@@ -2552,7 +2559,15 @@ function M:_expand_tool_call(call)
25522559 call .expanded = true
25532560 self :_update_tool_call_header_line (call )
25542561
2555- if chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2562+ -- Restore cursor position or move to last line based on config
2563+ if saved_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2564+ -- Adjust cursor row if it was after the insertion point
2565+ if saved_cursor [1 ] > call .header_line then
2566+ saved_cursor [1 ] = saved_cursor [1 ] + count
2567+ end
2568+ vim .api .nvim_win_set_cursor (chat .winid , saved_cursor )
2569+ elseif not preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2570+ -- Default behavior: move cursor to last line of expanded content
25562571 local last_line = call .header_line + (call ._last_arguments_count or 0 )
25572572 vim .api .nvim_win_set_cursor (chat .winid , { last_line , 0 })
25582573 vim .api .nvim_win_call (chat .winid , function ()
@@ -2588,8 +2603,15 @@ function M:_expand_tool_call(call)
25882603 -- Update header arrow
25892604 self :_update_tool_call_header_line (call )
25902605
2591- -- Move cursor to show the full arguments block
2592- if chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2606+ -- Restore cursor position or move to last line based on config
2607+ if saved_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2608+ -- Adjust cursor row if it was after the insertion point
2609+ if saved_cursor [1 ] > call .header_line then
2610+ saved_cursor [1 ] = saved_cursor [1 ] + count
2611+ end
2612+ vim .api .nvim_win_set_cursor (chat .winid , saved_cursor )
2613+ elseif not preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2614+ -- Default behavior: move cursor to last line of expanded content
25932615 local last_line = call .header_line + count
25942616 vim .api .nvim_win_set_cursor (chat .winid , { last_line , 0 })
25952617 vim .api .nvim_win_call (chat .winid , function ()
@@ -2609,6 +2631,13 @@ function M:_collapse_tool_call(call)
26092631 return
26102632 end
26112633
2634+ -- Save cursor position before collapsing (if configured)
2635+ local saved_cursor = nil
2636+ local preserve_cursor = Utils .should_preserve_cursor ()
2637+ if preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2638+ saved_cursor = vim .api .nvim_win_get_cursor (chat .winid )
2639+ end
2640+
26122641 local count = call .arguments_lines and # call .arguments_lines or 0
26132642 if count == 0 then
26142643 call .expanded = false
@@ -2633,6 +2662,19 @@ function M:_collapse_tool_call(call)
26332662
26342663 -- Update header arrow
26352664 self :_update_tool_call_header_line (call )
2665+
2666+ -- Restore cursor position, adjusting if it was after the collapsed region
2667+ if saved_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2668+ -- If cursor was inside the collapsed region, move it to the header
2669+ if saved_cursor [1 ] > call .header_line and saved_cursor [1 ] <= call .header_line + count then
2670+ saved_cursor [1 ] = call .header_line
2671+ saved_cursor [2 ] = 0
2672+ -- If cursor was after the collapsed region, adjust it up
2673+ elseif saved_cursor [1 ] > call .header_line + count then
2674+ saved_cursor [1 ] = saved_cursor [1 ] - count
2675+ end
2676+ vim .api .nvim_win_set_cursor (chat .winid , saved_cursor )
2677+ end
26362678end
26372679
26382680-- Expand a tool call's diff, inserting it below the diff label
@@ -2646,6 +2688,13 @@ function M:_expand_tool_call_diff(call)
26462688 return
26472689 end
26482690
2691+ -- Save cursor position before expansion (if configured)
2692+ local saved_cursor = nil
2693+ local preserve_cursor = Utils .should_preserve_cursor ()
2694+ if preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2695+ saved_cursor = vim .api .nvim_win_get_cursor (chat .winid )
2696+ end
2697+
26492698 call .diff_lines = call .diff_lines or self :_build_tool_call_diff_lines (call .details )
26502699 local count = call .diff_lines and # call .diff_lines or 0
26512700 if count == 0 then
@@ -2664,6 +2713,15 @@ function M:_expand_tool_call_diff(call)
26642713
26652714 -- Update the diff label to show the collapse indicator
26662715 self :_update_tool_call_diff_label_line (call )
2716+
2717+ -- Restore cursor position (no default cursor movement for diff expansion)
2718+ if saved_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2719+ -- Adjust cursor row if it was after the insertion point
2720+ if saved_cursor [1 ] > call .label_line then
2721+ saved_cursor [1 ] = saved_cursor [1 ] + count
2722+ end
2723+ vim .api .nvim_win_set_cursor (chat .winid , saved_cursor )
2724+ end
26672725end
26682726
26692727-- Collapse a tool call's diff, removing it from the buffer
@@ -2677,6 +2735,13 @@ function M:_collapse_tool_call_diff(call)
26772735 return
26782736 end
26792737
2738+ -- Save cursor position before collapsing (if configured)
2739+ local saved_cursor = nil
2740+ local preserve_cursor = Utils .should_preserve_cursor ()
2741+ if preserve_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2742+ saved_cursor = vim .api .nvim_win_get_cursor (chat .winid )
2743+ end
2744+
26802745 local count = call .diff_lines and # call .diff_lines or 0
26812746 if count == 0 then
26822747 call .diff_expanded = false
@@ -2696,6 +2761,19 @@ function M:_collapse_tool_call_diff(call)
26962761
26972762 -- Update the diff label to show the expand indicator again
26982763 self :_update_tool_call_diff_label_line (call )
2764+
2765+ -- Restore cursor position, adjusting if it was after the collapsed region
2766+ if saved_cursor and chat .winid and vim .api .nvim_win_is_valid (chat .winid ) then
2767+ -- If cursor was inside the collapsed region, move it to the label
2768+ if saved_cursor [1 ] > call .label_line and saved_cursor [1 ] <= call .label_line + count then
2769+ saved_cursor [1 ] = call .label_line
2770+ saved_cursor [2 ] = 0
2771+ -- If cursor was after the collapsed region, adjust it up
2772+ elseif saved_cursor [1 ] > call .label_line + count then
2773+ saved_cursor [1 ] = saved_cursor [1 ] - count
2774+ end
2775+ vim .api .nvim_win_set_cursor (chat .winid , saved_cursor )
2776+ end
26992777end
27002778
27012779-- Toggle tool call details at the current cursor position in the chat window
0 commit comments