src/Importify/Pretty.hs
-- | This module contains functions to print imports
-- preserving original formatting.
module Importify.Pretty
( printLovelyImports
) where
import Universum
import Control.Arrow ((&&&))
import Data.Text (strip)
import Language.Haskell.Exts (Comment (..), ImportDecl (..), SrcSpan (..), SrcSpanInfo (..),
exactPrint, startLine)
import Language.Haskell.Exts.Syntax (Annotated (..))
import Importify.Syntax (stripEndLineComment)
import qualified Data.IntMap.Strict as IM
-- | This function takes range of origin text for import and
-- AST of imports without unused entinties and then converts
-- import declarations into list of lines that should be printed.
printLovelyImports :: Int
-> Int
-> [Comment]
-> [Text]
-> [ImportDecl SrcSpanInfo]
-> [Text]
printLovelyImports start end comments importsText importDecls =
let -- map ImportDecl into (Int, [Text]) -- index of starting line to lines of text
indexedImportLines = map (importStartLine &&& exactPrintImport comments) importDecls
importsMap = IM.fromList indexedImportLines
-- find empty and single comment lines
indexedTextLines = zip [start..end] importsText
emptyOrCommentLines = filter (null . strip . stripEndLineComment . snd) indexedTextLines
-- add empty and single comment lines to result map
importsMapWithExtraLines = foldl' (\dict (i,l) -> IM.insert i [l] dict)
importsMap
emptyOrCommentLines
-- collect all values and concat them; order is guaranteed by IntMap
resultLines = concat $ IM.elems importsMapWithExtraLines
in resultLines
importStartLine :: ImportDecl SrcSpanInfo -> Int
importStartLine = srcSpanStartLine . srcInfoSpan . importAnn
exactPrintImport :: [Comment] -> ImportDecl SrcSpanInfo -> [Text]
exactPrintImport allComments importDecl = dropWhile null
$ lines
$ toText
$ importText ++ endComments
where
importText = exactPrint importDecl (filter commentOnLine allComments)
-- exactPrint stops printing comments after the import statement is finished, so print those manually.
-- This would be much easier if Language.Haskell.Exts.ExactPrint exported more stuff
endComments = printComments
$ filter (\(Comment _ (SrcSpan _ sl _ _ ec) _) -> ec > importEndPos && sl == curLine)
allComments
printComments = intercalate " " . map ((" " ++) . printComment)
printComment (Comment multi _ s) = if multi
then "{-" ++ s ++ "-}"
else "--" ++ s
curLine = (startLine . ann) importDecl
importEndPos = (srcSpanEndColumn . srcInfoSpan . ann) importDecl
commentSpan (Comment _ srcSpan _) = srcSpan
commentOnLine = (== curLine) . srcSpanStartLine . commentSpan