hpc-tracer-0.3.0: src/CodeRenderActor.hs
module CodeRenderActor where
import Trace.Hpc.Mix
import Trace.Hpc.Tix
import Trace.Hpc.Util
import Data.List
import Data.Maybe(fromJust)
import Reactive
import Common
codeRenderActor :: String -> String -> IO (String -> IO String)
codeRenderActor hpcDir srcDir = do
mkActor [] $
request $ \ file state -> do
(contents,state')
<- case lookup file state of
Nothing -> do
contents <- createHtml hpcDir srcDir file
return (contents,(file,contents) : state)
Just contents -> do
return (contents,state)
let wrap = "<html><HEAD>" ++
"<LINK REL =\"stylesheet\" HREF =\"default.css\" TYPE =\"text/css\"></LINK></HEAD>" ++
"<body>" ++ contents ++
"<script type=\"text/javascript\">\n" ++
"parent.status.codeloaded( " ++ show file ++ ");\n" ++
"</script>\n" ++
"</body></html>"
return (wrap,state')
------------------------------------------------------------------------------
createHtml :: String -> String -> String -> IO String
createHtml hpcDir srcDir modName = do
mix@(Mix origFile _ _ tabStop mix') <- readMix [srcDir ++ "/" ++ hpcDir] (Left modName)
-- print mix
let info = [ (pos,id)
| (id,(pos,boxLabel)) <- zip [0 ..] mix'
{-
, case boxLabel of
ExpBox {} -> True
AltBox {} -> True
TopLevelBox {} -> True
LocalBox {} -> True
_ -> False
-}
]
let tickLocs = [ (Loc ln1 c1,Loc ln2 c2,id)
| (pos,id) <- info
, let (ln1,c1,ln2,c2) = fromHpcPos pos
]
let sortedTickLocs = sortBy (\ (locA1,locZ1,_) (locA2,locZ2,_) ->
(locA1,locZ2) `compare` (locA2,locZ1)) tickLocs
-- print sortedTickLocs
content <- readFileFromPath origFile srcDir
let content' = addMarkup 1 content (Loc 1 1) [] sortedTickLocs
let show' = reverse . take 5 . (++ " ") . reverse . show
let addLine n xs = "<span class=\"lineno\">" ++ show' n ++ " </span>" ++ xs
let addLines = unlines . map (uncurry addLine) . zip [1..] . lines
return ("<pre>\n" ++ addLines content' ++ "\n</pre>\n")
type Markup = Int
addMarkup :: Int -- tabStop
-> String -- text to mark up
-> Loc -- current location
-> [(Loc,Markup)] -- stack of open ticks, with closing location
-> [(Loc,Loc,Markup)] -- sorted list of tick location pairs
-> String
-- check the pre-condition.
--addMarkup tabStop cs loc os ticks
-- | not (isSorted (map fst os)) = error $ "addMarkup: bad closing ordering: " ++ show os
--addMarkup tabStop cs loc os@(_:_) ticks
-- | trace (show (loc,os,take 10 ticks)) False = undefined
-- close all open ticks, if we have reached the end
addMarkup _ [] loc os [] =
concatMap (const closeTick) os
addMarkup tabStop cs loc ((o,_):os) ticks | loc > o =
closeTick ++ addMarkup tabStop cs loc os ticks
--addMarkup tabStop cs loc os ((t1,t2,tik@(TopLevelDecl {})):ticks) | loc == t1 =
-- openTick tik ++ closeTick ++ addMarkup tabStop cs loc os ticks
addMarkup tabStop cs loc os ((t1,t2,tik):ticks) | loc == t1 =
openTick tik ++ addMarkup tabStop cs loc (addTo (t2,tik) os) ticks
where
addTo (t,tik) [] = [(t,tik)]
addTo (t,tik) ((t',tik'):xs) | t <= t' = (t,tik):(t',tik'):xs
| t > t' = (t',tik):(t',tik'):xs
addMarkup tabStop cs loc os ((t1,t2,tik):ticks) | loc > t1 =
-- throw away this tick, because it is from a previous place ??
addMarkup tabStop cs loc os ticks
addMarkup tabStop ('\n':cs) loc@(Loc ln col) os@((Loc ln2 col2,_):_) ticks
| ln == ln2 && col < col2
= addMarkup tabStop (' ':'\n':cs) loc os ticks
addMarkup tabStop (c:cs) loc@(Loc _ p) os ticks =
if c=='\n' && os/=[] then
c : "<span class=\"spaces\">" ++ expand 1 w ++ "</span>" ++
addMarkup tabStop cs' loc' os ticks
else if c=='\t' then
expand p "\t" ++ addMarkup tabStop cs (incBy c loc) os ticks
else
escape c ++ addMarkup tabStop cs (incBy c loc) os ticks
where
(w,cs') = span (`elem` " \t") cs
loc' = foldl (flip incBy) loc (c:w)
escape '>' = ">"
escape '<' = "<"
escape '"' = """
escape '&' = "&"
escape c = [c]
expand :: Int -> String -> String
expand _ "" = ""
expand c ('\t':s) = replicate (c' - c) ' ' ++ expand c' s
where
c' = tabStopAfter 8 c
expand c (' ':s) = ' ' : expand (c+1) s
expand _ _ = error "bad character in string for expansion"
incBy :: Char -> Loc -> Loc
incBy '\n' (Loc ln c) = Loc (succ ln) 1
incBy '\t' (Loc ln c) = Loc ln (tabStopAfter tabStop c)
incBy _ (Loc ln c) = Loc ln (succ c)
tabStopAfter :: Int -> Int -> Int
tabStopAfter tabStop c = fromJust (find (>c) [1,(tabStop + 1)..])
addMarkup tabStop cs loc os ticks =
"ERROR: " ++ show (take 10 cs,tabStop,loc,take 10 os,take 10 ticks)
data Loc = Loc !Int !Int
deriving (Eq,Ord,Show)
openTick :: Markup -> String
openTick n = "<span class=\"tick\" id=\"t_" ++ show n ++ "\">"
closeTick = "</span>"
readFileFromPath :: String -> String -> IO String
readFileFromPath filename@('/':_) _ = readFile filename
readFileFromPath filename path = readTheFile (splitPath path)
where
splitPath path = case span (/= ':') path of
(dir,':':more) -> dir : splitPath more
(dir,[]) -> [dir]
readTheFile :: [String] -> IO String
readTheFile [] = error $ "could not find " ++ show filename
++ " in path " ++ show path
readTheFile (dir:dirs) =
Prelude.catch
(do str <- readFile (dir ++ "/" ++ filename)
return str)
(\ _ -> readTheFile dirs)
------------------------------------------------------------------------------