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 git commands with selectable remote

+156 -55
+62 -39
jj_tui/bin/graph_commands.ml
··· 13 13 module Process = Jj_process.Make (Vars) 14 14 open Process 15 15 open Jj_tui.Process_wrappers.Make (Process) 16 - 16 + open Jj_tui.Key_map 17 17 (* Helper functions from graph_view *) 18 18 let bookmark_select_prompt get_bookmark_list name func = 19 19 Selection_prompt ··· 22 22 , (fun x bookmark_name -> bookmark_name |> Base.String.is_substring ~substring:x) 23 23 , func ) 24 24 ;; 25 + let remote_select_prompt name func = 26 + Selection_prompt 27 + ( name 28 + , (fun () -> get_remotes_selectable () |> Lwd.pure) 29 + , (fun x remote_name -> remote_name |> Base.String.is_substring ~substring:x) 30 + , func ) 31 + ;; 25 32 26 33 let custom_commit ?(edit = true) msg = 27 34 let rev = Vars.get_hovered_rev () in ··· 29 36 (jj @@ [ "new"; "--insert-after"; rev ] @ if edit then [] else [ "--no-edit" ]) 30 37 |> ignore 31 38 ;; 39 + 40 + (** 41 + A submenu for git commands that are specific to a remote. 42 + *) 43 + let make_remote_git_submenu remote : _ Key_Map.t = 44 + let push_cmd () = 45 + Dynamic 46 + (fun () -> 47 + let revs = Vars.get_active_revs () in 48 + let rev_args = List.concat_map (fun r -> [ "-r"; r ]) revs in 49 + let title = Printf.sprintf "Git push (%s) will:" remote in 50 + let dry_run_cmd = 51 + [ "git"; "push"; "--allow-new"; "--dry-run"; "--remote"; remote ] @ rev_args 52 + in 53 + let real_cmd = 54 + Cmd ([ "git"; "push"; "--allow-new"; "--remote"; remote ] @ rev_args) 55 + in 56 + confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd) 57 + in 58 + (* simple fetch – no confirmation needed *) 59 + let fetch_cmd = Cmd [ "git"; "fetch"; "--remote"; remote ] in 60 + Key_Map.of_seq 61 + @@ List.to_seq 62 + [ ( Key.key_of_string_exn "p" 63 + , { key = Key.key_of_string_exn "p" 64 + ; sort_key = 0. 65 + ; description = Printf.sprintf "git push to %s" remote 66 + ; cmd = push_cmd () 67 + } ) 68 + ; ( Key.key_of_string_exn "f" 69 + , { key = Key.key_of_string_exn "f" 70 + ; sort_key = 1. 71 + ; description = Printf.sprintf "git fetch from %s" remote 72 + ; cmd = fetch_cmd 73 + } ) 74 + ] 32 75 33 76 (* Define all graph commands *) 34 77 let get_command_registry get_commands = ··· 266 309 ; description = "git push" 267 310 ; make_cmd = 268 311 (fun () -> 269 - Fun 270 - (fun _ -> 312 + Dynamic 313 + (fun () -> 271 314 let revs = Vars.get_active_revs () in 272 - let subcmds = 273 - [ 274 - { 275 - key = Key.key_of_string_exn "y" 276 - ; sort_key = 0.0 277 - ; description = "proceed" 278 - ; cmd = 279 - Cmd 280 - ([ "git"; "push"; "--allow-new" ] 281 - @ (revs |> List.concat_map (fun x -> [ "-r"; x ]))) 282 - } 283 - ; { 284 - key = Key.key_of_string_exn "n" 285 - ; sort_key = 1.0 286 - ; description = "exit" 287 - ; cmd = 288 - Fun 289 - (fun _ -> 290 - ui_state.input $= `Normal; 291 - show_popup None) 292 - } 293 - ] 294 - |> List.map (fun x -> x.key, x) 295 - |> Key_map.Key_Map.of_list 315 + let rev_args = revs |> List.concat_map (fun x -> [ "-r"; x ]) in 316 + let title = "Git push will:" in 317 + let dry_run_cmd = 318 + [ "git"; "push"; "--allow-new"; "--dry-run" ] @ rev_args 296 319 in 297 - let log = 298 - jj_no_log 299 - ~get_stderr:true 300 - ([ "git"; "push"; "--allow-new"; "--dry-run" ] 301 - @ (revs |> List.concat_map (fun x -> [ "-r"; x ]))) 302 - |> AnsiReverse.colored_string 303 - |> Ui.atom 304 - |> Lwd.pure 305 - in 306 - let ui = W.vbox [ log; commands_list_ui subcmds ] in 307 - show_popup @@ Some (ui, "Git push will:"); 308 - ui_state.input $= `Mode (command_input ~is_sub:true subcmds))) 320 + let real_cmd = Cmd ([ "git"; "push"; "--allow-new" ] @ rev_args) in 321 + confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd)) 309 322 } 310 323 ; { 311 324 id = "git_fetch" ··· 318 331 ; sorting_key = 25.0 319 332 ; description = "git fetch all remotes" 320 333 ; make_cmd = (fun () -> Cmd [ "git"; "fetch"; "--all-remotes" ]) 334 + } 335 + ; { 336 + id = "git_remote_menu" 337 + ; sorting_key = 25.5 338 + ; description = "Select remote, then run git commands" 339 + ; make_cmd = (fun () -> 340 + remote_select_prompt 341 + "Select remote" 342 + (fun remote -> SubCmd (make_remote_git_submenu remote))) 321 343 } 322 344 ; { 323 345 id = "parallelize" ··· 462 484 parent that modified that file" 463 485 ; make_cmd = (fun () -> Dynamic_r (fun r -> Cmd [ "absorb"; "--from"; r ])) 464 486 } 487 + 465 488 ] 466 489 |> List.to_seq 467 490 |> Seq.map (fun x -> x.id, x)
+37
jj_tui/bin/jj_commands.ml
··· 343 343 SubCmd sub_cmd 344 344 ;; 345 345 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*) 347 + let confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd = 348 + Fun 349 + (fun _ -> 350 + let subcmds = 351 + [ (let key = key_of_string_exn "y" in 352 + ( key 353 + , { key 354 + ; sort_key = 0. 355 + ; description = "proceed" 356 + ; cmd = real_cmd 357 + } )) 358 + ; (let key = key_of_string_exn "n" in 359 + ( key 360 + , { key 361 + ; sort_key = 1. 362 + ; description = "exit" 363 + ; cmd = 364 + Fun 365 + (fun _ -> 366 + ui_state.input $= `Normal; 367 + show_popup None) 368 + } )) 369 + ] 370 + |> Key_map.Key_Map.of_list 371 + in 372 + (*get the output of the dry run command*) 373 + let log = 374 + jj_no_log ~get_stderr:true dry_run_cmd 375 + |> AnsiReverse.colored_string 376 + |> Ui.atom 377 + |> Lwd.pure 378 + in 379 + show_popup @@ Some (W.vbox [ log; commands_list_ui subcmds ], title); 380 + ui_state.input $= `Mode (command_input ~is_sub:true subcmds)) 381 + ;; 382 + 346 383 (** Handles raw command mapping without regard for modes or the current intput state. Should be used when setting a new input mode*) 347 384 let command_input = command_input 348 385
+29
jj_tui/bin/jj_widgets.ml
··· 74 74 ^ {|"++label("bookmark", name++" @"++remote) ++ if(present, format_ref_targets(self), " (deleted)")++ "\n")|} 75 75 ;; 76 76 77 + let get_remotes () = 78 + let log = jj_no_log ~snapshot:false [ "git"; "remote"; "list" ] in 79 + let lines = String.split_on_char '\n' log in 80 + lines 81 + |> List.filter_map (fun line -> 82 + if line |>String.trim|> String.length =0 83 + then None 84 + else ( 85 + match Base.String.lsplit2 ~on:' ' line with 86 + | Some (name, _) -> Some (name, line) 87 + | None -> Some (line, line))) 88 + ;; 89 + 90 + let get_remotes_selectable () = 91 + get_remotes () 92 + |> List.map (fun (name, str) -> 93 + W.Lists. 94 + { 95 + data = name 96 + ; id = name |> String.hash 97 + ; ui = 98 + str ^ "\n" 99 + |> Jj_tui.AnsiReverse.colored_string 100 + |> Ui.atom 101 + |> Ui.resize ~w:100 ~h:1 ~mw:100 102 + |> W.Lists.selectable_item 103 + }) 104 + ;; 105 + 77 106 let selection_list ?(focus = Focus.make ()) items = 78 107 Focus.request focus; 79 108 let selected_var = Lwd.var 0 in
+28 -16
jj_tui/lib/key_map.ml
··· 1 1 module Key_Map = struct 2 - include Map.Make ( Key) 2 + include Map.Make (Key) 3 3 4 4 let pp inner_pp fmt this = 5 5 Format.fprintf fmt "@[<v>"; ··· 24 24 | Sub_menu of sub_menu 25 25 | Command of command 26 26 27 - and key_map = key_map_item Key_Map.t[@@deriving show] 27 + and key_map = key_map_item Key_Map.t [@@deriving show] 28 28 and key_map_update_t = key_map 29 29 30 30 let ( let* ) = Base.Result.Let_syntax.( >>= ) ··· 118 118 let key_map_apply_update override og = 119 119 Key_Map.merge 120 120 (fun k v1 v2 -> 121 - match v1, v2 with 122 - | Some og, Some override -> Some (override) 123 - | Some v, None | None, Some v -> Some v 124 - | None, None -> None) 121 + match v1, v2 with 122 + | Some og, Some override -> 123 + Some override 124 + | Some v, None | None, Some v -> 125 + Some v 126 + | None, None -> 127 + None) 125 128 og 126 129 override 127 130 ;; ··· 171 174 ] 172 175 ; cmd "y" "duplicate" 173 176 ; cmd "u" "undo" 174 - ; sub "c" "Commit" [ cmd "c" "commit_base"; cmd "C" "commit_no_edit"; cmd "D" "describe_editor" ] 177 + ; sub 178 + "c" 179 + "Commit" 180 + [ cmd "c" "commit_base"; cmd "C" "commit_no_edit"; cmd "D" "describe_editor" ] 175 181 ; cmd "S" "split" 176 182 ; sub 177 183 "s" ··· 190 196 ; sub 191 197 "r" 192 198 "Rebase" 193 - [ cmd "r" "rebase_single"; cmd "s" "rebase_with_descendants"; cmd "b" "rebase_with_bookmark" ] 194 - ; sub "g" "Git" [ cmd "p" "git_push"; cmd "f" "git_fetch"; cmd "F" "git_fetch_all" ] 199 + [ 200 + cmd "r" "rebase_single" 201 + ; cmd "s" "rebase_with_descendants" 202 + ; cmd "b" "rebase_with_bookmark" 203 + ] 204 + ; sub 205 + "g" 206 + "Git" 207 + [ 208 + cmd "p" "git_push" 209 + ; cmd "f" "git_fetch" 210 + ; cmd "F" "git_fetch_all" 211 + ; cmd "r" "git_remote_menu" 212 + ] 195 213 ; cmd "z" "parallelize" 196 214 ; cmd "a" "abandon" 197 215 ; sub ··· 218 236 ; cmd "P" "move_to_parent" 219 237 ; cmd "a" "abandon" 220 238 ; cmd "c" "commit" 221 - ; sub 222 - "A" 223 - "absorb" 224 - [ 225 - cmd "a" "absorb" 226 - ; cmd "t" "absorb-into" 227 - ] 239 + ; sub "A" "absorb" [ cmd "a" "absorb"; cmd "t" "absorb-into" ] 228 240 ; cmd "u" "undo" 229 241 ] 230 242 }