···1313 module Process = Jj_process.Make (Vars)
1414 open Process
1515 open Jj_tui.Process_wrappers.Make (Process)
1616-1616+ open Jj_tui.Key_map
1717 (* Helper functions from graph_view *)
1818 let bookmark_select_prompt get_bookmark_list name func =
1919 Selection_prompt
···2222 , (fun x bookmark_name -> bookmark_name |> Base.String.is_substring ~substring:x)
2323 , func )
2424 ;;
2525+ let remote_select_prompt name func =
2626+ Selection_prompt
2727+ ( name
2828+ , (fun () -> get_remotes_selectable () |> Lwd.pure)
2929+ , (fun x remote_name -> remote_name |> Base.String.is_substring ~substring:x)
3030+ , func )
3131+ ;;
25322633 let custom_commit ?(edit = true) msg =
2734 let rev = Vars.get_hovered_rev () in
···2936 (jj @@ [ "new"; "--insert-after"; rev ] @ if edit then [] else [ "--no-edit" ])
3037 |> ignore
3138 ;;
3939+4040+ (**
4141+ A submenu for git commands that are specific to a remote.
4242+ *)
4343+ let make_remote_git_submenu remote : _ Key_Map.t =
4444+ let push_cmd () =
4545+ Dynamic
4646+ (fun () ->
4747+ let revs = Vars.get_active_revs () in
4848+ let rev_args = List.concat_map (fun r -> [ "-r"; r ]) revs in
4949+ let title = Printf.sprintf "Git push (%s) will:" remote in
5050+ let dry_run_cmd =
5151+ [ "git"; "push"; "--allow-new"; "--dry-run"; "--remote"; remote ] @ rev_args
5252+ in
5353+ let real_cmd =
5454+ Cmd ([ "git"; "push"; "--allow-new"; "--remote"; remote ] @ rev_args)
5555+ in
5656+ confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd)
5757+ in
5858+ (* simple fetch – no confirmation needed *)
5959+ let fetch_cmd = Cmd [ "git"; "fetch"; "--remote"; remote ] in
6060+ Key_Map.of_seq
6161+ @@ List.to_seq
6262+ [ ( Key.key_of_string_exn "p"
6363+ , { key = Key.key_of_string_exn "p"
6464+ ; sort_key = 0.
6565+ ; description = Printf.sprintf "git push to %s" remote
6666+ ; cmd = push_cmd ()
6767+ } )
6868+ ; ( Key.key_of_string_exn "f"
6969+ , { key = Key.key_of_string_exn "f"
7070+ ; sort_key = 1.
7171+ ; description = Printf.sprintf "git fetch from %s" remote
7272+ ; cmd = fetch_cmd
7373+ } )
7474+ ]
32753376 (* Define all graph commands *)
3477 let get_command_registry get_commands =
···266309 ; description = "git push"
267310 ; make_cmd =
268311 (fun () ->
269269- Fun
270270- (fun _ ->
312312+ Dynamic
313313+ (fun () ->
271314 let revs = Vars.get_active_revs () in
272272- let subcmds =
273273- [
274274- {
275275- key = Key.key_of_string_exn "y"
276276- ; sort_key = 0.0
277277- ; description = "proceed"
278278- ; cmd =
279279- Cmd
280280- ([ "git"; "push"; "--allow-new" ]
281281- @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
282282- }
283283- ; {
284284- key = Key.key_of_string_exn "n"
285285- ; sort_key = 1.0
286286- ; description = "exit"
287287- ; cmd =
288288- Fun
289289- (fun _ ->
290290- ui_state.input $= `Normal;
291291- show_popup None)
292292- }
293293- ]
294294- |> List.map (fun x -> x.key, x)
295295- |> Key_map.Key_Map.of_list
315315+ let rev_args = revs |> List.concat_map (fun x -> [ "-r"; x ]) in
316316+ let title = "Git push will:" in
317317+ let dry_run_cmd =
318318+ [ "git"; "push"; "--allow-new"; "--dry-run" ] @ rev_args
296319 in
297297- let log =
298298- jj_no_log
299299- ~get_stderr:true
300300- ([ "git"; "push"; "--allow-new"; "--dry-run" ]
301301- @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
302302- |> AnsiReverse.colored_string
303303- |> Ui.atom
304304- |> Lwd.pure
305305- in
306306- let ui = W.vbox [ log; commands_list_ui subcmds ] in
307307- show_popup @@ Some (ui, "Git push will:");
308308- ui_state.input $= `Mode (command_input ~is_sub:true subcmds)))
320320+ let real_cmd = Cmd ([ "git"; "push"; "--allow-new" ] @ rev_args) in
321321+ confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd))
309322 }
310323 ; {
311324 id = "git_fetch"
···318331 ; sorting_key = 25.0
319332 ; description = "git fetch all remotes"
320333 ; make_cmd = (fun () -> Cmd [ "git"; "fetch"; "--all-remotes" ])
334334+ }
335335+ ; {
336336+ id = "git_remote_menu"
337337+ ; sorting_key = 25.5
338338+ ; description = "Select remote, then run git commands"
339339+ ; make_cmd = (fun () ->
340340+ remote_select_prompt
341341+ "Select remote"
342342+ (fun remote -> SubCmd (make_remote_git_submenu remote)))
321343 }
322344 ; {
323345 id = "parallelize"
···462484 parent that modified that file"
463485 ; make_cmd = (fun () -> Dynamic_r (fun r -> Cmd [ "absorb"; "--from"; r ]))
464486 }
487487+465488 ]
466489 |> List.to_seq
467490 |> Seq.map (fun x -> x.id, x)
+37
jj_tui/bin/jj_commands.ml
···343343 SubCmd sub_cmd
344344 ;;
345345346346+(**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*)
347347+ let confirm_dry_run_prompt ~title ~dry_run_cmd ~real_cmd =
348348+ Fun
349349+ (fun _ ->
350350+ let subcmds =
351351+ [ (let key = key_of_string_exn "y" in
352352+ ( key
353353+ , { key
354354+ ; sort_key = 0.
355355+ ; description = "proceed"
356356+ ; cmd = real_cmd
357357+ } ))
358358+ ; (let key = key_of_string_exn "n" in
359359+ ( key
360360+ , { key
361361+ ; sort_key = 1.
362362+ ; description = "exit"
363363+ ; cmd =
364364+ Fun
365365+ (fun _ ->
366366+ ui_state.input $= `Normal;
367367+ show_popup None)
368368+ } ))
369369+ ]
370370+ |> Key_map.Key_Map.of_list
371371+ in
372372+ (*get the output of the dry run command*)
373373+ let log =
374374+ jj_no_log ~get_stderr:true dry_run_cmd
375375+ |> AnsiReverse.colored_string
376376+ |> Ui.atom
377377+ |> Lwd.pure
378378+ in
379379+ show_popup @@ Some (W.vbox [ log; commands_list_ui subcmds ], title);
380380+ ui_state.input $= `Mode (command_input ~is_sub:true subcmds))
381381+ ;;
382382+346383 (** Handles raw command mapping without regard for modes or the current intput state. Should be used when setting a new input mode*)
347384 let command_input = command_input
348385
+29
jj_tui/bin/jj_widgets.ml
···7474 ^ {|"++label("bookmark", name++" @"++remote) ++ if(present, format_ref_targets(self), " (deleted)")++ "\n")|}
7575 ;;
76767777+ let get_remotes () =
7878+ let log = jj_no_log ~snapshot:false [ "git"; "remote"; "list" ] in
7979+ let lines = String.split_on_char '\n' log in
8080+ lines
8181+ |> List.filter_map (fun line ->
8282+ if line |>String.trim|> String.length =0
8383+ then None
8484+ else (
8585+ match Base.String.lsplit2 ~on:' ' line with
8686+ | Some (name, _) -> Some (name, line)
8787+ | None -> Some (line, line)))
8888+ ;;
8989+9090+ let get_remotes_selectable () =
9191+ get_remotes ()
9292+ |> List.map (fun (name, str) ->
9393+ W.Lists.
9494+ {
9595+ data = name
9696+ ; id = name |> String.hash
9797+ ; ui =
9898+ str ^ "\n"
9999+ |> Jj_tui.AnsiReverse.colored_string
100100+ |> Ui.atom
101101+ |> Ui.resize ~w:100 ~h:1 ~mw:100
102102+ |> W.Lists.selectable_item
103103+ })
104104+ ;;
105105+77106 let selection_list ?(focus = Focus.make ()) items =
78107 Focus.request focus;
79108 let selected_var = Lwd.var 0 in
+28-16
jj_tui/lib/key_map.ml
···11module Key_Map = struct
22- include Map.Make ( Key)
22+ include Map.Make (Key)
3344 let pp inner_pp fmt this =
55 Format.fprintf fmt "@[<v>";
···2424 | Sub_menu of sub_menu
2525 | Command of command
26262727-and key_map = key_map_item Key_Map.t[@@deriving show]
2727+and key_map = key_map_item Key_Map.t [@@deriving show]
2828and key_map_update_t = key_map
29293030let ( let* ) = Base.Result.Let_syntax.( >>= )
···118118let key_map_apply_update override og =
119119 Key_Map.merge
120120 (fun k v1 v2 ->
121121- match v1, v2 with
122122- | Some og, Some override -> Some (override)
123123- | Some v, None | None, Some v -> Some v
124124- | None, None -> None)
121121+ match v1, v2 with
122122+ | Some og, Some override ->
123123+ Some override
124124+ | Some v, None | None, Some v ->
125125+ Some v
126126+ | None, None ->
127127+ None)
125128 og
126129 override
127130;;
···171174 ]
172175 ; cmd "y" "duplicate"
173176 ; cmd "u" "undo"
174174- ; sub "c" "Commit" [ cmd "c" "commit_base"; cmd "C" "commit_no_edit"; cmd "D" "describe_editor" ]
177177+ ; sub
178178+ "c"
179179+ "Commit"
180180+ [ cmd "c" "commit_base"; cmd "C" "commit_no_edit"; cmd "D" "describe_editor" ]
175181 ; cmd "S" "split"
176182 ; sub
177183 "s"
···190196 ; sub
191197 "r"
192198 "Rebase"
193193- [ cmd "r" "rebase_single"; cmd "s" "rebase_with_descendants"; cmd "b" "rebase_with_bookmark" ]
194194- ; sub "g" "Git" [ cmd "p" "git_push"; cmd "f" "git_fetch"; cmd "F" "git_fetch_all" ]
199199+ [
200200+ cmd "r" "rebase_single"
201201+ ; cmd "s" "rebase_with_descendants"
202202+ ; cmd "b" "rebase_with_bookmark"
203203+ ]
204204+ ; sub
205205+ "g"
206206+ "Git"
207207+ [
208208+ cmd "p" "git_push"
209209+ ; cmd "f" "git_fetch"
210210+ ; cmd "F" "git_fetch_all"
211211+ ; cmd "r" "git_remote_menu"
212212+ ]
195213 ; cmd "z" "parallelize"
196214 ; cmd "a" "abandon"
197215 ; sub
···218236 ; cmd "P" "move_to_parent"
219237 ; cmd "a" "abandon"
220238 ; cmd "c" "commit"
221221- ; sub
222222- "A"
223223- "absorb"
224224- [
225225- cmd "a" "absorb"
226226- ; cmd "t" "absorb-into"
227227- ]
239239+ ; sub "A" "absorb" [ cmd "a" "absorb"; cmd "t" "absorb-into" ]
228240 ; cmd "u" "undo"
229241 ]
230242 }