Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uncheckedHsx + customHsx #2010

Merged
merged 29 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e05de32
add new module
kodeFant Oct 26, 2024
595f778
Fix uncheckedHSX quasiquoter
kodeFant Oct 26, 2024
0644e5b
Make sure closing tags are checked
kodeFant Oct 26, 2024
4a3553a
Fix it so it works
kodeFant Oct 26, 2024
6f6480a
remove UncheckedHSX and attept to restore QQ and Parser modules
kodeFant Oct 31, 2024
ef158e4
reset some unecessary whitespace stuff
kodeFant Oct 31, 2024
ba5bc56
remove unecessary whitespace
kodeFant Oct 31, 2024
d5b95f5
unchecked HSX working, with tests on the parser
kodeFant Oct 31, 2024
f54430b
Add customHsx + tests
kodeFant Oct 31, 2024
e85bb98
fix comment
kodeFant Oct 31, 2024
23bfdf7
fix comment
kodeFant Oct 31, 2024
b0ebf79
Update ihp-hsx/IHP/HSX/Parser.hs
kodeFant Oct 31, 2024
7e45f8a
Update ihp-hsx/IHP/HSX/QQ.hs
kodeFant Oct 31, 2024
ed49673
remove newtypes and use 'HsxSettings' directly
kodeFant Oct 31, 2024
3a7bd62
Fix Github resolve bug
kodeFant Oct 31, 2024
6eb7c17
Aesthethic nitpick
kodeFant Oct 31, 2024
83c3d2f
use customHsx to call hsx and uncheckedHsx
kodeFant Nov 1, 2024
0ad0581
Move CustomHsxCases to Test.HSX namespace
kodeFant Nov 1, 2024
a64d6ef
Fix import
kodeFant Nov 1, 2024
2ff2692
Fix module comment
kodeFant Nov 1, 2024
0185bea
For now, move CustomHsxCases back so the tests are working again
kodeFant Nov 1, 2024
0864979
Add documentation
kodeFant Nov 1, 2024
1de5cb8
Minor doc fix
kodeFant Nov 1, 2024
610ae2f
Formulate solution to QuasiQuoter shortcomings
kodeFant Nov 1, 2024
502aa2d
typo fix
kodeFant Nov 1, 2024
baafd7c
Add use-case example
kodeFant Nov 1, 2024
035ee28
Simplify langauge
kodeFant Nov 1, 2024
e7522e3
Improve examples a bit
kodeFant Nov 1, 2024
5c14759
Add spread example
kodeFant Nov 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ihp-hsx/IHP/HSX/QQ.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Module: IHP.HSX.QQ
Description: Defines the @[hsx||]@ syntax
Copyright: (c) digitally induced GmbH, 2022
-}
module IHP.HSX.QQ (hsx) where
module IHP.HSX.QQ (hsx, uncheckedHsx) where

