module Fixme.Comment
(
Comment
, parseComments
, readComments
, commentText
, filename
, startLine
, endLine
, Located
, locatedValue
, newComment
, Language
, languageForFile
, highlightCode
) where
import Protolude
import qualified Data.ByteString as ByteString
import GHC.IO (FilePath)
import Text.Highlighter
( lexerFromFilename
, runLexer
, Lexer
, Token(..)
, TokenType(..)
)
parseComments :: Filename -> Language -> ByteString -> [Comment]
parseComments filename language = parseComments' filename . highlightCode language
parseComments' :: Filename -> [Token] -> [Comment]
parseComments' filename =
coalesce appendComment . mapMaybe getComment . locateTokens filename
where
coalesce :: (a -> a -> Maybe a) -> [a] -> [a]
coalesce maybeAppend (x:y:ys) =
case x `maybeAppend` y of
Nothing -> x:(coalesce maybeAppend (y:ys))
Just z -> (coalesce maybeAppend (z:ys))
coalesce _ xs = xs
readComments :: FilePath -> Maybe (IO [Comment])
readComments filename =
case languageForFile (toS filename) of
Nothing -> Nothing
Just language -> Just $ do
contents <- ByteString.readFile filename
pure (parseComments (Just (toS filename)) language contents)
data Located a = Located { filename :: Filename
, startLine :: Int
, locatedValue :: a
} deriving (Eq, Show)
instance Functor Located where
fmap f (Located fn start x) = Located fn start (f x)
type Filename = Maybe Text
locateTokens :: Filename -> [Token] -> [Located Token]
locateTokens fn tokens =
[ Located fn i t | (i, t) <- zip startLines tokens ]
where
startLines = scanl (+) 0 [ numLines value | Token _ value <- tokens ]
numLines :: ByteString -> Int
numLines = ByteString.count newline
where newline = fromIntegral (ord '\n')
type Comment = Located ByteString
commentText :: Comment -> ByteString
commentText = locatedValue
newComment :: Filename -> Int -> ByteString -> Comment
newComment fn i text = Located fn i text
endLine :: Comment -> Int
endLine (Located _ startLine c) = startLine + (numLines c)
appendComment :: Comment -> Comment -> Maybe Comment
appendComment x y
| filename x /= filename y = Nothing
| startLine y - endLine x > 1 = Nothing
| startLine y - endLine x == 1 = Just (newComment (filename x) (startLine x) (commentText x <> "\n" <> commentText y))
| otherwise = Just (newComment (filename x) (startLine x) (commentText x <> commentText y))
getComment :: Located Token -> Maybe Comment
getComment (Located filename lineNum token) =
case getComment' token of
Nothing -> Nothing
Just comment -> Just $ Located filename lineNum comment
where
getComment' :: Token -> Maybe ByteString
getComment' (Token tType value) = bool Nothing (Just value) (isComment tType)
isComment Comment = True
isComment (Arbitrary "Comment") = True
isComment (x :. y) = isComment x || isComment y
isComment _ = False
type Language = Lexer
languageForFile :: FilePath -> Maybe Language
languageForFile = lexerFromFilename
highlightCode :: Language -> ByteString -> [Token]
highlightCode language code =
case runLexer language code of
Left _ -> []
Right tokens -> tokens