Skip to content

Commit ba08891

Browse files
committed
tui: add more features
We add more features to Dune's TUI. Using Lwd and Nottui we expand on the Notty TUI by creating an interactive UI for dune_console. - The user can now view all the messages and scroll in the console. - Each message can be minimized or expanded by clicking. - There is a help screen showing which buttons can be pressed. - The build status is printed in a status bar at the top. - And more. Signed-off-by: Ali Caglayan <alizter@gmail.com>
1 parent c50f637 commit ba08891

File tree

16 files changed

+770
-137
lines changed

16 files changed

+770
-137
lines changed

boot/libs.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,11 @@ let local_libraries =
7474
None)
7575
; ("src/dune_vcs", Some "Dune_vcs", false, None)
7676
; ("src/dune_threaded_console", Some "Dune_threaded_console", false, None)
77+
; ("vendor/lwd/lwd", None, false, None)
7778
; ("vendor/notty/src", None, true, None)
7879
; ("vendor/notty/src-unix", None, true, None)
79-
; ("src/dune_tui", Some "Dune_tui", false, None)
80+
; ("vendor/lwd/nottui", None, false, None)
81+
; ("src/dune_tui", Some "Dune_tui", true, None)
8082
; ("src/dune_config_file", Some "Dune_config_file", false, None)
8183
; ("src/dune_shared_cache", Some "Dune_shared_cache", false, None)
8284
; ("src/scheme", Some "Scheme", false, None)

src/dune_tui/drawing/box.ml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
open Import
2+
3+
let border_box ~attr image =
4+
let w, h = I.(width image, height image) in
5+
let border_element ?(width = 1) ?(height = 1) uchar valign halign =
6+
I.uchar attr uchar width height
7+
|> I.vsnap ~align:valign (h + 2)
8+
|> I.hsnap ~align:halign (w + 2)
9+
in
10+
I.zcat
11+
[ border_element Unicode.box_drawings_double_down_and_right `Top `Left
12+
; border_element Unicode.box_drawings_double_down_and_left `Top `Right
13+
; border_element Unicode.box_drawings_double_up_and_right `Bottom `Left
14+
; border_element Unicode.box_drawings_double_up_and_left `Bottom `Right
15+
; border_element Unicode.box_drawings_double_horizontal ~width:w `Top `Middle
16+
; border_element Unicode.box_drawings_double_horizontal ~width:w `Bottom `Middle
17+
; border_element Unicode.box_drawings_double_vertical ~height:h `Middle `Left
18+
; border_element Unicode.box_drawings_double_vertical ~height:h `Middle `Right
19+
; I.pad ~l:1 ~t:1 ~r:1 ~b:1 image
20+
; I.char A.empty ' ' (w + 2) (h + 2)
21+
]
22+
;;
23+
24+
let with_title ~attr ~title ~title_attr image =
25+
let title =
26+
[ I.uchar attr Unicode.box_drawings_vertical_single_and_left_double 1 1
27+
; I.string title_attr (" " ^ title ^ " ")
28+
; I.uchar attr Unicode.box_drawings_vertical_single_and_right_double 1 1
29+
]
30+
|> I.hcat
31+
|> I.hsnap ~align:`Middle (I.width image + 2)
32+
|> I.vsnap ~align:`Top (I.height image + 2)
33+
in
34+
I.(title </> border_box ~attr image)
35+
;;

src/dune_tui/drawing/box.mli

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
open Import
2+
3+
(** [Notty.image] utilities for drawing boxes. *)
4+
5+
(** [with_title ~attr ~title ~title_attr img] draws a bordered box around the [img] which
6+
has [attr] as a style. It also has a [title] at the top with [title_attr].
7+
8+
The box is drawn straight over the top of the image, so make sure to [I.pad] the
9+
outside. *)
10+
val with_title : attr:A.t -> title:string -> title_attr:A.t -> I.t -> I.t

