terminal user interface to jujutsu. Focused on speed and clarity
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

add throbber

Eli Dowling a7e2ccaf 333266a2

+150 -7
+6
jj_tui/bin/global_vars.ml
··· 37 37 ; trigger_update : unit Lwd.var 38 38 ; reset_selection : unit Signal.t 39 39 ; config : Config.t Lwd.var 40 + ; loading : string option Lwd.var 40 41 } 41 42 42 43 let get_unique_id maybe_unique_rev = ··· 69 70 val get_active_revs_lwd : unit -> string list Lwd.t 70 71 val config : Config.t Lwd.var 71 72 val show_popup: ((ui Lwd.t * string) option ) ->unit 73 + val set_loading : string option -> unit 72 74 end 73 75 74 76 module Vars : Vars = struct ··· 94 96 ; trigger_update = Lwd.var () 95 97 ; reset_selection = Signal.make ~equal:(fun _ _ -> false) () 96 98 ; config = Lwd.var (Config.default_config) 99 + ; loading = Lwd.var None 97 100 } 98 101 ;; 99 102 ··· 150 153 [%log debug "setting show popup"]; 151 154 Lwd.set ui_state.show_popup popup 152 155 let config = ui_state.config 156 + 157 + let set_loading loading = 158 + Lwd.set ui_state.loading loading 153 159 end
+16 -5
jj_tui/bin/graph_commands.ml
··· 51 51 [ "git"; "push"; "--allow-new"; "--dry-run"; "--remote"; remote ] @ rev_args 52 52 in 53 53 let real_cmd = 54 - Cmd ([ "git"; "push"; "--allow-new"; "--remote"; remote ] @ rev_args) 54 + Cmd_async 55 + ( "pushing to remote..." 56 + , [ "git"; "push"; "--allow-new"; "--remote"; remote ] @ rev_args ) 55 57 in 56 58 confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd) 57 59 in 58 60 (* simple fetch – no confirmation needed *) 59 - let fetch_cmd = Cmd [ "git"; "fetch"; "--remote"; remote ] in 61 + let fetch_cmd = 62 + Cmd_async ("fetching from remote...", [ "git"; "fetch"; "--remote"; remote ]) 63 + in 60 64 Key_Map.of_seq 61 65 @@ List.to_seq 62 66 [ ( Key.key_of_string_exn "p" ··· 317 321 let dry_run_cmd = 318 322 [ "git"; "push"; "--allow-new"; "--dry-run" ] @ rev_args 319 323 in 320 - let real_cmd = Cmd ([ "git"; "push"; "--allow-new" ] @ rev_args) in 324 + let real_cmd = 325 + Cmd_async 326 + ( "pushing to remote..." 327 + , [ "git"; "push"; "--allow-new" ] @ rev_args ) 328 + in 321 329 confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd)) 322 330 } 323 331 ; { 324 332 id = "git_fetch" 325 333 ; sorting_key = 24.0 326 334 ; description = "git fetch" 327 - ; make_cmd = (fun () -> Cmd [ "git"; "fetch" ]) 335 + ; make_cmd = 336 + (fun () -> Cmd_async ("fetching from remote...", [ "git"; "fetch" ])) 328 337 } 329 338 ; { 330 339 id = "git_fetch_all" 331 340 ; sorting_key = 25.0 332 341 ; description = "git fetch all remotes" 333 - ; make_cmd = (fun () -> Cmd [ "git"; "fetch"; "--all-remotes" ]) 342 + ; make_cmd = 343 + (fun () -> 344 + Cmd_async ("fetching all remotes...", [ "git"; "fetch"; "--all-remotes" ])) 334 345 } 335 346 ; { 336 347 id = "git_remote_menu"
+56 -2
jj_tui/bin/jj_commands.ml
··· 6 6 open Jj_tui.Key_map 7 7 open Jj_tui.Key 8 8 open Jj_tui 9 + 9 10 open Log 10 11 11 12 (** Internal to this module. I'm trying this out as a way to avoid .mli files*) ··· 45 46 (** Allows nesting of commands, shows a popup with command options and waits for the user to press the appropriate key*) 46 47 | Fun of (unit -> unit) 47 48 (** Execute an arbitrary function. Prefer other command types if possible *) 49 + | Cmd_async of string * cmd_args 48 50 [@@deriving show] 49 51 50 52 (** A command that should be run when it's key is pressed*) ··· 75 77 open Nottui 76 78 open Log 77 79 open! Jj_tui.Util 80 + module Jj_widgets = Jj_widgets.Make (Vars) 78 81 79 82 exception Handled 80 83 ··· 131 134 | Cmd_with_revs _ 132 135 | Cmd_r _ 133 136 | Prompt_r _ 134 - | Dynamic_r _ ) 137 + | Dynamic_r _ 138 + | Cmd_async _ ) 135 139 ; sort_key = _ 136 140 } -> 137 141 [ render_command_line ~indent_level (key_to_string key) description ] ··· 204 208 let change_view view = Lwd.set ui_state.view view in 205 209 let send_cmd args = change_view (`Cmd_I args) in 206 210 match cmd with 211 + | Cmd_async (loading_msg,args) -> 212 + jj_async 213 + args 214 + ~on_start:(fun () -> show_popup @@ Some (W.hbox [ Jj_widgets.throbber ;(W.string (" "^loading_msg)|>Lwd.pure) ], loading_msg)) 215 + ~on_success:(fun _ -> 216 + Global_funcs.update_status ~cause_snapshot:true (); 217 + show_popup None) 218 + ~on_error:(fun code str -> 219 + handle_jj_error 220 + ~cmd:("jj " ^ (args |> String.concat " ")) 221 + ~error:(Printf.sprintf "Exited with code %i; Message:\n%s" code str); 222 + ); 223 + raise Handled 207 224 | Cmd_I args -> 208 225 show_popup None; 209 226 send_cmd args; ··· 343 360 SubCmd sub_cmd 344 361 ;; 345 362 346 - (**A prompt that allows the user to confirm a command by running it with a dry run and then running the real command if the user confirms*) 363 + (**A prompt that allows the user to confirm a command by running it with a dry run and then running the real command if the user confirms*) 347 364 let confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd = 365 + Fun 366 + (fun _ -> 367 + let subcmds = 368 + [ (let key = key_of_string_exn "y" in 369 + ( key 370 + , { key 371 + ; sort_key = 0. 372 + ; description = "proceed" 373 + ; cmd = real_cmd 374 + } )) 375 + ; (let key = key_of_string_exn "n" in 376 + ( key 377 + , { key 378 + ; sort_key = 1. 379 + ; description = "exit" 380 + ; cmd = 381 + Fun 382 + (fun _ -> 383 + ui_state.input $= `Normal; 384 + show_popup None) 385 + } )) 386 + ] 387 + |> Key_map.Key_Map.of_list 388 + in 389 + (*get the output of the dry run command*) 390 + let log = 391 + jj_no_log ~get_stderr:true dry_run_cmd 392 + |> AnsiReverse.colored_string 393 + |> Ui.atom 394 + |> Lwd.pure 395 + in 396 + show_popup @@ Some (W.vbox [ log; commands_list_ui subcmds ], title); 397 + ui_state.input $= `Mode (command_input ~is_sub:true subcmds)) 398 + ;; 399 + 400 + (**A prompt that allows the user to confirm a command by running it with a dry run and then running the real command if the user confirms*) 401 + let confirm_dry_run_prompt_async ~title ~dry_run_cmd ~real_cmd = 348 402 Fun 349 403 (fun _ -> 350 404 let subcmds =
+49
jj_tui/bin/jj_process.ml
··· 196 196 code, status, stdout, stderr,pid 197 197 ;; 198 198 199 + let jj_async ?(snapshot = true) ?(color = true) args ~on_start ~on_success ~on_error = 200 + let run () = 201 + let locked = 202 + if snapshot 203 + then ( 204 + Mutex.lock access_lock; 205 + true) 206 + else false 207 + in 208 + let res = 209 + try 210 + let _, status, out, err, _ = 211 + picos_process 212 + "jj" 213 + (List.concat 214 + [ 215 + args 216 + ; [ "--no-pager" ] 217 + ; (if snapshot then [] else [ "--ignore-working-copy" ]) 218 + ; (if color then [ "--color"; "always" ] else [ "--color"; "never" ]) 219 + ]) 220 + in 221 + let exit_code = 222 + match status with 223 + | Unix.WEXITED code -> 224 + code 225 + | Unix.WSIGNALED _ | Unix.WSTOPPED _ -> 226 + -1 227 + in 228 + if exit_code = 0 then Ok (out, err) else Error (exit_code, out, err) 229 + with 230 + | exn -> 231 + if locked then Mutex.unlock access_lock; 232 + raise exn 233 + in 234 + if locked then Mutex.unlock access_lock; 235 + match res with 236 + | Ok (out, err) -> 237 + on_success (out, err) 238 + | Error (code, out, err) -> 239 + on_error code (err ^ out) 240 + in 241 + on_start (); 242 + Picos_std_structured.Flock.fork(fun _ -> 243 + run(); 244 + 245 + ); 246 + ;; 247 + 199 248 (* Ui_loop.run (Lwd.pure (W.printf "Hello world"));; *) 200 249 let cmdArgs cmd args = 201 250 let start_time = Unix.gettimeofday () in
+23
jj_tui/bin/jj_widgets.ml
··· 208 208 | _ -> 209 209 `Unhandled) 210 210 ;; 211 + 212 + let throbber = 213 + let frames = [| "⠋"; "⠙"; "⠹"; "⠸"; "⠼"; "⠴"; "⠦"; "⠧"; "⠇"; "⠏" |] in 214 + let len = Array.length frames in 215 + let frame_var = Lwd.var 0 in 216 + 217 + (* let update_throb= Lwd.var 0 in *) 218 + Lwd.bind (Lwd.get frame_var) ~f:(fun frame -> 219 + Picos_std_structured.Flock.fork(fun _ -> 220 + Unix.sleepf 0.2; 221 + Lwd.set frame_var (Lwd.peek frame_var + 1); 222 + ); 223 + W.string ~attr:A.(fg red) (frames.(frame mod len) )|>Lwd.pure 224 + ) 225 + ;; 226 + 227 + let main_widget_list = 228 + let open Lwd_infix in 229 + let$* loading = Vars.ui_state.loading|>Lwd.get in 230 + if loading |> Option.is_some 231 + then W.vbox [ throbber ] 232 + else W.vbox [ W.string "not loading" |> Lwd.pure] 233 + ;; 211 234 end