diff --git a/dune-project b/dune-project index 6007007..bc48013 100644 --- a/dune-project +++ b/dune-project @@ -2,44 +2,7 @@ (name progress) (implicit_transitive_deps false) -(generate_opam_files true) (source (github CraigFe/progress)) (license MIT) (maintainers "Craig Ferguson ") (authors "Craig Ferguson ") - -(package - (name progress) - (synopsis "User-definable progress bars") - (description "\ -A progress bar library for OCaml, featuring a DSL for declaratively specifying -progress bar formats. Supports rendering multiple progress bars simultaneously.\ -") - (documentation https://CraigFe.github.io/progress/) - (depends - (ocaml (>= 4.08.0)) - (terminal (= :version)) - (fmt (>= 0.8.5)) - (logs (>= 0.7.0)) - (mtime (>= 2.0.0)) - (uucp (>= 2.0.0)) - (uutf (>= 1.0.0)) - vector - (optint (>= 0.1.0)) - (alcotest (and :with-test (>= 1.4.0))) - (astring :with-test))) - -(package - (name terminal) - (synopsis "Basic utilities for interacting with terminals") - (description "Basic utilities for interacting with terminals") - (documentation https://CraigFe.github.io/progress/) - (depends - (ocaml (>= 4.03.0)) - (uucp (>= 2.0.0)) - (uutf (>= 1.0.0)) - stdlib-shims - (alcotest (and :with-test (>= 1.4.0))) - (fmt :with-test) - (astring :with-test) - (mtime (and :with-test (>= 2.0.0))))) diff --git a/examples/cargo.ml b/examples/cargo.ml index 4d546fb..1072c77 100644 --- a/examples/cargo.ml +++ b/examples/cargo.ml @@ -1,3 +1,5 @@ +open Utils + let packages = [ ("0install-solver", "2.17") ; ("afl-persistent", "1.3") @@ -31,7 +33,7 @@ let packages = ; ("yojson", "1.7.0") ; ("zarith", "1.9.1") ] - |> Vector.of_list ~dummy:("", "") + |> Dynlist.of_list let setup_logs () = let reporter = Progress.logs_reporter () in @@ -41,7 +43,7 @@ let setup_logs () = let bar = let open Progress.Line in - let total = Vector.length packages in + let total = Dynlist.length packages in list [ constf " %a" Fmt.(styled `Cyan string) "Building" ; using fst @@ -54,9 +56,9 @@ let bar = ] let rec package_worker (active_packages, reporter) = - match Vector.pop packages with - | exception Vector.Empty -> () - | package, version -> + match Dynlist.pop_opt packages with + | None -> () + | Some (package, version) -> active_packages := package :: !active_packages; Logs.app (fun f -> f " %a %s %s" Fmt.(styled `Green string) "Compiling" package version); diff --git a/examples/dune b/examples/dune index e99cc1c..48f883c 100644 --- a/examples/dune +++ b/examples/dune @@ -3,7 +3,7 @@ (modules (:standard \ main)) (libraries progress unix logs logs.fmt logs.threaded fmt fmt.tty mtime - mtime.clock.os vector threads.posix)) + mtime.clock.os threads.posix)) (executable (name main) diff --git a/examples/utils.ml b/examples/utils.ml index e111e26..d5dc28a 100644 --- a/examples/utils.ml +++ b/examples/utils.ml @@ -1,16 +1,17 @@ -let ( .%() ) v i = Vector.get v i -let ( .%()<- ) v i x = Vector.set v i x +module Dynlist = struct + type 'a t = 'a list ref -let shuffle_vector = - let shuffle_subvector rand_int v i j = - for k = j - 1 downto i + 1 do - let l = rand_int (k + 1) in - let tmp = v.%(l) in - v.%(l) <- v.%(k); - v.%(k) <- tmp - done - in - fun v -> shuffle_subvector Random.int v 0 (Vector.length v) + let of_list l = ref l + + let pop_opt l = + match !l with + | [] -> None + | x :: xs -> + l := xs; + Some x + + let length l = List.length !l +end let colors = (* import matplotlib.cm diff --git a/examples/utils.mli b/examples/utils.mli index 8edc6ff..c3fe3a6 100644 --- a/examples/utils.mli +++ b/examples/utils.mli @@ -1,2 +1,9 @@ -val shuffle_vector : _ Vector.t -> unit +module Dynlist : sig + type 'a t + + val of_list : 'a list -> 'a t + val length : 'a t -> int + val pop_opt : 'a t -> 'a option +end + val colour_picker : unit -> unit -> Progress.Color.t diff --git a/progress.opam b/progress.opam index 4e60ada..2527a08 100644 --- a/progress.opam +++ b/progress.opam @@ -1,4 +1,3 @@ -# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "User-definable progress bars" description: """ @@ -13,13 +12,13 @@ bug-reports: "https://github.com/CraigFe/progress/issues" depends: [ "dune" {>= "2.7"} "ocaml" {>= "4.08.0"} + ("ocaml" {>= "5.2.0"} | ("ocaml" {< "5.2.0"} & "vector")) "terminal" {= version} "fmt" {>= "0.8.5"} "logs" {>= "0.7.0"} "mtime" {>= "2.0.0"} "uucp" {>= "2.0.0"} "uutf" {>= "1.0.0"} - "vector" "optint" {>= "0.1.0"} "alcotest" {with-test & >= "1.4.0"} "astring" {with-test} diff --git a/src/progress/engine/dune b/src/progress/engine/dune index b1ff394..8f6de20 100644 --- a/src/progress/engine/dune +++ b/src/progress/engine/dune @@ -1,6 +1,43 @@ +; We use two different implementations of vectors depending on the availability of Dynarray in the standard library (starting OCaml 5.2). +; This reduces dependencies fo users of the newer OCaml versions, and reduces clashes with the ecosystem (https://github.com/craigfe/progress/pull/50). + +; Rules for OCaml 5.2 and later + +(rule + (enabled_if + (>= %{ocaml_version} "5.2")) + (action + (copy# pvector.dynarray.ml pvector.ml))) + +(library + (name pvector) + (public_name progress.vector) + (enabled_if + (>= %{ocaml_version} "5.2")) + (modules pvector)) + +; Rules for OCaml versions after 5.2 + +(rule + (enabled_if + (< %{ocaml_version} "5.2")) + (action + (copy# pvector.vector.ml pvector.ml))) + +(library + (name pvector) + (public_name progress.vector) + (enabled_if + (< %{ocaml_version} "5.2")) + (modules pvector) + (libraries vector)) + +; Main library + (library (name progress_engine) (public_name progress.engine) + (modules :standard \ pvector) (libraries (re_export mtime) (re_export optint) @@ -8,4 +45,4 @@ logs logs.fmt terminal_ansi - vector)) + progress.vector)) diff --git a/src/progress/engine/import.ml b/src/progress/engine/import.ml index 78c460e..24c7773 100644 --- a/src/progress/engine/import.ml +++ b/src/progress/engine/import.ml @@ -12,42 +12,6 @@ module Mtime = struct let span_to_s span = Mtime.Span.to_float_ns span *. 1e-9 end -module Vector = struct - include Vector - - let iter ~f t = iter f t - - let iteri_from ~f i t = - for i = i to length t - 1 do - f i (unsafe_get t i) - done - - let rec find_map_from i t ~f = - if i >= length t then None - else - let a = unsafe_get t i in - match f a with - | Some _ as some -> some - | None -> find_map_from (i + 1) t ~f - - let find_map t ~f = find_map_from 0 t ~f - - let insert t k v = - Vector.push t v (* Dummy insertion to expand *); - for i = Vector.length t - 1 downto k + 1 do - Vector.set t i (Vector.get t (pred i)) - done; - Vector.set t k v - - let remove (type a) (t : a t) k = - for i = k to Vector.length t - 2 do - Vector.set t i (Vector.get t (succ i)) - done; - ignore (Vector.pop t : a) - - let get_exn = get - let get = `shadowed -end (*———————————————————————————————————————————————————————————————————————————— Copyright (c) 2020–2021 Craig Ferguson diff --git a/src/progress/engine/pvector.dynarray.ml b/src/progress/engine/pvector.dynarray.ml new file mode 100644 index 0000000..38ced08 --- /dev/null +++ b/src/progress/engine/pvector.dynarray.ml @@ -0,0 +1,34 @@ +include Dynarray + +let iter ~f t = iter f t + +let iteri_from ~f i t = + for i = i to length t - 1 do + f i (get t i) + done + +let of_list ~dummy:_ l = Dynarray.of_list l + +let rec find_map_from i t ~f = + if i >= length t then None + else + let a = get t i in + match f a with Some _ as some -> some | None -> find_map_from (i + 1) t ~f + +let find_map t ~f = find_map_from 0 t ~f + +let insert t k v = + Dynarray.add_last t v (* Dummy insertion to expand *); + for i = Dynarray.length t - 1 downto k + 1 do + Dynarray.set t i (Dynarray.get t (pred i)) + done; + Dynarray.set t k v + +let remove (type a) (t : a t) k = + for i = k to Dynarray.length t - 2 do + Dynarray.set t i (Dynarray.get t (succ i)) + done; + ignore (Dynarray.pop_last t : a) + +let get_exn = get +let get = `shadowed diff --git a/src/progress/engine/pvector.vector.ml b/src/progress/engine/pvector.vector.ml new file mode 100644 index 0000000..c400a00 --- /dev/null +++ b/src/progress/engine/pvector.vector.ml @@ -0,0 +1,32 @@ +include Vector + +let iter ~f t = iter f t + +let iteri_from ~f i t = + for i = i to length t - 1 do + f i (unsafe_get t i) + done + +let rec find_map_from i t ~f = + if i >= length t then None + else + let a = unsafe_get t i in + match f a with Some _ as some -> some | None -> find_map_from (i + 1) t ~f + +let find_map t ~f = find_map_from 0 t ~f + +let insert t k v = + Vector.push t v (* Dummy insertion to expand *); + for i = Vector.length t - 1 downto k + 1 do + Vector.set t i (Vector.get t (pred i)) + done; + Vector.set t k v + +let remove (type a) (t : a t) k = + for i = k to Vector.length t - 2 do + Vector.set t i (Vector.get t (succ i)) + done; + ignore (Vector.pop t : a) + +let get_exn = get +let get = `shadowed diff --git a/src/progress/engine/renderer.ml b/src/progress/engine/renderer.ml index 45f0a70..adaba6c 100644 --- a/src/progress/engine/renderer.ml +++ b/src/progress/engine/renderer.ml @@ -143,7 +143,7 @@ end = struct { config : Config.t ; uid : Unique_id.t ; bars : (Bar_id.t, some_bar) Hashtbl.t - ; rows : some_bar option Vector.t + ; rows : some_bar option Pvector.t } let config t = t.config @@ -155,11 +155,11 @@ end = struct | None -> None | Some renderer -> Some (E { renderer; latest_width = 0; position = i }) in - Bar_list.mapi bars ~f:{ f } |> Vector.of_list ~dummy:None + Bar_list.mapi bars ~f:{ f } |> Pvector.of_list ~dummy:None in let bar_count = Bar_list.length bars in let bars = Hashtbl.create bar_count in - Vector.iter rows + Pvector.iter rows ~f: (Option.iter (fun (E { renderer; _ } as t) -> Hashtbl.add bars ~key:(Bar_renderer.id renderer) ~data:t)); @@ -190,8 +190,8 @@ end = struct let rerender_all_from_top ~stage ~starting_at ~unconditional ({ config = { ppf; _ }; rows; _ } as t) = - let total_rows = Vector.length rows in - Vector.iteri_from starting_at rows ~f:(fun idx slot -> + let total_rows = Pvector.length rows in + Pvector.iteri_from starting_at rows ~f:(fun idx slot -> let is_last = idx = total_rows - 1 in let () = match slot with @@ -227,7 +227,7 @@ end = struct match data with | `Clean _ -> () | `Dirty data -> - let distance_from_base = Vector.length rows - bar.position - 1 in + let distance_from_base = Pvector.length rows - bar.position - 1 in (* NOTE: we add an initial carriage return to avoid overflowing the line if the user has typed into the terminal between renders. *) @@ -242,13 +242,13 @@ end = struct Hashtbl.remove t.bars uid let add_line ?(above = 0) t renderer = - let position = Vector.length t.rows - above in + let position = Pvector.length t.rows - above in let key = Bar_renderer.id renderer in let bar = E { renderer; latest_width = 0; position } in Hashtbl.add t.bars ~key ~data:bar; - Vector.insert t.rows position (Some bar); - Vector.iteri_from (position + 1) t.rows ~f:(fun i -> function + Pvector.insert t.rows position (Some bar); + Pvector.iteri_from (position + 1) t.rows ~f:(fun i -> function | None -> () | Some (E bar) -> bar.position <- i); @@ -267,7 +267,7 @@ end = struct (* This can either mean that the line has already been finalised, or that this key is unknown. *) match - Vector.find_map t.rows ~f:(function + Pvector.find_map t.rows ~f:(function | None -> None | Some (E bar) as some_bar -> if Bar_id.equal key (Bar_renderer.id bar.renderer) then @@ -278,8 +278,8 @@ end = struct | None -> failwith "No such line in display") in if Hashtbl.mem t.bars key then Hashtbl.remove t.bars key; - Vector.remove t.rows position; - Vector.iteri_from position t.rows ~f:(fun i -> function + Pvector.remove t.rows position; + Pvector.iteri_from position t.rows ~f:(fun i -> function | None -> () | Some (E bar) -> bar.position <- i); @@ -287,7 +287,7 @@ end = struct position for a re-render of the affected suffix of the display. *) Format.pp_print_string t.config.ppf Terminal.Ansi.erase_display_suffix; Terminal.Ansi.move_up t.config.ppf 1; - Terminal.Ansi.move_up t.config.ppf (Vector.length t.rows - position - 1); + Terminal.Ansi.move_up t.config.ppf (Pvector.length t.rows - position - 1); rerender_all_from_top ~stage:`update ~starting_at:position ~unconditional:true t @@ -298,10 +298,10 @@ end = struct let handle_width_change ({ config = { ppf; _ }; rows; _ } as display) new_width = - let row_count = Vector.length rows in + let row_count = Pvector.length rows in let latest_widths = Array.init row_count ~f:(fun i -> - Vector.get_exn rows i + Pvector.get_exn rows i |> Option.fold ~none:0 ~some:(fun (E t) -> t.latest_width)) in let overflows = @@ -319,12 +319,12 @@ end = struct display let tick ({ config = { ppf; _ }; rows; _ } as t) = - Terminal.Ansi.move_up ppf (Vector.length rows - 1); + Terminal.Ansi.move_up ppf (Pvector.length rows - 1); rerender_all_from_top ~stage:`tick ~starting_at:0 ~unconditional:false t let pause { config = { ppf; _ }; rows; _ } = Format.fprintf ppf "%s%!" Terminal.Ansi.erase_line; - for _ = 1 to Vector.length rows - 1 do + for _ = 1 to Pvector.length rows - 1 do Format.fprintf ppf "%a%s%!" Terminal.Ansi.move_up 1 Terminal.Ansi.erase_line done @@ -342,7 +342,7 @@ end = struct let finalise ({ config = { ppf; hide_cursor; persistent; _ }; rows; _ } as display) = - Terminal.Ansi.move_up ppf (Vector.length rows - 1); + Terminal.Ansi.move_up ppf (Pvector.length rows - 1); if persistent then ( rerender_all_from_top ~stage:`finalise ~starting_at:0 ~unconditional:true display; diff --git a/terminal.opam b/terminal.opam index 3e90812..5a02bad 100644 --- a/terminal.opam +++ b/terminal.opam @@ -1,4 +1,3 @@ -# This file is generated by dune, edit dune-project instead opam-version: "2.0" synopsis: "Basic utilities for interacting with terminals" description: "Basic utilities for interacting with terminals"