From 8a912c924c4b2b941eed66d2ed14ce7164a65744 Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Tue, 11 Feb 2025 23:25:54 -0500 Subject: [PATCH 1/7] Use fc-list to check for Nerd Font --- lib/pretty/icon.ml | 60 ++++++++++++++++++++++++++++++++------- lib/pretty/icon.mli | 18 ++++++++++++ lib/pretty/pretty.ml | 2 ++ lib/pretty/pretty.mli | 4 +++ lib/tui/render/render.ml | 5 ++-- lib/tui/widget/code.ml | 10 +++---- lib/tui/widget/common.ml | 6 ++-- lib/tui/widget/generic.ml | 4 ++- lib/tui/widget/pr.ml | 6 ++-- 9 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 lib/pretty/icon.mli diff --git a/lib/pretty/icon.ml b/lib/pretty/icon.ml index 1467968..6084a1a 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -1,11 +1,49 @@ -let closed_char = "\u{ebda}" -let open_char = "\u{ea64}" -let merged_char = "\u{e725}" -let arrow_left_char = "\u{f0a8}" -let pwd_char = "\u{e5fd}" -let dir_char = "\u{f4d4}" -let empty_dir_char = "\u{f413}" -let file_char = "\u{f4a5}" -let bin_char = "\u{eae8}" -let warning = "\u{26A0}" -let issue_char = "\u{f41b}" +type t = { + closed_char : string; + open_char : string; + merged_char : string; + arrow_left_char : string; + pwd_char : string; + dir_char : string; + empty_dir_char : string; + file_char : string; + bin_char : string; + warning : string; + issue_char : string; +} + +(** Use fc-list to check if Hack Nerd Font is installed - this might be a little + brittle, but it's simple - update if it becomes an issue. **) +let nerd_font_installed () = + let cmd = "fc-list | grep 'Hack Nerd Font Mono' > /dev/null 2>&1" in + if Sys.command cmd = 0 then true else false + +let icons = + if nerd_font_installed () then + { + closed_char = "\u{ebda}"; + open_char = "\u{ea64}"; + merged_char = "\u{e725}"; + arrow_left_char = "\u{f0a8}"; + pwd_char = "\u{e5fd}"; + dir_char = "\u{f4d4}"; + empty_dir_char = "\u{f413}"; + file_char = "\u{f4a5}"; + bin_char = "\u{eae8}"; + warning = "\u{26A0}"; + issue_char = "\u{f41b}"; + } + else + { + closed_char = "x"; + open_char = "o"; + merged_char = "m"; + arrow_left_char = "<-"; + pwd_char = "*"; + dir_char = "/"; + empty_dir_char = "/"; + file_char = "f"; + bin_char = "b"; + warning = "!"; + issue_char = "i"; + } diff --git a/lib/pretty/icon.mli b/lib/pretty/icon.mli new file mode 100644 index 0000000..b8c1072 --- /dev/null +++ b/lib/pretty/icon.mli @@ -0,0 +1,18 @@ +(** A record representing the icon set **) +type t = { + closed_char : string; + open_char : string; + merged_char : string; + arrow_left_char : string; + pwd_char : string; + dir_char : string; + empty_dir_char : string; + file_char : string; + bin_char : string; + warning : string; + issue_char : string; +} + +(** Populated with the current icon set based on if Nerd Font is available or or + not**) +val icons : t diff --git a/lib/pretty/pretty.ml b/lib/pretty/pretty.ml index 397a8e5..85bb961 100644 --- a/lib/pretty/pretty.ml +++ b/lib/pretty/pretty.ml @@ -3,5 +3,7 @@ module Layout = Layout module Style = Style module Icon = Icon +let icons = Icon.icons + let render ~width ~height doc = doc |> Doc.render ~width ~height |> Layout.to_lines |> Extra.String.unlines diff --git a/lib/pretty/pretty.mli b/lib/pretty/pretty.mli index c961dd7..591cf23 100644 --- a/lib/pretty/pretty.mli +++ b/lib/pretty/pretty.mli @@ -19,6 +19,10 @@ module Layout = Layout - https://www.nerdfonts.com/cheat-sheet *) module Icon = Icon +val icons : Icon.t + +(** A record representing the icon set **) + (** Render a document into the final string All notes from {!Doc.render} apply. *) diff --git a/lib/tui/render/render.ml b/lib/tui/render/render.ml index 00911fa..c685241 100644 --- a/lib/tui/render/render.ml +++ b/lib/tui/render/render.ml @@ -1,5 +1,6 @@ module Style = Pretty.Style module Layout = Pretty.Layout +module Icon = Pretty.Icon type 'a t = { item : 'a; @@ -8,8 +9,8 @@ type 'a t = { let fmt_issue_state (state : Gh.Issue.state) = match state with - | Open -> Layout.(fmt Style.issue_open Pretty.Icon.issue_char) - | Closed -> Layout.(fmt Style.issue_closed Pretty.Icon.issue_char) + | Open -> Layout.(fmt Style.issue_open Icon.icons.issue_char) + | Closed -> Layout.(fmt Style.issue_closed Icon.icons.issue_char) let fmt_title (issue : Gh.Issue.t) = let open Layout in diff --git a/lib/tui/widget/code.ml b/lib/tui/widget/code.ml index f5dddf2..b980ce6 100644 --- a/lib/tui/widget/code.ml +++ b/lib/tui/widget/code.ml @@ -15,7 +15,7 @@ let pwd root_dir_path (fs : Fs.zipper) = let pwd_path = parents_path parents in let root_dir_name = Filename.basename root_dir_path in let full_path = - Pretty.Icon.pwd_char ^ " " ^ Filename.concat root_dir_name pwd_path + Pretty.Icon.icons.pwd_char ^ " " ^ Filename.concat root_dir_name pwd_path in Pretty.Doc.(fmt Style.directory full_path) @@ -52,12 +52,12 @@ let fmt_file ~max_name_len (tree : Fs.tree) = match tree with | File { name; file_type; _ } -> ( match Lazy.force file_type with - | Fs.Filec.Text -> Pretty.Icon.file_char ^ " " ^ pad name - | Fs.Filec.Binary -> Pretty.Icon.bin_char ^ " " ^ pad name) + | Fs.Filec.Text -> Pretty.icons.file_char ^ " " ^ pad name + | Fs.Filec.Binary -> Pretty.icons.bin_char ^ " " ^ pad name) | Dir { name; children = (lazy children) } -> ( match children with - | [||] -> Pretty.Icon.empty_dir_char ^ " " ^ pad name - | _ -> Pretty.Icon.dir_char ^ " " ^ pad name) + | [||] -> Pretty.icons.empty_dir_char ^ " " ^ pad name + | _ -> Pretty.icons.dir_char ^ " " ^ pad name) let current_level_to_doc (cursor : Fs.dir_cursor) ~has_next ~is_file_chosen = let open Pretty.Doc in diff --git a/lib/tui/widget/common.ml b/lib/tui/widget/common.ml index 51831db..5ec4b1e 100644 --- a/lib/tui/widget/common.ml +++ b/lib/tui/widget/common.ml @@ -4,7 +4,7 @@ let fmt_error (error : Gh.Client.error) = | No_github_token -> [ str - (Pretty.Icon.warning + (Pretty.icons.warning ^ " GITHUB_TOKEN not found. Make sure it's configured in your \ environment."); str ""; @@ -13,7 +13,7 @@ let fmt_error (error : Gh.Client.error) = ] | Bad_credentials { msg; doc_url; code } -> [ - str (Format.sprintf "%s [%d] %s" Pretty.Icon.warning code msg); + str (Format.sprintf "%s [%d] %s" Pretty.icons.warning code msg); str ""; str ("Documentation url: " ^ doc_url); ] @@ -21,7 +21,7 @@ let fmt_error (error : Gh.Client.error) = [ str (Format.sprintf "%s GitHub API returned error code: %d" - Pretty.Icon.warning code); + Pretty.icons.warning code); str ""; str msg; ] diff --git a/lib/tui/widget/generic.ml b/lib/tui/widget/generic.ml index ae83de7..f4aade3 100644 --- a/lib/tui/widget/generic.ml +++ b/lib/tui/widget/generic.ml @@ -66,7 +66,9 @@ let vlist_border ~scroll_start ~selected items = let first_line = Doc.( horizontal - [ fmt_selected_line hd; str " "; str Pretty.Icon.arrow_left_char ]) + [ + fmt_selected_line hd; str " "; str Pretty.icons.arrow_left_char; + ]) in let other_lines = List.map fmt_selected_line tl in first_line :: other_lines diff --git a/lib/tui/widget/pr.ml b/lib/tui/widget/pr.ml index 9611fb3..714073b 100644 --- a/lib/tui/widget/pr.ml +++ b/lib/tui/widget/pr.ml @@ -7,9 +7,9 @@ let section (tab : Model.Pr.t) = | None -> let fmt_state = function | None -> str "" - | Some Gh.Pr.Merged -> fmt Style.pr_merged Pretty.Icon.merged_char - | Some Gh.Pr.Open -> fmt Style.pr_open Pretty.Icon.open_char - | Some Gh.Pr.Closed -> fmt Style.pr_closed Pretty.Icon.closed_char + | Some Gh.Pr.Merged -> fmt Style.pr_merged Pretty.icons.merged_char + | Some Gh.Pr.Open -> fmt Style.pr_open Pretty.icons.open_char + | Some Gh.Pr.Closed -> fmt Style.pr_closed Pretty.icons.closed_char in let fmt_pr (pr : Gh.Pr.t) = Pretty.Doc.( From d42efd553952faa0bc8229c658fb9bb8aace9ea5 Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Tue, 11 Feb 2025 23:53:46 -0500 Subject: [PATCH 2/7] Doc format fix --- lib/pretty/pretty.mli | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pretty/pretty.mli b/lib/pretty/pretty.mli index 591cf23..62b1882 100644 --- a/lib/pretty/pretty.mli +++ b/lib/pretty/pretty.mli @@ -19,9 +19,8 @@ module Layout = Layout - https://www.nerdfonts.com/cheat-sheet *) module Icon = Icon -val icons : Icon.t - (** A record representing the icon set **) +val icons : Icon.t (** Render a document into the final string From 42a5d1a33a1f1d303439af46099031d2f48b1bb3 Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Thu, 13 Feb 2025 18:31:19 -0500 Subject: [PATCH 3/7] Use existing shell util --- lib/pretty/icon.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pretty/icon.ml b/lib/pretty/icon.ml index 6084a1a..9d56eb3 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -12,11 +12,12 @@ type t = { issue_char : string; } +let fc_list_cmd = {| fc-list | grep 'Hack Nerd Font Mono' |} + (** Use fc-list to check if Hack Nerd Font is installed - this might be a little brittle, but it's simple - update if it becomes an issue. **) let nerd_font_installed () = - let cmd = "fc-list | grep 'Hack Nerd Font Mono' > /dev/null 2>&1" in - if Sys.command cmd = 0 then true else false + fc_list_cmd |> Shell.proc_stdout |> String.trim |> String.length > 0 let icons = if nerd_font_installed () then From 8124e040c2c72df96c51078fc80a288c35bc88c7 Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Fri, 21 Feb 2025 18:28:58 -0500 Subject: [PATCH 4/7] WIP: checking for fonts --- lib/pretty/dune | 1 + lib/pretty/icon.ml | 55 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/lib/pretty/dune b/lib/pretty/dune index ff0a01f..e6a451c 100644 --- a/lib/pretty/dune +++ b/lib/pretty/dune @@ -3,5 +3,6 @@ (libraries ansifmt shell + str ;; Internal dependencies extra)) diff --git a/lib/pretty/icon.ml b/lib/pretty/icon.ml index 9d56eb3..5ce8177 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -12,15 +12,58 @@ type t = { issue_char : string; } -let fc_list_cmd = {| fc-list | grep 'Hack Nerd Font Mono' |} +let hack_nerd_font = "Hack Nerd Font Mono" +let fc_list_cmd = Printf.sprintf "fc-list | grep '%s'" hack_nerd_font +let uname_cmd = {| uname |} -(** Use fc-list to check if Hack Nerd Font is installed - this might be a little - brittle, but it's simple - update if it becomes an issue. **) -let nerd_font_installed () = - fc_list_cmd |> Shell.proc_stdout |> String.trim |> String.length > 0 +type os_name = + | Linux + | MacOS + +let uname_result = + Shell.proc_stdout uname_cmd |> String.trim |> function + | "Linux" -> Some Linux + | "Darwin" -> Some MacOS + | _ -> None + +(* Based on the list of possible folders here: https://github.com/adrg/xdg/blob/master/README.md#other-directories*) +let mac_font_dirs = + [ + "~/Library/Fonts"; + "/Library/Fonts"; + "/System/Library/Fonts"; + "/Network/Library/Fonts"; + ] + +let string_contains str substr = + let re = Str.regexp_string substr in + try + ignore (Str.search_forward re str 0); + true + with Not_found -> false + +let rec font_exists_in_dirs font_name = function + | [] -> false + | dir :: dirs -> + if Sys.file_exists dir then + let files = Sys.readdir dir in + if Array.exists (fun file -> string_contains file font_name) files then + true + else font_exists_in_dirs font_name dirs + else font_exists_in_dirs font_name dirs + +(* We only support mac and linux right now - if the system is unix based and +it's linux, we can use the fc-list cmd, otherwise on mac we look in common +directories manually for our font *) +let nerd_font_installed = + match uname_result with + | Some Linux -> + Shell.proc_stdout fc_list_cmd |> String.trim |> String.length > 0 + | Some MacOS -> font_exists_in_dirs hack_nerd_font mac_font_dirs + | None -> false (* do we want this be false or true if we can't tell? *) let icons = - if nerd_font_installed () then + if nerd_font_installed then { closed_char = "\u{ebda}"; open_char = "\u{ea64}"; From d45419f917e806f341cfd957eea82d6c5e7b0f6a Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Fri, 21 Feb 2025 18:37:10 -0500 Subject: [PATCH 5/7] Export just icons in pretty --- lib/pretty/pretty.ml | 1 - lib/pretty/pretty.mli | 8 +++----- lib/tui/render/render.ml | 5 ++--- lib/tui/widget/code.ml | 2 +- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/pretty/pretty.ml b/lib/pretty/pretty.ml index 85bb961..a59cf81 100644 --- a/lib/pretty/pretty.ml +++ b/lib/pretty/pretty.ml @@ -1,7 +1,6 @@ module Doc = Doc module Layout = Layout module Style = Style -module Icon = Icon let icons = Icon.icons diff --git a/lib/pretty/pretty.mli b/lib/pretty/pretty.mli index 62b1882..4d43265 100644 --- a/lib/pretty/pretty.mli +++ b/lib/pretty/pretty.mli @@ -13,13 +13,11 @@ module Doc = Doc {!Layout.t} is the output of {!Doc.render} with all the sizes calculated. *) module Layout = Layout -(** Icons and symbols used for identifying different parts. Symbols from Hack - Nerd Font Mono are used when it's available. Symbols list: +(** A record representign the Icons and symbols used for identifying different + parts. Symbols from Hack Nerd Font Mono are used when it's available. + Symbols list: - https://www.nerdfonts.com/cheat-sheet *) -module Icon = Icon - -(** A record representing the icon set **) val icons : Icon.t (** Render a document into the final string diff --git a/lib/tui/render/render.ml b/lib/tui/render/render.ml index c685241..b043b26 100644 --- a/lib/tui/render/render.ml +++ b/lib/tui/render/render.ml @@ -1,6 +1,5 @@ module Style = Pretty.Style module Layout = Pretty.Layout -module Icon = Pretty.Icon type 'a t = { item : 'a; @@ -9,8 +8,8 @@ type 'a t = { let fmt_issue_state (state : Gh.Issue.state) = match state with - | Open -> Layout.(fmt Style.issue_open Icon.icons.issue_char) - | Closed -> Layout.(fmt Style.issue_closed Icon.icons.issue_char) + | Open -> Layout.(fmt Style.issue_open Pretty.icons.issue_char) + | Closed -> Layout.(fmt Style.issue_closed Pretty.icons.issue_char) let fmt_title (issue : Gh.Issue.t) = let open Layout in diff --git a/lib/tui/widget/code.ml b/lib/tui/widget/code.ml index b980ce6..46f8890 100644 --- a/lib/tui/widget/code.ml +++ b/lib/tui/widget/code.ml @@ -15,7 +15,7 @@ let pwd root_dir_path (fs : Fs.zipper) = let pwd_path = parents_path parents in let root_dir_name = Filename.basename root_dir_path in let full_path = - Pretty.Icon.icons.pwd_char ^ " " ^ Filename.concat root_dir_name pwd_path + Pretty.icons.pwd_char ^ " " ^ Filename.concat root_dir_name pwd_path in Pretty.Doc.(fmt Style.directory full_path) From 1c98f79c5abec56cdb34f4034ac39caffce838ea Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Fri, 21 Feb 2025 18:43:19 -0500 Subject: [PATCH 6/7] Filename has no space (fc-list prints font name too) --- lib/pretty/icon.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pretty/icon.ml b/lib/pretty/icon.ml index 5ce8177..953c2ea 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -12,7 +12,7 @@ type t = { issue_char : string; } -let hack_nerd_font = "Hack Nerd Font Mono" +let hack_nerd_font = "HackNerdFontMono" let fc_list_cmd = Printf.sprintf "fc-list | grep '%s'" hack_nerd_font let uname_cmd = {| uname |} From 5176f03543fca3fd6db55ac2a8e9cb467d4a2494 Mon Sep 17 00:00:00 2001 From: Heath Henley Date: Sun, 23 Feb 2025 11:36:49 -0500 Subject: [PATCH 7/7] Fix: home dir on mac Co-authored-by: Beiming Zhang --- lib/pretty/icon.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pretty/icon.ml b/lib/pretty/icon.ml index 953c2ea..9d71002 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -29,7 +29,7 @@ let uname_result = (* Based on the list of possible folders here: https://github.com/adrg/xdg/blob/master/README.md#other-directories*) let mac_font_dirs = [ - "~/Library/Fonts"; + Unix.getenv "HOME" ^ "/Library/Fonts"; "/Library/Fonts"; "/System/Library/Fonts"; "/Network/Library/Fonts";