yi-0.6.1: Yi/Syntax/Latex.hs
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
-- Copyright (c) JP Bernardy 2008
module Yi.Syntax.Latex where
import Yi.IncrementalParse
import Yi.Lexer.Alex
import Yi.Lexer.Latex
import Yi.Style
import Yi.Syntax.Tree
import Yi.Syntax.BList
import Yi.Syntax
import Yi.Prelude
import Prelude ()
import Data.Monoid
import Data.List (zip)
isNoise :: Token -> Bool
isNoise Text = True
isNoise Comment = True
isNoise (Command _) = True
isNoise NewCommand = True
isNoise (Special ' ') = True
isNoise (Special _) = False
isNoise (Begin _) = False
isNoise (End _) = False
type Expr t = BList (Tree t)
type TT = Tok Token
data Tree t
= Paren t (Expr t) t -- A parenthesized expression (maybe with [ ] ...)
| Atom t
| Error t
deriving Show
--instance Functor Tree where
-- fmap = fmapDefault
instance Foldable Tree where
foldMap f (Atom t) = f t
foldMap f (Error t ) = f t
foldMap f (Paren l g r) = f l <> foldMap (foldMap f) g <> f r
--instance Traversable Tree where
-- traverse f (Atom t) = Atom <$> f t
-- traverse f (Error t) = Error <$> f t
-- traverse f (Paren l g r) = Paren <$> f l <*> traverse (traverse f) g <*> f r
instance SubTree (Tree TT) where
type Element (Tree TT) = TT
foldMapToksAfter begin f t0 = work t0
where work (Atom t) = f t
work (Error t) = f t
work (Paren l g r) = f l <> foldMapAfter begin (foldMapToksAfter begin f) g <> f r
foldMapToks f = foldMap (foldMapToks f)
instance IsTree Tree where
subtrees (Paren _ g _) = toList g
subtrees _ = []
parse :: P TT (Expr TT)
parse = pExpr True <* eof
where
-- | Create a special character symbol
newT c = tokFromT (Special c)
-- errT = (\next -> case next of
-- Nothing -> newT '!'
-- Just (Tok {tokPosn = posn}) -> Tok { tokT = Special '!', tokPosn = posn-1, tokSize = 1 -- FIXME: size should be 1 char, not one byte!
-- }) <$> lookNext
errT = pure (newT '!')
-- | parse a special symbol
sym' p = symbol (p . tokT)
sym t = sym' (== t)
pleaseSym c = recoverWith errT <|> sym c
-- pleaseSym' c = recoverWith errT <|> sym' c
-- pExpr :: P TT [Expr TT]
pExpr = Yi.Syntax.BList.many . pTree
parens = [(Special x, Special y) | (x,y) <- zip "({[" ")}]"]
openParens = fmap fst parens
pBlock = sym' isBegin >>= \beg@Tok {tokT = Begin env} -> Paren <$> pure beg <*> pExpr True <*> pleaseSym (End env)
pTree :: Bool -> P TT (Tree TT)
pTree outsideMath =
(if outsideMath then pBlock <|> (Paren <$> sym (Special '$') <*> pExpr False <*> pleaseSym (Special '$'))
else empty)
<|> foldr1 (<|>) [(Paren <$> sym l <*> pExpr outsideMath <*> pleaseSym r) | (l,r) <- parens]
<|> (Atom <$> sym' isNoise)
<|> (Error <$> recoverWith (sym' (not . ((||) <$> isNoise <*> (`elem` openParens)))))
getStrokes :: Point -> Point -> Point -> Expr TT -> [Stroke]
getStrokes point begin _end t0 = appEndo result []
where getStrokes' :: Tree TT -> Endo [Stroke]
getStrokes' (Atom t) = ts id t
getStrokes' (Error t) = ts (modStroke errorStyle) t -- paint in red
getStrokes' (Paren l g r)
-- we have special treatment for (Begin, End) because these blocks are typically very large.
-- we don't force the "end" part to prevent parsing the whole file.
| isBegin (tokT l) = if (posnOfs $ tokPosn $ l) /= point
then normalPaint
else case (tokT l, tokT r) of
(Begin b, End e) | b == e -> hintPaint
_ -> errPaint
| isErrorTok (tokT r) = errPaint
-- left paren wasn't matched: paint it in red.
-- note that testing this on the "Paren" node actually forces the parsing of the
-- right paren, undermining online behaviour.
| (posnOfs $ tokPosn $ l) == point || (posnOfs $ tokPosn $ r) == point - 1
= hintPaint
| otherwise = normalPaint
where normalPaint = ts id l <> getStrokesL g <> tsEnd id l r
hintPaint = ts (modStroke hintStyle) l <> getStrokesL g <> tsEnd (modStroke hintStyle) l r
errPaint = ts (modStroke errorStyle) l <> getStrokesL g
tsEnd _ (Tok{tokT = Begin b}) t@(Tok{tokT = End e})
| b /= e = ts (modStroke errorStyle) t
tsEnd f _ t = ts f t
getStrokesL :: Expr TT -> Endo [Stroke]
getStrokesL = foldMapAfter begin getStrokes'
ts f t
| isErrorTok (tokT t) = mempty
| otherwise = Endo (f (tokenToStroke t) :)
result = getStrokesL t0
modStroke :: StyleName -> Stroke -> Stroke
modStroke f = fmap (f `mappend`)
tokenToStroke :: TT -> Stroke
tokenToStroke = fmap tokenToStyle . tokToSpan
tokenToStyle :: Token -> StyleName
tokenToStyle t =
case t of
Comment -> commentStyle
Text -> defaultStyle
Special _ -> defaultStyle
Command _ -> typeStyle
Begin _ -> keywordStyle
End _ -> keywordStyle
NewCommand -> keywordStyle
isSpecial :: [Char] -> Token -> Bool
isSpecial cs (Special c) = c `elem` cs
isSpecial _ _ = False
isBegin, isEnd :: Token -> Bool
isBegin (Begin _) = True
isBegin _ = False
isEnd (End _) = True
isEnd _ = False
isErrorTok :: Token -> Bool
isErrorTok = isSpecial "!"