···3434 "squash"
3535 ; "-u"
3636 ; "--from"
3737- ; Lwd.peek ui_state.selected_revision
3737+ ; get_selected_rev()
3838 ; "--into"
3939 ; rev
4040 ; Lwd.peek selected_file
···7373 (**Get the status for the currently selected file*)
7474 let file_status () =
7575 let$ selected = Lwd.get selected_file
7676- and$ rev = Lwd.get Vars.ui_state.selected_revision in
7676+ and$ rev = Vars.get_selected_rev_lwd()in
7777 if selected != "" then jj_no_log [ "diff"; "-r"; rev; selected ] else ""
7878 ;;
7979end
+32-32
jj_tui/bin/global_funcs.ml
···3636(**Updates the status windows; Without snapshotting the working copy by default
3737 This should be called after any command that performs a change *)
3838let update_status ?(update_graph = true) ?(cause_snapshot = false) () =
3939- let rev = Lwd.peek Vars.ui_state.selected_revision in
4040- let log_res =
4141- jj_no_log ~snapshot:cause_snapshot [ "log" ]
4242- |> colored_string
4343- in
4444- if update_graph then Vars.ui_state.trigger_update $= ();
3939+ safe_jj (fun () ->
4040+ let rev = Lwd.peek Vars.ui_state.selected_revision in
4141+ let log_res = jj_no_log ~snapshot:cause_snapshot [ "log" ] |> colored_string in
4242+ if update_graph then Vars.ui_state.trigger_update $= ())
4543;;
4444+4645(**Updates the status windows; Without snapshotting the working copy by default
4746 This should be called after any command that performs a change *)
4848-let update_views ?(cause_snapshot = false) () =
4949- let rev = Lwd.peek Vars.ui_state.selected_revision in
5050- Eio.Switch.run @@ fun sw ->
5151- let log_res =
5252- jj_no_log ~snapshot:cause_snapshot [ "show"; "-s"; "--color-words"; "-r"; rev ]
5353- |> colored_string
5454- in
5555- (* From now on we use ignore-working-copy so we don't re-snapshot the state and so
5656- we can operate in paralell *)
5757- let tree =
5858- Eio.Fiber.fork_promise ~sw (fun _ ->
5959- jj_no_log ~snapshot:false [ "log"; "-r"; rev ] |> colored_string)
6060- (* TODO: stop using dop last twice *)
6161- and branches =
6262- Eio.Fiber.fork_promise ~sw (fun _ ->
6363- jj_no_log ~snapshot:false [ "branch"; "list"; "-a" ] |> colored_string)
6464- and files_list = Eio.Fiber.fork_promise ~sw (fun _ -> list_files ~rev ()) in
6565- (*wait for all our tasks*)
6666- let tree = Eio.Promise.await_exn tree
6767- and files_list = Eio.Promise.await_exn files_list
6868- and branches = Eio.Promise.await_exn branches in
6969- (*now we can assign our results*)
7070- Vars.ui_state.jj_show $= log_res;
7171- Vars.ui_state.jj_branches $= branches;
7272- Vars.ui_state.jj_tree $= tree;
7373- Vars.ui_state.jj_change_files $= files_list
4747+let update_views ?(cause_snapshot = false) () =
4848+ safe_jj (fun () ->
4949+ let rev = Vars.get_selected_rev() in
5050+ Eio.Switch.run @@ fun sw ->
5151+ let log_res =
5252+ jj_no_log ~snapshot:cause_snapshot [ "show"; "-s"; "--color-words"; "-r"; rev ]
5353+ |> colored_string
5454+ in
5555+ (* From now on we use ignore-working-copy so we don't re-snapshot the state and so
5656+ we can operate in paralell *)
5757+ let tree =
5858+ Eio.Fiber.fork_promise ~sw (fun _ ->
5959+ jj_no_log ~snapshot:false [ "log"; "-r"; rev ] |> colored_string)
6060+ (* TODO: stop using dop last twice *)
6161+ and branches =
6262+ Eio.Fiber.fork_promise ~sw (fun _ ->
6363+ jj_no_log ~snapshot:false [ "branch"; "list"; "-a" ] |> colored_string)
6464+ and files_list = Eio.Fiber.fork_promise ~sw (fun _ -> list_files ~rev ()) in
6565+ (*wait for all our tasks*)
6666+ let tree = Eio.Promise.await_exn tree
6767+ and files_list = Eio.Promise.await_exn files_list
6868+ and branches = Eio.Promise.await_exn branches in
6969+ (*now we can assign our results*)
7070+ Vars.ui_state.jj_show $= log_res;
7171+ Vars.ui_state.jj_branches $= branches;
7272+ Vars.ui_state.jj_tree $= tree;
7373+ Vars.ui_state.jj_change_files $= files_list)
7474;;
+37-2
jj_tui/bin/global_vars.ml
···11open Notty
22open Nottui
33open Eio.Std
44+open Lwd_infix
4556type cmd_args = string list
77+88+type rev_id = {
99+ change_id : string
1010+ ; commit_id : string
1111+}
1212+1313+type 'a maybe_unique =
1414+ | Unique of 'a
1515+ | Duplicate of 'a
616717type ui_state_t = {
818 view :
···2232 ; jj_show : I.t Lwd.var
2333 ; jj_branches : I.t Lwd.var
2434 ; jj_change_files : (string * string) list Lwd.var
2525- ; selected_revision : string Lwd.var
3535+ ; selected_revision : rev_id maybe_unique Lwd.var
2636 ; trigger_update : unit Lwd.var
2737}
2838···4454 val ui_state : ui_state_t
4555 val update_ui_state : (ui_state_t -> unit) -> unit
4656 val render_mutex : Eio.Mutex.t
5757+5858+ (**returns either a change_id or if their are change_id conflicts, a commit_id *)
5959+ val get_selected_rev : unit -> string
6060+6161+ (**returns either a change_id or if their are change_id conflicts, a commit_id *)
6262+ val get_selected_rev_lwd : unit -> string Lwd.t
4763end
48644965module Vars : Vars = struct
···6480 ; jj_show = Lwd.var I.empty
6581 ; jj_branches = Lwd.var I.empty
6682 ; jj_change_files = Lwd.var []
6767- ; selected_revision = Lwd.var "@"
8383+ ; selected_revision = Lwd.var (Unique { change_id = "@"; commit_id = "@" })
6884 ; input = Lwd.var `Normal
6985 ; show_popup = Lwd.var None
7086 ; show_prompt = Lwd.var None
···92108 let get_eio_env () = (Option.get !eio).env
93109 let get_eio_vars () = Option.get !eio
94110 let get_term () = Option.get !term
111111+112112+ (**Gets an id for the selected revision. If the change_id is unique we use that, if it's not we return a commit_id instead*)
113113+ let get_selected_rev () =
114114+ match Lwd.peek ui_state.selected_revision with
115115+ | Unique { change_id; _ } ->
116116+ change_id
117117+ | Duplicate { commit_id; _ } ->
118118+ commit_id
119119+ ;;
120120+121121+ (**see [get_selected_rev]*)
122122+ let get_selected_rev_lwd () =
123123+ let$ a = Lwd.get ui_state.selected_revision in
124124+ match a with
125125+ | Unique { change_id; _ } ->
126126+ change_id
127127+ | Duplicate { commit_id; _ } ->
128128+ commit_id
129129+ ;;
95130end
+8-8
jj_tui/bin/graph_view.ml
···5656 SubCmd
5757 [
5858 {
5959- key = 'S'
6060- ; cmd = Dynamic_r (fun rev -> Cmd_I [ "unsquash"; "-r"; rev; "-i" ])
6161- ; description = "Interactivaly unsquash"
6262- }
6363- ; {
6459 key = 's'
6560 ; description = "Squash into parent"
6661 ; cmd =
···6863 (fun _ ->
6964 let curr_msg, prev_msg = get_messages () in
7065 let new_msg = prev_msg ^ curr_msg in
7171- let rev = Lwd.peek Vars.ui_state.selected_revision in
6666+ let rev = Vars.get_selected_rev () in
7267 jj [ "squash"; "--quiet"; "-r"; rev; "-m"; new_msg ] |> ignore)
7368 }
7469 ; {
···8075 , fun str ->
8176 let curr_msg, prev_msg = get_messages () in
8277 let new_msg = prev_msg ^ curr_msg in
8383- Cmd_r [ "squash"; "--quiet"; "-m"; new_msg; "--into"; str ] )
7878+ Dynamic_r(fun rev->Cmd [ "squash"; "--quiet"; "-m"; new_msg;"--from";rev; "--into"; str ] ))
7979+ }
8080+ ; {
8181+ key = 'u'
8282+ ; cmd = Dynamic_r (fun rev -> Cmd_I [ "unsquash"; "-r"; rev; "-i" ])
8383+ ; description = "Interactivaly unsquash"
8484 }
8585 ; {
8686 key = 'i'
···239239 let ui =
240240 let$ graph, rev_ids =
241241 (*TODO I think this ads a slight delay to everything becasue it makes things need to be renedered twice. maybe I could try getting rid of it*)
242242- Vars.ui_state.trigger_update |> Lwd.get |> Lwd.map ~f:(fun _ -> seperate_revs ())
242242+ Vars.ui_state.trigger_update |> Lwd.get |> Lwd.map ~f:(fun _ -> graph_and_revs ())
243243 in
244244 let selectable_idx = ref 0 in
245245 graph
+6-20
jj_tui/bin/jj_commands.ml
···4747 open! Jj_tui.Util
4848 module Wd = Jj_tui.Widgets
49495050+5051 exception Handled
51525253 let render_command_line ~indent_level key desc =
···7576 :: render_commands ~indent_level:(indent_level + 1) subs
7677 ;;
77787878- (*handle exception from jj*)
7979- let handle_jj_error error =
8080- ui_state.show_prompt $= None;
8181- ui_state.show_popup
8282- $= Some
8383- ( error
8484- |> Jj_tui.AnsiReverse.colored_string
8585- |> Ui.atom
8686- |> Ui.resize ~sw:1 ~sh:1
8787- |> Lwd.pure
8888- , "An error occured running that command" );
8989- ui_state.input $= `Mode (fun _ -> `Unhandled)
9090- ;;
91799292- (*catch any exceptions from jj*)
9393- let safe_jj f = try f () with JJError error -> handle_jj_error error
94809581 let commands_list_ui commands =
9682 let move_command =
···146132 raise Handled
147133 | Cmd_r args ->
148134 ui_state.show_popup $= None;
149149- noOut (args@["-r";Lwd.peek ui_state.selected_revision]);
135135+ noOut (args@["-r";Vars.get_selected_rev()]);
150136 raise Handled
151137 | Prompt (str, args) ->
152138 ui_state.show_popup $= None;
···154140 raise Handled
155141 | Prompt_r (str, args) ->
156142 ui_state.show_popup $= None;
157157- prompt str (`Cmd (args@["-r";Lwd.peek ui_state.selected_revision]));
143143+ prompt str (`Cmd (args@["-r";Vars.get_selected_rev()]));
158144 raise Handled
159145 | PromptThen (label, next) ->
160146 ui_state.show_popup $= None;
···177163 | Dynamic f ->
178164 f () |> handleCommand description
179165 | Dynamic_r f ->
180180- f (Lwd.peek Vars.ui_state.selected_revision) |> handleCommand description
166166+ f (Vars.get_selected_rev()) |> handleCommand description
181167182168 (** Try mapching the command mapping to the provided key and run the command if it matches *)
183169 and command_input ~is_sub keymap key =
···191177 | Handled ->
192178 if is_sub then ui_state.input $= `Normal;
193179 `Handled
194194- | JJError error ->
195195- handle_jj_error error;
180180+ | JJError (cmd,error) ->
181181+ handle_jj_error cmd error;
196182 `Unhandled
197183198184 and command_no_input description cmd =
+23-4
jj_tui/bin/jj_process.ml
···6060 Mutex.lock access_lock;
6161 true)
6262 else false
6363- in
6363+ in
6464 let res =
6565 cmdArgs
6666 "jj"
···7575 res
7676 ;;
77777878- exception JJError of string
7878+ exception JJError of string*string
7979+7980 (** Run a jj command without outputting to the command_log.
8081 @param ?snapshot=true
8182 When true snapshots the state when running the command and also aquires a lock before running it. Set to false for commands you wish to run concurrently. like those for generating content in the UI
···8687 | Ok a ->
8788 a
8889 | Error (`BadExit (code, str)) ->
8989- raise (JJError (Printf.sprintf "Exited with code %i; Message:\n%s" code str))
9090+ raise (JJError( "jj"::args|>String.concat " " ,Printf.sprintf "Exited with code %i; Message:\n%s" code str))
9091 | Error (`EioErr a) ->
9191- raise (JJError (Printf.sprintf
9292+ raise (JJError ("jj"::args|>String.concat " ",Printf.sprintf
9293 "Error running jj process:\n%a"
9394 (fun _ -> Base.Error.to_string_hum)
9495 a))
···119120 let current, prev = output |>Jj_tui.OutputParsing.parse_descriptions|>Result.get_ok in
120121 current |> String.concat "", prev |> String.concat ""
121122 ;;
123123+ open Vars
124124+ open Nottui
125125+ open Lwd_infix
126126+ (*handle exception from jj*)
127127+ let handle_jj_error cmd error =
128128+ ui_state.show_prompt $= None;
129129+ ui_state.show_popup
130130+ $= Some
131131+ ( error
132132+ |> Jj_tui.AnsiReverse.colored_string
133133+ |> Ui.atom
134134+ |> Ui.resize ~sw:1 ~sh:1
135135+ |> Lwd.pure
136136+ , Printf.sprintf"An error occured running %s" cmd );
137137+ ui_state.input $= `Mode (fun _ -> `Unhandled)
138138+ ;;
139139+ (*catch any exceptions from jj*)
140140+ let safe_jj f = try f () with JJError (cmd,error) -> handle_jj_error cmd error
122141end
+40-15
jj_tui/bin/jj_widgets.ml
···10101111module Make (Vars : Global_vars.Vars) = struct
1212 open Vars
1313+ open Global_vars
1314 open Jj_process.Make (Vars)
14151516 exception FoundStart
···6263 (*chars like these: ├─╮*)
6364 let is_pipe = i > 0x2500 && i < 0x259f in
6465 let is_whitespace = is_whitespace_char i in
6565- is_pipe || is_whitespace
6666+ is_pipe || is_whitespace
6667 ;;
67686869 let test_data =
···134135 |> String.iteri (fun i char ->
135136 let uchar = String.get_utf_8_uchar line i |> Uchar.utf_decode_uchar in
136137 (*I've removed the part that tries to precisely skip all the start chars. this is becasue it gets all stuffed up by the terminal escape codes
137137- FIXME currently this will get stuffed up if a line has that rev symbol in it
138138+ FIXME currently this will get stuffed up if a line has that rev symbol in it
138139 *)
139140140141 (* if not (uchar |> is_graph_start_char) *)
141142 (* then *)
142142- if uchar |> Uchar.equal rev_symbol || char == '@'
143143- then raise FoundStart
144144- (* else raise FoundFiller *)
145145- (* else () *)
146146- )
143143+ if uchar |> Uchar.equal rev_symbol || char == '@' then raise FoundStart
144144+ (* else raise FoundFiller *)
145145+ (* else () *))
146146+ ;;
147147+148148+(** Function to tag duplicated items in a list *)
149149+let tag_duplicates lst =
150150+ (* Create a frequency map to count occurrences of each element *)
151151+ let freq_map =
152152+ List.fold_left (fun acc {change_id;_} ->
153153+ let count = try List.assoc change_id acc with Not_found -> 0 in
154154+ (change_id, count + 1) :: List.remove_assoc change_id acc
155155+ ) [] lst
156156+ in
157157+ (* Tag each item in the list based on the frequency map *)
158158+ List.map (fun ({change_id;_} as x) ->
159159+ if List.assoc change_id freq_map > 1 then Duplicate x else Unique x
160160+ ) lst
161161+162162+ (**Returns a list of revs with both the change_id and commit_id*)
163163+ let get_revs () =
164164+ jj_no_log
165165+ ~color:false
166166+ [ "log"; "-T"; {|"|"++change_id++"|"++commit_id++"\n"|} ]
167167+ |> String.split_on_char '\n'
168168+ |> List.filter_map (fun x ->
169169+ let items = x |> String.split_on_char '|' in
170170+ match items with
171171+ | [ _graph; change_id; commit_id ] ->
172172+ Some { change_id; commit_id }
173173+ | _ ->
174174+ None)
175175+ |>tag_duplicates
176176+ |> Array.of_list
147177 ;;
148178149149- let seperate_revs () =
179179+ (** returns the graph and a list of revs within that graph*)
180180+ let graph_and_revs () =
150181 let graph =
151182 jj_no_log [ "log" ]
152183 |> String.split_on_char '\n'
···171202 |> List.rev
172203 |> Array.of_list
173204 in
174174- let revs =
175175- jj_no_log ~color:false [ "log"; "--no-graph"; "-T"; {|change_id++"\n"|} ]
176176- |> String.split_on_char '\n'
177177- |> List.filter (fun x -> x |> String.trim <> "")
178178- |> Array.of_list
179179- in
205205+ let revs = get_revs () in
180206 graph, revs
181207 ;;
182208···220246 string)
221247 else string
222248 ;;
223223-224249225250 (* (*TODO:make a custom widget the renders the commit with and without selection. *)
226251 (* with selection replace the dot with a blue version and slightly blue tint the background *) *)