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 1467968..9d71002 100644 --- a/lib/pretty/icon.ml +++ b/lib/pretty/icon.ml @@ -1,11 +1,93 @@ -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; +} + +let hack_nerd_font = "HackNerdFontMono" +let fc_list_cmd = Printf.sprintf "fc-list | grep '%s'" hack_nerd_font +let uname_cmd = {| uname |} + +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 = + [ + Unix.getenv "HOME" ^ "/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 + { + 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..a59cf81 100644 --- a/lib/pretty/pretty.ml +++ b/lib/pretty/pretty.ml @@ -1,7 +1,8 @@ module Doc = Doc 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..4d43265 100644 --- a/lib/pretty/pretty.mli +++ b/lib/pretty/pretty.mli @@ -13,11 +13,12 @@ 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 +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 00911fa..b043b26 100644 --- a/lib/tui/render/render.ml +++ b/lib/tui/render/render.ml @@ -8,8 +8,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 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 f5dddf2..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.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) @@ -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.(