moved box constructors into PrintBoxs toplevel;

more convenience constructors;
more accurate printing of nested boxs bars
This commit is contained in:
Simon Cruanes 2014-06-15 23:10:51 +02:00
parent ebb8310f84
commit 6ef51a5717
2 changed files with 128 additions and 66 deletions

View file

@ -137,7 +137,8 @@ let rec _find s c i =
else _find s c (i+1)
let rec _lines s i k = match _find s '\n' i with
| None -> ()
| None ->
if i<String.length s then k (String.sub s i (String.length s-i))
| Some j ->
let s' = String.sub s i (j-i) in
k s';
@ -227,37 +228,53 @@ module Box = struct
let _make shape =
{ shape; size=(lazy (_size shape)); }
let line s =
assert (_find s '\n' 0 = None);
_make (Line s)
let text s =
let acc = ref [] in
_lines s 0 (fun x -> acc := x :: !acc);
_make (Text (List.rev !acc))
let lines l =
assert (List.for_all (fun s -> _find s '\n' 0 = None) l);
_make (Text l)
let frame b = _make (Frame b)
let grid ?(framed=true) m =
_make (Grid ((if framed then GridFramed else GridBase), m))
let init_grid ?framed ~line ~col f =
let m = Array.init line (fun j-> Array.init col (fun i -> f ~line:j ~col:i)) in
grid ?framed m
let vlist ?framed l =
let a = Array.of_list l in
grid ?framed (Array.map (fun line -> [| line |]) a)
let hlist ?framed l =
grid ?framed [| Array.of_list l |]
end
let line s =
assert (_find s '\n' 0 = None);
Box._make (Box.Line s)
let text s =
let acc = ref [] in
_lines s 0 (fun x -> acc := x :: !acc);
Box._make (Box.Text (List.rev !acc))
let lines l =
assert (List.for_all (fun s -> _find s '\n' 0 = None) l);
Box._make (Box.Text l)
let int_ x = line (string_of_int x)
let float_ x = line (string_of_float x)
let bool_ x = line (string_of_bool x)
let frame b =
Box._make (Box.Frame b)
let grid ?(framed=true) m =
Box._make (Box.Grid ((if framed then Box.GridFramed else Box.GridBase), m))
let init_grid ?framed ~line ~col f =
let m = Array.init line (fun j-> Array.init col (fun i -> f ~line:j ~col:i)) in
grid ?framed m
let vlist ?framed l =
let a = Array.of_list l in
grid ?framed (Array.map (fun line -> [| line |]) a)
let hlist ?framed l =
grid ?framed [| Array.of_list l |]
let hlist_map ?framed f l = hlist ?framed (List.map f l)
let vlist_map ?framed f l = vlist ?framed (List.map f l)
let grid_map ?framed f m = grid ?framed (Array.map (Array.map f) m)
let grid_text ?framed m = grid_map ?framed text m
let transpose m =
let dim = Box._dim_matrix m in
Array.init dim.x
(fun i -> Array.init dim.y (fun j -> m.(j).(i)))
let _write_vline ~out pos n =
for j=0 to n-1 do
Output.put_char out (_move_y pos j) '|'
@ -269,8 +286,9 @@ let _write_hline ~out pos n =
done
(* render given box on the output, starting with upper left corner
at the given position. *)
let rec _render ~out b pos =
at the given position. [expected_size] is the size of the
available surrounding space *)
let rec _render ?expected_size ~out b pos =
match Box.shape b with
| Box.Line s -> Output.put_string out pos s
| Box.Text l ->
@ -296,22 +314,35 @@ let rec _render ~out b pos =
(* write boxes *)
for j = 0 to dim.y - 1 do
for i = 0 to dim.x - 1 do
let expected_size = {
x=columns.(i+1)-columns.(i);
y=lines.(j+1)-lines.(j);
} in
let pos' = _move pos (columns.(i)) (lines.(j)) in
_render ~out m.(j).(i) pos'
_render ~expected_size ~out m.(j).(i) pos'
done;
done;
let len_hlines, len_vlines = match expected_size with
| None -> columns.(dim.x), lines.(dim.y)
| Some {x;y} -> x,y
in
(* write frame if needed *)
begin match grid_shape with
| Box.GridBase -> ()
| Box.GridFramed ->
let size = Box.size b in
for j=1 to dim.y - 1 do
_write_hline ~out {pos with y=lines.(j)} size.x
_write_hline ~out (_move_y pos (lines.(j)-1)) len_hlines
done;
for i=1 to dim.x - 1 do
_write_vline ~out {pos with x=columns.(i)} size.y
_write_vline ~out (_move_x pos (columns.(i)-1)) len_vlines
done;
for j=1 to dim.y - 1 do
for i=1 to dim.x - 1 do
Output.put_char out (_move pos (columns.(i)-1) (lines.(j)-1)) '+'
done
done
end
let render out b =

