Skip to content

Commit

Permalink
Merge pull request srid#117 from srid/query-refactor
Browse files Browse the repository at this point in the history
Refactor query types
  • Loading branch information
srid authored Apr 19, 2020
2 parents 9eb34bd + d2bed0b commit 58c1db4
Show file tree
Hide file tree
Showing 18 changed files with 300 additions and 170 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
os: [macos-latest, ubuntu-latest]
steps:
- uses: actions/checkout@v2
- uses: cachix/install-nix-action@v7
- uses: cachix/install-nix-action@v8
# This also runs nix-build.
- uses: cachix/cachix-action@v4
- uses: cachix/cachix-action@v5
with:
name: srid
signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}'
Expand Down
2 changes: 1 addition & 1 deletion bin/run
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
set -xe
nix-shell --run "ghcid -c 'cabal new-repl exe:neuron' -T \":main $*\""
nix-shell --pure --run "ghcid -c 'cabal new-repl exe:neuron' -T \":main $*\""
2 changes: 1 addition & 1 deletion bin/test
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env bash
set -xe
nix-shell --run "ghcid -c 'cabal new-repl test:neuron-test' -T \":main $*\""
nix-shell --pure --run "ghcid $* -c 'cabal new-repl test:neuron-test' -T \":main $*\""
6 changes: 5 additions & 1 deletion default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ let
# revision you would like to upgrade to and set it here. Consult rib's
# ChangeLog.md to check any notes on API migration.
ribRevision = "2dcd420";
nixpkgsRev = "10100a97c896";
projectRoot = ./.;
in {
# Rib library source to use
Expand All @@ -13,7 +14,7 @@ in {
, name ? "neuron"
, gitRev ? ""
, source-overrides ? {}
, pkgs ? import <nixpkgs> {}
, pkgs ? import (builtins.fetchTarball "https://github.com/nixos/nixpkgs/archive/${nixpkgsRev}.tar.gz") {}
, ...
}:

Expand Down Expand Up @@ -53,6 +54,7 @@ let
version = "${neuronRev}"
EOF
'';
dsumSrc = builtins.fetchTarball "https://github.com/obsidiansystems/dependent-sum/archive/73ab6cb.tar.gz";

in import rib {
inherit name additional-packages;
Expand All @@ -61,6 +63,8 @@ in import rib {
neuron = neuronRoot;
# Until https://github.com/obsidiansystems/which/pull/6 is merged
which = builtins.fetchTarball "https://github.com/srid/which/archive/5061a97.tar.gz";
dependent-sum = dsumSrc + "/dependent-sum";
dependent-sum-template = dsumSrc + "/dependent-sum-template";
} // source-overrides;
overrides = self: super: with pkgs.haskell.lib; {
# We must add neuron-search as a runtime dependency to the 'neuron'
Expand Down
7 changes: 5 additions & 2 deletions neuron.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: 2.4
name: neuron
-- This version must be in sync with what's in Default.dhall
version: 0.3.0.1
version: 0.3.1.0
license: BSD-3-Clause
copyright: 2020 Sridhar Ratnakumar
maintainer: srid@srid.ca
Expand Down Expand Up @@ -59,7 +59,10 @@ common library-common
dhall >= 1.30,
which,
unix,
megaparsec >= 8.0
megaparsec >= 8.0,
some,
dependent-sum,
dependent-sum-template

library
import: library-common
Expand Down
15 changes: 13 additions & 2 deletions src/Neuron/CLI.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
Expand All @@ -11,6 +12,7 @@ where

import Data.Aeson
import qualified Data.Aeson.Text as Aeson
import Data.Some
import Development.Shake (Action)
import Neuron.CLI.New (newZettelFile)
import Neuron.CLI.Rib
Expand Down Expand Up @@ -56,10 +58,19 @@ runWith act App {..} = do
putStrLn indexHtmlPath
let opener = if os == "darwin" then "open" else "xdg-open"
liftIO $ executeFile opener True [indexHtmlPath] Nothing
Query queries ->
Query q ->
runRibOnceQuietly notesDir $ do
store <- Z.mkZettelStore =<< Rib.forEvery ["*.md"] pure
putLTextLn $ Aeson.encodeToLazyText $ zettelJsonWith <$> Z.runQuery store queries
case q of
Some (Z.Query_ZettelByID zid) -> do
let res = Z.lookupStore zid store
putLTextLn $ Aeson.encodeToLazyText $ zettelJsonWith res
Some (Z.Query_ZettelsByTag pats) -> do
let res = Z.runQuery store (Z.Query_ZettelsByTag pats)
putLTextLn $ Aeson.encodeToLazyText $ zettelJsonWith <$> res
Some (Z.Query_Tags pats) -> do
let res = Z.runQuery store (Z.Query_Tags pats)
putLTextLn $ Aeson.encodeToLazyText res
Search searchCmd ->
runSearch notesDir searchCmd
where
Expand Down
7 changes: 3 additions & 4 deletions src/Neuron/CLI/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Neuron.CLI.Types
)
where

import Data.Some
import qualified Neuron.Zettelkasten.Query as Z
import qualified Neuron.Zettelkasten.Tag as Z
import Options.Applicative
Expand All @@ -28,7 +29,6 @@ data App = App
{ notesDir :: FilePath,
cmd :: Command
}
deriving (Eq, Show)

data NewCommand = NewCommand {title :: Text, edit :: Bool}
deriving (Eq, Show)
Expand All @@ -52,10 +52,9 @@ data Command
| -- | Search a zettel by title
Search SearchCommand
| -- | Run a query against the Zettelkasten
Query [Z.Query]
Query (Some Z.Query)
| -- | Delegate to Rib's command parser
Rib RibConfig
deriving (Eq, Show)

data RibConfig = RibConfig
{ ribOutputDir :: Maybe FilePath,
Expand Down Expand Up @@ -95,7 +94,7 @@ commandParser defaultNotesDir = do
pure Open
queryCommand =
fmap Query $
many (Z.Query_ZettelsByTag . Z.TagPattern <$> option str (long "tag" <> short 't'))
fmap (Some . Z.Query_ZettelsByTag) (many (Z.mkTagPattern <$> option str (long "tag" <> short 't')))
<|> option uriReader (long "uri" <> short 'u')
searchCommand = do
searchBy <-
Expand Down
13 changes: 7 additions & 6 deletions src/Neuron/Web/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import qualified Neuron.Web.Theme as Theme
import Neuron.Zettelkasten.Graph
import Neuron.Zettelkasten.ID (ZettelID (..), zettelIDSourceFileName, zettelIDText)
import Neuron.Zettelkasten.Link.Theme (LinkTheme (..))
import Neuron.Zettelkasten.Link.View (zLinkExt, renderZettelLink)
import Neuron.Zettelkasten.Link.View (neuronLinkExt, renderZettelLink)
import Neuron.Zettelkasten.Markdown (neuronMMarkExts)
import Neuron.Zettelkasten.Query
import Neuron.Zettelkasten.Store
Expand Down Expand Up @@ -112,7 +112,7 @@ renderIndex Config {..} (store, graph) = do
script_ helloScript
where
-- Sort clusters with newer mother zettels appearing first.
sortMothers ms = reverse $ sortOn maximum $ fmap (reverse . sort . toList) ms
sortMothers ms = sortOn (Down . maximum) $ fmap (sortOn Down . toList) ms
countNounBe noun nounPlural = \case
1 -> "is 1 " <> noun
n -> "are " <> show n <> " " <> nounPlural
Expand All @@ -124,7 +124,7 @@ renderSearch store = do
input_ [type_ "text", id_ "search-input"]
fa "search icon fas fa-search"
div_ [class_ "ui hidden divider"] mempty
let allZettels = runQuery store []
let allZettels = runQuery store $ Query_ZettelsByTag []
allTags = Set.fromList $ concatMap zettelTags allZettels
index = object ["zettels" .= fmap (object . zettelJson) allZettels, "tags" .= allTags]
div_ [class_ "ui fluid multiple search selection dropdown", id_ "search-tags"] $ do
Expand All @@ -148,7 +148,7 @@ renderZettel config@Config {..} (store, graph) zid = do
div_ [class_ "ui top attached segment"] $ do
h1_ [class_ "header"] $ toHtml zettelTitle
let mmarkExts = neuronMMarkExts config
MMark.render $ useExtensions (zLinkExt store : mmarkExts) zettelContent
MMark.render $ useExtensions (neuronLinkExt store : mmarkExts) zettelContent
whenNotNull zettelTags $ \_ ->
renderTags zettelTags
div_ [class_ $ "ui inverted " <> Theme.semanticColor neuronTheme <> " top attached connections segment"] $ do
Expand Down Expand Up @@ -223,8 +223,9 @@ renderForest isRoot maxLevel ltheme s g trees =
let zettelDiv =
div_
[class_ $ bool "" "ui black label" $ ltheme == LinkTheme_Default]
bool id zettelDiv isRoot $
renderZettelLink ltheme $ lookupStore zid s
bool id zettelDiv isRoot
$ renderZettelLink ltheme
$ lookupStore zid s
when (ltheme == LinkTheme_Default) $ do
" "
case backlinks zid g of
Expand Down
7 changes: 4 additions & 3 deletions src/Neuron/Zettelkasten/Graph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Tree (Forest, Tree (..))
import Neuron.Zettelkasten.ID
import Neuron.Zettelkasten.Link (zLinkConnections, mkZLink)
import Neuron.Zettelkasten.Link (neuronLinkConnections, neuronLinkFromMarkdownLink)
import Neuron.Zettelkasten.Markdown (extractLinks)
import Neuron.Zettelkasten.Store (ZettelStore)
import Neuron.Zettelkasten.Zettel
Expand Down Expand Up @@ -64,8 +64,9 @@ mkZettelGraph store =
fmap (first pure) . outgoingLinks
outgoingLinks :: Zettel -> [(Connection, ZettelID)]
outgoingLinks Zettel {..} =
(maybe [] (zLinkConnections store) . mkZLink)
`concatMap` extractLinks zettelContent
flip concatMap (extractLinks zettelContent) $ \mlink ->
let nlink = either error id $ neuronLinkFromMarkdownLink mlink
in maybe [] (neuronLinkConnections store) nlink

-- | Return the backlinks to the given zettel
backlinks :: ZettelID -> ZettelGraph -> [ZettelID]
Expand Down
108 changes: 70 additions & 38 deletions src/Neuron/Zettelkasten/Link.hs
Original file line number Diff line number Diff line change
@@ -1,60 +1,92 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- | Special Zettel links in Markdown
module Neuron.Zettelkasten.Link where

import Control.Monad.Except
import Data.Some
import Neuron.Zettelkasten.ID
import Neuron.Zettelkasten.Link.Theme
import Neuron.Zettelkasten.Markdown (MarkdownLink (..))
import Neuron.Zettelkasten.Query (Query (..), queryFromURI, runQuery)
import Neuron.Zettelkasten.Query (Query (..), queryFromMarkdownLink, runQuery)
import Neuron.Zettelkasten.Store
import Neuron.Zettelkasten.Tag
import Neuron.Zettelkasten.Zettel
import Relude
import qualified Text.URI as URI

-- | A ZLink is a special link supported by Neuron
--
-- z:, zcf:, zquery: and zcfquery:
data ZLink
= ZLink_ConnectZettel Connection ZettelID
| -- | Render a list (or should it be tree?) of links to queries zettels
ZLink_QueryZettels Connection LinkTheme [Query]
deriving (Eq, Show)

mkZLink :: HasCallStack => MarkdownLink -> Maybe ZLink
mkZLink MarkdownLink {markdownLinkUri = uri, markdownLinkText = linkText} =
-- NOTE: We should probably drop the 'cf' variants in favour of specifying
-- the connection type as a query param or something.
case fmap URI.unRText (URI.uriScheme uri) of
Just "z" ->
-- The inner link text is supposed to be the zettel ID
let zid = parseZettelID linkText
in Just $ ZLink_ConnectZettel Folgezettel zid
Just "zcf" ->
-- The inner link text is supposed to be the zettel ID
let zid = parseZettelID linkText
in Just $ ZLink_ConnectZettel OrdinaryConnection zid
Just "zquery" ->
Just $ ZLink_QueryZettels Folgezettel (linkThemeFromURI uri) (either error id $ queryFromURI uri)
Just "zcfquery" ->
Just $ ZLink_QueryZettels OrdinaryConnection (linkThemeFromURI uri) (either error id $ queryFromURI uri)
_ -> do
let uriS = URI.render uri
guard $ uriS == linkText
zid <- rightToMaybe $ parseZettelID' uriS
pure $ ZLink_ConnectZettel Folgezettel zid

-- | The connections referenced in a zlink.
zLinkConnections :: ZettelStore -> ZLink -> [(Connection, ZettelID)]
zLinkConnections store = \case
ZLink_ConnectZettel conn zid ->
type family QueryConnection q

type instance QueryConnection Zettel = Connection

type instance QueryConnection [Zettel] = Connection

type instance QueryConnection [Tag] = ()

type family QueryViewTheme q

type instance QueryViewTheme Zettel = LinkTheme

type instance QueryViewTheme [Zettel] = LinkTheme

type instance QueryViewTheme [Tag] = ()

-- TODO: Refactor to be a GADT using Some, and derive GEq, etc. correctly
data NeuronLink
= forall r.
(Show (Query r), Show (QueryConnection r), Show (QueryViewTheme r)) =>
NeuronLink (Query r, QueryConnection r, QueryViewTheme r)

deriving instance Show NeuronLink

instance Eq NeuronLink where
(==) (NeuronLink (Query_ZettelByID zid1, c1, t1)) (NeuronLink (Query_ZettelByID zid2, c2, t2)) =
and [zid1 == zid2, c1 == c2, t1 == t2]
(==) (NeuronLink (Query_ZettelsByTag p1, c1, t1)) (NeuronLink (Query_ZettelsByTag p2, c2, t2)) =
and [p1 == p2, c1 == c2, t1 == t2]
(==) (NeuronLink (Query_Tags p1, c1, t1)) (NeuronLink (Query_Tags p2, c2, t2)) =
and [p1 == p2, c1 == c2, t1 == t2]
(==) _ _ =
False

neuronLinkFromMarkdownLink :: MonadError Text m => MarkdownLink -> m (Maybe NeuronLink)
neuronLinkFromMarkdownLink ml@MarkdownLink {markdownLinkUri = uri} = do
queryFromMarkdownLink ml >>= \case
Nothing -> pure Nothing
Just someQ -> Just <$> do
withSome someQ $ \q -> case q of
Query_ZettelByID _ ->
pure $ NeuronLink (q, connectionFromURI uri, linkThemeFromURI uri)
Query_ZettelsByTag _ ->
pure $ NeuronLink (q, connectionFromURI uri, linkThemeFromURI uri)
Query_Tags _ ->
pure $ NeuronLink (q, (), ())

neuronLinkConnections :: ZettelStore -> NeuronLink -> [(Connection, ZettelID)]
neuronLinkConnections store = \case
NeuronLink (Query_ZettelByID zid, conn, _) ->
[(conn, zid)]
ZLink_QueryZettels conn _linkTheme q ->
NeuronLink (q@(Query_ZettelsByTag _pats), conn, _) ->
(conn,) . zettelID <$> runQuery store q
_ ->
[]

connectionFromURI :: URI.URI -> Connection
connectionFromURI uri =
fromMaybe Folgezettel $
case fmap URI.unRText (URI.uriScheme uri) of
Just scheme
| scheme `elem` ["zcf", "zcfquery"] ->
Just OrdinaryConnection
_ ->
Nothing
3 changes: 2 additions & 1 deletion src/Neuron/Zettelkasten/Link/Theme.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ data LinkTheme
| LinkTheme_WithDate
deriving (Eq, Show, Ord)

linkThemeFromURI :: URI.URI -> LinkTheme
-- TODO: MonadError
linkThemeFromURI :: HasCallStack => URI.URI -> LinkTheme
linkThemeFromURI uri =
fromMaybe LinkTheme_Default $ listToMaybe $ flip mapMaybe (URI.uriQuery uri) $ \case
URI.QueryFlag _ -> Nothing
Expand Down
Loading

0 comments on commit 58c1db4

Please sign in to comment.