···11+open Jj_tui.Logging
22+33+module Make (Vars : Global_vars.Vars) = struct
44+ open Lwd_infix
55+ open Vars
66+ open Notty
77+ open Jj_tui
88+ open Nottui
99+ open! Jj_tui.Util
1010+ open Jj_commands.Shared
1111+ open Jj_commands.Make (Vars)
1212+ open Jj_widgets.Make (Vars)
1313+ module Process = Jj_process.Make (Vars)
1414+ open Process
1515+ open Jj_tui.Process_wrappers.Make (Process)
1616+1717+ (* Helper functions from graph_view *)
1818+ let bookmark_select_prompt get_bookmark_list name func =
1919+ Selection_prompt
2020+ ( name
2121+ , (fun () -> get_bookmark_list () |> Lwd.pure)
2222+ , (fun x bookmark_name -> bookmark_name |> Base.String.is_substring ~substring:x)
2323+ , func )
2424+ ;;
2525+2626+ let custom_commit ?(edit = true) msg =
2727+ let rev = Vars.get_hovered_rev () in
2828+ jj [ "describe"; "-r"; rev; "-m"; msg ] |> ignore;
2929+ (jj @@ [ "new"; "--insert-after"; rev ] @ if edit then [] else [ "--no-edit" ])
3030+ |> ignore
3131+ ;;
3232+3333+ (* Define all graph commands *)
3434+ let get_command_registry get_commands =
3535+ [
3636+ {
3737+ id = "show_help"
3838+ ; description = "Show help"
3939+ ; make_cmd =
4040+ (fun () ->
4141+ Fun
4242+ (fun _ ->
4343+ ui_state.show_popup
4444+ $= Some (commands_list_ui ~include_arrows:true (get_commands ()), "Help");
4545+ ui_state.input $= `Mode (fun _ -> `Unhandled)))
4646+ }
4747+ ; {
4848+ id = "prev"
4949+ ; description = "Move the working copy to the previous child"
5050+ ; make_cmd = (fun () -> Cmd [ "prev" ])
5151+ }
5252+ ; {
5353+ id = "new_base"
5454+ ; description = "Make new child commit"
5555+ ; make_cmd = (fun () -> Cmd_with_revs (Active [ "new" ]))
5656+ }
5757+ ; {
5858+ id = "new_no_edit"
5959+ ; description = "Same as 'new', but without editing the new commit"
6060+ ; make_cmd = (fun () -> Cmd_with_revs (Active [ "new"; "--no-edit" ]))
6161+ }
6262+ ; {
6363+ id = "new_inline"
6464+ ; description = "Make a new change and insert it after the selected rev"
6565+ ; make_cmd =
6666+ (fun () ->
6767+ Dynamic
6868+ (fun () -> Cmd ([ "new"; "--insert-after" ] @ Vars.get_active_revs ())))
6969+ }
7070+ ; {
7171+ id = "new_inline_no_edit"
7272+ ; description = "Same as 'new insert', but without editing the new commit"
7373+ ; make_cmd =
7474+ (fun () ->
7575+ Dynamic
7676+ (fun () ->
7777+ Cmd ([ "new"; "--no-edit"; "--insert-after" ] @ Vars.get_active_revs ())))
7878+ }
7979+ ; {
8080+ id = "duplicate"
8181+ ; description = "Duplicate the current selected commits "
8282+ ; make_cmd =
8383+ (fun () -> Dynamic (fun () -> Cmd ([ "duplicate" ] @ Vars.get_active_revs ())))
8484+ }
8585+ ; {
8686+ id = "undo"
8787+ ; description = "Undo the last operation"
8888+ ; make_cmd = (fun () -> Cmd [ "undo" ])
8989+ }
9090+ ; {
9191+ id = "commit_base"
9292+ ; description =
9393+ "Describe this change and start working on a new rev (same as `describe` then \
9494+ `new`)"
9595+ ; make_cmd =
9696+ (fun () ->
9797+ PromptThen ("commit msg", fun msg -> Fun (fun () -> custom_commit msg)))
9898+ }
9999+ ; {
100100+ id = "commit_no_edit"
101101+ ; description = "Same as commit but without editing the new commit"
102102+ ; make_cmd =
103103+ (fun () ->
104104+ PromptThen
105105+ ("commit msg", fun msg -> Fun (fun () -> custom_commit ~edit:false msg)))
106106+ }
107107+ ; {
108108+ id = "split"
109109+ ; description = "Split the current commit interacively"
110110+ ; make_cmd = (fun () -> Dynamic_r (fun rev -> Cmd_I [ "split"; "-r"; rev; "-i" ]))
111111+ }
112112+ ; {
113113+ id = "squash_into_parent"
114114+ ; description = "Squash into parent"
115115+ ; make_cmd =
116116+ (fun () ->
117117+ Fun
118118+ (fun _ ->
119119+ let rev = Vars.get_hovered_rev () in
120120+ let source_msg, dest_msg = get_messages rev (rev ^ "-") in
121121+ let new_msg = [ dest_msg; source_msg ] |> String.concat_non_empty "\n" in
122122+ jj [ "squash"; "--quiet"; "-r"; rev; "-m"; new_msg ] |> ignore))
123123+ }
124124+ ; {
125125+ id = "squash_into_rev"
126126+ ; description = "Squash into any commit"
127127+ ; make_cmd =
128128+ (fun () ->
129129+ PromptThen
130130+ ( "target revision"
131131+ , fun target ->
132132+ Dynamic_r
133133+ (fun rev ->
134134+ let src_msg, dest_msg = get_messages rev target in
135135+ let new_msg =
136136+ [ dest_msg; src_msg ] |> String.concat_non_empty "\n"
137137+ in
138138+ Cmd
139139+ [
140140+ "squash"
141141+ ; "--quiet"
142142+ ; "-m"
143143+ ; new_msg
144144+ ; "--from"
145145+ ; rev
146146+ ; "--into"
147147+ ; target
148148+ ]) ))
149149+ }
150150+ ; {
151151+ id = "squash_unsquash"
152152+ ; description = "Interactivaly unsquash"
153153+ ; make_cmd =
154154+ (fun () -> Dynamic_r (fun rev -> Cmd_I [ "unsquash"; "-r"; rev; "-i" ]))
155155+ }
156156+ ; {
157157+ id = "squash_interactive_parent"
158158+ ; description = "Interactively choose what to squash into parent"
159159+ ; make_cmd = (fun () -> Dynamic_r (fun rev -> Cmd_I [ "squash"; "-r"; rev; "-i" ]))
160160+ }
161161+ ; {
162162+ id = "squash_interactive_rev"
163163+ ; description = "Interactively choose what to squash into a commit"
164164+ ; make_cmd =
165165+ (fun () ->
166166+ Dynamic_r
167167+ (fun rev ->
168168+ Prompt_I ("target revision", [ "squash"; "-i"; "--from"; rev; "--into" ])))
169169+ }
170170+ ; {
171171+ id = "edit"
172172+ ; description = "Edit the selected revision"
173173+ ; make_cmd = (fun () -> Dynamic_r (fun rev -> Cmd [ "edit"; rev ]))
174174+ }
175175+ ; {
176176+ id = "describe"
177177+ ; description = "Describe this revision"
178178+ ; make_cmd =
179179+ (fun () ->
180180+ Dynamic_r (fun rev -> Prompt ("description", [ "describe"; "-r"; rev; "-m" ])))
181181+ }
182182+ ; {
183183+ id = "describe_editor"
184184+ ; description = "Describe this revision using an editor"
185185+ ; make_cmd = (fun () -> Dynamic_r (fun rev -> Cmd_I [ "describe"; "-r"; rev ]))
186186+ }
187187+ ; {
188188+ id = "resolve"
189189+ ; description = "Resolve conflicts at this revision"
190190+ ; make_cmd = (fun () -> Dynamic_r (fun rev -> Cmd_I [ "resolve"; "-r"; rev ]))
191191+ }
192192+ ; {
193193+ id = "rebase_single"
194194+ ; description = "Rebase single revision "
195195+ ; make_cmd =
196196+ (fun () ->
197197+ Dynamic_r
198198+ (fun rev -> Prompt ("Dest rev for " ^ rev, [ "rebase"; "-r"; rev; "-d" ])))
199199+ }
200200+ ; {
201201+ id = "rebase_with_descendants"
202202+ ; description = "Rebase revision and its decendents"
203203+ ; make_cmd =
204204+ (fun () ->
205205+ Dynamic_r
206206+ (fun rev ->
207207+ Prompt
208208+ ( Printf.sprintf "Dest rev for %s and it's decendents" rev
209209+ , [ "rebase"; "-s"; rev; "-d" ] )))
210210+ }
211211+ ; {
212212+ id = "rebase_with_bookmark"
213213+ ; description = "Rebase revision and all other revissions on its bookmark"
214214+ ; make_cmd =
215215+ (fun () ->
216216+ Dynamic_r
217217+ (fun rev ->
218218+ Prompt
219219+ ("Dest rev for bookmark including " ^ rev, [ "rebase"; "-b"; rev; "-d" ])))
220220+ }
221221+ ; {
222222+ id = "git_push"
223223+ ; description = "git push"
224224+ ; make_cmd =
225225+ (fun () ->
226226+ Fun
227227+ (fun _ ->
228228+ let revs = Vars.get_active_revs () in
229229+ let subcmds =
230230+ [
231231+ {
232232+ key = Key.key_of_string_exn "y"
233233+ ; description = "proceed"
234234+ ; cmd =
235235+ Cmd
236236+ ([ "git"; "push"; "--allow-new" ]
237237+ @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
238238+ }
239239+ ; {
240240+ key = Key.key_of_string_exn "n"
241241+ ; description = "exit"
242242+ ; cmd =
243243+ Fun
244244+ (fun _ ->
245245+ ui_state.input $= `Normal;
246246+ ui_state.show_popup $= None)
247247+ }
248248+ ]
249249+ |> List.map (fun x -> x.key, x)
250250+ |> Key_map.Key_Map.of_list
251251+ in
252252+ let log =
253253+ jj_no_log
254254+ ~get_stderr:true
255255+ ([ "git"; "push"; "--allow-new"; "--dry-run" ]
256256+ @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
257257+ |> AnsiReverse.colored_string
258258+ |> Ui.atom
259259+ |> Lwd.pure
260260+ in
261261+ let ui = W.vbox [ log; commands_list_ui subcmds ] in
262262+ ui_state.show_popup $= Some (ui, "Git push will:");
263263+ ui_state.input $= `Mode (command_input ~is_sub:true subcmds)))
264264+ }
265265+ ; {
266266+ id = "git_fetch"
267267+ ; description = "git fetch"
268268+ ; make_cmd = (fun () -> Cmd [ "git"; "fetch" ])
269269+ }
270270+ ; {
271271+ id = "git_fetch_all"
272272+ ; description = "git fetch all remotes"
273273+ ; make_cmd = (fun () -> Cmd [ "git"; "fetch"; "--all-remotes" ])
274274+ }
275275+ ; {
276276+ id = "parallelize"
277277+ ; description =
278278+ "Parallelize commits. Takes 2 commits and makes them have the\n\
279279+ same parent and child. Run `jj parallelize` --help for details"
280280+ ; make_cmd =
281281+ (fun () ->
282282+ PromptThen
283283+ ( "list commits to parallelize"
284284+ , fun x -> Cmd ([ "paralellize" ] @ (x |> String.split_on_char ' ')) ))
285285+ }
286286+ ; {
287287+ id = "abandon"
288288+ ; description = "Abandon this change(removes just this change and rebases parents)"
289289+ ; make_cmd =
290290+ (fun () ->
291291+ Dynamic
292292+ (fun () ->
293293+ let revs = Vars.get_active_revs () in
294294+ Cmd ([ "abandon" ] @ revs)
295295+ |> confirm_prompt
296296+ ("abandon the revisions:\n" ^ (revs |> String.concat "\n"))))
297297+ }
298298+ ; {
299299+ id = "bookmark_create"
300300+ ; description = "Create new bookmark"
301301+ ; make_cmd =
302302+ (fun () ->
303303+ PromptThen
304304+ ( "Bookmark name to create"
305305+ , fun x ->
306306+ Cmd_r
307307+ ([ "bookmark"; "create" ]
308308+ @ [ x |> String.map (fun c -> if c = ' ' then '_' else c) ]) ))
309309+ }
310310+ ; {
311311+ id = "bookmark_delete"
312312+ ; description = "Delete bookmark"
313313+ ; make_cmd =
314314+ (fun () ->
315315+ bookmark_select_prompt
316316+ branches_no_remote
317317+ "Bookmark to delete"
318318+ (fun bookmark ->
319319+ Cmd [ "bookmark"; "delete"; bookmark ]
320320+ |> confirm_prompt
321321+ (Printf.sprintf
322322+ "delete the bookmark: '%s' This will also delete it on the \
323323+ remote next \"git push\"."
324324+ bookmark)))
325325+ }
326326+ ; {
327327+ id = "bookmark_forget"
328328+ ; description = "Forget bookmark"
329329+ ; make_cmd =
330330+ (fun () ->
331331+ bookmark_select_prompt
332332+ branches_no_remote
333333+ "Bookmark to forget"
334334+ (fun bookmark ->
335335+ Cmd [ "bookmark"; "forget"; bookmark ]
336336+ |> confirm_prompt
337337+ (Printf.sprintf
338338+ "forget the bookmark: '%s' . This will not delete it on the \
339339+ remote."
340340+ bookmark)))
341341+ }
342342+ ; {
343343+ id = "bookmark_rename"
344344+ ; description = "Rename bookmark"
345345+ ; make_cmd =
346346+ (fun () ->
347347+ bookmark_select_prompt
348348+ branches_no_remote
349349+ "Select the bookmark to rename (only local/tracked bookmarks are shown)"
350350+ (fun curr_name ->
351351+ Prompt ("New bookmark name", [ "bookmark"; "rename"; curr_name ])))
352352+ }
353353+ ; {
354354+ id = "bookmark_set"
355355+ ; description = "Set bookmark to this change"
356356+ ; make_cmd =
357357+ (fun () ->
358358+ Dynamic_r
359359+ (fun rev ->
360360+ bookmark_select_prompt
361361+ branches_no_remote
362362+ ("Select the bookmark to set to rev: " ^ rev)
363363+ (fun bookmark ->
364364+ Cmd [ "bookmark"; "set"; "-r"; get_hovered_rev (); "-B"; bookmark ])))
365365+ }
366366+ ; {
367367+ id = "bookmark_track"
368368+ ; description = "track given remote bookmark"
369369+ ; make_cmd =
370370+ (fun () ->
371371+ bookmark_select_prompt
372372+ branches_remotes_not_tracked
373373+ "Select the bookmark to begin tracking"
374374+ (fun bookmark -> Cmd [ "bookmark"; "track"; bookmark ]))
375375+ }
376376+ ; {
377377+ id = "bookmark_untrack"
378378+ ; description = "untrack given remote bookmark"
379379+ ; make_cmd =
380380+ (fun () ->
381381+ bookmark_select_prompt
382382+ branches_remotes_tracked
383383+ "Select the bookmark to untrack"
384384+ (fun bookmark -> Cmd [ "bookmark"; "untrack"; bookmark ]))
385385+ }
386386+ ; {
387387+ id = "filter"
388388+ ; description = "Filter using revset"
389389+ ; make_cmd =
390390+ (fun () ->
391391+ PromptThen
392392+ ( "Filter using revset"
393393+ , fun revset ->
394394+ Fun
395395+ (fun () ->
396396+ if revset = ""
397397+ then Vars.ui_state.revset $= None
398398+ else Vars.ui_state.revset $= Some revset) ))
399399+ }
400400+ ; {
401401+ id = "absorb"
402402+ ; description =
403403+ "Absorb: Move changes of each file in this commit into the closest mutable \
404404+ parent that modified that file"
405405+ ; make_cmd = (fun () -> Cmd_r [ "absorb"; "--from"; ])
406406+ }
407407+ ]
408408+ |> List.to_seq
409409+ |> Seq.map (fun x -> x.id, x)
410410+ |> Hashtbl.of_seq
411411+ ;;
412412+end
+14-413
jj_tui/bin/graph_view.ml
···1212 module Process = Jj_process.Make (Vars)
1313 open Process
1414 open Jj_tui.Process_wrappers.Make (Process)
1515-1515+1616+ (* Import graph commands *)
1717+ module GraphCommands = Graph_commands.Make (Vars)
1818+1619 let bookmark_select_prompt get_bookmark_list name func =
1720 Selection_prompt
1821 ( name
···2831 |> ignore
2932 ;;
30333131- let rec make_command_mapping (key_map_base : Key_map.t) : 'acommand list =
3232- let key_map = key_map_base.graph in
3333- [
3434- {
3535- key = key_map.show_help
3636- ; description = "Show help"
3737- ; cmd =
3838- Fun
3939- (fun _ ->
4040- ui_state.show_popup
4141- $= Some
4242- (commands_list_ui ~include_arrows:true (get_command_mapping ()), "Help");
4343- ui_state.input $= `Mode (fun _ -> `Unhandled))
4444- }
4545- ; {
4646- key = key_map.prev
4747- ; description = "Move the working copy to the previous child "
4848- ; cmd = Cmd [ "prev" ]
4949- }
5050- ; {
5151- key = key_map.new_child.menu
5252- ; description = "Make a new change"
5353- ; cmd =
5454- SubCmd
5555- [
5656- {
5757- key = key_map.new_child.base
5858- ; cmd = Cmd_with_revs (Active [ "new" ])
5959- ; description = "Make new child commit"
6060- }
6161- ; {
6262- key = key_map.new_child.no_edit
6363- ; cmd = Cmd_with_revs (Active [ "new"; "--no-edit" ])
6464- ; description = "Same as 'new', but without editing the new commit"
6565- }
6666- ; {
6767- key = key_map.new_child.inline
6868- ; description = "Make a new change and insert it after the selected rev"
6969- ; cmd =
7070- Dynamic
7171- (fun () ->
7272- Cmd ([ "new"; "--insert-after" ] @ Vars.get_active_revs ()))
7373- }
7474- ; {
7575- key = key_map.new_child.inline_no_edit
7676- ; description = "Same as 'new insert', but without editing the new commit"
7777- ; cmd =
7878- Dynamic
7979- (fun () ->
8080- Cmd
8181- ([ "new"; "--no-edit"; "--insert-after" ]
8282- @ Vars.get_active_revs ()))
8383- }
8484- ]
8585- }
8686- ; {
8787- key = key_map.duplicate
8888- ; description = "Duplicate the current selected commits "
8989- ; cmd = Dynamic (fun () -> Cmd ([ "duplicate" ] @ Vars.get_active_revs ()))
9090- }
9191- ; {
9292- key = key_map.undo
9393- ; description = "Undo the last operation"
9494- ; cmd = Cmd [ "undo" ]
9595- }
9696- ; {
9797- key = key_map.commit.menu
9898- ; description = "Commit"
9999- ; cmd =
100100- SubCmd
101101- [
102102- {
103103- key = key_map.commit.base
104104- ; description =
105105- "Describe this change and start working on a new rev (same as \
106106- `describe` then `new`)"
107107- ; cmd =
108108- PromptThen ("commit msg", fun msg -> Fun (fun () -> custom_commit msg))
109109- }
110110- ; {
111111- key = key_map.commit.no_edit
112112- ; description = "Same as commit but without editing the new commit"
113113- ; cmd =
114114- PromptThen
115115- ( "commit msg"
116116- , fun msg -> Fun (fun () -> custom_commit ~edit:false msg) )
117117- }
118118- ]
119119- }
120120- ; {
121121- key = key_map.split
122122- ; description = "Split the current commit interacively"
123123- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "split"; "-r"; rev; "-i" ])
124124- }
125125- ; {
126126- key = key_map.squash.menu
127127- ; description = "Squash/unsquash"
128128- ; cmd =
129129- SubCmd
130130- [
131131- {
132132- key = key_map.squash.into_parent
133133- ; description = "Squash into parent"
134134- ; cmd =
135135- Fun
136136- (fun _ ->
137137- let rev = Vars.get_hovered_rev () in
138138- let source_msg, dest_msg = get_messages rev (rev ^ "-") in
139139- let new_msg =
140140- [ dest_msg; source_msg ] |> String.concat_non_empty "\n"
141141- in
142142- jj [ "squash"; "--quiet"; "-r"; rev; "-m"; new_msg ] |> ignore)
143143- }
144144- ; {
145145- key = key_map.squash.into_rev
146146- ; description = "Squash into any commit"
147147- ; cmd =
148148- PromptThen
149149- ( "target revision"
150150- , fun target ->
151151- Dynamic_r
152152- (fun rev ->
153153- let src_msg, dest_msg = get_messages rev target in
154154- let new_msg =
155155- [ dest_msg; src_msg ] |> String.concat_non_empty "\n"
156156- in
157157- Cmd
158158- [
159159- "squash"
160160- ; "--quiet"
161161- ; "-m"
162162- ; new_msg
163163- ; "--from"
164164- ; rev
165165- ; "--into"
166166- ; target
167167- ]) )
168168- }
169169- ; {
170170- key = key_map.squash.unsquash
171171- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "unsquash"; "-r"; rev; "-i" ])
172172- ; description = "Interactivaly unsquash"
173173- }
174174- ; {
175175- key = key_map.squash.interactive_parent
176176- ; description = "Interactively choose what to squash into parent"
177177- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "squash"; "-r"; rev; "-i" ])
178178- }
179179- ; {
180180- key = key_map.squash.interactive_rev
181181- ; description = "Interactively choose what to squash into a commit"
182182- ; cmd =
183183- Dynamic_r
184184- (fun rev ->
185185- Prompt_I
186186- ("target revision", [ "squash"; "-i"; "--from"; rev; "--into" ]))
187187- }
188188- ]
189189- }
190190- ; {
191191- key = key_map.edit
192192- ; cmd = Dynamic_r (fun rev -> Cmd [ "edit"; rev ])
193193- ; description = "Edit the selected revision"
194194- }
195195- ; {
196196- key = key_map.describe
197197- ; cmd =
198198- Dynamic_r (fun rev -> Prompt ("description", [ "describe"; "-r"; rev; "-m" ]))
199199- ; description = "Describe this revision"
200200- }
201201- ; {
202202- key = key_map.describe_editor
203203- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "describe"; "-r"; rev ])
204204- ; description = "Describe this revision using an editor"
205205- }
206206- ; {
207207- key = key_map.resolve
208208- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "resolve"; "-r"; rev ])
209209- ; description = "Resolve conflicts at this revision"
210210- }
211211- ; {
212212- key = key_map.rebase.menu
213213- ; description = "Rebase revision "
214214- ; cmd =
215215- SubCmd
216216- [
217217- {
218218- key = key_map.rebase.single
219219- ; description = "Rebase single revision "
220220- ; cmd =
221221- Dynamic_r
222222- (fun rev ->
223223- Prompt ("Dest rev for " ^ rev, [ "rebase"; "-r"; rev; "-d" ]))
224224- }
225225- ; {
226226- key = key_map.rebase.with_descendants
227227- ; description = "Rebase revision and its decendents"
228228- ; cmd =
229229- Dynamic_r
230230- (fun rev ->
231231- Prompt
232232- ( Printf.sprintf "Dest rev for %s and it's decendents" rev
233233- , [ "rebase"; "-s"; rev; "-d" ] ))
234234- }
235235- ; {
236236- key = key_map.rebase.with_bookmark
237237- ; description = "Rebase revision and all other revissions on its bookmark"
238238- ; cmd =
239239- Dynamic_r
240240- (fun rev ->
241241- Prompt
242242- ( "Dest rev for bookmark including " ^ rev
243243- , [ "rebase"; "-b"; rev; "-d" ] ))
244244- }
245245- ]
246246- }
247247- ; {
248248- key = key_map.git.menu
249249- ; description = "Git commands"
250250- ; cmd =
251251- SubCmd
252252- [
253253- {
254254- key = key_map.git.push
255255- ; description = "git push"
256256- ; cmd =
257257- Fun
258258- (fun _ ->
259259- let revs = Vars.get_active_revs () in
260260- let subcmds =
261261- [
262262- {
263263- key = key_map_base.confirm
264264- ; description = "proceed"
265265- ; cmd =
266266- Cmd
267267- ([ "git"; "push"; "--allow-new" ]
268268- @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
269269- }
270270- ; {
271271- key = key_map_base.decline
272272- ; description = "exit"
273273- ; cmd =
274274- Fun
275275- (fun _ ->
276276- ui_state.input $= `Normal;
277277- ui_state.show_popup $= None)
278278- }
279279- ]
280280- in
281281- let log =
282282- jj_no_log
283283- ~get_stderr:true
284284- ([ "git"; "push"; "--allow-new"; "--dry-run" ]
285285- @ (revs |> List.concat_map (fun x -> [ "-r"; x ])))
286286- |> AnsiReverse.colored_string
287287- |> Ui.atom
288288- |> Lwd.pure
289289- in
290290- let ui = W.vbox [ log; commands_list_ui subcmds ] in
291291- ui_state.show_popup $= Some (ui, "Git push will:");
292292- ui_state.input $= `Mode (command_input ~is_sub:true subcmds))
293293- }
294294- ; {
295295- key = key_map.git.fetch
296296- ; description = "git fetch"
297297- ; cmd = Cmd [ "git"; "fetch" ]
298298- }
299299- ; {
300300- key = key_map.git.fetch_all
301301- ; description = "git fetch all remotes"
302302- ; cmd = Cmd [ "git"; "fetch"; "--all-remotes" ]
303303- }
304304- ]
305305- }
306306- ; {
307307- key = key_map.parallelize
308308- ; description =
309309- "Parallelize commits. Takes 2 commits and makes them have the\n\
310310- same parent and child. Run `jj parallelize` --help for details"
311311- ; cmd =
312312- PromptThen
313313- ( "list commits to parallelize"
314314- , fun x -> Cmd ([ "paralellize" ] @ (x |> String.split_on_char ' ')) )
315315- }
316316- ; {
317317- key = key_map.abandon
318318- ; description = "Abandon this change(removes just this change and rebases parents)"
319319- ; cmd =
320320- Dynamic
321321- (fun () ->
322322- let revs = Vars.get_active_revs () in
323323- Cmd ([ "abandon" ] @ revs)
324324- |> confirm_prompt ("abandon the revisions:\n" ^ (revs |> String.concat "\n")))
325325- }
326326- ; {
327327- key = key_map.bookmark.menu
328328- ; description = "Bookmark commands"
329329- ; cmd =
330330- SubCmd
331331- [
332332- {
333333- key = key_map.bookmark.create
334334- ; description = "Create new bookmark"
335335- ; cmd =
336336- PromptThen
337337- ( "Bookmark name to create"
338338- , fun x ->
339339- Cmd_r
340340- ([ "bookmark"; "create" ]
341341- @ [ x |> String.map (fun c -> if c = ' ' then '_' else c) ]) )
342342- }
343343- ; {
344344- key = key_map.bookmark.delete
345345- ; description = "Delete bookmark"
346346- ; cmd =
347347- bookmark_select_prompt
348348- branches_no_remote
349349- "Bookmark to delete"
350350- (fun bookmark ->
351351- Cmd [ "bookmark"; "delete"; bookmark ]
352352- |> confirm_prompt
353353- (Printf.sprintf
354354- "delete the bookmark: '%s' This will also delete it on \
355355- the remote next \"git push\"."
356356- bookmark))
357357- }
358358- ; {
359359- key = key_map.bookmark.forget
360360- ; description = "Forget bookmark"
361361- ; cmd =
362362- bookmark_select_prompt
363363- branches_no_remote
364364- "Bookmark to forget"
365365- (fun bookmark ->
366366- Cmd [ "bookmark"; "forget"; bookmark ]
367367- |> confirm_prompt
368368- (Printf.sprintf
369369- "forget the bookmark: '%s' . This will not delete it on \
370370- the remote."
371371- bookmark))
372372- }
373373- ; {
374374- key = key_map.bookmark.rename
375375- ; description = "Rename bookmark"
376376- ; cmd =
377377- bookmark_select_prompt
378378- branches_no_remote
379379- "Select the bookmark to rename (only local/tracked bookmarks are \
380380- shown)"
381381- (fun curr_name ->
382382- Prompt ("New bookmark name", [ "bookmark"; "rename"; curr_name ]))
383383- }
384384- ; {
385385- key = key_map.bookmark.set
386386- ; description = "Set bookmark to this change"
387387- ; cmd =
388388- Dynamic_r
389389- (fun rev ->
390390- bookmark_select_prompt
391391- branches_no_remote
392392- ("Select the bookmark to set to rev: " ^ rev)
393393- (fun bookmark ->
394394- Cmd
395395- [
396396- "bookmark"; "set"; "-r"; get_hovered_rev (); "-B"; bookmark
397397- ]))
398398- }
399399- ; {
400400- key = key_map.bookmark.track
401401- ; description = "track given remote bookmark"
402402- ; cmd =
403403- bookmark_select_prompt
404404- branches_remotes_not_tracked
405405- "Select the bookmark to begin tracking"
406406- (fun bookmark -> Cmd [ "bookmark"; "track"; bookmark ])
407407- }
408408- ; {
409409- key = key_map.bookmark.untrack
410410- ; description = "untrack given remote bookmark"
411411- ; cmd =
412412- bookmark_select_prompt
413413- branches_remotes_tracked
414414- "Select the bookmark to untrack"
415415- (fun bookmark -> Cmd [ "bookmark"; "untrack"; bookmark ])
416416- }
417417- ]
418418- }
419419- ; {
420420- key = key_map.filter
421421- ; description = "Filter using revset"
422422- ; cmd =
423423- PromptThen
424424- ( "Filter using revset"
425425- , fun revset ->
426426- Fun
427427- (fun () ->
428428- if revset = ""
429429- then Vars.ui_state.revset $= None
430430- else Vars.ui_state.revset $= Some revset) )
431431- }
432432- ]
433433-434434- and command_mapping = ref None
435435-436436- and get_command_mapping () =
3434+ (* Remove the hardcoded make_command_mapping function and use the dynamic one *)
3535+ let command_mapping = ref None
3636+3737+ let rec get_command_mapping () =
43738 match !command_mapping with
438438- | Some mapping ->
439439- mapping
3939+ | Some mapping -> mapping
44040 | None ->
441441- let mapping = make_command_mapping (Lwd.peek ui_state.config).key_map in
4141+ let key_map = (Lwd.peek ui_state.config).key_map.graph in
4242+ let registry = GraphCommands.get_command_registry get_command_mapping in
4343+ let mapping = build_command_list key_map registry in
44244 command_mapping := Some mapping;
44345 mapping
44446 ;;
···48183 | `Selectable x ->
48284 let ui =
48385 W.Lists.selectable_item
484484- (
485485- x ^ "\n"
8686+ (x ^ "\n"
48687 (* TODO This won't work if we are on a branch, because that puts the @ further out*)
48788 |> Jj_tui.AnsiReverse.colored_string
48889 |> Ui.atom)
···502103 W.Lists.(Selectable data)
503104 | `Filler x ->
504105 W.Lists.(
505505- Filler ( x |> Jj_tui.AnsiReverse.colored_string |> Ui.atom |> Lwd.pure)))
106106+ Filler (x |> Jj_tui.AnsiReverse.colored_string |> Ui.atom |> Lwd.pure)))
506107 in
507108 items
508109 in
+71-25
jj_tui/bin/jj_commands.ml
···4455open Jj_tui.Logging
66open Jj_tui.Key_map
77-open Jj_tui.Key_map
77+open Jj_tui.Key
88+open Jj_tui
99+810(** Internal to this module. I'm trying this out as a way to avoid .mli files*)
911module Shared = struct
1012 type cmd_args = string list [@@deriving show]
···3840 (** Same as prompt except you can run another command after. Useful if you want multiple prompts *)
3941 | Prompt_I of string * cmd_args
4042 (** Same as prompt but expects the command to be interactive same as [Cmd_I] *)
4141- | SubCmd of 'a command list
4343+ | SubCmd of 'a command Key_Map.t
4244 (** Allows nesting of commands, shows a popup with command options and waits for the user to press the appropriate key*)
4345 | Fun of (unit -> unit)
4446 (** Execute an arbitrary function. Prefer other command types if possible *)
···46484749 (** A command that should be run when it's key is pressed*)
4850 and 'a command = {
4949- key : key
5151+ key : Key.t
5052 ; description : string
5153 ; cmd : 'a command_variant
5254 }
5355 [@@deriving show]
5656+5757+ (* Common type for command definition registry *)
5858+ type 'a command_definition = {
5959+ id : string
6060+ ; description : string
6161+ ; make_cmd : unit -> 'a command_variant
6262+ }
5463end
55645665(** Internal to this module. I'm trying this out as a way to avoid .mli files*)
···99108100109 let rec render_commands ?(indent_level = 0) commands =
101110 commands
102102- |> List.concat_map @@ fun command ->
111111+ |> Key_Map.to_list
112112+ |> List.concat_map @@ fun (key,command) ->
103113 match command with
104114 | {
105115 key
···126136127137 let commands_list_ui ?(include_arrows = false) commands =
128138 let move_command =
129129- render_command_line
130130- ~indent_level:0
131131- ("Arrows" )
132132- "navigation between windows"
139139+ render_command_line ~indent_level:0 "Arrows" "navigation between windows"
133140 in
134141 ((commands |> render_commands) @ if include_arrows then [ move_command ] else [])
135142 |> I.vcat
···248255 and command_input ~is_sub keymap key =
249256 (* Use exceptions so we can break out of the list*)
250257 let input = Lwd.peek ui_state.input in
251251-252258 try
253253- keymap
254254- |> List.iter (fun cmd ->
255255- (*log keys*)
256259 match key with
257257- |`ASCII k,modifiers->
258258-259259- (*log keys*)
260260- [%log info "key: %s"(key_to_string {key=k;modifiers})];
261261- if (`ASCII cmd.key.key,cmd.key.modifiers) = key then
262262- handleCommand cmd.description cmd.cmd;
263263- |_->()
264264- );
265265- `Unhandled
260260+ | `ASCII k, modifiers ->
261261+ let key = { key = k; modifiers } in
262262+ [%log info "key: %s" (key_to_string key)];
263263+ let cmd = keymap|>Key_Map.find_opt key in
264264+ (match cmd with
265265+ | Some cmd ->
266266+ handleCommand cmd.description cmd.cmd;
267267+ `Handled
268268+ | None ->
269269+ `Unhandled)
270270+ | _ ->
271271+ `Unhandled
266272 with
267273 | Handled ->
268274 (*If this is a sub command and we didn't change to some other subcommand we should exit back to normal command operation*)
···272278 handle_jj_error ~cmd ~error;
273279 `Unhandled
274280275275-276281 and command_no_input description cmd =
277282 (* Use exceptions so we can break out of the list*)
278283 try
···296301 include Shared
297302298303 (** A handy command_list that just has this help command for areas that don't have any commands to still show help*)
299299- let rec default_list =
304304+ let rec make_default_list (): string command Key_Map.t=
300305 [
301306 {
302307 key = { key = '?'; modifiers = [] }
···305310 Fun
306311 (fun _ ->
307312 ui_state.show_popup
308308- $= Some (commands_list_ui ~include_arrows:true default_list, "Help");
313313+ $= Some (commands_list_ui ~include_arrows:true (make_default_list ()), "Help");
309314 ui_state.input $= `Mode (fun _ -> `Unhandled))
310315 }
311316 ]
317317+ |> List.to_seq
318318+ |> Seq.map (fun x -> x.key, x)
319319+ |> Key_Map.of_seq
312320 ;;
321321+ let default_list=make_default_list()
313322314323 (**Generate a UI object with all the commands nicely formatted and layed out. Useful for help text*)
315324 let commands_list_ui = commands_list_ui
316325317326 (**`Prompt`:Allows running one command and then running another using the input of the first*)
318327 let confirm_prompt prompt cmd =
319319- SubCmd [ {key=(Lwd.peek Vars.ui_state.config).key_map.confirm; description = "Yes I want to " ^ prompt; cmd } ]
328328+ let key = key_of_string_exn "y" in
329329+330330+ let sub_cmd= Key_Map.of_list [key, { key; description = "Yes I want to " ^ prompt; cmd }] in
331331+ SubCmd sub_cmd
320332 ;;
321333322334 (** Handles raw command mapping without regard for modes or the current intput state. Should be used when setting a new input mode*)
···330342 | `Normal ->
331343 command_input ~is_sub:false command_mapping
332344 ;;
345345+346346+ (* Function to build command list from key_map and a command registry *)
347347+ let build_command_list key_map command_registry =
348348+349349+ (* Process a key_map item *)
350350+ let rec process_key_map_item key item =
351351+ match item with
352352+ | Command { command = id } ->
353353+ (match Hashtbl.find_opt command_registry id with
354354+ | Some cmd_def ->
355355+ Some { key; description = cmd_def.description; cmd = cmd_def.make_cmd () }
356356+ | None ->
357357+ [%log warn "Unknown command ID: %s" id];
358358+ None)
359359+ | Sub_menu { title; subcommands } ->
360360+ (* Process submenu items *)
361361+ let sub_cmds =
362362+ subcommands
363363+ |> Key_Map.to_seq
364364+ |> Seq.filter_map (fun (k, v) ->
365365+ process_key_map_item k v |> Option.map (fun x -> k, x))
366366+ |> Key_Map.of_seq
367367+ in
368368+ Some { key; description = title; cmd = SubCmd sub_cmds }
369369+ in
370370+ (* Process all items in the key_map *)
371371+ key_map
372372+ |> Key_Map.to_seq
373373+ |> Seq.filter_map (fun (k, v) ->
374374+ process_key_map_item k v |> Option.map (fun x -> k, x))
375375+ |> Key_Map.of_seq
376376+ ;;
377377+378378+ (* List.rev !result *)
333379end