{-# LANGUAGE DeriveDataTypeable #-}
module Language.Haskell.Exts.Comments
( associateHaddock
, Comment(..), UnknownPragma(..)
) where
import Language.Haskell.Exts.Syntax
import Language.Haskell.Exts.SrcLoc
import Data.Char (isSpace)
import Data.Traversable
import Data.Data
data Comment = Comment Bool SrcSpan String
deriving (Eq,Show,Typeable,Data)
data UnknownPragma = UnknownPragma SrcSpan String
deriving (Eq,Show,Typeable,Data)
associateHaddock
::(Annotated ast,Traversable ast)
=> (ast SrcSpanInfo,[Comment])
-> ast (SrcSpanInfo,[Comment])
associateHaddock (ast,[]) = fmap (\src->(src,[])) ast
associateHaddock (ast,comments) =
let
(ca,assocs1) = mapAccumL associate1 (newAccumulator comments) ast
in snd $ mapAccumL merge (lastPost ca) assocs1
merge
:: [(SrcSpanInfo,[Comment])]
-> (SrcSpanInfo,[Comment])
-> ([(SrcSpanInfo,[Comment])], (SrcSpanInfo,[Comment]))
merge [] ret = ([],ret)
merge (x:xs) (src,cmts) =
if fst x == src
then (xs,(src,cmts ++ snd x))
else (x:xs,(src,cmts))
lastPost :: CommentAccumulator -> [(SrcSpanInfo, [Comment])]
lastPost (CommentAccumulator (Post cmt : rest) past assocs) =
let (toMerge, _) = span isNone rest
psrc = matchPreviousSrc past
in (assocs ++ [(psrc, cmt : map hcComment toMerge)])
lastPost (CommentAccumulator _ _ assocs) = assocs
associate1
:: CommentAccumulator
-> SrcSpanInfo
-> (CommentAccumulator,(SrcSpanInfo,[Comment]))
associate1 ca@(CommentAccumulator [] _ _) src = (ca,(src,[]))
associate1 (CommentAccumulator (hc@(Pre cmt):rest) _ assocs) src =
if isBefore hc src
then
let (toMerge,next) = getToMerge src rest
newAssoc = (src,cmt : map hcComment toMerge)
in (CommentAccumulator next [] assocs,newAssoc)
else (CommentAccumulator (hc:rest) [] assocs,(src,[]))
associate1 (CommentAccumulator (hc@(Post cmt):rest) past assocs) src =
if isBefore hc src
then
let (toMerge,next) = getToMerge src rest
newAssocs =
if null past
then assocs
else assocs++[(matchPreviousSrc past,cmt : map hcComment toMerge)]
in associate1 (CommentAccumulator next [] newAssocs) src
else (CommentAccumulator (hc:rest) (src:past) assocs,(src,[]))
associate1 (CommentAccumulator (_:rest) past assocs) src =
(CommentAccumulator rest (src:past) assocs,(src,[]))
data CommentAccumulator = CommentAccumulator
[HaddockComment]
[SrcSpanInfo]
[(SrcSpanInfo,[Comment])]
newAccumulator :: [Comment] -> CommentAccumulator
newAccumulator comments = CommentAccumulator (commentsToHaddock comments) [] []
getToMerge
:: SrcSpanInfo
-> [HaddockComment]
-> ([HaddockComment],[HaddockComment])
getToMerge src = span (\hc-> isNone hc && isBefore hc src)
matchPreviousSrc :: [SrcSpanInfo] -> SrcSpanInfo
matchPreviousSrc [] =
error "Language.Haskell.Exts.Annotated.Comments.matchPreviousSrc: empty list"
matchPreviousSrc srcs =
let end = srcSpanEnd $ srcInfoSpan $ head srcs
in last $ filter ((end ==) . srcSpanEnd . srcInfoSpan) srcs
isBefore :: HaddockComment -> SrcSpanInfo -> Bool
isBefore hc src=
let
(Comment _ csrc _) = hcComment hc
in csrc < srcInfoSpan src
data HaddockComment =
Pre
{
hcComment::Comment
}
| Post {
hcComment::Comment
}
| None {
hcComment::Comment
}
isNone :: HaddockComment -> Bool
isNone (None _) = True
isNone _ = False
commentsToHaddock :: [Comment] -> [HaddockComment]
commentsToHaddock = map commentToHaddock
commentToHaddock :: Comment -> HaddockComment
commentToHaddock c@(Comment _ _ txt) =
case dropWhile isSpace txt of
('|':_) -> Pre c
('^':_) -> Post c
_ -> None c