A src/Stylish/Style/Selector/Common.hs => src/Stylish/Style/Selector/Common.hs +30 -0
@@ 0,0 1,30 @@
+module Stylish.Style.Selector.Common(
+ RuleStore(..), StyleRule'(..), selector, properties, styleRule'
+ ) where
+
+import Stylish.Element
+import Stylish.Parse
+
+class RuleStore a where
+ addStyleRule :: a -> Int -> StyleRule' -> a
+ lookupRules :: a -> Element -> [StyleRule']
+
+type SelectorFunc = Element -> Bool
+data StyleRule' = StyleRule' {
+ inner :: StyleRule,
+ compiledSelector :: SelectorFunc,
+ rank :: (Int, (Int, Int, Int), Int) -- This reads ugly, but oh well.
+}
+styleRule' rule = StyleRule' {
+ inner = rule,
+ compiledSelector = \_ -> True,
+ rank = (0, (0, 0, 0), 0)
+}
+
+instance Eq StyleRule' where
+ a == b = inner a == inner b
+instance Show StyleRule' where show a = show $ inner a
+instance Ord StyleRule' where compare x y = rank x `compare` rank y
+
+selector rule | StyleRule selector _ <- inner rule = selector
+properties rule | StyleRule _ properties <- inner rule = properties
M src/Stylish/Style/Selector/Index.hs => src/Stylish/Style/Selector/Index.hs +16 -12
@@ 9,14 9,15 @@ import Data.HashMap.Strict
import Data.List (nub)
import Stylish.Parse
import Stylish.Element
+import Stylish.Style.Selector.Common
import Data.Hashable
import Data.Text (unpack, pack)
import Data.Text.Internal (Text(..))
data StyleIndex = StyleIndex {
- indexed :: HashMap SimpleSelector [StyleRule],
- unindexed :: [StyleRule]
+ indexed :: HashMap SimpleSelector [StyleRule'],
+ unindexed :: [StyleRule']
}
styleIndex = StyleIndex {indexed = empty, unindexed = []}
@@ 24,9 25,19 @@ styleIndex = StyleIndex {indexed = empty, unindexed = []}
lookup' :: SimpleSelector -> HashMap SimpleSelector [a] -> [a]
lookup' = lookupDefault []
-instance StyleSheet StyleIndex where
- addRule self (StyleRule _ []) = self
- addRule self rule@(StyleRule selector _) = addRuleForSelector self rule $ simpleSelector selector
+instance RuleStore StyleIndex where
+ addStyleRule self _ rule | [] == properties rule = self
+ | otherwise = addRuleForSelector self rule $ simpleSelector $ selector rule
+ lookupRules self element = nub $ Prelude.foldr (++) [] rules
+ where
+ get key = lookup' key index
+ index = indexed self
+ rules = unindexed self : Prelude.map get (testsForElement element)
+
+rulesForElement :: StyleIndex -> Element -> [StyleRule] -- For testing
+rulesForElement self element = Prelude.map inner $ lookupRules self element
+
+---
simpleSelector (Element s) = s
simpleSelector (Child _ s) = s
@@ 48,13 59,6 @@ selectorKey (Property prop _ : _) = Property prop Exists
----
-rulesForElement :: StyleIndex -> Element -> [StyleRule]
-rulesForElement self element = nub $ Prelude.foldr (++) [] rules
- where
- get key = lookup' key index
- index = indexed self
- rules = unindexed self : Prelude.map get (testsForElement element)
-
testsForElement :: Element -> [SimpleSelector]
testsForElement element =
(Tag $ name element) : (testsForAttributes $ attributes element)
M src/Stylish/Style/Selector/Interpret.hs => src/Stylish/Style/Selector/Interpret.hs +15 -1
@@ 1,10 1,12 @@
{-# LANGUAGE OverloadedStrings #-}
module Stylish.Style.Selector.Interpret(
- compile, SelectorFunc(..)
+ compile, SelectorFunc(..),
+ InterpretedRuleStore(..)
) where
import Stylish.Parse.Selector
import Stylish.Element
+import Stylish.Style.Selector.Common
import Data.Text.Internal (Text(..))
import Data.Text (unpack)
@@ 75,3 77,15 @@ testAttr _ _ _ [] = False
hasWord expected value = expected `elem` words value
hasLang expected value = expected == value || isPrefixOf (expected ++ "-") value
+
+--------
+---- RuleStore wrapper
+--------
+data InterpretedRuleStore inner = InterpretedRuleStore inner
+instance RuleStore inner => RuleStore (InterpretedRuleStore inner) where
+ addStyleRule (InterpretedRuleStore self) priority rule =
+ InterpretedRuleStore $ addStyleRule self priority $ rule {
+ compiledSelector = compile $ selector rule
+ }
+ lookupRules (InterpretedRuleStore self) el = filter call $ lookupRules self el
+ where call (StyleRule' _ test _) = test el
M src/Stylish/Style/Selector/Specificity.hs => src/Stylish/Style/Selector/Specificity.hs +15 -1
@@ 1,8 1,10 @@
module Stylish.Style.Selector.Specificity(
- computeSpecificity
+ OrderedRuleStore
) where
import Stylish.Parse.Selector
+import Stylish.Style.Selector.Common
+import Data.List
computeSpecificity :: Selector -> (Int, Int, Int)
computeSpecificity (Element selector) = computeSpecificity' selector
@@ 19,3 21,15 @@ computeSpecificity' [] = (0, 0, 0)
add :: (Int, Int, Int) -> (Int, Int, Int) -> (Int, Int, Int)
add (a, b, c) (x, y, z) = (a + x, b + y, c + z)
+
+---
+
+data OrderedRuleStore inner = OrderedRuleStore inner Int
+
+instance RuleStore inner => RuleStore (OrderedRuleStore inner) where
+ addStyleRule (OrderedRuleStore self count) priority rule = OrderedRuleStore (
+ addStyleRule self priority $ rule {
+ rank = (priority, computeSpecificity $ selector rule, count)
+ }
+ ) (count + 1)
+ lookupRules (OrderedRuleStore self _) el = sort $ lookupRules self el
M test/Test.hs => test/Test.hs +5 -4
@@ 10,6 10,7 @@ import Stylish.Style.Selector.Index
import Stylish.Element
import Stylish.Style.Selector.Interpret
import Stylish.Style.Selector.Specificity
+import Stylish.Style.Selector.Common
main = hspec spec
@@ 73,7 74,7 @@ spec = do
]
describe "Style Index" $ do
it "Retrieves appropriate styles" $ do
- let index = addRule styleIndex sampleRule
+ let index = addStyleRule styleIndex 0 $ styleRule' sampleRule
let element = ElementNode {
name = "a",
parent = Nothing,
@@ 94,17 95,17 @@ spec = do
rulesForElement index element2 `shouldBe` []
let rule = StyleRule (Element [Class "external"]) [("color", [Ident "green"])]
- let index = addRule styleIndex rule
+ let index = addStyleRule styleIndex 0 $ styleRule' rule
rulesForElement index element `shouldBe` [rule]
rulesForElement index element2 `shouldBe` []
let rule = StyleRule (Element [Id "mysite"]) [("color", [Ident "green"])]
- let index = addRule styleIndex rule
+ let index = addStyleRule styleIndex 0 $ styleRule' rule
rulesForElement index element `shouldBe` [rule]
rulesForElement index element2 `shouldBe` []
let rule = StyleRule (Element [Property "href" $ Prefix "https://"]) [("color", [Ident "green"])]
- let index = addRule styleIndex rule
+ let index = addStyleRule styleIndex 0 $ styleRule' rule
rulesForElement index element `shouldBe` [rule]
rulesForElement index element2 `shouldBe` []
describe "Selector Compiler" $ do