From 5d84d820a9be7bb05f9d03b7ae7da4ce1cd2417a Mon Sep 17 00:00:00 2001 From: austin-artificial <126663376+austin-artificial@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:56:18 +0000 Subject: [PATCH] Add filtering to api page (#81) * add `stack` and `pandoc-cli` to flake shell so we can build the docs * add filtering on the api * gen docs --- docs/api/index.html | 99 +++++++++++++++++++++++++++++++++++++++++---- docs/index.js | 85 ++++++++++++++++++++++++++++++++++++++ flake.nix | 2 + src/Hell.hs | 60 +++++++++++++++++++++++---- 4 files changed, 231 insertions(+), 15 deletions(-) create mode 100644 docs/index.js diff --git a/docs/api/index.html b/docs/api/index.html index 2cc7ac2..2c3551f 100644 --- a/docs/api/index.html +++ b/docs/api/index.html @@ -18,16 +18,101 @@ } li { line-height: 2em } -Hell's API

Hell's API

Version: 2025-01-13

Back to homepage

Types

Terms

$

<$>

<**>

<*>

<>

Applicative

Argument

Async

Bool

ByteString

Concurrent

Directory

Double

Either

Environment

Eq

Error

Exit

Flag

Function

Functor

IO

Int

Json

Record

Set

Show

Temp

Text

Timeout

Tree

Tuple

Vector

\ No newline at end of file diff --git a/docs/index.js b/docs/index.js new file mode 100644 index 0000000..b7f4195 --- /dev/null +++ b/docs/index.js @@ -0,0 +1,85 @@ +// parse the json in the index +const indexEl = document.getElementById("searchIndex"); +const index = JSON.parse(indexEl.textContent); + +function hideItem(item) { + item.style.display = "none"; + item.classList.add("hidden"); +} + +function showItem(item) { + item.style.display = "block"; + item.classList.remove("hidden"); +} + +function hideSearchable() { + const allItems = document.querySelectorAll(".searchable"); + // hide all items + allItems.forEach((item) => { + hideItem(item); + }); +} + +function showSearchable() { + const allItems = document.querySelectorAll(".searchable"); + // show all items + allItems.forEach((item) => { + showItem(item); + }); +} + +function showUsedHeadings() { + // fancy query which selects any headings which have + // non hidden children + const els = document.querySelectorAll( + ".searchableHeading:has(+ul li:not(.hidden))", + ); + els.forEach((el) => { + el.style.display = "block"; + }); +} + +function hideAllHeadings() { + const els = document.querySelectorAll(".searchableHeading"); + els.forEach((el) => { + el.style.display = "none"; + }); +} + +function showAllHeadings() { + const els = document.querySelectorAll(".searchableHeading"); + els.forEach((el) => { + el.style.display = "block"; + }); +} + +function search(query) { + if (query === "" || query === null) { + showAllHeadings(); + showSearchable(); + return; + } + + // + const results = index.filter((item) => { + return item.text.toLowerCase().includes(query.toLowerCase()); + }); + const resultIds = results.map((item) => item.elementId); + + // hide everything (we will show relevant things next) + hideSearchable(); + hideAllHeadings(); + // show the items that match the search + resultIds.forEach((id) => { + const item = document.getElementById(id); + showItem(item); + }); + showUsedHeadings(); +} + +// when the search changes, hide non-matching elements +const searchInput = document.getElementById("search"); +searchInput.addEventListener("input", function (event) { + const value = event.target.value; + search(value); +}); diff --git a/flake.nix b/flake.nix index e030d42..a722d8d 100644 --- a/flake.nix +++ b/flake.nix @@ -20,8 +20,10 @@ devShells.default = haskellPackages.shellFor { packages = p: [ p.hell ]; buildInputs = with haskellPackages; [ + stack cabal-install haskell-language-server + pandoc-cli ]; }; } diff --git a/src/Hell.hs b/src/Hell.hs index 6753542..b44dde6 100644 --- a/src/Hell.hs +++ b/src/Hell.hs @@ -2354,6 +2354,7 @@ instance Pretty InferError where _generateApiDocs :: IO () _generateApiDocs = do css <- Text.readFile "docs/style.css" + js <- Text.readFile "docs/index.js" Lucid.renderToFile "docs/api/index.html" do doctypehtml_ do style_ css @@ -2363,6 +2364,7 @@ _generateApiDocs = do h1_ "Hell's API" h2_ $ do "Version: "; toHtml hellVersion p_ $ a_ [href_ "../"] $ "Back to homepage" + input_ [type_ "text", id_ "search", placeholder_ "Filter..."] h2_ "Types" let excludeHidden = filter (not . List.isPrefixOf "hell:Hell." . fst) ul_ do @@ -2370,20 +2372,62 @@ _generateApiDocs = do h2_ "Terms" let groups = excludeHidden $ - Map.toList $ fmap (Left . snd) $ - supportedLits - let groups' = excludeHidden $ - Map.toList $ fmap (\(_, _, _, ty) -> Right ty) polyLits + Map.toList $ + fmap (Left . snd) $ + supportedLits + let groups' = + excludeHidden $ + Map.toList $ + fmap (\(_, _, _, ty) -> Right ty) polyLits for_ (List.groupBy (Function.on (==) (takeWhile (/= '.') . fst)) $ List.sortOn fst $ groups <> groups') \group -> do - h3_ $ for_ (take 1 group) \(x, _) -> toHtml $ takeWhile (/= '.') x + h3_ [class_ "searchableHeading"] $ for_ (take 1 group) \(x, _) -> toHtml $ takeWhile (/= '.') x ul_ do for_ group \(x, a) -> case a of Left e -> litToHtml (x, e) Right e -> polyToHtml (x, e) + script_ [id_ "searchIndex"] $ Json.encode makeSearchIndex + script_ [type_ "text/javascript"] js + +makeSearchIndex :: Json.Value +makeSearchIndex = Json.Array $ typeConstructorsIndex <> litsIndex <> polysIndex + where + typeConstructorsIndex = + Vector.fromList $ + map + ( \(name, _) -> + Json.object + [ ("elementId", Json.String $ nameToElementId name), + ("text", Json.String $ Text.pack name) + ] + ) + (Map.toList supportedTypeConstructors) + litsIndex = + Vector.fromList $ + map + ( \(name, _) -> + Json.object + [ ("elementId", Json.String $ nameToElementId name), + ("text", Json.String $ Text.pack name) + ] + ) + (Map.toList supportedLits) + polysIndex = + Vector.fromList $ + map + ( \(name, _) -> + Json.object + [ ("elementId", Json.String $ nameToElementId name), + ("text", Json.String $ Text.pack name) + ] + ) + (Map.toList polyLits) + +nameToElementId :: String -> Text +nameToElementId = Text.pack typeConsToHtml :: (String, SomeTypeRep) -> Html () typeConsToHtml (name, SomeTypeRep rep) = - li_ do + li_ [id_ (nameToElementId name), class_ "searchable"] $ do code_ do em_ "data " strong_ $ toHtml name @@ -2392,7 +2436,7 @@ typeConsToHtml (name, SomeTypeRep rep) = litToHtml :: (String, SomeTypeRep) -> Html () litToHtml (name, SomeTypeRep rep) = - li_ do + li_ [id_ (nameToElementId name), class_ "searchable"] $ do code_ do strong_ $ toHtml name em_ " :: " @@ -2400,7 +2444,7 @@ litToHtml (name, SomeTypeRep rep) = polyToHtml :: (String, TH.Type) -> Html () polyToHtml (name, ty) = - li_ do + li_ [id_ (nameToElementId name), class_ "searchable"] $ do code_ do strong_ $ toHtml name em_ " :: "