Skip to content

Commit

Permalink
Implement pretty issue rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
chshersh committed Jan 25, 2025
1 parent 6e6bd3d commit 03f6aaf
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 13 deletions.
43 changes: 39 additions & 4 deletions lib/gh/issue.ml
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
module Json = Yojson.Basic.Util

type label = {
name : string;
color : string;
}

type t = {
number : int;
title : string;
author : string;
labels : label list;
}

let mk_issues_query ~owner ~repo =
Expand All @@ -18,28 +24,57 @@ query {
author {
login
}
labels(first: 100) {
nodes {
name
color
}
}
}
}
}
}
|}
owner repo

(* Parse single label from JSON:
{
"name": "good first issue",
"color": "7057ff"
}
*)
let parse_label json =
let name = Json.(json |> member "name" |> to_string) in
let color = Json.(json |> member "color" |> to_string) in
{ name; color }

(* Parse single issue from JSON:
{
"number": 18,
"title": "GitHub TUI: Release Tracker for v1.0",
"author": {
"login": "chshersh"
},
"labels": {
"nodes": [
{
"name": "good first issue",
"color": "7057ff"
}
]
}
}
*)
let parse_issue json =
let number = Json.(member "number" json |> to_int) in
let title = Json.(member "title" json |> to_string) in
let author = Json.(member "author" json |> member "login" |> to_string) in
{ number; title; author }
let number = Json.(json |> member "number" |> to_int) in
let title = Json.(json |> member "title" |> to_string) in
let author = Json.(json |> member "author" |> member "login" |> to_string) in
let labels =
Json.(json |> member "labels" |> member "nodes" |> convert_each parse_label)
in
{ number; title; author; labels }

(* Parse a list of 't' from the following JSON:
Expand Down
6 changes: 6 additions & 0 deletions lib/gh/issue.mli
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
type label = {
name : string;
color : string;
}

type t = {
number : int;
title : string;
author : string;
labels : label list;
}

(** Query all open issues *)
Expand Down
27 changes: 27 additions & 0 deletions lib/pretty/color.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type t = RGB of int * int * int

let to_255 str =
match int_of_string_opt ("0x" ^ str) with
| None -> 0
| Some c -> c

let rgb r g b = RGB (to_255 r, to_255 g, to_255 b)

let rgb str =
match String.to_seq str |> List.of_seq |> List.map (String.make 1) with
| [ "#"; r1; r2; g1; g2; b1; b2 ] -> rgb (r1 ^ r2) (g1 ^ g2) (b1 ^ b2)
| [ "#"; r1; g1; b1 ] -> rgb r1 g1 b1
| _ -> RGB (0, 0, 0)

let of_hex str =
if String.starts_with ~prefix:"#" str then rgb str else rgb ("#" ^ str)

(* Calculate the luminance of colour. When applied to background colour, this
function could be used to detect the corresponding foreground colour. *)
let luminance (RGB (r, g, b)) =
let scale x = float_of_int x /. 255.0 in
let r, g, b = (scale r, scale g, scale b) in
(0.2126 *. r) +. (0.7152 *. g) +. (0.0722 *. b)

let foreground color = if luminance color < 0.3 then `Light else `Dark
let to_escape_seq (RGB (r, g, b)) = Format.sprintf "2;%d;%d;%d" r g b
16 changes: 16 additions & 0 deletions lib/pretty/color.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(** This module provides utilities to work with colors. *)

(** An RGB colour code *)
type t

(** Returns RGB colour from a hex string like "7057f" or "#fbca04".
Returns black colour by default if there's a parsing error. *)
val of_hex : string -> t

(** This function calculates the luminance of colour and suggests a foreground
colour. *)
val foreground : t -> [ `Light | `Dark ]

(** Convert code to escape sequence. *)
val to_escape_seq : t -> string
1 change: 1 addition & 0 deletions lib/pretty/pretty.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
module Color = Color
module Doc = Doc
module Layout = Layout

Expand Down
3 changes: 3 additions & 0 deletions lib/pretty/pretty.mli
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ module Doc = Doc
{!Layout.t} is the output of {!Doc.render} with all the sizes calculated. *)
module Layout = Layout

(** Color-related functions. *)
module Color = Color

(** Render a document into the final string
All notes from {!Doc.render} apply. *)
Expand Down
3 changes: 3 additions & 0 deletions lib/pretty/style.ml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
type t = ANSITerminal.style list

let fg_black = ANSITerminal.[ Foreground Black ]
let fg_white = ANSITerminal.[ Foreground White ]
let green = ANSITerminal.[ green ]
let repo = ANSITerminal.[ Bold; blue ]
let selected = ANSITerminal.[ Bold; green ]
let directory = ANSITerminal.[ Bold; magenta ]
Expand Down
45 changes: 36 additions & 9 deletions lib/tui/view.ml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,44 @@ let code_section (code_tab : Model.code_tab) =
let fs_doc = Widget.file_view code_tab.fs in
Pretty.Doc.vertical [ current_path_doc; fs_doc ]

let issue_char = "\u{f41b}"

let apply_background_color ~color text =
let module Color = Pretty.Color in
let color = Color.of_hex color in
let foreground_suggestion = Color.foreground color in
let color_code = Pretty.Color.to_escape_seq color in
let text = Printf.sprintf "\027[48;%sm %s \027[0m" color_code text in
(text, foreground_suggestion)

let issues_section (issues_tab : Model.issues_tab) =
let open Pretty.Layout in
let fmt_title (issue : Gh.Issue.t) =
horizontal
[
fmt Style.green issue_char;
str " ";
fmt Style.secondary (Printf.sprintf "#%d " issue.number);
str issue.title;
str " by ";
fmt Style.bold (Printf.sprintf "@%s" issue.author);
]
in
let fmt_labels labels =
let fmt_label (label : Gh.Issue.label) =
let label, foreground =
apply_background_color ~color:label.color label.name
in
let label = label ^ " " in
match foreground with
| `Light -> fmt Style.fg_white label
| `Dark -> fmt Style.fg_black label
in
labels |> List.map fmt_label |> fun labels ->
horizontal (str " " :: labels)
in
let fmt_issue (issue : Gh.Issue.t) =
Pretty.Layout.(
horizontal
[
str " ";
fmt Style.secondary (Printf.sprintf "#%d " issue.number);
str issue.title;
str " by ";
fmt Style.bold (Printf.sprintf "@%s" issue.author);
])
vertical [ fmt_title issue; fmt_labels issue.labels ]
in
issues_tab.issues
|> Lazy.force
Expand Down

0 comments on commit 03f6aaf

Please sign in to comment.