diff options
| author | Marius Peter <marius.peter@tutanota.com> | 2025-03-02 19:23:19 +0100 | 
|---|---|---|
| committer | Marius Peter <marius.peter@tutanota.com> | 2025-03-02 19:23:19 +0100 | 
| commit | 55993c4d0857aec8a372aee989aaeef61ea37b3c (patch) | |
| tree | a6da37b5fda45d80f84251633426dee51db87575 | |
| parent | c67581470cbdf2a1c8efabce78afffb2e3a2a2c6 (diff) | |
Great work today! 🔥
| -rw-r--r-- | bin/main.ml | 3 | ||||
| -rw-r--r-- | lib/git_unhelpers.ml | 35 | ||||
| -rw-r--r-- | lib/handlers.ml | 7 | ||||
| -rw-r--r-- | lib/views.ml | 145 | 
4 files changed, 146 insertions, 44 deletions
| diff --git a/bin/main.ml b/bin/main.ml index c30e4fc..74712e1 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -6,5 +6,8 @@ let () =           Dream.get "/:repo_name" (fun req ->               let repo_name = Dream.param req "repo_name" in               Ogit.Handlers.repo_root repo_name); +         Dream.get "/:repo_name/tree" (fun req -> +             let repo_name = Dream.param req "repo_name" in +             Ogit.Handlers.repo_tree repo_name);           Dream.get "/static/**" (Dream.static "./lib/static");         ] diff --git a/lib/git_unhelpers.ml b/lib/git_unhelpers.ml index 28d4d3a..5b55c16 100644 --- a/lib/git_unhelpers.ml +++ b/lib/git_unhelpers.ml @@ -1,11 +1,17 @@  (* These will be reimplemented using OCaml's Git library, one day... *) -let get_git_log repo_path = +let get_latest_commits repo_path count = +  let open Printf in    let full_path = Filename.concat Config.git_directory repo_path in    let full_cmd = -    let cmd = Printf.sprintf "git -C %s log" full_path in -    let options = [ "--pretty=format:'%ad %s'"; "--date=short"; "-n 10" ] in -    String.concat " " (cmd :: options) +    let command = sprintf "git -C %s log" full_path in +    let options = +      let format = "--pretty=format:'%ad %s'" in +      let date = "--date=short" in +      let count = sprintf "-n %s" (string_of_int count) in +      [ format; date; count ] +    in +    String.concat " " (command :: options)    in    let ic = Unix.open_process_in full_cmd in    let rec read_lines acc = @@ -17,3 +23,24 @@ let get_git_log repo_path =        List.rev acc    in    read_lines [] + +let get_all_branches repo_path = +  let open Printf in +  let full_path = Filename.concat Config.git_directory repo_path in +  let full_cmd = sprintf "git -C %s branch" full_path in +  let ic = Unix.open_process_in full_cmd in +  let rec read_lines acc = +    try +      let line = input_line ic |> String.trim in +      let clean_line = +        if String.length line > 2 && String.sub line 0 2 = "* " then +          String.sub line 2 (String.length line - 2) +          (* Remove "* " from active branch *) +        else line +      in +      read_lines (clean_line :: acc) +    with End_of_file -> +      ignore (Unix.close_process_in ic); +      List.rev acc +  in +  read_lines [] diff --git a/lib/handlers.ml b/lib/handlers.ml index 4a6e0ca..46df354 100644 --- a/lib/handlers.ml +++ b/lib/handlers.ml @@ -1,6 +1,3 @@ -open Lwt.Infix -  let ogit_root _ = Views.Ogit_root.render () |> Dream_html.respond - -let repo_root repo_name = -  Views.Repo_root.render repo_name >>= Dream_html.respond +let repo_root repo_name = Views.Repo_root.render repo_name |> Dream_html.respond +let repo_tree repo_name = Views.Repo_tree.render repo_name |> Dream_html.respond diff --git a/lib/views.ml b/lib/views.ml index 446d601..2d11674 100644 --- a/lib/views.ml +++ b/lib/views.ml @@ -32,7 +32,7 @@ module Layout = struct            ];          body []            [ -            header "Ogit" body_data.subtitle; +            header body_data.title body_data.subtitle;              body_data.topnav;              div [ id "main" ] body_data.content;              footer; @@ -40,6 +40,60 @@ module Layout = struct        ]  end +module Topnav = struct +  open Dream_html +  open HTML + +  let repo repo_path current_path = +    let li_of_a (path, text) = +      let is_active = path = current_path in +      let attrs = if is_active then [ id "active" ] else [] in +      let url = +        if String.length path = 0 then Printf.sprintf "/%s" repo_path +        else Printf.sprintf "/%s/%s" repo_path path +      in +      li attrs [ a [ href "%s" url ] [ txt text ] ] +    in +    nav +      [ id "top" ] +      [ +        ul [] +        @@ List.map li_of_a +             [ +               ("", "summary"); +               ("refs", "refs"); +               ("log", "log"); +               ("tree", "tree"); +               ("commit", "commit"); +               ("diff", "diff"); +             ]; +      ] + +  (* let li_of_a (path, text) = *) +  (*     let is_active = path = current_path in *) +  (*     li (if is_active then [ id "active" ] else []) in *) +  (*     let link_of_path = *) +  (* if String.length path = 0 then Printf.sprintf "/%s" repo_path *) +  (* else Printf.sprintf "/%s/%s" repo_path path *) +  (*     in *) +  (*     li [ id "active" ] [ a [ href "%s" link_of_path ] [ txt text ] ] *) +  (*   in *) +  (*   nav *) +  (*     [ id "top" ] *) +  (*     [ *) +  (*       ul [] *) +  (*       @@ List.map li_of_a *) +  (*            [ *) +  (*              ("", "summary"); *) +  (*              ("refs", "refs"); *) +  (*              ("log", "log"); *) +  (*              ("tree", "tree"); *) +  (*              ("commit", "commit"); *) +  (*              ("diff", "diff"); *) +  (*            ]; *) +  (*     ] *) +end +  module Ogit_root = struct    open Dream_html    open HTML @@ -56,7 +110,7 @@ module Ogit_root = struct    let body_data =      { -      title = "My repositories"; +      title = "Ogit";        subtitle = "Repositories for " ^ Config.author;        topnav = null [];        content = [ repositories_in Config.git_directory ]; @@ -68,57 +122,78 @@ end  module Repo_root = struct    open Dream_html    open HTML -  (* open Lwt.Syntax *) + +  (* let href_for_git_object kind identifier = *) +  (*   let open Printf in *) +  (*   href "%s" @@ *) +  (*   match kind with *) +  (*   | `Branch -> sprintf "/tree/%s" identifier *) +  (*   | `Commit -> sprintf "/commit/%s" identifier *)    let render repo_path = -    (* let* title_result = Git_helpers.get_head_commit_hash repo_path in *) -    (* let title = *) -    (*   match title_result with Ok hash -> hash | Error msg -> "Error: " ^ msg *) -    (* in *) -    let title = "Finble" in +    let title = repo_path in      let subtitle = Filename.concat Config.git_directory repo_path in -    let topnav = -      nav -        [ id "top" ] -        [ -          ul [] -            [ -              li [] [ a [ href "/" ] [ txt "summary" ] ]; -              li [] [ a [ href "/" ] [ txt "refs" ] ]; -              li [] [ a [ href "/" ] [ txt "log" ] ]; -              li [] [ a [ href "/" ] [ txt "tree" ] ]; -              li [] [ a [ href "/" ] [ txt "commit" ] ]; -              li [] [ a [ href "/" ] [ txt "diff" ] ]; -            ]; -        ] +    let all_branches = Git_unhelpers.get_all_branches repo_path in +    let li_of_branch branch = +      li [] [ a [ href "%s" branch ] [ txt "%s" branch ] ]      in -    let recent_commits = Git_unhelpers.get_git_log repo_path in +    let recent_commits = Git_unhelpers.get_latest_commits repo_path 10 in      let li_of_commit commit =        li [] [ a [ href "%s" commit ] [ txt "%s" commit ] ]      in      let content =        [ +        h3 [] [ txt "Branches" ]; +        ul [] (List.map li_of_branch all_branches);          h3 [] [ txt "Recent commits" ]; -        ul [] @@ List.map li_of_commit recent_commits; +        ul [] (List.map li_of_commit recent_commits);        ]      in -    let body_data = { title; subtitle; topnav; content } in -    Lwt.return @@ Layout.application body_data +    let body_data = +      { title; subtitle; topnav = Topnav.repo repo_path ""; content } +    in +    Layout.application body_data  end  module Repo_tree = struct    open Dream_html    open HTML -  open Lwt.Syntax +  let full_path repo_path = Filename.concat Config.git_directory repo_path + +  (* Helper function to list top-level files and directories *) +  let ls_repo repo_path = +    try +      Sys.readdir (full_path repo_path) +      |> Array.to_list +      |> List.filter (fun name -> name <> ".git") (* Exclude .git *) +      |> List.sort String.compare +    with Sys_error _ -> [] + +  (* Function to create a link based on file type *) +  let link_for_entry repo_path entry = +    let entry_path = Filename.concat (full_path repo_path) entry in +    if Sys.is_directory entry_path then +      a +        [ href "%s" @@ Printf.sprintf "/%s/tree/%s" repo_path entry ] +        [ txt "%s" (entry ^ "/") ] +    else +      a +        [ href "%s" @@ Printf.sprintf "/%s/blob/%s" repo_path entry ] +        [ txt "%s" entry ] + +  let li_of_entry repo_path entry = li [] [ link_for_entry repo_path entry ] + +  (* Render function *)    let render repo_path = -    let* title_result = Git_helpers.get_head_commit_hash repo_path in -    let title = -      match title_result with Ok hash -> hash | Error msg -> "Error: " ^ msg +    let title = repo_path in +    let subtitle = "Files" in + +    let repo_entries = ls_repo repo_path in +    let content = [ ul [] (List.map (li_of_entry repo_path) repo_entries) ] in + +    let body_data = +      { title; subtitle; topnav = Topnav.repo repo_path "tree"; content }      in -    let subtitle = "Dinglefops" in -    let topnav = null [] in -    let content = [ txt "foobar" ] in -    let body_data = { title; subtitle; topnav; content } in -    Lwt.return @@ Layout.application body_data +    Layout.application body_data  end | 
