@@ -157,13 +157,16 @@ pub(crate) async fn worktree(
157157 return Ok ( ( ) ) ;
158158 } ;
159159
160- let status_output = build_status_output ( ctx, & status_ctx) ?;
161160 match render_mode {
162161 StatusRenderMode :: Oneshot => {
163- render_oneshot:: render_oneshot ( status_output, human_out) ?;
162+ let mut output = StatusOutput :: Immediate { out : human_out } ;
163+ build_status_output ( ctx, & status_ctx, & mut output) ?;
164164 }
165165 StatusRenderMode :: Tui { debug } => {
166- tui:: render_tui ( ctx, out, & mode, flags, status_output, debug) . await ?;
166+ let mut lines = Vec :: new ( ) ;
167+ let mut output = StatusOutput :: Buffer { lines : & mut lines } ;
168+ build_status_output ( ctx, & status_ctx, & mut output) ?;
169+ tui:: render_tui ( ctx, out, & mode, flags, lines, debug) . await ?;
167170 }
168171 }
169172
@@ -347,26 +350,26 @@ fn truncation_policy(render_mode: StatusRenderMode, is_paged: bool) -> bool {
347350fn build_status_output (
348351 ctx : & mut Context ,
349352 status_ctx : & StatusContext < ' _ > ,
350- ) -> anyhow :: Result < StatusOutput > {
351- let mut output = StatusOutput :: default ( ) ;
352- print_update_notice ( ctx, status_ctx, & mut output) ?;
353- print_worktree_status ( ctx, status_ctx, & mut output) ?;
354- print_upstream_state ( ctx, status_ctx, & mut output) ?;
355- print_common_merge_base_summary ( status_ctx, & mut output) ?;
353+ output : & mut StatusOutput < ' _ > ,
354+ ) -> anyhow :: Result < ( ) > {
355+ print_update_notice ( ctx, status_ctx, output) ?;
356+ print_worktree_status ( ctx, status_ctx, output) ?;
357+ print_upstream_state ( ctx, status_ctx, output) ?;
358+ print_common_merge_base_summary ( status_ctx, output) ?;
356359 let not_on_workspace = matches ! (
357360 status_ctx. mode,
358361 gitbutler_operating_modes:: OperatingMode :: OutsideWorkspace ( _)
359362 ) ;
360- print_outside_workspace_warning ( not_on_workspace, & mut output) ?;
361- print_hint ( status_ctx, not_on_workspace, & mut output) ?;
362- Ok ( output )
363+ print_outside_workspace_warning ( not_on_workspace, output) ?;
364+ print_hint ( status_ctx, not_on_workspace, output) ?;
365+ Ok ( ( ) )
363366}
364367
365368/// Print update information for human output when a newer `but` version is available.
366369fn print_update_notice (
367370 ctx : & mut Context ,
368371 status_ctx : & StatusContext < ' _ > ,
369- output : & mut StatusOutput ,
372+ output : & mut StatusOutput < ' _ > ,
370373) -> anyhow:: Result < ( ) > {
371374 let cache = ctx. app_cache . get_cache ( ) ?;
372375 if let Ok ( Some ( update) ) = but_update:: available_update ( & cache) {
@@ -378,8 +381,8 @@ fn print_update_notice(
378381 )
379382 . into_iter ( )
380383 . collect ( ) ,
381- ) ;
382- output. connector ( Vec :: from ( [ Span :: raw ( "" ) ] ) ) ;
384+ ) ? ;
385+ output. connector ( Vec :: from ( [ Span :: raw ( "" ) ] ) ) ? ;
383386 }
384387
385388 Ok ( ( ) )
@@ -388,15 +391,15 @@ fn print_update_notice(
388391/// Print a warning when operating outside the GitButler workspace.
389392fn print_outside_workspace_warning (
390393 not_on_workspace : bool ,
391- output : & mut StatusOutput ,
394+ output : & mut StatusOutput < ' _ > ,
392395) -> anyhow:: Result < ( ) > {
393396 if not_on_workspace {
394397 output. warning ( Vec :: from ( [ Span :: raw (
395398 "⚠️ You are in plain Git mode, directly on a branch. Some commands may be unavailable. ⚠️" ,
396- ) ] ) ) ;
399+ ) ] ) ) ? ;
397400 output. warning ( Vec :: from ( [ Span :: raw (
398401 "⚠️ More info: https://github.com/gitbutlerapp/gitbutler/issues/11866 ⚠️" ,
399- ) ] ) ) ;
402+ ) ] ) ) ? ;
400403 }
401404
402405 Ok ( ( ) )
@@ -406,13 +409,13 @@ fn print_outside_workspace_warning(
406409fn print_hint (
407410 status_ctx : & StatusContext < ' _ > ,
408411 not_on_workspace : bool ,
409- output : & mut StatusOutput ,
412+ output : & mut StatusOutput < ' _ > ,
410413) -> anyhow:: Result < ( ) > {
411414 if !status_ctx. flags . hint {
412415 return Ok ( ( ) ) ;
413416 }
414417
415- output. connector ( Vec :: from ( [ Span :: raw ( "" ) ] ) ) ;
418+ output. connector ( Vec :: from ( [ Span :: raw ( "" ) ] ) ) ? ;
416419
417420 // Determine what hint to show based on workspace state
418421 let has_uncommitted_files = !status_ctx. worktree_changes . is_empty ( ) ;
@@ -427,7 +430,7 @@ fn print_hint(
427430 "Hint: run `but help` for all commands"
428431 } ;
429432
430- output. hint ( Vec :: from ( [ Span :: styled ( hint_text, Style :: default ( ) . dim ( ) ) ] ) ) ;
433+ output. hint ( Vec :: from ( [ Span :: styled ( hint_text, Style :: default ( ) . dim ( ) ) ] ) ) ? ;
431434
432435 Ok ( ( ) )
433436}
@@ -436,7 +439,7 @@ fn print_hint(
436439fn print_upstream_state (
437440 ctx : & mut Context ,
438441 status_ctx : & StatusContext < ' _ > ,
439- output : & mut StatusOutput ,
442+ output : & mut StatusOutput < ' _ > ,
440443) -> anyhow:: Result < ( ) > {
441444 let Some ( upstream) = & status_ctx. upstream_state else {
442445 return Ok ( ( ) ) ;
@@ -470,7 +473,7 @@ fn print_upstream_state(
470473 Style :: default ( ) . dim ( ) ,
471474 ) ) ;
472475 }
473- output. upstream_changes ( Vec :: from ( [ Span :: raw ( "┊╭┄" ) ] ) , upstream_summary) ;
476+ output. upstream_changes ( Vec :: from ( [ Span :: raw ( "┊╭┄" ) ] ) , upstream_summary) ? ;
474477
475478 // Display detailed list of upstream commits
476479 if let Some ( base_branch) = & status_ctx. base_branch
@@ -493,7 +496,7 @@ fn print_upstream_state(
493496 Span :: raw ( " " ) ,
494497 Span :: styled ( truncated_msg, Style :: default ( ) . dim ( ) ) ,
495498 ] ) ,
496- ) ;
499+ ) ? ;
497500 }
498501 let hidden_commits = base_branch. behind . saturating_sub ( 8 ) ;
499502 if hidden_commits > 0 {
@@ -503,10 +506,10 @@ fn print_upstream_state(
503506 format ! ( "and {hidden_commits} more…" ) ,
504507 Style :: default ( ) . dim ( ) ,
505508 ) ] ) ,
506- ) ;
509+ ) ? ;
507510 }
508511 }
509- output. connector ( Vec :: from ( [ Span :: raw ( "┊┊" ) ] ) ) ;
512+ output. connector ( Vec :: from ( [ Span :: raw ( "┊┊" ) ] ) ) ? ;
510513 } else {
511514 // Without --upstream, show the summary with latest commit info
512515 let mut upstream_summary = Vec :: from ( [
@@ -523,7 +526,7 @@ fn print_upstream_state(
523526 output. upstream_changes (
524527 Vec :: from ( [ Span :: raw ( "┊" ) , dot, Span :: raw ( " " ) ] ) ,
525528 upstream_summary,
526- ) ;
529+ ) ? ;
527530 }
528531
529532 Ok ( ( ) )
@@ -532,7 +535,7 @@ fn print_upstream_state(
532535/// Print the common merge-base summary line at the bottom of the status tree.
533536fn print_common_merge_base_summary (
534537 status_ctx : & StatusContext < ' _ > ,
535- output : & mut StatusOutput ,
538+ output : & mut StatusOutput < ' _ > ,
536539) -> anyhow:: Result < ( ) > {
537540 let first_line = status_ctx
538541 . common_merge_base_data
@@ -566,15 +569,15 @@ fn print_common_merge_base_summary(
566569 Span :: raw ( " " ) ,
567570 Span :: raw ( first_line. to_string ( ) ) ,
568571 ] ) ,
569- ) ;
572+ ) ? ;
570573 Ok ( ( ) )
571574}
572575
573576/// Print per-stack status sections for human-readable output.
574577fn print_worktree_status (
575578 ctx : & mut Context ,
576579 status_ctx : & StatusContext < ' _ > ,
577- output : & mut StatusOutput ,
580+ output : & mut StatusOutput < ' _ > ,
578581) -> anyhow:: Result < ( ) > {
579582 for ( i, ( stack_id, ( stack_with_id, assignments) ) ) in status_ctx. stack_details . iter ( ) . enumerate ( )
580583 {
@@ -651,14 +654,14 @@ fn print_assignments(
651654 branch_name : Option < & BStr > ,
652655 assignments : & [ FileAssignment ] ,
653656 unstaged : bool ,
654- output : & mut StatusOutput ,
657+ output : & mut StatusOutput < ' _ > ,
655658) -> anyhow:: Result < ( ) > {
656659 // if there are no assignments and we're in the unstaged section, print "(no changes)" and return
657660 if assignments. is_empty ( ) && unstaged {
658661 output. no_assignments_unstaged (
659662 Vec :: from ( [ Span :: raw ( "┊ " ) ] ) ,
660663 Vec :: from ( [ Span :: styled ( "no changes" , Style :: default ( ) . dim ( ) . italic ( ) ) ] ) ,
661- ) ;
664+ ) ? ;
662665 return Ok ( ( ) ) ;
663666 }
664667
@@ -687,7 +690,7 @@ fn print_assignments(
687690 Span :: raw ( "]" ) ,
688691 ] ) ,
689692 staged_changes_cli_id,
690- ) ;
693+ ) ? ;
691694 }
692695
693696 let max_id_width = assignments
@@ -759,14 +762,14 @@ fn print_assignments(
759762 }
760763
761764 if unstaged {
762- output. unstaged_file ( Vec :: from ( [ Span :: raw ( "┊ " ) ] ) , file_line, file_cli_id) ;
765+ output. unstaged_file ( Vec :: from ( [ Span :: raw ( "┊ " ) ] ) , file_line, file_cli_id) ? ;
763766 } else {
764- output. staged_file ( Vec :: from ( [ Span :: raw ( "┊ │ " ) ] ) , file_line, file_cli_id) ;
767+ output. staged_file ( Vec :: from ( [ Span :: raw ( "┊ │ " ) ] ) , file_line, file_cli_id) ? ;
765768 }
766769 }
767770
768771 if !unstaged && !assignments. is_empty ( ) {
769- output. connector ( Vec :: from ( [ Span :: raw ( "┊ │" ) ] ) ) ;
772+ output. connector ( Vec :: from ( [ Span :: raw ( "┊ │" ) ] ) ) ? ;
770773 }
771774
772775 Ok ( ( ) )
@@ -779,7 +782,7 @@ fn print_group(
779782 assignments : & [ FileAssignment ] ,
780783 stack_mark : & mut Option < Span < ' static > > ,
781784 first : bool ,
782- output : & mut StatusOutput ,
785+ output : & mut StatusOutput < ' _ > ,
783786) -> anyhow:: Result < ( ) > {
784787 let repo = ctx
785788 . legacy_project
@@ -790,7 +793,7 @@ fn print_group(
790793 for segment in & stack_with_id. segments {
791794 let notch = if first { "╭" } else { "├" } ;
792795 if !first {
793- output. connector ( Vec :: from ( [ Span :: raw ( "┊│" ) ] ) ) ;
796+ output. connector ( Vec :: from ( [ Span :: raw ( "┊│" ) ] ) ) ? ;
794797 }
795798
796799 let no_commits = if segment. workspace_commits . is_empty ( ) {
@@ -901,7 +904,7 @@ fn print_group(
901904 Vec :: from ( [ Span :: raw ( format ! ( "┊{notch}┄" ) ) ] ) ,
902905 branch_line,
903906 branch_cli_id,
904- ) ;
907+ ) ? ;
905908
906909 * stack_mark = None ; // Only show the stack mark for the first branch
907910 first = false ;
@@ -913,14 +916,14 @@ fn print_group(
913916 . as_ref ( )
914917 . and_then ( |rtb| rtb. as_bstr ( ) . strip_prefix ( b"refs/remotes/" ) )
915918 . unwrap_or ( b"unknown" ) ;
916- output. connector ( Vec :: from ( [ Span :: raw ( "┊┊" ) ] ) ) ;
919+ output. connector ( Vec :: from ( [ Span :: raw ( "┊┊" ) ] ) ) ? ;
917920 output. upstream_changes (
918921 Vec :: from ( [ Span :: raw ( "┊╭┄┄" ) ] ) ,
919922 Vec :: from ( [ Span :: styled (
920923 format ! ( "(upstream: on {})" , BStr :: new( tracking_branch) ) ,
921924 Style :: default ( ) . yellow ( ) ,
922925 ) ] ) ,
923- ) ;
926+ ) ? ;
924927 }
925928 for commit in & segment. remote_commits {
926929 let details =
@@ -938,7 +941,7 @@ fn print_group(
938941 ) ?;
939942 }
940943 if !segment. remote_commits . is_empty ( ) {
941- output. connector ( Vec :: from ( [ Span :: raw ( "┊-" ) ] ) ) ;
944+ output. connector ( Vec :: from ( [ Span :: raw ( "┊-" ) ] ) ) ? ;
942945 }
943946 for commit in segment. workspace_commits . iter ( ) {
944947 let marked = crate :: command:: legacy:: mark:: commit_marked (
@@ -989,13 +992,13 @@ fn print_group(
989992 line. push ( Span :: raw ( " " ) ) ;
990993 line. push ( stack_mark. clone ( ) ) ;
991994 }
992- output. unstaged_changes ( Vec :: from ( [ Span :: raw ( "╭┄" ) ] ) , line, cli_id. clone ( ) ) ;
995+ output. unstaged_changes ( Vec :: from ( [ Span :: raw ( "╭┄" ) ] ) , line, cli_id. clone ( ) ) ? ;
993996 print_assignments ( & repo, status_ctx, None , None , assignments, true , output) ?;
994997 }
995998 if !first {
996- output. connector ( Vec :: from ( [ Span :: raw ( "├╯" ) ] ) ) ;
999+ output. connector ( Vec :: from ( [ Span :: raw ( "├╯" ) ] ) ) ? ;
9971000 }
998- output. connector ( Vec :: from ( [ Span :: raw ( "┊" ) ] ) ) ;
1001+ output. connector ( Vec :: from ( [ Span :: raw ( "┊" ) ] ) ) ? ;
9991002 Ok ( ( ) )
10001003}
10011004
@@ -1081,7 +1084,7 @@ fn print_commit(
10811084 classification : CommitClassification ,
10821085 marked : bool ,
10831086 review_url : Option < String > ,
1084- output : & mut StatusOutput ,
1087+ output : & mut StatusOutput < ' _ > ,
10851088) -> anyhow:: Result < ( ) > {
10861089 let dot = match classification {
10871090 CommitClassification :: Upstream => Span :: styled ( "●" , Style :: default ( ) . yellow ( ) ) ,
@@ -1181,7 +1184,7 @@ fn print_commit(
11811184 )
11821185 . collect ( ) ,
11831186 commit_cli_id. clone ( ) ,
1184- ) ;
1187+ ) ? ;
11851188 let ( message, is_empty_message) = commit_message_display_cli (
11861189 & commit. message ,
11871190 status_ctx. flags . verbose ,
@@ -1196,9 +1199,9 @@ fn print_commit(
11961199 ) ;
11971200 let line = Vec :: from ( [ message] ) ;
11981201 if is_empty_message {
1199- output. empty_commit_message ( Vec :: from ( [ Span :: raw ( "┊│ " ) ] ) , line) ;
1202+ output. empty_commit_message ( Vec :: from ( [ Span :: raw ( "┊│ " ) ] ) , line) ? ;
12001203 } else {
1201- output. commit_message ( Vec :: from ( [ Span :: raw ( "┊│ " ) ] ) , line) ;
1204+ output. commit_message ( Vec :: from ( [ Span :: raw ( "┊│ " ) ] ) , line) ? ;
12021205 }
12031206 } else {
12041207 output. commit (
@@ -1226,7 +1229,7 @@ fn print_commit(
12261229 )
12271230 . collect ( ) ,
12281231 commit_cli_id. clone ( ) ,
1229- ) ;
1232+ ) ? ;
12301233 }
12311234 if status_ctx. flags . show_files {
12321235 match commit_changes {
@@ -1248,7 +1251,7 @@ fn print_commit(
12481251 . chain ( inner. display_cli ( false , status_ctx. should_truncate_for_terminal ) )
12491252 . collect ( ) ,
12501253 file_cli_id,
1251- ) ;
1254+ ) ? ;
12521255 }
12531256 }
12541257 CommitChanges :: Remote ( tree_changes) => {
@@ -1260,7 +1263,7 @@ fn print_commit(
12601263 . into_iter ( )
12611264 . collect ( ) ,
12621265 commit_cli_id. clone ( ) ,
1263- ) ;
1266+ ) ? ;
12641267 }
12651268 }
12661269 }
0 commit comments