src/dune_tui/drawing/unicode.ml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(** Here are some commonly used unicode constants. *)
2+
3+
(** ᚛ U+169B *)
4+
let ogham_feather_mark = Uchar.of_int 0x169B
5+
6+
(** ᚜ U+169C *)
7+
let ogham_reversed_feather_mark = Uchar.of_int 0x169C
8+
9+
(** ― U+2015 *)
10+
let horizontal_bar = Uchar.of_int 0x2015
11+
12+
(** ═ U+2550 *)
13+
let box_drawings_double_horizontal = Uchar.of_int 0x2550
14+
15+
(** ║ U+2551 *)
16+
let box_drawings_double_vertical = Uchar.of_int 0x2551
17+
18+
(** ╔ U+2554 *)
19+
let box_drawings_double_down_and_right = Uchar.of_int 0x2554
20+
21+
(** ╗ U+2557 *)
22+
let box_drawings_double_down_and_left = Uchar.of_int 0x2557
23+
24+
(** ╚ U+255A *)
25+
let box_drawings_double_up_and_right = Uchar.of_int 0x255A
26+
27+
(** ╝ U+255D *)
28+
let box_drawings_double_up_and_left = Uchar.of_int 0x255D
29+
30+
(** ╞ U+255E *)
31+
let box_drawings_vertical_single_and_right_double = Uchar.of_int 0x255E
32+
33+
(** ╡ U+2561 *)
34+
let box_drawings_vertical_single_and_left_double = Uchar.of_int 0x2561
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
open Import
2+
3+
let attr_of_ansi_color_rgb8 (c : Ansi_color.RGB8.t) =
4+
match Ansi_color.RGB8.to_int c with
5+
| 0 -> A.black
6+
| 1 -> A.red
7+
| 2 -> A.green
8+
| 3 -> A.yellow
9+
| 4 -> A.blue
10+
| 5 -> A.magenta
11+
| 6 -> A.cyan
12+
| 7 -> A.white
13+
| 8 -> A.lightblack
14+
| 9 -> A.lightred
15+
| 10 -> A.lightgreen
16+
| 11 -> A.lightyellow
17+
| 12 -> A.lightblue
18+
| 13 -> A.lightmagenta
19+
| 14 -> A.lightcyan
20+
| 15 -> A.lightwhite
21+
| i when i <= 231 ->
22+
let i = i - 16 in
23+
let r = i / 36 in
24+
let g = i / 6 mod 6 in
25+
let b = i mod 6 in
26+
A.rgb ~r ~g ~b
27+
| i when i <= 255 -> A.gray (i - 232)
28+
| i -> Code_error.raise "invalid 8-bit color" [ "value", Dyn.int i ]
29+
;;
30+
31+
let attr_of_ansi_color_rgb24 (c : Ansi_color.RGB24.t) =
32+
A.rgb
33+
~r:(Ansi_color.RGB24.red c)
34+
~g:(Ansi_color.RGB24.green c)
35+
~b:(Ansi_color.RGB24.blue c)
36+
;;
37+
38+
let attr_of_ansi_color_style (s : Ansi_color.Style.t) =
39+
match s with
40+
| `Fg_black -> A.(fg black)
41+
| `Fg_red -> A.(fg red)
42+
| `Fg_green -> A.(fg green)
43+
| `Fg_yellow -> A.(fg yellow)
44+
| `Fg_blue -> A.(fg blue)
45+
| `Fg_magenta -> A.(fg magenta)
46+
| `Fg_cyan -> A.(fg cyan)
47+
| `Fg_white -> A.(fg white)
48+
| `Fg_default -> A.empty
49+
| `Fg_bright_black -> A.(fg lightblack)
50+
| `Fg_bright_red -> A.(fg lightred)
51+
| `Fg_bright_green -> A.(fg lightgreen)
52+
| `Fg_bright_yellow -> A.(fg lightyellow)
53+
| `Fg_bright_blue -> A.(fg lightblue)
54+
| `Fg_bright_magenta -> A.(fg lightmagenta)
55+
| `Fg_bright_cyan -> A.(fg lightcyan)
56+
| `Fg_bright_white -> A.(fg lightwhite)
57+
| `Fg_8_bit_color c -> A.fg (attr_of_ansi_color_rgb8 c)
58+
| `Fg_24_bit_color c -> A.fg (attr_of_ansi_color_rgb24 c)
59+
| `Bg_black -> A.(bg black)
60+
| `Bg_red -> A.(bg red)
61+
| `Bg_green -> A.(bg green)
62+
| `Bg_yellow -> A.(bg yellow)
63+
| `Bg_blue -> A.(bg blue)
64+
| `Bg_magenta -> A.(bg magenta)
65+
| `Bg_cyan -> A.(bg cyan)
66+
| `Bg_white -> A.(bg white)
67+
| `Bg_default -> A.empty
68+
| `Bg_bright_black -> A.(bg lightblack)
69+
| `Bg_bright_red -> A.(bg lightred)
70+
| `Bg_bright_green -> A.(bg lightgreen)
71+
| `Bg_bright_yellow -> A.(bg lightyellow)
72+
| `Bg_bright_blue -> A.(bg lightblue)
73+
| `Bg_bright_magenta -> A.(bg lightmagenta)
74+
| `Bg_bright_cyan -> A.(bg lightcyan)
75+
| `Bg_bright_white -> A.(bg lightwhite)
76+
| `Bg_8_bit_color c -> A.bg (attr_of_ansi_color_rgb8 c)
77+
| `Bg_24_bit_color c -> A.bg (attr_of_ansi_color_rgb24 c)
78+
| `Bold -> A.(st bold)
79+
| `Italic -> A.(st italic)
80+
| `Dim -> A.(st dim)
81+
| `Underline -> A.(st underline)
82+
;;
83+
84+
let attr_of_user_message_style fmt t (pp : User_message.Style.t Pp.t) : unit =
85+
let attr =
86+
match (t : User_message.Style.t) with
87+
| Loc -> A.(st bold)
88+
| Error -> A.(st bold ++ fg red)
89+
| Warning -> A.(st bold ++ fg magenta)
90+
| Kwd -> A.(st bold ++ fg blue)
91+
| Id -> A.(st bold ++ fg yellow)
92+
| Prompt -> A.(st bold ++ fg green)
93+
| Hint -> A.(st italic ++ fg white)
94+
| Details -> A.(st dim ++ fg white)
95+
| Ok -> A.(st italic ++ fg green)
96+
| Debug -> A.(st underline ++ fg lightcyan)
97+
| Success -> A.(st bold ++ fg green)
98+
| Ansi_styles l ->
99+
List.fold_left ~init:A.empty l ~f:(fun attr s ->
100+
A.(attr ++ attr_of_ansi_color_style s))
101+
in
102+
Notty.I.pp_attr attr Pp.to_fmt fmt pp
103+
;;
104+
105+
let pp = Notty.I.strf "%a" (Pp.to_fmt_with_tags ~tag_handler:attr_of_user_message_style)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
open Stdune
2+
3+
(** [User_message_to_image.pp ~attr pp] converts a pretty-printer [pp] to a [Notty.image]. *)
4+
val pp : User_message.Style.t Pp.t -> Notty.image

src/dune_tui/dune

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
(name dune_tui)
33
(libraries
44
stdune
5+
dune_lwd
6+
dune_util
7+
dune_nottui
58
dune_notty
69
dune_notty_unix
710
dune_console
811
dune_threaded_console
912
threads.posix)
1013
(instrumentation
1114
(backend bisect_ppx)))
15+
16+
(include_subdirs unqualified)

0 commit comments

Comments
 (0)