import Prelude
import Data.Text (Text)
Expand All @@ -27,6 +27,7 @@ import Data.List (foldl')
import IHP.HSX.Attribute
import qualified Text.Blaze.Html5.Attributes as Attributes
import qualified Data.HashMap.Strict as HashMap
import IHP.HSX.UncheckedHSX (uncheckedHsx)

hsx :: QuasiQuoter
hsx = QuasiQuoter {
kodeFant marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -459,3 +460,4 @@ textToStaticString text = StaticString (Text.unpack text ++) (Text.encodeUtf8 te

instance Show (MarkupM ()) where
show html = BlazeString.renderHtml html

113 changes: 113 additions & 0 deletions ihp-hsx/IHP/HSX/UncheckedHSX.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}
module IHP.HSX.UncheckedHSX (uncheckedHsx) where
kodeFant marked this conversation as resolved.
Show resolved Hide resolved

import Prelude
import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Text.Blaze.Html5 ((!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Data.Text (Text)
import qualified Data.Text as T
import Text.Megaparsec
import Text.Megaparsec.Char
import Data.Void
import Data.String.Conversions (cs)
import qualified IHP.HSX.HaskellParser as HaskellParser
import Text.Blaze.Internal (MarkupM(Parent, Leaf), attribute)
import qualified Data.List.NonEmpty as NE

type Parser = Parsec Void Text

data UNode = UNode Text [(Text, AttributeValue)] [UNode] Bool
| UTextNode Text
| USplicedNode Exp
deriving (Show)

data AttributeValue = TextValue Text | ExpressionValue Exp deriving (Show)

uncheckedHsx :: QuasiQuoter
uncheckedHsx = QuasiQuoter
{ quoteExp = quoteUncheckedHsxExpression
, quotePat = error "quotePat not implemented for uncheckedHsx"
, quoteType = error "quoteType not implemented for uncheckedHsx"
, quoteDec = error "quoteDec not implemented for uncheckedHsx"
}

quoteUncheckedHsxExpression :: String -> Q Exp
quoteUncheckedHsxExpression code = do
loc <- location
let position = SourcePos (loc_filename loc) (mkPos (fst (loc_start loc))) (mkPos (snd (loc_start loc)))
extensions <- extsEnabled
case runParser (uncheckedParser extensions) "" (cs code) of
Left err -> fail $ errorBundlePretty err
Right result -> compileToHaskell result

uncheckedParser :: [Extension] -> Parser UNode
uncheckedParser extensions = space *> manyUncheckedElement <* eof

manyUncheckedElement :: Parser UNode
manyUncheckedElement = do
children <- many (try uncheckedElement <|> uncheckedTextNode <|> uncheckedSplicedNode)
return $ UNode "div" [] children False

uncheckedElement :: Parser UNode
uncheckedElement = do
char '<'
tagName <- some (alphaNumChar <|> char '-' <|> char '_' <|> char ':')
attrs <- many uncheckedAttribute
space
(do string "/>"
return $ UNode (T.pack tagName) attrs [] True)
<|>
(do char '>'
children <- many (try uncheckedElement <|> uncheckedTextNode <|> uncheckedSplicedNode)
closing <- optional (try $ string "</" >> string (T.pack tagName) >> char '>')
case closing of
Just _ -> return $ UNode (T.pack tagName) attrs children False
Nothing -> return $ UNode (T.pack tagName) attrs children True)

uncheckedAttribute :: Parser (Text, AttributeValue)
uncheckedAttribute = do
space
name <- some (alphaNumChar <|> char '-' <|> char '_' <|> char ':')
value <- option (TextValue "") (char '=' *> (quotedValue <|> unquotedValue <|> expressionValue))
return (T.pack name, value)
where
quotedValue = TextValue . T.pack <$> (char '"' *> manyTill anySingle (char '"'))
unquotedValue = TextValue . T.pack <$> some (alphaNumChar <|> char '-' <|> char '_')
expressionValue = ExpressionValue <$> (char '{' *> parseHaskellExpression <* char '}')

uncheckedTextNode :: Parser UNode
uncheckedTextNode = UTextNode . T.pack <$> some (satisfy (\c -> c /= '<' && c /= '{' && c /= '}'))

uncheckedSplicedNode :: Parser UNode
uncheckedSplicedNode = USplicedNode <$> (char '{' *> parseHaskellExpression <* char '}')

parseHaskellExpression :: Parser Exp
parseHaskellExpression = do
pos <- getSourcePos
code <- takeWhileP Nothing (\c -> c /= '}')
case HaskellParser.parseHaskellExpression pos [] (cs code) of
Right exp -> return exp
Left err -> fail $ show err

compileToHaskell :: UNode -> Q Exp
compileToHaskell (UNode name attrs children isLeaf) =
let element = [| H.preEscapedText $(litE $ stringL $ "<" ++ T.unpack name) |]
applyAttrs = foldr (\(k, v) e -> [| $e <> $(compileAttribute k v) |]) element attrs
closeTag = if isLeaf
then [| H.preEscapedText $(litE $ stringL "/>") |]
else [| H.preEscapedText $(litE $ stringL ">") |]
applyChildren = if null children
then [| $applyAttrs <> $closeTag |]
else [| $applyAttrs <> H.preEscapedText $(litE $ stringL ">") <>
mconcat $(listE (map compileToHaskell children)) <>
H.preEscapedText $(litE $ stringL $ "</" ++ T.unpack name ++ ">") |]
in applyChildren
compileToHaskell (UTextNode text) = [| H.text $(litE $ stringL $ T.unpack text) |]
compileToHaskell (USplicedNode exp) = [| H.preEscapedToHtml $(return exp) |]

compileAttribute :: Text -> AttributeValue -> Q Exp
compileAttribute name (TextValue value) = [| H.preEscapedText $(litE $ stringL $ " " ++ T.unpack name ++ "=\"" ++ T.unpack value ++ "\"") |]
compileAttribute name (ExpressionValue exp) = [| H.preEscapedText (T.pack $ " " ++ T.unpack name ++ "=\"") <> H.toHtml $(return exp) <> H.preEscapedText "\"" |]
3 changes: 2 additions & 1 deletion ihp-hsx/ihp-hsx.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.2
name: ihp-hsx
version: 1.3.0
version: 1.4.0
mpscholten marked this conversation as resolved.
Show resolved Hide resolved
synopsis: JSX-like but for Haskell
description: JSX-like templating syntax for Haskell
license: MIT
Expand Down Expand Up @@ -89,6 +89,7 @@ library
, IHP.HSX.HaskellParser
, IHP.HSX.HsExpToTH
, IHP.HSX.Attribute
, IHP.HSX.UncheckedHSX

test-suite ihp-hsx-tests
type: exitcode-stdio-1.0
Expand Down