diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 5e9964c..8779772 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -60,7 +60,7 @@ jobs: linux5: strategy: matrix: - resolver: [lts-10, lts-11, lts-12, lts-13, lts-14, lts-15, lts-16, lts-17, lts-18, lts-19, lts-20, lts-21] + resolver: [lts-10, lts-11, lts-12, lts-13, lts-14, lts-15, lts-16, lts-18, lts-19, lts-20, lts-21] name: Run the tests runs-on: ubuntu-latest diff --git a/css-selectors.cabal b/css-selectors.cabal index 84010f2..ae94476 100644 --- a/css-selectors.cabal +++ b/css-selectors.cabal @@ -66,7 +66,7 @@ test-suite css-selectors-test main-is: Spec.hs build-depends: base - , binary >=0.2 && <0.8.8.0 + , binary >=0.2 , css-selectors , hashable >=1.2.7.0 , text diff --git a/src/Css/Selector.hs b/src/Css/Selector.hs index 483822f..b7bf142 100644 --- a/src/Css/Selector.hs +++ b/src/Css/Selector.hs @@ -1,14 +1,15 @@ -{-| -Module : Css.Selector -Description : Css 3 selectors in Haskell. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module for backwards compatibility that re-exports 'Css3.Selector'. This module is deprecated and eventually will be removed. --} -module Css.Selector {-# DEPRECATED "Use Css3.Selector instead" #-} ( - module Css3.Selector, - ) where +-- | +-- Module : Css.Selector +-- Description : Css 3 selectors in Haskell. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module for backwards compatibility that re-exports 'Css3.Selector'. This module is deprecated and eventually will be removed. +module Css.Selector + {-# DEPRECATED "Use Css3.Selector instead" #-} + ( module Css3.Selector, + ) +where import Css3.Selector diff --git a/src/Css/Selector/Core.hs b/src/Css/Selector/Core.hs index d09b9ee..37b18de 100644 --- a/src/Css/Selector/Core.hs +++ b/src/Css/Selector/Core.hs @@ -1,14 +1,15 @@ -{-| -Module : Css.Selector.Core -Description : A module where we define the tree of types to represent and maniplate a css selector. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module for backwards compatibility that re-exports 'Css3.Selector.Core'. This module is deprecated and eventually will be removed. --} -module Css.Selector.Core {-# DEPRECATED "Use Css3.Selector.Core instead" #-} ( - module Css3.Selector.Core - ) where +-- | +-- Module : Css.Selector.Core +-- Description : A module where we define the tree of types to represent and maniplate a css selector. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module for backwards compatibility that re-exports 'Css3.Selector.Core'. This module is deprecated and eventually will be removed. +module Css.Selector.Core + {-# DEPRECATED "Use Css3.Selector.Core instead" #-} + ( module Css3.Selector.Core, + ) +where import Css3.Selector.Core diff --git a/src/Css/Selector/QuasiQuoters.hs b/src/Css/Selector/QuasiQuoters.hs index 4f00b57..3f9fae5 100644 --- a/src/Css/Selector/QuasiQuoters.hs +++ b/src/Css/Selector/QuasiQuoters.hs @@ -1,14 +1,15 @@ -{-| -Module : Css.Selector.QuasiQuoters -Description : A module that defines a quasiquoter to parse a string to a css selector. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module for backwards compatibility that re-exports 'Css3.Selector.QuasiQuoters'. This module is deprecated and eventually will be removed. --} -module Css.Selector.QuasiQuoters {-# DEPRECATED "Use Css3.Selector.QuasiQuoters instead" #-} ( - module Css3.Selector.QuasiQuoters - ) where +-- | +-- Module : Css.Selector.QuasiQuoters +-- Description : A module that defines a quasiquoter to parse a string to a css selector. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module for backwards compatibility that re-exports 'Css3.Selector.QuasiQuoters'. This module is deprecated and eventually will be removed. +module Css.Selector.QuasiQuoters + {-# DEPRECATED "Use Css3.Selector.QuasiQuoters instead" #-} + ( module Css3.Selector.QuasiQuoters, + ) +where import Css3.Selector.QuasiQuoters diff --git a/src/Css/Selector/Utils.hs b/src/Css/Selector/Utils.hs index be386b0..a99e6b0 100644 --- a/src/Css/Selector/Utils.hs +++ b/src/Css/Selector/Utils.hs @@ -1,14 +1,15 @@ -{-| -Module : Css.Selector.Utils -Description : A set of utility methods to encode and decode strings. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module for backwards compatibility that re-exports 'Css3.Selector.Utils'. This module is deprecated and eventually will be removed. --} -module Css.Selector.Utils {-# DEPRECATED "Use Css3.Selector.Utils instead" #-} ( - module Css3.Selector.Utils - ) where +-- | +-- Module : Css.Selector.Utils +-- Description : A set of utility methods to encode and decode strings. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module for backwards compatibility that re-exports 'Css3.Selector.Utils'. This module is deprecated and eventually will be removed. +module Css.Selector.Utils + {-# DEPRECATED "Use Css3.Selector.Utils instead" #-} + ( module Css3.Selector.Utils, + ) +where import Css3.Selector.Utils diff --git a/src/Css3/Selector.hs b/src/Css3/Selector.hs index 19f7724..d86f7ad 100644 --- a/src/Css3/Selector.hs +++ b/src/Css3/Selector.hs @@ -1,17 +1,17 @@ -{-| -Module : Css3.Selector -Description : Css 3 selectors in Haskell. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module to define css selectors by making use of a quasiquoter, and manipulating these css selectors. --} -module Css3.Selector ( - module Css3.Selector.Core, +-- | +-- Module : Css3.Selector +-- Description : Css 3 selectors in Haskell. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module to define css selectors by making use of a quasiquoter, and manipulating these css selectors. +module Css3.Selector + ( module Css3.Selector.Core, module Css3.Selector.QuasiQuoters, - module Css3.Selector.Utils - ) where + module Css3.Selector.Utils, + ) +where import Css3.Selector.Core import Css3.Selector.QuasiQuoters diff --git a/src/Css3/Selector/Core.hs b/src/Css3/Selector/Core.hs index 5dad421..703591f 100644 --- a/src/Css3/Selector/Core.hs +++ b/src/Css3/Selector/Core.hs @@ -813,17 +813,19 @@ instance IsList SelectorGroup where _textToPattern :: Text -> Pat _textToPattern t = ViewP (AppE (ConE '(==)) (AppE (ConE 'pack) (LitE (StringL (unpack t))))) (_constantP 'True) -_constantP :: Name -> Pat #if MIN_VERSION_template_haskell(2,18,0) +_constantP :: Name -> Pat _constantP = flip (`ConP` []) [] #else +_constantP :: Name -> Pat _constantP = (`ConP` []) #endif -_conP :: Name -> [Pat] -> Pat #if MIN_VERSION_template_haskell(2,18,0) +_conP :: Name -> [Pat] -> Pat _conP = (`ConP` []) #else +_conP :: Name -> [Pat] -> Pat _conP = ConP #endif @@ -1327,10 +1329,11 @@ instance Binary SelectorGroup where -- Lift instances #if MIN_VERSION_template_haskell(2,17,0) _apply :: Quote m => Name -> [m Exp] -> m Exp +_apply = foldl appE . conE #else _apply :: Name -> [Q Exp] -> Q Exp -#endif _apply = foldl appE . conE +#endif instance Lift SelectorGroup where lift (SelectorGroup sg) = _apply 'SelectorGroup [liftNe sg] @@ -1532,7 +1535,7 @@ instance Arbitrary Class where shrink (Class a) = Class <$> _shrinkIdent a instance Arbitrary Nth where - arbitrary = Nth <$> ((1+) . abs <$> arbitrary) <*> arbitrary + arbitrary = Nth . (1+) . abs <$> arbitrary <*> arbitrary shrink nth | nth == nnth = [] | otherwise = [nnth] diff --git a/src/Css3/Selector/QuasiQuoters.hs b/src/Css3/Selector/QuasiQuoters.hs index e5e9178..065fa76 100644 --- a/src/Css3/Selector/QuasiQuoters.hs +++ b/src/Css3/Selector/QuasiQuoters.hs @@ -1,35 +1,39 @@ {-# LANGUAGE TemplateHaskellQuotes #-} -{-| -Module : Css3.Selector.QuasiQuoters -Description : A module that defines a quasiquoter to parse a string to a css selector. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module that defines a quasiquoter to parse a string to a css selector. --} -module Css3.Selector.QuasiQuoters ( - csssel, cssselFile, parseCss - ) where - -import Css3.Selector.Core(SelectorGroup, toPattern) -import Css3.Selector.Lexer(alexScanTokens) -import Css3.Selector.Parser(cssselector) - -import Data.Data(Data, cast) -import Data.Text(pack, unpack) - -import Language.Haskell.TH.Quote(QuasiQuoter(QuasiQuoter, quoteExp, quotePat, quoteType, quoteDec), quoteFile) -import Language.Haskell.TH.Syntax(Exp(AppE, VarE), Q, Type(ConT), dataToExpQ, lift, reportWarning) +-- | +-- Module : Css3.Selector.QuasiQuoters +-- Description : A module that defines a quasiquoter to parse a string to a css selector. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module that defines a quasiquoter to parse a string to a css selector. +module Css3.Selector.QuasiQuoters + ( csssel, + cssselFile, + parseCss, + ) +where + +import Css3.Selector.Core (SelectorGroup, toPattern) +import Css3.Selector.Lexer (alexScanTokens) +import Css3.Selector.Parser (cssselector) +import Data.Data (Data, cast) +import Data.Text (pack, unpack) +import Language.Haskell.TH.Quote (QuasiQuoter (QuasiQuoter, quoteDec, quoteExp, quotePat, quoteType), quoteFile) +import Language.Haskell.TH.Syntax (Exp (AppE, VarE), Q, Type (ConT), dataToExpQ, lift, reportWarning) -- | Parse the string to a 'SelectorGroup'. -parseCss :: String -- ^ The string to be parsed to a 'SelectorGroup' - -> SelectorGroup -- ^ The selectorgroup that is the equivalent of the given 'String'. +parseCss :: + -- | The string to be parsed to a 'SelectorGroup' + String -> + -- | The selectorgroup that is the equivalent of the given 'String'. + SelectorGroup parseCss st = al (alexScanTokens st') - where st' = filter ('\r' /=) st - al (Left er) = error er - al (Right val) = cssselector val + where + st' = filter ('\r' /=) st + al (Left er) = error er + al (Right val) = cssselector val liftDataWithText :: Data a => a -> Q Exp liftDataWithText = dataToExpQ ((((AppE (VarE 'pack) <$>) . lift . unpack) <$>) . cast) @@ -38,12 +42,13 @@ liftDataWithText = dataToExpQ ((((AppE (VarE 'pack) <$>) . lift . unpack) <$>) . -- css selector. In case the css selector is invalid. A compiler error will be -- thrown (at compile time). csssel :: QuasiQuoter -csssel = QuasiQuoter { - quoteExp = liftDataWithText . parseCss, - quotePat = pure . toPattern . parseCss, - quoteType = const (reportWarning "The type of the quasiquoter will always use the SelectorGroup type." >> pure (ConT ''SelectorGroup)), - quoteDec = const (reportWarning "The use of this quasiquoter will not make any declarations." >> pure []) - } +csssel = + QuasiQuoter + { quoteExp = liftDataWithText . parseCss, + quotePat = pure . toPattern . parseCss, + quoteType = const (reportWarning "The type of the quasiquoter will always use the SelectorGroup type." >> pure (ConT ''SelectorGroup)), + quoteDec = const (reportWarning "The use of this quasiquoter will not make any declarations." >> pure []) + } -- | A quasiquoter that takes the content from the file, and then runs the -- content of that file as a 'csssel' quasiquote. diff --git a/src/Css3/Selector/Utils.hs b/src/Css3/Selector/Utils.hs index ecacb3b..e3016c2 100644 --- a/src/Css3/Selector/Utils.hs +++ b/src/Css3/Selector/Utils.hs @@ -1,36 +1,42 @@ -{-# LANGUAGE CPP, Safe #-} - -{-| -Module : Css3.Selector.Utils -Description : A set of utility methods to encode and decode strings. -Maintainer : hapytexeu+gh@gmail.com -Stability : experimental -Portability : POSIX - -A module to encode and decode css selector strings. These are used in the parser and renderer to parse and render css selector strings. --} -module Css3.Selector.Utils ( - -- * Identifiers - readIdentifier, encodeIdentifier - , isValidIdentifier, toIdentifier - -- * Css strings - , readCssString, encodeString, encodeText - ) where - -import Control.Arrow(first) +{-# LANGUAGE CPP #-} +{-# LANGUAGE Safe #-} + +-- | +-- Module : Css3.Selector.Utils +-- Description : A set of utility methods to encode and decode strings. +-- Maintainer : hapytexeu+gh@gmail.com +-- Stability : experimental +-- Portability : POSIX +-- +-- A module to encode and decode css selector strings. These are used in the parser and renderer to parse and render css selector strings. +module Css3.Selector.Utils + ( -- * Identifiers + readIdentifier, + encodeIdentifier, + isValidIdentifier, + toIdentifier, -import Data.Char(chr, digitToInt, intToDigit, isAsciiLower, isAsciiUpper, isHexDigit, ord) + -- * Css strings + readCssString, + encodeString, + encodeText, + ) +where + +import Control.Arrow (first) +import Data.Char (chr, digitToInt, intToDigit, isAsciiLower, isAsciiUpper, isHexDigit, ord) #if __GLASGOW_HASKELL__ < 803 import Data.Semigroup((<>)) #endif -import Data.Text(Text, cons, pack, singleton, snoc) +import Data.Text (Text, cons, pack, singleton, snoc) import qualified Data.Text as T _initLast :: [a] -> Maybe ([a], a) _initLast [] = Nothing -_initLast (a:as) = Just (go as a) - where go [] x = ([], x) - go (y:ys) x = first (x:) (go ys y) +_initLast (a : as) = Just (go as a) + where + go [] x = ([], x) + go (y : ys) x = first (x :) (go ys y) _isQuote :: Char -> Bool _isQuote '"' = True @@ -39,27 +45,40 @@ _isQuote _ = False -- | Parses a css string literal to a string that ontains the content of that -- string literal. -readCssString :: String -- ^ The string that contains the string literal in the css selector. - -> String -- ^ A string that contains the content of the string literal. -readCssString (c:xs) | _isQuote c = f - where f | Just (vs, c') <- _initLast xs = g c' vs - | otherwise = "The string literal should contain at least two quotation marks." - where g c' vs | c == c' = _readCssString c vs - | otherwise = "The start and end quotation mark should be the same." +readCssString :: + -- | The string that contains the string literal in the css selector. + String -> + -- | A string that contains the content of the string literal. + String +readCssString (c : xs) | _isQuote c = f + where + f + | Just (vs, c') <- _initLast xs = g c' vs + | otherwise = "The string literal should contain at least two quotation marks." + where + g c' vs + | c == c' = _readCssString c vs + | otherwise = "The start and end quotation mark should be the same." readCssString _ = error "The string should start with an \" or ' and end with the same quotation." _readCssString :: Char -> String -> String _readCssString c' = go - where go [] = [] - go ('\\':'\n':xs) = go xs - go ('\\':ca@(c:xs)) | c == c' = c : go xs - | otherwise = let ~(y,ys) = _parseEscape ca in y : go ys - go (x:xs) | x == c' = error "The string can not contain a " ++ show x ++ ", you should escape it." - | otherwise = x : go xs + where + go [] = [] + go ('\\' : '\n' : xs) = go xs + go ('\\' : ca@(c : xs)) + | c == c' = c : go xs + | otherwise = let ~(y, ys) = _parseEscape ca in y : go ys + go (x : xs) + | x == c' = error "The string can not contain a " ++ show x ++ ", you should escape it." + | otherwise = x : go xs -- | Parse a given css identifier to the content of the identifier. -readIdentifier :: String -- ^ The given css identifier to parse. - -> String -- ^ The result of the parsing: the content of the identifier. +readIdentifier :: + -- | The given css identifier to parse. + String -> + -- | The result of the parsing: the content of the identifier. + String readIdentifier = _readCssString '\\' _notEncode :: Char -> Bool @@ -67,56 +86,81 @@ _notEncode x = isAsciiLower x || isAsciiUpper x -- | Convert a string to a css selector string literal. This is done by putting -- quotes around the content, and escaping certain characters. -encodeString :: Char -- ^ The type of quotes that should be put around the content (should be @'@ or @"@). - -> String -- ^ The string that should be converted to a css selector string literal. - -> String -- ^ The corresponding css selector string literal. +encodeString :: + -- | The type of quotes that should be put around the content (should be @'@ or @"@). + Char -> + -- | The string that should be converted to a css selector string literal. + String -> + -- | The corresponding css selector string literal. + String encodeString c' = (c' :) . go - where go [] = [c'] - go (c:cs) | _notEncode c = c : go cs - | otherwise = '\\' : _showHex (ord c) (go cs) + where + go [] = [c'] + go (c : cs) + | _notEncode c = c : go cs + | otherwise = '\\' : _showHex (ord c) (go cs) -- | Convert a string to a css selector string literal. This is done by putting -- quotes around the content, and escaping certain characters. -encodeText :: Char -- ^ The type of quotes that should be put around the content (should be @'@ or @"@). - -> Text -- ^ The string that should be converted to a css selector string literal. - -> Text -- ^ The corresponding css selector string literal. +encodeText :: + -- | The type of quotes that should be put around the content (should be @'@ or @"@). + Char -> + -- | The string that should be converted to a css selector string literal. + Text -> + -- | The corresponding css selector string literal. + Text encodeText c' t = cons c' (snoc (T.concatMap _encodeCharacter t) c') _encodeCharacter :: Char -> Text _encodeCharacter c - | _notEncode c = singleton c - | otherwise = cons '\\' (pack (_showHex (ord c) "")) + | _notEncode c = singleton c + | otherwise = cons '\\' (pack (_showHex (ord c) "")) -- | Encode a given identifier to its css selector equivalent by escaping -- certain characters. -encodeIdentifier :: Text -- ^ The identifier to encode. - -> Text -- ^ The encoded identifier. +encodeIdentifier :: + -- | The identifier to encode. + Text -> + -- | The encoded identifier. + Text encodeIdentifier = T.concatMap _encodeCharacter _showHex :: Int -> ShowS _showHex = go (6 :: Int) - where go 0 _ s = s - go k n rs = go (k-1) q (intToDigit r : rs) - where ~(q, r) = quotRem n 16 + where + go 0 _ s = s + go k n rs = go (k - 1) q (intToDigit r : rs) + where + ~(q, r) = quotRem n 16 _parseEscape :: String -> (Char, String) _parseEscape = go (6 :: Int) 0 - where go 0 n cs = yield n cs - go _ n "" = yield n "" - go i n ca@(c:cs) | isHexDigit c = go (i-1) (16*n+digitToInt c) cs - | otherwise = yield n ca - yield n s = (chr n, s) + where + go 0 n cs = yield n cs + go _ n "" = yield n "" + go i n ca@(c : cs) + | isHexDigit c = go (i - 1) (16 * n + digitToInt c) cs + | otherwise = yield n ca + yield n s = (chr n, s) -- | Check if the given identifier is a valid css selector identifier. -isValidIdentifier :: String -- ^ The given identifier to check. - -> Bool -- ^ 'True' if the given identifier is valid, 'False' otherwise. +isValidIdentifier :: + -- | The given identifier to check. + String -> + -- | 'True' if the given identifier is valid, 'False' otherwise. + Bool isValidIdentifier = not . null -- | Convert the given string to a given object by first checking if it is a -- valid identifier, and if not raising an error. If it is a valid identifier, -- the string is packed, and wrapped in the given function. -toIdentifier :: (Text -> a) -- ^ The given function to wrap the 'Text' identifier to an object. - -> String -- ^ The string to validate, and wrap into the given function. - -> a -- ^ The identifier object to return if the identifier is valid. -toIdentifier f ident | isValidIdentifier ident = f (pack ident) - | otherwise = error ("The identifier \"" <> show ident <> "\" is not a valid identifier.") +toIdentifier :: + -- | The given function to wrap the 'Text' identifier to an object. + (Text -> a) -> + -- | The string to validate, and wrap into the given function. + String -> + -- | The identifier object to return if the identifier is valid. + a +toIdentifier f ident + | isValidIdentifier ident = f (pack ident) + | otherwise = error ("The identifier \"" <> show ident <> "\" is not a valid identifier.") diff --git a/stack.yaml b/stack.yaml index e0c3cd0..02f41bf 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,7 +17,7 @@ # # resolver: ./custom-snapshot.yaml # resolver: https://example.com/snapshots/2018-01-01.yaml -resolver: lts-14.10 +resolver: lts-21.1 save-hackage-creds: false # User packages to be built.