about summary refs log tree commit diff
path: root/web/todolist
diff options
context:
space:
mode:
Diffstat (limited to 'web/todolist')
-rw-r--r--web/todolist/default.nix116
-rw-r--r--web/todolist/extract-todos.jq30
2 files changed, 146 insertions, 0 deletions
diff --git a/web/todolist/default.nix b/web/todolist/default.nix
new file mode 100644
index 000000000000..4712ad21bab0
--- /dev/null
+++ b/web/todolist/default.nix
@@ -0,0 +1,116 @@
+# Generates a simple web view of open TODOs in the depot.
+#
+# Only TODOs that match the form 'TODO($username)' are considered, and
+# only for users that are known to us.
+{ depot, lib, pkgs, ... }:
+
+let
+  inherit (pkgs)
+    jq
+    ripgrep
+    runCommand
+    writeTextFile
+    ;
+
+  inherit (builtins)
+    elem
+    filter
+    fromJSON
+    head
+    readFile
+    map
+    ;
+
+  inherit (lib) concatStringsSep;
+
+  inherit (depot.nix.yants)
+    defun
+    int
+    string
+    struct
+    ;
+
+  knownUsers = map (u: u.username) depot.ops.users;
+
+  todo = struct {
+    file = string;
+    line = int;
+    todo = string;
+    user = string;
+  };
+
+  allTodos = fromJSON (readFile (runCommand "depot-todos.json" { } ''
+    ${ripgrep}/bin/rg --json 'TODO\(\w+\):.*$' ${depot.path} | \
+      ${jq}/bin/jq -s -f ${./extract-todos.jq} > $out
+  ''));
+
+  knownUserTodos = filter (todos: elem (head todos).user knownUsers) allTodos;
+
+  fileLink = defun [ todo string ] (t:
+    ''<a style="color: inherit;"
+         href="https://cs.tvl.fyi/depot/-/blob/${t.file}#L${toString t.line}">
+      //${t.file}:${toString t.line}</a>'');
+
+  todoElement = defun [ todo string ] (t: ''
+    <p>${fileLink t}:</p>
+    <blockquote>${t.todo}</blockquote>
+
+  '');
+
+  userParagraph = todos:
+    let user = (head todos).user;
+    in ''
+      <p>
+        <h3>
+          <a style="color:inherit; text-decoration: none;"
+             name="${user}"
+             href="#${user}">${user}</a>
+        </h3>
+        ${concatStringsSep "\n" (map todoElement todos)}
+      </p>
+      <hr>
+    '';
+
+  staticUrl = "https://static.tvl.fyi/${depot.web.static.drvHash}";
+
+in
+writeTextFile {
+  name = "tvl-todos";
+  destination = "/index.html";
+  text = ''
+    <!DOCTYPE html>
+    <head>
+      <meta charset="utf-8">
+      <meta name="viewport" content="width=device-width, initial-scale=1">
+      <meta name="description" content="TVL's todo-list">
+      <link rel="stylesheet" type="text/css" media="all" href="${staticUrl}/tvl.css">
+      <link rel="icon" type="image/webp" href="${staticUrl}/favicon.webp">
+      <title>TVL's todo-list</title>
+      <style>
+        svg {
+          max-width: inherit;
+          height: auto;
+        }
+      </style>
+    </head>
+    <body class="dark">
+      <header>
+        <h1><a class="blog-title" href="/">The Virus Lounge's todo-list</a> </h1>
+        <hr>
+      </header>
+      <main>
+      ${concatStringsSep "\n" (map userParagraph knownUserTodos)}
+      </main>
+      <footer>
+        <p class="footer">
+          <a class="uncoloured-link" href="https://tvl.fyi">homepage</a>
+          |
+          <a class="uncoloured-link" href="https://cs.tvl.fyi/depot/-/blob/README.md">code</a>
+          |
+          <a class="uncoloured-link" href="https://cl.tvl.fyi">reviews</a>
+        </p>
+        <p class="lod">ಠ_ಠ</p>
+      </footer>
+    </body>
+  '';
+}
diff --git a/web/todolist/extract-todos.jq b/web/todolist/extract-todos.jq
new file mode 100644
index 000000000000..d4e0476c403a
--- /dev/null
+++ b/web/todolist/extract-todos.jq
@@ -0,0 +1,30 @@
+# Simple jq script to extract all TODO comments in the code base from
+# ripgrep's JSON output.
+#
+# This assumes that the filter used is something like 'TODO\(\w+\):'
+
+# Capture the username and todo entry from an input string.
+def capture_todo:
+  capture("TODO\\((?<user>\\w+)\\):\\s+(?<todo>.*)$");
+
+# Construct a structure with only the fields we need to populate the
+# page.
+def simplify_match:
+  .data as $data
+  | (.data.submatches[0].match.text | capture_todo) as $capture
+  | {
+     file: ($data | .path.text | sub("/nix/store/.+-depot/"; "")),
+     line: ($data | .line_number),
+     todo: ($capture | .todo),
+     user: ($capture | .user),
+     };
+
+# Group all matches first by the user and return them in sorted order
+# by the file in which they appear. This matches the presentation
+# order on the website.
+def group_by_user: .
+    | group_by(.user)
+    | map(sort_by(.file));
+
+# main:
+map(select(.type == "match") | simplify_match) | group_by_user