fix tags with spaces

add a filter that replaces tags e.g.
"tag name" → {name: "tag name", slug: "tag_name"}
This commit is contained in:
rhiannon morris 2022-09-17 20:54:16 +02:00
parent 5108acba61
commit be46a2fc5c
9 changed files with 87 additions and 54 deletions

View File

@ -29,9 +29,10 @@ LAANTAS_SCRIPT = $(TMPDIR)/laantas-script
ALL_TAGS = $(TMPDIR)/all-tags ALL_TAGS = $(TMPDIR)/all-tags
POST_LISTS = $(TMPDIR)/post-lists POST_LISTS = $(TMPDIR)/post-lists
NICE_DATE = $(TMPDIR)/nice-date NICE_DATE = $(TMPDIR)/nice-date
SLUG_TAGS = $(TMPDIR)/slug-tags
EXECS = \ EXECS = \
$(LANGFILTER) $(LAANTAS_SCRIPT) \ $(LANGFILTER) $(LAANTAS_SCRIPT) \
$(ALL_TAGS) $(POST_LISTS) $(NICE_DATE) $(ALL_TAGS) $(POST_LISTS) $(NICE_DATE) $(SLUG_TAGS)
CABAL_FLAGS ?= -O -v0 CABAL_FLAGS ?= -O -v0
@ -67,7 +68,8 @@ define pandoc
pandoc -s --toc --template $(TEMPLATEDIR)/$(1).html -o $@ $< \ pandoc -s --toc --template $(TEMPLATEDIR)/$(1).html -o $@ $< \
-f markdown+emoji \ -f markdown+emoji \
$(SYNTAXFLAGS) \ $(SYNTAXFLAGS) \
--filter $(LANGFILTER) --filter $(NICE_DATE) --mathjax --filter $(LANGFILTER) --filter $(NICE_DATE) --filter $(SLUG_TAGS) \
--mathjax
endef endef
@ -89,6 +91,7 @@ $(LAANTAS_SCRIPT): lang/laantas-script/* ; $(call cabal-exe)
$(ALL_TAGS): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:) $(ALL_TAGS): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:)
$(POST_LISTS): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:) $(POST_LISTS): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:)
$(NICE_DATE): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:) $(NICE_DATE): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:)
$(SLUG_TAGS): $(BLOG_META_DEPS) ; $(call cabal-exe,blog-meta:)
define cabal-exe define cabal-exe
@echo "[build] $(notdir $@)" @echo "[build] $(notdir $@)"

View File

@ -15,7 +15,6 @@ import qualified YAML
import System.Environment import System.Environment
import qualified Data.Text.IO as Text import qualified Data.Text.IO as Text
import qualified Data.Text as Text import qualified Data.Text as Text
import Data.Char
main :: IO () main :: IO ()
@ -36,8 +35,8 @@ getTags file = do
makeYAML :: [Set Text] -> LazyBS.ByteString makeYAML :: [Set Text] -> LazyBS.ByteString
makeYAML tags = "---\n" <> yaml <> "\n...\n" where makeYAML tags = "---\n" <> yaml <> "\n...\n" where
yaml = YAML.encode1 $ YAML.obj yaml = YAML.encode1 $ YAML.obj
[("title" ##= YAML.str "all tags"), [("title" ##= YAML.str "all tags"),
("tags" ##= collate tags)] ("all-tags" ##= collate tags)]
makeMake :: [Set Text] -> Text makeMake :: [Set Text] -> Text
makeMake tags' = Text.unlines $ build : allPosts : map makeRule tags where makeMake tags' = Text.unlines $ build : allPosts : map makeRule tags where
@ -46,7 +45,7 @@ makeMake tags' = Text.unlines $ build : allPosts : map makeRule tags where
t <- ["all-tags", "index"] <> map slug' tags] t <- ["all-tags", "index"] <> map slug' tags]
makeRule' opt title file = makeRule' opt title file =
"$(TMPDIR)/" <> file <> ".md : $(POSTS) $(POST_LISTS)\n\ "$(TMPDIR)/" <> file <> ".md : $(POSTS) $(POST_LISTS)\n\
\\t@echo \"[post-lists] $<\"\n\ \\t@echo \"[post-lists] " <> file <> "\"\n\
\\t$(POST_LISTS) " <> opt <> " --out $@ \\\n\ \\t$(POST_LISTS) " <> opt <> " --out $@ \\\n\
\\t $(POSTSDIR) \"" <> title <> "\"" \\t $(POSTSDIR) \"" <> title <> "\""
allPosts = makeRule' "" "all posts" "index" allPosts = makeRule' "" "all posts" "index"
@ -70,25 +69,19 @@ instance YAML.ToYAML Tag where
[("name" ##= name), ("slug" ##= slug), ("count" ##= count)] [("name" ##= name), ("slug" ##= slug), ("count" ##= count)]
collate :: [Set Text] -> [Tag] collate :: [Set Text] -> [Tag]
collate tags = collate =
toList $ fst $ foldl' add1 (mempty, mempty) $ foldMap toList tags toList . foldl' add1 mempty . foldMap toList
where where
add1 (tags, slugs) name add1 tags name = Map.alter (add1' tag) (slug tag) tags
| Map.member name tags = where tag = makeTag name
(Map.adjust incrCount name tags, slugs)
| otherwise = add1' new Nothing = Just new
let tag = makeTag slugs name in add1' new (Just old)
(Map.insert name tag tags, | name new == name old = Just (old {count = succ (count old)})
Set.insert (slug tag) slugs) | otherwise = error $ "slug collision between " <> show (name old) <>
makeTag slugs name = " and " <> show (name new)
Tag {name, slug = makeSlug slugs name, count = 1}
makeSlug slugs name = head $ filter (`notElem` slugs) candidates where makeTag name = Tag {name, slug = makeSlug name, count = 1}
slug = Text.map toSlugChar name
toSlugChar c
| isAlphaNum c && isAscii c || c == '-' = toLower c
| otherwise = '_'
candidates = slug : [slug <> Text.pack (show i) | i <- [(0 :: Int) ..]]
incrCount t@(Tag {count}) = t {count = succ count}
data Options = data Options =
Opts { Opts {

View File

@ -50,3 +50,8 @@ executable nice-date
import: deps, exe import: deps, exe
hs-source-dirs: . hs-source-dirs: .
main-is: nice-date.hs main-is: nice-date.hs
executable slug-tags
import: deps, exe
hs-source-dirs: .
main-is: slug-tags.hs

View File

@ -3,6 +3,10 @@ module Misc where
import qualified System.Console.GetOpt as GetOpt import qualified System.Console.GetOpt as GetOpt
import System.Environment import System.Environment
import System.Exit import System.Exit
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Char (isAlphaNum, isAscii, toLower)
import Text.Pandoc.Definition
-- | exception on 'Left' -- | exception on 'Left'
unwrap :: Show a => FilePath -> Either a b -> IO b unwrap :: Show a => FilePath -> Either a b -> IO b
@ -20,3 +24,34 @@ getOptionsWith hdr mkDef descrs = do
putStrLn $ GetOpt.usageInfo (hdr prog) descrs putStrLn $ GetOpt.usageInfo (hdr prog) descrs
exitFailure exitFailure
makeSlug :: Text -> Text
makeSlug name = Text.map toSlugChar name where
toSlugChar c
| isAlphaNum c && isAscii c || c == '-' = toLower c
| otherwise = '_'
toTextList :: MetaValue -> Maybe [Text]
toTextList (MetaList vs) = traverse toText vs
toTextList _ = Nothing
toText :: MetaValue -> Maybe Text
toText (MetaString str) = Just str
toText (MetaInlines is) = foldMap inlineText is
toText (MetaBlocks bs) = foldMap blockText bs
toText _ = Nothing
inlineText :: Inline -> Maybe Text
inlineText (Str txt) = Just txt
inlineText Space = Just " "
inlineText SoftBreak = Just " "
inlineText LineBreak = Just " "
inlineText (RawInline _ txt) = Just txt
inlineText _ = Nothing
blockText :: Block -> Maybe Text
blockText (Plain is) = foldMap inlineText is
blockText (Para is) = foldMap inlineText is
blockText Null = Just ""
blockText (RawBlock _ txt) = Just txt
blockText _ = Nothing

View File

@ -1,19 +1,16 @@
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import Data.Time import Data.Time
import Text.Pandoc.JSON import Text.Pandoc.JSON
import Data.Text (Text, unpack, pack) import Data.Text (unpack, pack)
import Data.Char (toLower) import Data.Char (toLower)
import Misc
main :: IO () main :: IO ()
main = toJSONFilter \(Pandoc (Meta m) body) -> do main = toJSONFilter \(Pandoc (Meta m) body) -> do
m' <- niceDate m m' <- Map.alterF reformat "date" m
pure $ Pandoc (Meta m') body pure $ Pandoc (Meta m') body
niceDate :: Map Text MetaValue -> IO (Map Text MetaValue)
niceDate = Map.alterF reformat "date"
reformat :: Maybe MetaValue -> IO (Maybe MetaValue) reformat :: Maybe MetaValue -> IO (Maybe MetaValue)
reformat Nothing = pure Nothing reformat Nothing = pure Nothing
reformat (Just (toText -> Just txt)) = do reformat (Just (toText -> Just txt)) = do
@ -22,24 +19,3 @@ reformat (Just (toText -> Just txt)) = do
let str = formatTime defaultTimeLocale "%A %-e %B %Y" (date :: Day) let str = formatTime defaultTimeLocale "%A %-e %B %Y" (date :: Day)
pure $ Just $ MetaString $ pack $ map toLower str pure $ Just $ MetaString $ pack $ map toLower str
reformat (Just d) = fail $ "date is\n" <> show d <> "\nwanted a string" reformat (Just d) = fail $ "date is\n" <> show d <> "\nwanted a string"
toText :: MetaValue -> Maybe Text
toText (MetaString str) = Just str
toText (MetaInlines is) = foldMap inlineText is
toText (MetaBlocks bs) = foldMap blockText bs
toText _ = Nothing
inlineText :: Inline -> Maybe Text
inlineText (Str txt) = Just txt
inlineText Space = Just " "
inlineText SoftBreak = Just " "
inlineText LineBreak = Just " "
inlineText (RawInline _ txt) = Just txt
inlineText _ = Nothing
blockText :: Block -> Maybe Text
blockText (Plain is) = foldMap inlineText is
blockText (Para is) = foldMap inlineText is
blockText Null = Just ""
blockText (RawBlock _ txt) = Just txt
blockText _ = Nothing

21
blog-meta/slug-tags.hs Normal file
View File

@ -0,0 +1,21 @@
import Text.Pandoc.Definition
import qualified Data.Map.Strict as Map
import Text.Pandoc.JSON
import Data.Text (Text)
import Misc
main :: IO ()
main = toJSONFilter \(Pandoc (Meta m) body) -> do
m' <- Map.alterF addSlugs "tags" m
pure $ Pandoc (Meta m') body
addSlugs :: Maybe MetaValue -> IO (Maybe MetaValue)
addSlugs Nothing = pure Nothing
addSlugs (Just (toTextList -> Just tags)) =
pure $ Just $ MetaList $ map addSlug1 tags
addSlugs (Just t) = fail $
"'tags' is\n" <> show t <> "\nwanted a list of strings"
addSlug1 :: Text -> MetaValue
addSlug1 tag = MetaMap $ Map.fromList
[("name", MetaString tag), ("slug", MetaString $ makeSlug tag)]

View File

@ -8,7 +8,7 @@ $endif$
$if(posts)$ $if(posts)$
$postlist()$ $postlist()$
$elseif(tags)$ $elseif(all-tags)$
$taglist()$ $taglist()$
$endif$ $endif$

View File

@ -13,7 +13,7 @@ $head()$
tags: tags:
<ul> <ul>
$for(tags)$ $for(tags)$
<li><a href=/tag-$it$.html>$it$</a> <li><a href=/tag-$it.slug$.html>$it.name$</a>
$endfor$ $endfor$
</ul> </ul>
</nav> </nav>

View File

@ -1,6 +1,6 @@
<main> <main>
<ul class=tag-list> <ul class=tag-list>
$for(tags)$ $for(all-tags)$
<li> <li>
<a href=tag-$it.slug$.html>$it.name$</a> <a href=tag-$it.slug$.html>$it.name$</a>
<span class=count>($it.count$)</span> <span class=count>($it.count$)</span>