activehs-0.3.0.1: AgdaHighlight.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module AgdaHighlight
( highlightAgdaAsXHtml
-- , FormatOption (..)
) where
import Text.Highlighting.Kate as Kate
import Text.Blaze.Renderer.String
import Control.Monad.State
import System.IO.Unsafe
import Agda.Syntax.Literal
import Agda.Syntax.Position
import Agda.Syntax.Parser
import Agda.Syntax.Parser.Tokens (Token(..))
highlightAgdaAsXHtml :: String -> String
highlightAgdaAsXHtml code = formattedCode
where
language = "agda"
(Right highlightInfo) = highlight code
formattedCode = renderHtml $ formatHtmlBlock defaultFormatOpts highlightInfo
-- | Highlight source code using this syntax definition.
highlight :: String -> Either String [SourceLine]
highlight input = Right $ runSourceLineGen $ do
tokens <- liftIO $ parse tokensParser input
-- tokens :: IO [Tokens]
-- Other parsing method needed - or unsafePerformIO
-- map tokenToSourceLine tokens
mapM_ processToken tokens
where
-- processToken :: Token -> SourceLineGen ()
processToken token =
case token of
TokKeyword kw iv -> addByInterval (KeywordTok, getSourceByInterval iv) iv -- kw
TokId (iv, id) -> addByInterval (DataTypeTok, getSourceByInterval iv) iv -- dt
TokQId ivds -> mapM_ (\(iv, id) -> addByInterval (DataTypeTok, getSourceByInterval iv) iv) ivds -- dt
TokLiteral lit ->
case lit of
LitInt r i -> addByRange (DecValTok, getSourceByRange r) r -- dv
LitFloat r d -> addByRange (FloatTok, getSourceByRange r) r -- fl
LitString r str -> addByRange (StringTok, getSourceByRange r) r -- st
LitChar r ch -> addByRange (CharTok, getSourceByRange r) r -- ch
LitQName r qn -> addByRange (OtherTok, getSourceByRange r) r -- ?
TokSymbol sym iv -> addByInterval (FunctionTok, getSourceByInterval iv) iv -- TODO: switch by symbol?
TokString (iv, str) -> addByInterval (StringTok, getSourceByInterval iv) iv -- st
TokSetN (iv, n) -> addByInterval (OtherTok, "TokSetN") iv -- ?
TokTeX (iv, str) -> addByInterval (OtherTok, "TokTex") iv -- ?
TokComment (iv, str) -> addByInterval (CommentTok, getSourceByInterval iv) iv -- co
TokDummy -> return ()
TokEOF -> return ()
getSourceByInterval :: Interval -> String
getSourceByInterval iv = take len $ drop (st - 1) input
where
st = (fromIntegral . posPos . iStart) iv
end = (fromIntegral . posPos . iEnd) iv
len = end - st
getSourceByRange :: Range -> String
getSourceByRange (Range ivs) = foldl (++) [] $ map getSourceByInterval ivs
type LabeledSource = Kate.Token -- ([String], String)
data SourceLineState = SLS
{ slsLines :: [SourceLine]
, slsCurrentLine :: !Int
, slsCurrentChar :: !Int
} deriving (Show)
newtype SourceLineGen a = SLG (StateT SourceLineState IO a)
deriving (Monad, MonadState SourceLineState, MonadIO)
newLine :: SourceLineGen ()
newLine = modify (\s -> s
{ slsLines = (slsLines s) ++ [[]]
, slsCurrentLine = (slsCurrentLine s) + 1
, slsCurrentChar = 1
})
extendCurrentLine :: LabeledSource -> SourceLineGen ()
extendCurrentLine ls@(attrs, source) = modify (\s -> s { slsLines = modLines s, slsCurrentChar = modChars s })
where
modChars s = (slsCurrentChar s) + (length source)
modLines s = (start s) ++ [ modLine s ] ++ (end s)
modLine s = ((slsLines s) !! ((slsCurrentLine s) - 1)) ++ [ ls ]
start s = fst $ parts s
end s = drop 1 $ snd $ parts s
parts s = splitAt ((slsCurrentLine s) - 1) (slsLines s)
addAt :: LabeledSource -> Int -> Int -> SourceLineGen ()
addAt ls line char = do
currentLine <- gets slsCurrentLine
let lineDiff = line - currentLine - 1
mapM_ (\x -> newLine) [0..lineDiff]
currentChar <- gets slsCurrentChar
let charDiff = char - currentChar
if charDiff > 0 then extendCurrentLine $ spaceLS charDiff else return ()
extendCurrentLine ls
where
spaceLS n = (OtherTok, replicate n ' ')
addAtPosition :: LabeledSource -> Position -> SourceLineGen ()
addAtPosition ls p = addAt ls (fromIntegral $ posLine p) (fromIntegral $ posCol p)
addByInterval :: LabeledSource -> Interval -> SourceLineGen ()
addByInterval ls i = addAtPosition ls (iStart i)
addByRange :: LabeledSource -> Range -> SourceLineGen ()
addByRange ls (Range is) = mapM_ (addByInterval ls) is
runSourceLineGen :: SourceLineGen a -> [SourceLine]
runSourceLineGen (SLG body) = slsLines $ unsafePerformIO $ execStateT body (SLS
{ slsLines = [[]]
, slsCurrentLine = 1
, slsCurrentChar = 1
})