diff --git a/make-pages/BuilderQQ.hs b/make-pages/BuilderQQ.hs
index 7c05879..6f8eb78 100644
--- a/make-pages/BuilderQQ.hs
+++ b/make-pages/BuilderQQ.hs
@@ -8,12 +8,14 @@ where
import Data.Char (isLower, isSpace, isDigit, isAlphaNum)
import Language.Haskell.TH
import Language.Haskell.TH.Quote
+import Data.List (intersperse)
import Data.Maybe (mapMaybe)
import Data.Text.Lazy.Builder
- (Builder, fromText, fromString, singleton, toLazyText)
+ (Builder, fromText, fromLazyText, fromString, singleton, toLazyText)
import Text.Read (readMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
+import qualified Data.Text.Lazy as LText
import Data.Text.Lazy (toStrict)
import Data.Foldable
import Data.Semigroup
@@ -21,36 +23,31 @@ import Data.Semigroup
data ChunkType = Lit | Var VarType deriving Show
data VarType =
Plain
- | FromText
- | FromString
- | FromChar
- | Show
| Reindent !Int
- | ReindentList !Int
deriving Show
type Chunk = (ChunkType, Text)
-indent :: Int -> Text -> Builder
+indent :: Int -> LText.Text -> Builder
indent i str
- | Text.all isSpace str = ""
- | otherwise = replicateB i ' ' <> fromText str
+ | LText.all isSpace str = ""
+ | otherwise = replicateB i ' ' <> fromLazyText str
-reindent :: Int -> Text -> Builder
-reindent i str =
- fold $ mapInit (<> "\n") $
- map2 (fromText . dropIndent) (indent i . dropIndent) ls
+reindentB :: Int -> Builder -> Builder
+reindentB i (toLazyText -> str) =
+ fold $ intersperse "\n" $
+ map2 (fromLazyText . dropIndent) (indent i . dropIndent) ls
where
- ls = dropWhile (Text.all isSpace) $ Text.lines str
- ls' = filter (Text.any $ not . isSpace) ls
+ ls = dropWhile (LText.all isSpace) $ LText.lines str
+ ls' = filter (LText.any $ not . isSpace) ls
- dropIndent = Text.drop minIndent
+ dropIndent = LText.drop minIndent
minIndent =
getMin $ option 0 id $ foldMap (Option . Just . Min . indentOf) ls'
indentOf = go 0 where
- go n (' ' :. cs) = go (n + 1) cs
- go n ('\t' :. cs) = go (((n `mod` 8) + 1) * 8) cs
+ go n (' ' :.. cs) = go (n + 1) cs
+ go n ('\t' :.. cs) = go (((n `mod` 8) + 1) * 8) cs
go n _ = n
map2 _ _ [] = []
@@ -68,38 +65,12 @@ chunks = reverse . go "" [] . trimEnd where
-- $$: expands to one $
go acc cs ('$' :. '$' :. rest) = go (acc <> "$") cs rest
- -- $*var: expands to (fromText $var)
- go acc cs ('$' :. '*' :. rest) =
- go "" ((Var FromText, var) : lit acc : cs) rest2
- where (var, rest2) = splitVar rest
-
- -- $@var: expands to (fromString $var)
- go acc cs ('$' :. '@' :. rest) =
- go "" ((Var FromString, var) : lit acc : cs) rest2
- where (var, rest2) = splitVar rest
-
- -- $'var: expands to (singleton $var)
- go acc cs ('$' :. '\'' :. rest) =
- go "" ((Var FromChar, var) : lit acc : cs) rest2
- where (var, rest2) = splitVar rest
-
- -- $^var: expands to (fromString (show $var))
- go acc cs ('$' :. '^' :. rest) =
- go "" ((Var Show, var) : lit acc : cs) rest2
- where (var, rest2) = splitVar rest
-
- -- $n*var (n a number): expands to builder var indented by n
- -- $n.var: same but var is a list
+ -- $n.var (n a number): expands to builder var indented by n
go acc cs ('$' :. rest@(d :. _)) | isDigit d =
- go "" ((Var ty, var) : lit acc : cs) rest3
+ go "" ((Var (Reindent n), var) : lit acc : cs) rest3
where
- (n', c :. rest2) = Text.span isDigit rest
- n = read $ Text.unpack n'
+ ((read . Text.unpack -> n), '.' :. rest2) = Text.span isDigit rest
(var, rest3) = splitVar rest2
- ty = case c of
- '*' -> Reindent n
- '.' -> ReindentList n
- _ -> error $ "unknown reindent type " ++ show c
-- $var: expands to that var's contents
go acc cs ('$' :. rest) =
@@ -132,35 +103,18 @@ toStrictText = toStrict . toLazyText
chunksToExpQ :: [Chunk] -> ExpQ
-chunksToExpQ cs = [|mconcat $es|] where
+chunksToExpQ cs = [|mconcat $es :: Builder|] where
es = listE $ mapMaybe chunk1 cs
chunk1 (Lit, "") = Nothing
chunk1 (Lit, lit) = Just $ stringE $ Text.unpack lit
chunk1 (Var t, name) = Just $ case t of
- Plain -> var
- FromText -> [|fromText $var|]
- FromString -> [|fromString $var|]
- FromChar -> [|singleton $var|]
- Show -> [|fromString $ show $var|]
+ Plain -> [|build $var|]
Reindent n -> [|reindent n $var|]
- ReindentList n -> [|reindentList n $var|]
where var = varE (mkName $ Text.unpack name)
-reindentList :: Int -> [Builder] -> Builder
-reindentList n = fold . mapInit (<> "\n") . mapTail (replicateB n ' ' <>)
-
replicateB :: Int -> Char -> Builder
replicateB n c = fromText $ Text.replicate n $ Text.singleton c
-mapInit :: (a -> a) -> [a] -> [a]
-mapInit _ [] = []
-mapInit _ [x] = [x]
-mapInit f (x:xs) = f x : mapInit f xs
-
-mapTail :: (a -> a) -> [a] -> [a]
-mapTail _ [] = []
-mapTail f (x:xs) = x : map f xs
-
b :: QuasiQuoter
b = QuasiQuoter {
quoteExp = chunksToExpQ . chunksWithReindent,
@@ -181,6 +135,12 @@ pattern c :. t <- (Text.uncons -> Just (c, t))
{-# COMPLETE NilT, (:.) :: Text #-}
+infixr 5 :..
+pattern (:..) :: Char -> LText.Text -> LText.Text
+pattern c :.. t <- (LText.uncons -> Just (c, t))
+ where c :.. t = LText.cons c t
+
+
fromChar :: Char -> Builder
fromChar = singleton
@@ -198,3 +158,25 @@ escId = foldMap esc1 . Text.unpack where
| otherwise = fromChar c
latin1Special c =
c <= 'ΓΏ' && not (isAlphaNum c) && c /= '-'
+
+
+class CanBuild a where
+ build :: a -> Builder
+ reindent :: Int -> a -> Builder
+ reindent i = reindentB i . build
+
+instance CanBuild Builder where build = id
+instance CanBuild Text where build = fromText
+instance CanBuild LText.Text where build = fromLazyText
+instance CanBuild Char where build = singleton
+instance CanBuild String where build = fromString
+
+newtype ShowBuild a = ShowBuild a deriving newtype Show
+instance Show a => CanBuild (ShowBuild a) where build = build . show
+
+deriving via ShowBuild Int instance CanBuild Int
+deriving via ShowBuild Integer instance CanBuild Integer
+
+instance {-# OVERLAPPABLE #-} CanBuild a => CanBuild [a] where
+ build = foldMap \x -> build x <> "\n"
+ reindent n = fold . intersperse ("\n" <> replicateB n ' ') . map build
diff --git a/make-pages/Depend.hs b/make-pages/Depend.hs
index 9b5d035..df3db9c 100644
--- a/make-pages/Depend.hs
+++ b/make-pages/Depend.hs
@@ -23,7 +23,7 @@ dependSingle yamlDir info prefix build nsfw =
dependSingle' :: FilePath -> Info -> FilePath -> FilePath -> Bool -> Builder
dependSingle' yamlDir info prefix build nsfw =
- [b|$@page: $@deps $$(MAKEPAGES)|]
+ [b|$page: $deps $$(MAKEPAGES)|]
where
images = if nsfw then #images info else #sfwImages info
@@ -50,13 +50,13 @@ dependGallery' :: GalleryInfo -> FilePath -> [(FilePath, Info)]
-> FilePath -> FilePath -> FilePath -> Builder
dependGallery' (GalleryInfo {prefix, filters})
indexFile infos' build data_ tmp = [b|@0
- $@index: $@gallery
+ $index: $gallery
- $@gallery: $@pages' $@files' $@rss $@indexFile $$(MAKEPAGES)
- $$(call gallery,$@indexFile,$@prefix)
+ $gallery: $pages' $files' $rss $indexFile $$(MAKEPAGES)
+ $$(call gallery,$indexFile,$prefix)
- $@rss: $@files' $@indexFile $$(MAKEPAGES)
- $$(call rss,$@indexFile,$@prefix,$@data_)
+ $rss: $files' $indexFile $$(MAKEPAGES)
+ $$(call rss,$indexFile,$prefix,$data_)
$rules
@@ -80,7 +80,7 @@ dependGallery' (GalleryInfo {prefix, filters})
inc d = tmp > prefix > takeDirectory d <.> "mk"
incFiles = unwords $ map inc files
- incs = if null infos then "" else [b|include $@incFiles|]
+ incs = if null infos then "" else [b|include $incFiles|]
makeRules :: FilePath -- ^ prefix
-> GalleryFilters
@@ -89,16 +89,16 @@ makeRules :: FilePath -- ^ prefix
-> FilePath -- ^ tmp dir
-> Builder
makeRules prefix filters build data_ tmp = [b|@0
- $@buildPrefix/%/index.html: $@data_/%/info.yaml $$(MAKEPAGES)
- $$(call single,$@data_,$@prefix,$flags)
+ $buildPrefix/%/index.html: $data_/%/info.yaml $$(MAKEPAGES)
+ $$(call single,$data_,$prefix,$flags)
- $@tmpPrefix/%.mk: $@data_/%/info.yaml $$(MAKEPAGES)
- $$(call depend-single,$@prefix,$@build,$@data_,$flags)
+ $tmpPrefix/%.mk: $data_/%/info.yaml $$(MAKEPAGES)
+ $$(call depend-single,$prefix,$build,$data_,$flags)
- $@buildPrefix/%: $@tmp/%
+ $buildPrefix/%: $tmp/%
$$(call copy,-l)
- $@buildPrefix/%: $@data_/%
+ $buildPrefix/%: $data_/%
$$(call copy)
|]
where
diff --git a/make-pages/GalleryPage.hs b/make-pages/GalleryPage.hs
index 5b16cf0..f9c3263 100644
--- a/make-pages/GalleryPage.hs
+++ b/make-pages/GalleryPage.hs
@@ -28,20 +28,20 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
-
-
-
-
+
+
+
+
-
$*title
+ $title
- $*title
+ $title
@@ -74,7 +74,7 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
|]
where
@@ -100,7 +100,7 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
nsfw = #nsfw filters /= NoNsfw
- url = [b|$*root/$@prefix|]
+ url = [b|$root/$prefix|]
imagepath0
| (_, (p0, i0) : _) : _ <- infosByYear = getThumb (takeDirectory p0) i0
| otherwise = "/style/card.png"
@@ -108,13 +108,13 @@ make' root (GalleryInfo {title, desc, prefix, filters, hidden}) infos = [b|@0
makeFilter :: Text -> HashSet Text -> Text -> Int -> Builder
makeFilter prefix initial tag _count = [b|@8
-
-
+
+
|]
where
- id' = [b|$*prefix$&_$tag'|]
+ id' = [b|$prefix$&_$tag'|]
tag' = escId tag
- checked = if HashSet.member tag initial then " checked" else ""
+ checked = if HashSet.member tag initial then [b| checked|] else ""
makeYearItems :: Bool -- ^ nsfw
-> Integer -- ^ year
@@ -127,21 +127,21 @@ makeYearItems nsfw year infos = [b|@4
|]
where
items = map (uncurry $ makeItem nsfw) infos
- year' = show year & foldMap \c -> [b|$'c|]
+ year' = show year & foldMap \c -> [b|$c|]
makeItem :: Bool -> FilePath -> Info -> Builder
makeItem nsfw file info@(Info {title, bg}) = [b|@4
|]
where
dir = takeDirectory file
thumb = getThumb dir info
- nsfw' = if nsfw && #anyNsfw info then " nsfw" else ""
+ nsfw' = if nsfw && #anyNsfw info then [b| nsfw|] else ""
tags' = fold $ intersperse ";" $ map fromText $ tagsFor nsfw info
- bgStyle = ifJust bg \col -> [b| style="background: $*col"|]
+ bgStyle = ifJust bg \col -> [b| style="background: $col"|]
diff --git a/make-pages/IndexPage.hs b/make-pages/IndexPage.hs
index e77f3ba..30db347 100644
--- a/make-pages/IndexPage.hs
+++ b/make-pages/IndexPage.hs
@@ -17,18 +17,18 @@ make' root (IndexInfo {title, desc, galleries, links, footer}) = [b|@0
-
-
-
-
+
+
+
+
- $*title
+ $title
@@ -59,22 +59,22 @@ make' root (IndexInfo {title, desc, galleries, links, footer}) = [b|@0
Nothing -> ""
Just f -> [b|@0
|]
- url = [b|$*root|]
+ url = [b|$root|]
makeItem :: GalleryInfo -> Builder
makeItem (GalleryInfo {title, desc, prefix, filters}) = [b|@6
- $*title
+ $title
|]
- where nsfw = if hasNsfw filters then " class=nsfw" else ""
+ where nsfw = if hasNsfw filters then [b| class=nsfw|] else ""
makeLink :: Link -> Builder
makeLink (Link {title, url, nsfw}) = [b|@6
- $*title
+ $title
|]
- where nsfw' = if nsfw then " class=nsfw" else ""
+ where nsfw' = if nsfw then [b| class=nsfw|] else ""
hasNsfw :: GalleryFilters -> Bool
hasNsfw (GalleryFilters {nsfw}) = nsfw /= NoNsfw
diff --git a/make-pages/Info.hs b/make-pages/Info.hs
index 2a1cce9..6cebea4 100644
--- a/make-pages/Info.hs
+++ b/make-pages/Info.hs
@@ -21,6 +21,7 @@ import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import qualified Data.Map.Strict as Map
import Data.Maybe (isJust, isNothing)
+import Data.List (nub)
import Data.Ord (comparing)
import Data.String (IsString)
import Data.Text (Text)
@@ -108,7 +109,7 @@ descFor :: Bool -> Info -> Maybe Text
descFor nsfw (Info {desc, nsfwDesc}) = desc <> (guard nsfw *> nsfwDesc)
tagsFor :: Bool -> Info -> [Text]
-tagsFor nsfw i = if nsfw then #tags i <> #nsfwTags i else #tags i
+tagsFor nsfw i = if nsfw then nub (#tags i <> #nsfwTags i) else #tags i
imagesFor :: Bool -> Info -> [Image]
imagesFor nsfw = if nsfw then #images else #sfwImages
diff --git a/make-pages/RSS.hs b/make-pages/RSS.hs
index 44b2cb4..370ee64 100644
--- a/make-pages/RSS.hs
+++ b/make-pages/RSS.hs
@@ -27,9 +27,9 @@ make' root (GalleryInfo {title, desc, prefix}) output infos = [b|@0
- $*title
+ $title
$link
- $*desc
+ $desc
$selflink
$4.items
@@ -37,16 +37,16 @@ make' root (GalleryInfo {title, desc, prefix}) output infos = [b|@0
|]
where
- link = [b|$*root/$@prefix|]
+ link = [b|$root/$prefix|]
items = map (uncurry $ makeItem root prefix) $ sortOn (Down . #second) infos
selflink = case output of
Nothing -> ""
- Just o -> [b||]
+ Just o -> [b||]
makeItem :: Strict.Text -> FilePath -> FilePath -> Info -> Builder
makeItem root prefix path (Info {title, desc, date, artist}) = [b|@4
-
- $*title
+ $title
$link
$link
$descArtist'
@@ -55,11 +55,11 @@ makeItem root prefix path (Info {title, desc, date, artist}) = [b|@4
|]
where
dir = takeDirectory path
- link = [b|$*root/$@prefix/$@dir|]
+ link = [b|$root/$prefix/$dir|]
artist' = ifJust artist \case
- Artist {name, url = Nothing} -> [b|
by $*name|]
- Artist {name, url = Just url} -> [b|
by $*name|]
- desc' = ifJust desc \d -> [b|$10*d|]
+ Artist {name, url = Nothing} -> [b|
by $name|]
+ Artist {name, url = Just url} -> [b|
by $name|]
+ desc' = ifJust desc \d -> [b|$10.d|]
descArtist' = if isJust desc || isJust artist then [b|@6
|]
+ let makePrefetch (Image {path}) = [b||]
let prefetches = map (makePrefetch . #first) $ tail images
let warning' = ifJust (#warning image0) \w -> [b|@4
- cw: $*w
+ cw: $w
|]
let bgStyle = ifJust bg \col -> [b|@0
-
+
|]
- let url = [b|$*root/$@prefix/$@dir|]
+ let url = [b|$root/$prefix/$dir|]
let desc = case artist of
- Just (Artist {name}) -> [b|by $*name|]
+ Just (Artist {name}) -> [b|by $name|]
Nothing -> "by niss"
let thumb = getThumb "" info
@@ -82,10 +82,10 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
-
-
+
+
-
+
@@ -95,10 +95,10 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
$0.prefetches
- $*title
+ $title
- $*title
+ $title
$artistTag
$formattedDate
@@ -106,11 +106,10 @@ make' root prefix nsfw dataDir dir info@(Info {date, title, artist, bg}) = do
$buttonBar
-
|]
@@ -133,15 +132,15 @@ makeArtist (Artist {name, url}) =
[b|by $artistLink
|]
where
artistLink = case url of
- Just u -> [b|$*name|]
- Nothing -> [b|$*name|]
+ Just u -> [b|$name|]
+ Nothing -> [b|$name|]
makeDesc :: Maybe Strict.Text -> Builder
makeDesc mdesc = ifJust mdesc \desc -> [b|@4
about
- $8*desc
+ $8.desc
|]
@@ -163,18 +162,18 @@ makeButtonBar title images =
altButton :: Int -> Image -> Size -> Builder
altButton i (Image {label, path, nsfw, warning}) (Size {width, height}) = [b|@4
-
-
+
+
|]
where
- nsfwClass = if nsfw then " class=nsfw" else ""
- nsfwLabelClass = if nsfw then " class=nsfw-label" else ""
- checked = if i == 0 then " checked" else ""
+ nsfwClass = if nsfw then [b| class=nsfw|] else ""
+ nsfwLabelClass = if nsfw then [b| class=nsfw-label|] else ""
+ checked = if i == 0 then [b| checked|] else ""
idLabel = escId label
path' = pageFile path
- warning' = ifJust warning \w -> [b| data-warning="$*w"|]
+ warning' = ifJust warning \w -> [b| data-warning="$w"|]
makeTags :: FilePath -> [Strict.Text] -> Builder
makeTags undir tags =
@@ -188,7 +187,7 @@ makeTags undir tags =
|]
where
tagList = map makeTag tags
- makeTag tag = [b|$*tag|]
+ makeTag tag = [b|$tag|]
where tag' = escId tag
extLinks :: [Link] -> Builder
@@ -206,13 +205,13 @@ extLinks links =
extLink :: Link -> Builder
extLink (Link {title, url}) = [b|@8
-
- $*title
+
+ $title
|]
formatDate :: Day -> Builder
-formatDate date = [b|$*week $day $*month $^year|] where
+formatDate date = [b|$week $day $month $year|] where
(year, month', day') = Time.toGregorian date
week' = Time.dayOfWeek date
day = nth day'
@@ -222,12 +221,12 @@ formatDate date = [b|$*week $day $*month $^year|] where
week = Strict.words "mon tue wed thu fri sat sun" !! (fromEnum week' - 1)
nth :: Int -> Builder
-nth n = [b|$^n$suf|] where
- suf | n >= 10, n <= 19 = "th"
- | n `mod` 10 == 1 = "st"
- | n `mod` 10 == 2 = "nd"
- | n `mod` 10 == 3 = "rd"
- | otherwise = "th"
+nth n = [b|$n$suf|] where
+ suf | n >= 10, n <= 19 = [b|th|]
+ | n `mod` 10 == 1 = [b|st|]
+ | n `mod` 10 == 2 = [b|nd|]
+ | n `mod` 10 == 3 = [b|rd|]
+ | otherwise = [b|th|]
data Size = Size {width, height :: !Int} deriving (Eq, Show)
diff --git a/make-pages/make-pages.cabal b/make-pages/make-pages.cabal
index 651c4cb..0e127f1 100644
--- a/make-pages/make-pages.cabal
+++ b/make-pages/make-pages.cabal
@@ -31,8 +31,10 @@ executable make-pages
DataKinds,
DeriveAnyClass,
DerivingStrategies,
+ DerivingVia,
DuplicateRecordFields,
FlexibleInstances,
+ GeneralizedNewtypeDeriving,
LambdaCase,
NamedFieldPuns,
OverloadedLabels,
@@ -41,6 +43,7 @@ executable make-pages
PatternSynonyms,
QuasiQuotes,
RankNTypes,
+ StandaloneDeriving,
TupleSections,
TypeSynonymInstances,
ViewPatterns