lucid 2.9.13 → 2.11.20260427
raw patch · 6 files changed
Files
- CHANGELOG.md +27/−1
- README.md +10/−2
- lucid.cabal +5/−6
- src/Lucid/Base.hs +45/−35
- src/Lucid/Html5.hs +12/−0
- test/Main.hs +2/−1
CHANGELOG.md view
@@ -1,4 +1,30 @@-## 2.9.13+## Upcoming++## 2.11.20260427++* Support GHC 9.14++## 2.11.20250303++* Support GHC 9.12++## 2.11.20230408++* Don't expect Control.Monad to be re-exported from mtl anymore++## 2.11.1++* Use explicit imports for mtl, avoids the mtl-2.3 issue+* Added `minlength` attribute.+* Added `loading` attribute.++## 2.11.0++* Change internal attributes to `Seq Attribute`. This preserves+ ordering. Attributes are merged in a left-biased way, preserving the+ key order as first encountered.++## 2.10.0 * Change internal attributes representation from HashMap to Map. This introduces stable ordering, at a negligible performance cost for
README.md view
@@ -3,9 +3,12 @@ Clear to write, read and edit DSL for writing HTML -[Documentation](http://chrisdone.github.io/lucid/)+**Table of Contents** -[lucid-from-html](https://github.com/dbaynard/lucid-from-html) will convert html to the `lucid` DSL, though it is experimental.+- [Introduction](#introduction)+- [Rendering](#rendering)+- [Good to know](#good-to-know)+- [Transforming](#transforming) ## Introduction @@ -119,6 +122,11 @@ See the documentation for the `Lucid` module for information about using it as a monad transformer.++## Good to know++* Attributes are escaped, so you cannot write arbitrary JavaScript in attributes. Instead, do something like `onclick_ "foo()"`.+* Attributes are rendered in the order that they are written in your Haskell code. ## Transforming
lucid.cabal view
@@ -1,5 +1,5 @@ name: lucid-version: 2.9.13+version: 2.11.20260427 synopsis: Clear to write, read and edit DSL for HTML description: Clear to write, read and edit DSL for HTML.@@ -15,13 +15,13 @@ license: BSD3 license-file: LICENSE author: Chris Done-maintainer: chrisdone@gmail.com, oleg.grenrus@iki.fi-copyright: 2014-2017 Chris Done+maintainer: chrisdone@gmail.com+copyright: 2014-2021 Chris Done category: Web build-type: Simple cabal-version: >=1.10 extra-source-files: README.md, CHANGELOG.md-tested-with: GHC==7.10.3,GHC==8.0.2,GHC==8.2.2,GHC==8.4.4,GHC==8.6.5,GHC==8.8.4,GHC==8.10.4,GHC==9.0.1+tested-with: GHC==7.10.3,GHC==8.0.2,GHC==8.2.2,GHC==8.4.4,GHC==8.6.5,GHC==8.8.4,GHC==8.10.7,GHC==9.0.1,GHC==9.2.1, GHC==9.4.1 library default-language: Haskell2010@@ -33,7 +33,7 @@ Lucid.Bootstrap -- GHC boot libraries- build-depends: base >=4.8 && <4.16+ build-depends: base >=4.8 && <5 , bytestring >=0.10.6.0 , containers >=0.5.6.2 , transformers >=0.4.2.0@@ -50,7 +50,6 @@ build-depends: blaze-builder >=0.4.0.0 , hashable >=1.2.3.2 , mmorph >=1.0.3- , unordered-containers >=0.2.5.1 source-repository head type: git
src/Lucid/Base.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE TypeOperators #-} -- Search for UndecidableInstances to see why this is needed {-# LANGUAGE UndecidableInstances #-}@@ -44,8 +45,11 @@ import qualified Blaze.ByteString.Builder.Html.Utf8 as Blaze import Control.Applicative import Control.Monad-import Control.Monad.Morph-import Control.Monad.Reader+import Control.Monad.Morph (MFunctor(..))+import Control.Monad.Reader (MonadReader(..))+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad.Fix (MonadFix(..))+import Control.Monad.Trans (MonadTrans(..)) import Control.Monad.Error.Class (MonadError(..)) import Control.Monad.State.Class (MonadState(..)) import Control.Monad.Writer.Class (MonadWriter(..))@@ -53,7 +57,6 @@ import qualified Data.ByteString.Lazy as L import qualified Data.ByteString as S import Data.Functor.Identity-import Data.Map.Strict (Map) import qualified Data.Map.Strict as M import Data.Hashable (Hashable(..)) import Data.Semigroup (Semigroup (..))@@ -63,15 +66,21 @@ import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT import qualified Data.Text.Encoding as T-import Data.Typeable (Typeable) import Prelude+import Data.Maybe+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import Data.Foldable (toList)+import qualified Data.Set as Set -------------------------------------------------------------------------------- -- Types --- | A simple attribute. Don't use the constructor, use 'makeAttribute'.+-- | A simple attribute. Don't use the constructor, use+-- 'makeAttribute'. Attributes are case sensitive, so if you want+-- attributes to be merged properly, use a single case representation. data Attribute = Attribute !Text !Text- deriving (Show,Eq,Typeable)+ deriving (Show,Eq) instance Hashable Attribute where hashWithSalt salt (Attribute a b) = salt `hashWithSalt` a `hashWithSalt` b@@ -85,21 +94,16 @@ -- | A monad transformer that generates HTML. Use the simpler 'Html' -- type if you don't want to transform over some other monad.+--+-- Don't rely on the internal representation of this type. Use the+-- monad and functor classes. newtype HtmlT m a =- HtmlT {runHtmlT :: m (Map Text Text -> Builder,a)+ HtmlT {runHtmlT :: m (Seq Attribute -> Builder,a) -- ^ This is the low-level way to run the HTML transformer, -- finally returning an element builder and a value. You can -- pass 'mempty' for this argument for a top-level call. See -- 'evalHtmlT' and 'execHtmlT' for easier to use functions. }--- GHC 7.4 errors with--- Can't make a derived instance of `Typeable (HtmlT m a)':--- `HtmlT' must only have arguments of kind `*'--- GHC 7.6 errors with--- `HtmlT' must only have arguments of kind `*'-#if __GLASGOW_HASKELL__ >= 707- deriving (Typeable)-#endif -- | @since 2.9.5 instance MFunctor HtmlT where@@ -112,7 +116,9 @@ -- | Monoid is right-associative, a la the 'Builder' in it. instance (a ~ (),Applicative m) => Monoid (HtmlT m a) where mempty = pure mempty+#if !MIN_VERSION_base(4,11,0) mappend = liftA2 mappend+#endif -- | Based on the monad instance. instance Applicative m => Applicative (HtmlT m) where@@ -140,21 +146,12 @@ -- | Basically acts like Writer. instance Monad m => Monad (HtmlT m) where- return a = HtmlT (return (mempty,a))- {-# INLINE return #-}- m >>= f = HtmlT $ do ~(g,a) <- runHtmlT m ~(h,b) <- runHtmlT (f a) return (g <> h,b) {-# INLINE (>>=) #-} - m >> n = HtmlT $ do- ~(g, _) <- runHtmlT m- ~(h, b) <- runHtmlT n- return (g <> h, b)- {-# INLINE (>>) #-}- -- | Used for 'lift'. instance MonadTrans HtmlT where lift m =@@ -342,22 +339,16 @@ instance (Functor m) => With (HtmlT m a) where with f = \attr -> HtmlT (mk attr <$> runHtmlT f) where- mk attr ~(f',a) = (\attr' -> f' (unionArgs (M.fromListWith (<>) (map toPair attr)) attr')+ mk attr ~(f',a) = (\attr' -> f' (attr' <> Seq.fromList attr) ,a)- toPair (Attribute x y) = (x,y) -- | For the contentful elements: 'Lucid.Html5.div_' instance (Functor m) => With (HtmlT m a -> HtmlT m a) where with f = \attr inner -> HtmlT (mk attr <$> runHtmlT (f inner)) where- mk attr ~(f',a) = (\attr' -> f' (unionArgs (M.fromListWith (<>) (map toPair attr)) attr')+ mk attr ~(f',a) = (\attr' -> f' (attr' <> Seq.fromList attr) ,a)- toPair (Attribute x y) = (x,y) --- | Union two sets of arguments and append duplicate keys.-unionArgs :: Map Text Text -> Map Text Text -> Map Text Text-unionArgs = M.unionWith (<>)- -------------------------------------------------------------------------------- -- Running @@ -529,8 +520,27 @@ else s "=\"" <> Blaze.fromHtmlEscapedText val <> s "\"" -- | Folding and monoidally appending attributes.-foldlMapWithKey :: Monoid m => (k -> v -> m) -> Map k v -> m-foldlMapWithKey f = M.foldlWithKey' (\m k v -> m `mappend` f k v) mempty+foldlMapWithKey :: (Text -> Text -> Builder) -> Seq Attribute -> Builder+foldlMapWithKey f attributes =+ case nubOrdMaybe (map fst pairs) of+ Just keyList ->+ foldMap (\k -> fromMaybe mempty (fmap (f k) (M.lookup k values))) keyList+ where values = M.fromListWith (<>) pairs+ Nothing -> foldMap (\(Attribute k v) -> f k v) attributes+ where+ pairs = map (\(Attribute k v) -> (k,v)) (toList attributes)++-- | Do a nubOrd, but only return Maybe if it actually removes anything.+nubOrdMaybe :: Ord a => [a] -> Maybe [a]+nubOrdMaybe = go False Set.empty []+ where+ go (!removed) set acc (x:xs)+ | x `Set.member` set = go True set acc xs+ | otherwise = go removed (Set.insert x set) (x : acc) xs+ go removed _set acc [] =+ if removed+ then pure (reverse acc)+ else Nothing -- | Convenience function for constructing builders. s :: String -> Builder
src/Lucid/Html5.hs view
@@ -679,6 +679,10 @@ list_ :: Text -> Attribute list_ = makeAttribute "list" +-- | The @loading@ attribute.+loading_ :: Text -> Attribute+loading_ = makeAttribute "loading"+ -- | The @loop@ attribute. loop_ :: Text -> Attribute loop_ = makeAttribute "loop"@@ -711,6 +715,10 @@ min_ :: Text -> Attribute min_ = makeAttribute "min" +-- | The @minlength@ attribute.+minlength_ :: Text -> Attribute+minlength_ = makeAttribute "minlength"+ -- | The @multiple@ attribute. multiple_ :: Text -> Attribute multiple_ = makeAttribute "multiple"@@ -998,6 +1006,10 @@ -- | The @placeholder@ attribute. placeholder_ :: Text -> Attribute placeholder_ = makeAttribute "placeholder"++-- | The @poster@ attribute.+poster_ :: Text -> Attribute+poster_ = makeAttribute "poster" -- | The @preload@ attribute. preload_ :: Text -> Attribute
test/Main.hs view
@@ -11,6 +11,7 @@ import Lucid.Base import Lucid.Bootstrap +import Control.Monad import Control.Applicative import Control.Monad.State.Strict @@ -193,7 +194,7 @@ "<div class=\" container \">Foo!</div>") it "bootstrap-attributes-extended" $ renderText (container_ [class_ "bar",id_ "zot"] "Foo!") ==?*- [ "<div id=\"zot\" class=\" container bar\">Foo!</div>"+ [ "<div class=\" container bar\" id=\"zot\">Foo!</div>" , "<div class=\" container bar\" id=\"zot\">Foo!</div>" ]