hat-2.7.0.0: tools/Slice.hs
module Slice (Slice,union,makeSlice,sliceStartLine,sliceEndLine,sliceFile
,offsetSlice,highlightSlice,srcRef2Slice,RangeOrd(..)) where
import Data.Char (isSpace)
import NodeExp (NodeExp(..),children,getNode)
import LowLevel (getSrcRef,nil)
import SrcRef (SrcRef(..),readSrcRef)
import HighlightStyle (Highlight(..),Colour(..),highlight)
import Explore (Location(..),Coord(..),getDefLocation)
import System.IO.Unsafe (unsafePerformIO)
newtype FilePos = FilePos (Int,Int) deriving Eq -- Line, Col
data PartSlice = Part {file :: FilePath
,start :: FilePos
,finish :: FilePos}
type Slice = [PartSlice]
instance Eq PartSlice where
(==) (Part {file=f, start=s, finish=e}) (Part {file=f', start=s', finish=e'})
= f == f' && s == s' && e == e'
instance Show PartSlice where
show (Part {file=f, start=s, finish=e}) =
"(" ++ show f ++ ":" ++ show s ++ "-" ++ show e ++ ")"
instance Show FilePos where
show (FilePos (l,c)) = show l ++ ":" ++ show c
instance Ord FilePos where
compare (FilePos (x, y)) (FilePos (x',y'))
| x == x' = compare y y'
| otherwise = compare x x'
class Eq a => RangeOrd a where
(<<+) :: a -> a -> Bool
(+>>) :: a -> a -> Bool
(+>>) x y = (<<+) y x
(<+) :: a -> a -> Bool
(+>) :: a -> a -> Bool
(+>) x y = (<+) y x
(<+>) :: a -> a -> Bool
(>+<) :: a -> a -> Bool
(>+<) x y = (<+>) y x
instance RangeOrd PartSlice where
(<<+) (Part {file=f, start=s, finish=e}) (Part {file=f', start=s', finish=e'})
| f == f' = e <= s'
| otherwise = f < f'
(<+) (Part {file=f, start=s, finish=e}) (Part {file=f', start=s', finish=e'})
= f == f' && s < s' && e > s' && e <= e'
(<+>) (Part {file=f, start=s, finish=e}) (Part {file=f', start=s', finish=e'})
= f == f' && s <= s' && e >= e'
union :: Slice -> Slice -> Slice
union x = compress . merge x
merge :: Slice -> Slice -> Slice
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys)
| x <<+ y || x <+ y || x <+> y = x : merge xs (y:ys)
| otherwise = y : merge (x:xs) ys
compress :: Slice -> Slice
compress [] = []
compress [x] = [x]
compress (x:x1:xs)
| x <+ x1 = compress (join x x1 : xs)
| x <+> x1 = compress (x : xs)
| otherwise = x : compress (x1:xs)
join :: PartSlice -> PartSlice -> PartSlice
join (Part {file=f, start=s, finish=e}) (Part {file=f', start=s', finish=e'})
= Part {file=f, start=s, finish=e'}
makeSlice :: Int -> NodeExp -> Maybe Slice
makeSlice depth exp =
if depth >= 1 then
let
parts = map (filter (not . isZeroLoc))
$ map (srcRef2Slice . readSrcRef)
$ filter (/= LowLevel.nil)
$ map (getSrcRef . getNode)
$ (nTimes depth (concatMap children)) [exp]
nTimes 0 _ x = x
nTimes n f x = nTimes (n-1) f (f x) -- (new ++ acc) where new = f x
isZeroLoc (Part {start=FilePos(0,0),finish=FilePos(0,0)}) = True
isZeroLoc _ = False
in
if null parts
then Nothing
else Just $ foldr union (head parts) (tail parts)
else
let
(f,Location{begin=Coord{row=rB,col=cB}
,end=Coord{row=rE,col=cE}}) = getDefLocation $ getNode exp
in
Just [Part {file=f
,start=FilePos(rB,cB)
,finish=FilePos(rE,cE)}]
sliceStartLine :: Slice -> Int
sliceStartLine = (\(Part {start=(FilePos (s,_))}) -> s) . head
sliceEndLine :: Slice -> Int
sliceEndLine = (\(Part {finish=(FilePos (e,_))}) -> e) . last
sliceFile :: Slice -> FilePath
sliceFile = (\(Part {file=f}) -> f) . head
offsetSlice :: (Int,Int) -> Int -> Slice -> Slice
offsetSlice (l,c) so=
map doOffset
where
doOffset (Part {file=f
,start=(FilePos (sl,sc))
,finish=(FilePos(el,ec))}) =
Part {file=f
,start=FilePos(sl+l,if (sl+l) == 0 then sc + c + so else sc + c)
,finish=FilePos(el+l,if (el+l) == 0 then ec + c + so else ec + c)}
highlightSlice :: Slice -> String -> String
highlightSlice [] x = x
highlightSlice _ [] = []
highlightSlice ((Part {start=(FilePos (sl,sc)),finish=(FilePos (el,ec))}):others) x
= ls
++ cs
++ highlightedLines
++ rest
where
(ls,r) = grabLines sl x
(cs,r1) = grabChars sc r
(rls,r2) = grabLines (el-sl) r1
(rcs,r3) = grabChars (if el-sl == 0 then ec-sc else ec) r2
rest = highlightSlice (offsetSlice (-el,0) (-(ec + 2)) others) r3
highlightedLines = (unlines $ map highlightLine $ lines rls)
++ highlightLine rcs
highlightLine l = takeWhile isSpace l
++ (highlight [Background Red]
$ dropWhile isSpace l)
grabLines :: Int -> String -> (String,String)
grabLines n = (\(x,y) -> (unlines x,unlines y)) . splitAt n . lines
grabChars :: Int -> String -> (String,String)
grabChars n = splitAt (n + 1)
srcRef2Slice :: SrcRef -> Slice
srcRef2Slice (SrcRef {filename=f
,line=ls
,column=cs
,lineend=le
,columnend=ce}) =
[Part {file=f,start=(FilePos(ls,cs)),finish=(FilePos(le,ce))}]