Soft line break support
This commit is contained in:
parent
2e66b03baa
commit
e4fe5f98e6
3 changed files with 111 additions and 18 deletions
|
@ -2,7 +2,7 @@
|
||||||
module SSG.Djot.Inline
|
module SSG.Djot.Inline
|
||||||
|
|
||||||
import Control.Eff
|
import Control.Eff
|
||||||
import Data.List.Lazy
|
import Data.List1
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.String
|
import Data.String
|
||||||
import Derive.Prelude
|
import Derive.Prelude
|
||||||
|
@ -21,6 +21,7 @@ import System
|
||||||
public export
|
public export
|
||||||
data Inline : Type where
|
data Inline : Type where
|
||||||
Text : (text : String) -> Inline
|
Text : (text : String) -> Inline
|
||||||
|
SoftLineBreak : Inline
|
||||||
|
|
||||||
%runElab derive "Inline" [Eq, Show, Pretty]
|
%runElab derive "Inline" [Eq, Show, Pretty]
|
||||||
|
|
||||||
|
@ -30,13 +31,13 @@ data Inline : Type where
|
||||||
|
|
||||||
||| Type alias for inline parsing
|
||| Type alias for inline parsing
|
||||||
IParser : Type -> Type
|
IParser : Type -> Type
|
||||||
IParser t = Eff [State (List Char), Choose] t
|
IParser t = Eff [State (List Char), Fail] t
|
||||||
|
|
||||||
||| Get the next char, modifiying the state
|
||| Get the next char, modifiying the state
|
||||||
popChar : IParser Char
|
popChar : IParser Char
|
||||||
popChar = do
|
popChar = do
|
||||||
x :: xs <- get
|
x :: xs <- get
|
||||||
| [] => empty
|
| [] => fail
|
||||||
put xs
|
put xs
|
||||||
pure x
|
pure x
|
||||||
|
|
||||||
|
@ -44,24 +45,32 @@ popChar = do
|
||||||
peekChar : IParser Char
|
peekChar : IParser Char
|
||||||
peekChar = do
|
peekChar = do
|
||||||
x :: xs <- get
|
x :: xs <- get
|
||||||
| [] => empty
|
| [] => fail
|
||||||
pure x
|
pure x
|
||||||
|
|
||||||
||| Attempt to parse something without propagating the failure
|
||| Attempt to parse something without propagating the failure
|
||||||
try : IParser t -> IParser (Maybe t)
|
try : IParser t -> IParser (Maybe t)
|
||||||
try x = do
|
try x = do
|
||||||
state <- get
|
state <- get
|
||||||
x <- lift . runChoose {f = Maybe} $ x
|
x <- lift . runFail $ x
|
||||||
case x of
|
case x of
|
||||||
Nothing => do
|
Nothing => do
|
||||||
put state
|
put state
|
||||||
pure Nothing
|
pure Nothing
|
||||||
Just y => pure $ Just y
|
Just y => pure $ Just y
|
||||||
|
|
||||||
|
|
||||||
||| Choose a parser
|
||| Choose a parser
|
||||||
oneOf : List (IParser t) -> IParser t
|
oneOf : List (IParser t) -> IParser t
|
||||||
oneOf [] = empty
|
oneOf [] = fail
|
||||||
oneOf (x :: xs) = x `alt` oneOf xs
|
oneOf (x :: xs) = do
|
||||||
|
state <- get
|
||||||
|
x <- lift . runFail $ x
|
||||||
|
case x of
|
||||||
|
Nothing => do
|
||||||
|
put state
|
||||||
|
oneOf xs
|
||||||
|
Just y => pure y
|
||||||
|
|
||||||
||| Parse 0+ of something
|
||| Parse 0+ of something
|
||||||
many : IParser t -> IParser (List t)
|
many : IParser t -> IParser (List t)
|
||||||
|
@ -70,10 +79,44 @@ many x = do
|
||||||
| _ => pure []
|
| _ => pure []
|
||||||
map (y ::) $ many x
|
map (y ::) $ many x
|
||||||
|
|
||||||
|
||| Parse 1+ of something
|
||||||
|
atLeastOne : IParser t -> IParser (List1 t)
|
||||||
|
atLeastOne x = do
|
||||||
|
Just y <- try x
|
||||||
|
| _ => fail
|
||||||
|
map (y :::) (many x)
|
||||||
|
|
||||||
|
||| Match exactly the given string
|
||||||
|
exactly : String -> IParser (List Char)
|
||||||
|
exactly str = exactly' (unpack str)
|
||||||
|
where
|
||||||
|
exactly' : List Char -> IParser (List Char)
|
||||||
|
exactly' [] = pure []
|
||||||
|
exactly' (x :: xs) = do
|
||||||
|
y <- popChar
|
||||||
|
if x == y
|
||||||
|
then map (x ::) (exactly' xs)
|
||||||
|
else fail
|
||||||
|
|
||||||
|
||| Match vertical whitespace
|
||||||
|
vert : IParser (List Char)
|
||||||
|
vert = oneOf
|
||||||
|
[ exactly "\n"
|
||||||
|
, exactly "\r\n"
|
||||||
|
, exactly "\r"
|
||||||
|
]
|
||||||
|
|
||||||
|
||| Match horizontal whitespace
|
||||||
|
horiz : IParser (List Char)
|
||||||
|
horiz = oneOf
|
||||||
|
[ exactly " "
|
||||||
|
, exactly "\t"
|
||||||
|
]
|
||||||
|
|
||||||
||| Run a parser
|
||| Run a parser
|
||||||
runIParser : (str : String) -> IParser t -> Maybe t
|
runIParser : (str : String) -> IParser t -> Maybe t
|
||||||
runIParser str x =
|
runIParser str x =
|
||||||
fst . extract . runState (unpack str) . runChoose {f = Maybe} $ x
|
fst . extract . runState (unpack str) . runFail $ x
|
||||||
|
|
||||||
--******************************
|
--******************************
|
||||||
--* Syntax *
|
--* Syntax *
|
||||||
|
@ -89,9 +132,23 @@ text = do
|
||||||
c <- popChar
|
c <- popChar
|
||||||
pure $ Text (singleton c)
|
pure $ Text (singleton c)
|
||||||
|
|
||||||
|
||| Parse a soft linebreak
|
||||||
|
softLineBreak : IParser Inline
|
||||||
|
softLineBreak = do
|
||||||
|
_ <- atLeastOne singleBreak
|
||||||
|
pure SoftLineBreak
|
||||||
|
where
|
||||||
|
singleBreak : IParser ()
|
||||||
|
singleBreak = do
|
||||||
|
_ <- many horiz
|
||||||
|
_ <- vert
|
||||||
|
_ <- many horiz
|
||||||
|
pure ()
|
||||||
|
|
||||||
-- Definition of pInline
|
-- Definition of pInline
|
||||||
pInline = oneOf
|
pInline = oneOf
|
||||||
[ text
|
[ softLineBreak
|
||||||
|
, text
|
||||||
]
|
]
|
||||||
|
|
||||||
--******************************
|
--******************************
|
||||||
|
@ -100,13 +157,22 @@ pInline = oneOf
|
||||||
|
|
||||||
||| Combine adjacent Text entries
|
||| Combine adjacent Text entries
|
||||||
combineTexts : List Inline -> List Inline
|
combineTexts : List Inline -> List Inline
|
||||||
|
combineTexts [] = []
|
||||||
combineTexts (Text a :: (Text b :: xs)) =
|
combineTexts (Text a :: (Text b :: xs)) =
|
||||||
combineTexts (Text (a ++ b) :: xs)
|
combineTexts (Text (a ++ b) :: xs)
|
||||||
combineTexts xs = xs
|
combineTexts (x :: xs) = x :: combineTexts xs
|
||||||
|
|
||||||
|
||| Remove trailing soft line breaks
|
||||||
|
removeTrailingSoftbreak : List Inline -> List Inline
|
||||||
|
removeTrailingSoftbreak xs = reverse (removeTrailingSoftbreak' (reverse xs))
|
||||||
|
where
|
||||||
|
removeTrailingSoftbreak' : List Inline -> List Inline
|
||||||
|
removeTrailingSoftbreak' (SoftLineBreak :: xs) = removeTrailingSoftbreak' xs
|
||||||
|
removeTrailingSoftbreak' xs = xs
|
||||||
|
|
||||||
||| Top level post processor
|
||| Top level post processor
|
||||||
postProcess : List Inline -> List Inline
|
postProcess : List Inline -> List Inline
|
||||||
postProcess xs = combineTexts xs
|
postProcess xs = removeTrailingSoftbreak . combineTexts $ xs
|
||||||
|
|
||||||
--******************************
|
--******************************
|
||||||
--* Top Level Parsing Function *
|
--* Top Level Parsing Function *
|
||||||
|
@ -137,3 +203,23 @@ golden input reference = do
|
||||||
testTextAsText : IO Bool
|
testTextAsText : IO Bool
|
||||||
testTextAsText =
|
testTextAsText =
|
||||||
golden "Hello World!" [Text "Hello World!"]
|
golden "Hello World!" [Text "Hello World!"]
|
||||||
|
|
||||||
|
-- @@test Soft line break smoke
|
||||||
|
smokeSoftLineBreak : IO Bool
|
||||||
|
smokeSoftLineBreak =
|
||||||
|
golden "Hello\nWorld!" [Text "Hello", SoftLineBreak, Text "World!"]
|
||||||
|
|
||||||
|
-- @@test Soft line break double line break
|
||||||
|
testDoubleSoftLineBreak : IO Bool
|
||||||
|
testDoubleSoftLineBreak =
|
||||||
|
golden "Hello\n\nWorld!" [Text "Hello", SoftLineBreak, Text "World!"]
|
||||||
|
|
||||||
|
-- @@test Soft line break trailing line break
|
||||||
|
testTrailingSoftLineBreak : IO Bool
|
||||||
|
testTrailingSoftLineBreak =
|
||||||
|
golden "Hello\n\nWorld!\n" [Text "Hello", SoftLineBreak, Text "World!"]
|
||||||
|
|
||||||
|
-- @@test Soft line break multiple trailing line breaks
|
||||||
|
testTrailingSoftLineBreaks : IO Bool
|
||||||
|
testTrailingSoftLineBreaks =
|
||||||
|
golden "Hello\n\nWorld!\n\n\n" [Text "Hello", SoftLineBreak, Text "World!"]
|
||||||
|
|
|
@ -5,16 +5,19 @@ import SSG.HTML
|
||||||
import SSG.Djot.Inline
|
import SSG.Djot.Inline
|
||||||
import SSG.Djot.Block
|
import SSG.Djot.Block
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
|
||||||
||| Render a single inline element as HTML
|
||| Render a single inline element as HTML
|
||||||
export
|
export
|
||||||
renderInline : Inline -> (t ** Html t)
|
renderInline : Inline -> Maybe (t ** Html t)
|
||||||
-- FIXME: escape text
|
-- FIXME: escape text
|
||||||
renderInline (Text text) = (_ ** Text text)
|
renderInline (Text text) = Just (_ ** Text text)
|
||||||
|
renderInline SoftLineBreak = Nothing
|
||||||
|
|
||||||
||| Render a list of inline elments to html
|
||| Render a list of inline elments to html
|
||||||
export
|
export
|
||||||
renderInlines : List Inline -> (types ** DList _ Html types)
|
renderInlines : List Inline -> (types ** DList _ Html types)
|
||||||
renderInlines xs = fromList $ map renderInline xs
|
renderInlines xs = fromList . catMaybes $ map renderInline xs
|
||||||
|
|
||||||
||| Render a single block element as HTML
|
||| Render a single block element as HTML
|
||||||
export
|
export
|
||||||
|
|
|
@ -3,10 +3,14 @@
|
||||||
<body>
|
<body>
|
||||||
<p>Hello World!</p>
|
<p>Hello World!</p>
|
||||||
<p>Hello Alice!</p>
|
<p>Hello Alice!</p>
|
||||||
<p>A Multi-line
|
<p>
|
||||||
Paragraph</p>
|
A Multi-line
|
||||||
|
Paragraph
|
||||||
|
</p>
|
||||||
<p>Two line breaks</p>
|
<p>Two line breaks</p>
|
||||||
<p>A multiline paragraph
|
<p>
|
||||||
with some indentation</p>
|
A multiline paragraph
|
||||||
|
with some indentation
|
||||||
|
</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Reference in a new issue