mirror of
https://github.com/chshersh/github-tui.git
synced 2024-10-05 14:57:53 +03:00
Move Lines API into a separate module, add dummy Horizontal_fill
This commit is contained in:
parent
9ce06264bd
commit
e7b216cdfb
39
lib/line.ml
Normal file
39
lib/line.ml
Normal file
@ -0,0 +1,39 @@
|
||||
type styles = ANSITerminal.style list
|
||||
|
||||
type chunk = {
|
||||
styles : styles;
|
||||
string : string;
|
||||
}
|
||||
|
||||
let fmt_chunk { styles; string } = ANSITerminal.sprintf styles "%s" string
|
||||
|
||||
let padding_chunk width =
|
||||
let padding = String.make width ' ' in
|
||||
{ styles = []; string = padding }
|
||||
|
||||
type t = {
|
||||
chunks : chunk list;
|
||||
length : int;
|
||||
}
|
||||
|
||||
let length line = line.length
|
||||
|
||||
let of_chunks chunks =
|
||||
let length =
|
||||
List.fold_left
|
||||
(fun acc { string; _ } -> acc + String_extra.graphemes_len string)
|
||||
0 chunks
|
||||
in
|
||||
{ chunks; length }
|
||||
|
||||
let prepend_chunk chunk line =
|
||||
let chunks = chunk :: line.chunks in
|
||||
let length = String_extra.graphemes_len chunk.string + line.length in
|
||||
{ chunks; length }
|
||||
|
||||
let append line1 line2 =
|
||||
let chunks = line1.chunks @ line2.chunks in
|
||||
let length = line1.length + line2.length in
|
||||
{ chunks; length }
|
||||
|
||||
let fmt line = line.chunks |> List.map fmt_chunk |> String.concat ""
|
32
lib/line.mli
Normal file
32
lib/line.mli
Normal file
@ -0,0 +1,32 @@
|
||||
(** Formatting of a string. *)
|
||||
type styles = ANSITerminal.style list
|
||||
|
||||
(** A part of a line with the formatting added to it. *)
|
||||
type chunk = {
|
||||
styles : styles;
|
||||
string : string;
|
||||
}
|
||||
|
||||
(** [padding_chunk n] created a [chunk] without formatting of [n] spaces. *)
|
||||
val padding_chunk : int -> chunk
|
||||
|
||||
(** A type defining a single line of text with different parts ("chunks") having
|
||||
potentially different formatting. *)
|
||||
type t
|
||||
|
||||
(** Returns the length of a string as in the number of graphemes (before
|
||||
formatting applied). *)
|
||||
val length : t -> int
|
||||
|
||||
(** Smart constructor for a line from a list of chunks to calculate the final
|
||||
[length] automatically as well. *)
|
||||
val of_chunks : chunk list -> t
|
||||
|
||||
(** Add a chunk to the beginning of a line. *)
|
||||
val prepend_chunk : chunk -> t -> t
|
||||
|
||||
(** Append two lines into a single line. *)
|
||||
val append : t -> t -> t
|
||||
|
||||
(** Format a single line as string, applying formatting. *)
|
||||
val fmt : t -> string
|
@ -1,9 +1,6 @@
|
||||
(* A simple pretty printing combinator library *)
|
||||
|
||||
type styles = ANSITerminal.style list
|
||||
|
||||
type doc =
|
||||
| Str of styles * string
|
||||
| Str of Line.styles * string
|
||||
| Horizontal_fill
|
||||
| Vertical of doc list
|
||||
| Horizontal of doc list
|
||||
|
||||
@ -11,29 +8,10 @@ let str string = Str ([], string)
|
||||
let fmt styles string = Str (styles, string)
|
||||
let horizontal cols = Horizontal cols
|
||||
let vertical rows = Vertical rows
|
||||
let horizontal_fill = Horizontal_fill
|
||||
|
||||
type chunk = {
|
||||
styles : styles;
|
||||
string : string;
|
||||
}
|
||||
|
||||
let fmt_chunk { styles; string } = ANSITerminal.sprintf styles "%s" string
|
||||
|
||||
let mk_padding_chunk n =
|
||||
let padding = String.make n ' ' in
|
||||
{ styles = []; string = padding }
|
||||
|
||||
type line = { chunks : chunk list }
|
||||
|
||||
let fmt_line line = line.chunks |> List.map fmt_chunk |> String.concat ""
|
||||
|
||||
let line_len line =
|
||||
List.fold_left
|
||||
(fun acc { string; _ } -> acc + String_extra.graphemes_len string)
|
||||
0 line.chunks
|
||||
|
||||
let zip_lines (l : line list) (r : line list) =
|
||||
let max_len_l = List.map line_len l |> List.fold_left max 0 in
|
||||
let zip_lines (l : Line.t list) (r : Line.t list) =
|
||||
let max_len_l = List.map Line.length l |> List.fold_left max 0 in
|
||||
|
||||
let rec zip l r =
|
||||
match (l, r) with
|
||||
@ -41,37 +19,38 @@ let zip_lines (l : line list) (r : line list) =
|
||||
| [], r ->
|
||||
(* Optimisation: Add extra chunk only if padding is needed *)
|
||||
if max_len_l > 0 then
|
||||
let padding_chunk = mk_padding_chunk max_len_l in
|
||||
List.map (fun line -> { chunks = padding_chunk :: line.chunks }) r
|
||||
let padding_chunk = Line.padding_chunk max_len_l in
|
||||
List.map (Line.prepend_chunk padding_chunk) r
|
||||
else r
|
||||
| hd_l :: tl_l, hd_r :: tl_r ->
|
||||
let left_len = line_len hd_l in
|
||||
let left_len = Line.length hd_l in
|
||||
|
||||
(* Optimisation: Combine chunks when left is already max len *)
|
||||
if left_len >= max_len_l then
|
||||
let new_line = { chunks = hd_l.chunks @ hd_r.chunks } in
|
||||
let new_line = Line.append hd_l hd_r in
|
||||
new_line :: zip tl_l tl_r
|
||||
else
|
||||
let padding_chunk = mk_padding_chunk (max_len_l - left_len) in
|
||||
let padding_chunk = Line.padding_chunk (max_len_l - left_len) in
|
||||
let new_line =
|
||||
{ chunks = hd_l.chunks @ [ padding_chunk ] @ hd_r.chunks }
|
||||
Line.append hd_l (Line.append (Line.of_chunks [ padding_chunk ]) hd_r)
|
||||
in
|
||||
new_line :: zip tl_l tl_r
|
||||
in
|
||||
|
||||
zip l r
|
||||
|
||||
let rec render_to_lines = function
|
||||
| Str (styles, string) -> [ { chunks = [ { styles; string } ] } ]
|
||||
| Vertical rows -> List.concat_map render_to_lines rows
|
||||
let rec render_to_lines ~width = function
|
||||
| Str (styles, string) -> [ Line.of_chunks [ { styles; string } ] ]
|
||||
| Horizontal_fill -> [ Line.of_chunks [ Line.padding_chunk width ] ]
|
||||
| Vertical rows -> List.concat_map (render_to_lines ~width) rows
|
||||
| Horizontal cols -> (
|
||||
match cols with
|
||||
| [] -> []
|
||||
(* TODO: This is potentially really slow; optimise *)
|
||||
| hd :: tl ->
|
||||
List.fold_left
|
||||
(fun acc col -> zip_lines acc (render_to_lines col))
|
||||
(render_to_lines hd) tl)
|
||||
(fun acc col -> zip_lines acc (render_to_lines ~width col))
|
||||
(render_to_lines ~width hd) tl)
|
||||
|
||||
let render doc =
|
||||
doc |> render_to_lines |> List.map fmt_line |> String_extra.unlines
|
||||
let render ~width doc =
|
||||
doc |> render_to_lines ~width |> List.map Line.fmt |> String_extra.unlines
|
||||
|
@ -1,14 +1,11 @@
|
||||
(** A type representing a structured document. *)
|
||||
type doc
|
||||
|
||||
(** Formatting of a string. *)
|
||||
type styles = ANSITerminal.style list
|
||||
|
||||
(** Create a single chunk of string without formatting. *)
|
||||
val str : string -> doc
|
||||
|
||||
(** Create a single chunk of string with formatting. *)
|
||||
val fmt : styles -> string -> doc
|
||||
val fmt : Line.styles -> string -> doc
|
||||
|
||||
(** Put all documents in a list horizontally, automatically adding required padding. *)
|
||||
val horizontal : doc list -> doc
|
||||
@ -16,5 +13,19 @@ val horizontal : doc list -> doc
|
||||
(** Put all documents in a list vertically after each other with a line separator. *)
|
||||
val vertical : doc list -> doc
|
||||
|
||||
(** Render the resulting document. *)
|
||||
val render : doc -> string
|
||||
(** Fill all the empty horizontal space with spaces. Useful for alignment.
|
||||
|
||||
Only one [horizontal_fill] is allowed per [horizontal] element. The first one
|
||||
will be used, the remaining ones will be ignored.
|
||||
*)
|
||||
val horizontal_fill : doc
|
||||
|
||||
(** Render the resulting document.
|
||||
|
||||
Parameters:
|
||||
|
||||
* [width]: the max allowed width for the document. Passed recursively and
|
||||
currently only used for [horizontal_fill].
|
||||
|
||||
*)
|
||||
val render : width:int -> doc -> string
|
||||
|
@ -7,11 +7,11 @@ let init ~repo ~root_dir_path : Model.initial_data =
|
||||
exit 1
|
||||
| Fs.Dir (_, files) -> files
|
||||
in
|
||||
let terminal_rows = Option.value (Terminal_size.get_rows ()) ~default:120 in
|
||||
let terminal_cols =
|
||||
let height = Option.value (Terminal_size.get_rows ()) ~default:120 in
|
||||
let width =
|
||||
Option.value (Terminal_size.get_columns ()) ~default:140
|
||||
in
|
||||
{ repo; root_dir_path; files; terminal_rows; terminal_cols }
|
||||
{ repo; root_dir_path; files; width; height }
|
||||
|
||||
let app = Minttea.app ~init:Init.init ~update:Update.update ~view:View.view ()
|
||||
|
||||
|
@ -11,8 +11,8 @@ type tab =
|
||||
| PullRequests
|
||||
|
||||
type t = {
|
||||
terminal_rows : int;
|
||||
terminal_cols : int;
|
||||
width : int;
|
||||
height : int;
|
||||
repo : string;
|
||||
current_tab : tab;
|
||||
code_tab : code_tab;
|
||||
@ -22,14 +22,14 @@ type initial_data = {
|
||||
repo : string;
|
||||
root_dir_path : string;
|
||||
files : Fs.tree array;
|
||||
terminal_rows : int;
|
||||
terminal_cols : int;
|
||||
width : int;
|
||||
height : int;
|
||||
}
|
||||
|
||||
let initial_model { repo; root_dir_path; files; terminal_rows; terminal_cols } =
|
||||
let initial_model { repo; root_dir_path; files; width; height } =
|
||||
{
|
||||
terminal_rows;
|
||||
terminal_cols;
|
||||
width;
|
||||
height;
|
||||
repo;
|
||||
current_tab = Code;
|
||||
code_tab = { root_dir_path; fs = Fs.zip_it files };
|
||||
|
@ -4,7 +4,7 @@ let style_directory = ANSITerminal.[ Bold; magenta ]
|
||||
|
||||
let debug_section (model : Model.t) =
|
||||
let debug_info =
|
||||
Printf.sprintf "%dw x %dh" model.terminal_cols model.terminal_rows
|
||||
Printf.sprintf "%dw x %dh" model.width model.height
|
||||
in
|
||||
Pretty.str debug_info
|
||||
|
||||
@ -42,4 +42,4 @@ let to_doc (model : Model.t) =
|
||||
|
||||
vertical [ horizontal [ repo; str " "; debug ]; empty; tabs; content; empty ]
|
||||
|
||||
let view (model : Model.t) = model |> to_doc |> Pretty.render
|
||||
let view (model : Model.t) = model |> to_doc |> Pretty.render ~width:model.width
|
||||
|
Loading…
Reference in New Issue
Block a user