package miaou-core
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Miaou core/widgets (no drivers, no SDL)
Install
dune-project
Dependency
Authors
Maintainers
Sources
v0.5.2.tar.gz
md5=60a3b9f181f24572a06a9492532bfdda
sha512=fcc35a275066be2900e6201782faf47503076fa4640f08cf78067835a6f447b74613009e55b2ac799adb7ca46f1bffa261fc5971753f2cc3c6bef327511c7ef6
doc/src/miaou_widgets_display/file_pager.ml.html
Source file file_pager.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173(*****************************************************************************) (* *) (* SPDX-License-Identifier: MIT *) (* Copyright (c) 2025 Nomadic Labs <contact@nomadic-labs.com> *) (* Copyright (c) 2026 Mathias Bourgoin <mathias.bourgoin@atacama.tech> *) (* *) (*****************************************************************************) module Pager = Pager_widget module Fibers = Miaou_helpers.Fiber_runtime type tail_state = { path : string; mutable pos : int; mutable last_check : float; poll_interval_s : float; mutable closed : bool; } type t = { pager : Pager.t; mutable tail : tail_state option; mutable closed : bool; mutable cancel : (unit -> unit) option; notify_render : unit -> unit; } let runtime_error = Error "Eio runtime not initialized; call Miaou_helpers.Fiber_runtime.init inside \ Eio_main.run" let read_all_lines env path = let p = Eio.Path.(env#fs / path) in Eio.Path.with_open_in p @@ fun flow -> let buf = Eio.Buf_read.of_flow ~max_size:(16 * 1024 * 1024) flow in let rec loop acc = match Eio.Buf_read.line buf with | line -> loop (line :: acc) | exception End_of_file -> List.rev acc in loop [] let make_tail env path poll_interval_s = try let st = Unix.stat path in Some { path; pos = st.Unix.st_size; last_check = Eio.Time.now env#clock; poll_interval_s; closed = false; } with _ -> None let close_tail (ts : tail_state) = ts.closed <- true let read_new_lines (ts : tail_state) pager = match try Some (Unix.stat ts.path) with _ -> None with | None -> false | Some st -> ( if st.Unix.st_size < ts.pos then ts.pos <- st.Unix.st_size ; if st.Unix.st_size <= ts.pos then false else try let ic = open_in_bin ts.path in seek_in ic ts.pos ; let rec loop acc = match input_line ic with | line -> loop (line :: acc) | exception End_of_file -> (List.rev acc, pos_in ic) in let lines, new_pos = loop [] in close_in_noerr ic ; ts.pos <- new_pos ; if lines <> [] then ( Pager.append_lines_batched pager lines ; true) else false with _ -> false) let rec tail_loop env (fp : t) (ts : tail_state) = if fp.closed || ts.closed || Fibers.is_shutdown () then close_tail ts else let now = Eio.Time.now env#clock in if now -. ts.last_check >= ts.poll_interval_s then ( ts.last_check <- now ; let read_any = read_new_lines ts fp.pager in Pager.flush_pending_if_needed ~force:true fp.pager ; if read_any then fp.notify_render ()) ; if (not fp.closed) && not (Fibers.is_shutdown ()) then ( Eio.Time.sleep env#clock ts.poll_interval_s ; tail_loop env fp ts) let close (fp : t) = fp.closed <- true ; Option.iter (fun cancel -> cancel ()) fp.cancel ; fp.cancel <- None ; Option.iter (fun (ts : tail_state) -> close_tail ts ; fp.tail <- None ; Pager.stop_streaming fp.pager) fp.tail let start_tail_watcher (fp : t) (ts : tail_state) = let env, sw = Fibers.require_env_and_switch () in let cancel_promise, cancel_resolver = Eio.Promise.create () in let resolved = ref false in fp.cancel <- Some (fun () -> if not !resolved then ( resolved := true ; Eio.Promise.resolve cancel_resolver ())) ; Eio.Switch.on_release sw (fun () -> close fp) ; let tail_worker () = Fun.protect ~finally:(fun () -> fp.closed <- true ; close_tail ts ; fp.tail <- None ; Pager.stop_streaming fp.pager) (fun () -> Eio.Fiber.first (fun () -> tail_loop env fp ts) (fun () -> Eio.Promise.await cancel_promise)) in Eio.Fiber.fork ~sw tail_worker let pager (fp : t) = fp.pager let open_file ?(follow = false) ?notify_render ?(poll_interval = 0.25) ?title path = match Fibers.env_opt () with | None -> runtime_error | Some env -> ( try let lines = read_all_lines env path in let display_title = Option.value title ~default:path in let pager = Pager.open_lines ~title:display_title ?notify_render lines in let notify_cb = match notify_render with | Some f -> f | None -> ( match pager.Pager.notify_render with | Some f -> f | None -> fun () -> ()) in let fp = { pager; tail = None; closed = false; cancel = None; notify_render = notify_cb; } in if follow then ( Pager.start_streaming fp.pager ; fp.pager.Pager.follow <- true ; match make_tail env path poll_interval with | Some ts -> fp.tail <- Some ts ; start_tail_watcher fp ts | None -> ()) ; Ok fp with exn -> Error (Printexc.to_string exn)) [@@@enforce_exempt] (* non-widget module *)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>