View file

@ -24,7 +24,19 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*)
(** {1 Pretty-Printing of Boxes} *)
(** {1 Pretty-Printing of nested Boxes}
Allows to print nested boxes, lists, arrays, tables in a nice way
on any monospaced support.
{[
let b = PrintBox.(
]}
*)
type position = { x:int ; y: int }
(** Positions are relative to the upper-left corner, that is,
@ -62,37 +74,56 @@ module Box : sig
val size : t -> position
(** Size needed to print the box *)
val line : string -> t
(** Make a single-line box.
@raise Invalid_argument if the string contains ['\n'] *)
val text : string -> t
(** Any text, possibly with several lines *)
val lines : string list -> t
(** Shortcut for {!text}, with a list of lines *)
val frame : t -> t
(** Put a single frame around the box *)
val grid : ?framed:bool -> t array array -> t
(** Grid of boxes (no frame between boxes). The matrix is indexed
with lines first, then columns.
@param framed if [true], each item of the grid will be framed.
default value is [true] *)
val init_grid : ?framed:bool ->
line:int -> col:int -> (line:int -> col:int -> t) -> t
(** Same as {!grid} but takes the matrix as a function *)
val vlist : ?framed:bool -> t list -> t
(** Vertical list of boxes *)
val hlist : ?framed:bool -> t list -> t
(** Horizontal list of boxes *)
end
val line : string -> Box.t
(** Make a single-line box.
@raise Invalid_argument if the string contains ['\n'] *)
val text : string -> Box.t
(** Any text, possibly with several lines *)
val lines : string list -> Box.t
(** Shortcut for {!text}, with a list of lines *)
val int_ : int -> Box.t
val bool_ : bool -> Box.t
val float_ : float -> Box.t
val frame : Box.t -> Box.t
(** Put a single frame around the box *)
val grid : ?framed:bool -> Box.t array array -> Box.t
(** Grid of boxes (no frame between boxes). The matrix is indexed
with lines first, then columns. The array must be a proper matrix,
that is, all lines must have the same number of columns!
@param framed if [true], each item of the grid will be framed.
default value is [true] *)
val grid_text : ?framed:bool -> string array array -> Box.t
(** Same as {!grid}, but wraps every cell into a {!text} box *)
val transpose : 'a array array -> 'a array array
(** Transpose a matrix *)
val init_grid : ?framed:bool ->
line:int -> col:int -> (line:int -> col:int -> Box.t) -> Box.t
(** Same as {!grid} but takes the matrix as a function *)
val vlist : ?framed:bool -> Box.t list -> Box.t
(** Vertical list of boxes *)
val hlist : ?framed:bool -> Box.t list -> Box.t
(** Horizontal list of boxes *)
val grid_map : ?framed:bool -> ('a -> Box.t) -> 'a array array -> Box.t
val vlist_map : ?framed:bool -> ('a -> Box.t) -> 'a list -> Box.t
val hlist_map : ?framed:bool -> ('a -> Box.t) -> 'a list -> Box.t
val render : Output.t -> Box.t -> unit
val to_string : Box.t -> string