{-| Description : Facade for the location handling of HSE See also "Language.Haskell.Formatter.Source". -} module Language.Haskell.Formatter.Location (SrcLoc.SrcLoc, SrcLoc.SrcSpan, SrcLoc.SrcSpanInfo, base, plus, minus, Portioned(..), Line, Column, streamName, getLine, getColumn, createPosition, SrcLoc.getPointLoc, getEndPosition, replaceNestedPortionLines, stringPortion, getStartLine, getStartColumn, getEndLine, getEndColumn) where import qualified Data.Function as Function import qualified Language.Haskell.Exts.Comments as Comments import qualified Language.Haskell.Exts.SrcLoc as SrcLoc import qualified Language.Haskell.Exts.Syntax as Syntax import qualified Language.Haskell.Formatter.Internal.Newline as Newline import qualified Language.Haskell.Formatter.Toolkit.ListTool as ListTool import qualified Language.Haskell.Formatter.Toolkit.StreamName as StreamName import Prelude hiding (getLine) class Enum a => Natural a where base :: a plus :: Integral b => b -> a -> a plus difference natural = toEnum $ fromIntegral difference + fromEnum natural minus :: Num b => a -> a -> b minus minuend = fromIntegral . Function.on (-) fromEnum minuend class Portioned a where getPortion :: a -> SrcLoc.SrcSpan newtype Line = Line Int deriving (Eq, Ord, Show) newtype Column = Column Int deriving (Eq, Ord, Show) instance Enum Line where toEnum = Line fromEnum (Line line) = line instance Enum Column where toEnum = Column fromEnum (Column column) = column instance Natural Line where base = Line 1 instance Natural Column where base = Column 1 instance Portioned SrcLoc.SrcSpanInfo where getPortion = SrcLoc.srcInfoSpan instance Portioned a => Portioned (Syntax.Module a) where getPortion = getPortion . Syntax.ann instance Portioned Comments.Comment where getPortion (Comments.Comment _ commentPortion _) = commentPortion streamName :: SrcLoc.SrcInfo a => a -> StreamName.StreamName streamName = StreamName.createStreamName . SrcLoc.fileName getLine :: SrcLoc.SrcLoc -> Line getLine = Line . SrcLoc.srcLine getColumn :: SrcLoc.SrcLoc -> Column getColumn = Column . SrcLoc.srcColumn createPosition :: StreamName.StreamName -> Line -> Column -> SrcLoc.SrcLoc createPosition stream (Line line) (Column column) = SrcLoc.SrcLoc{SrcLoc.srcFilename = show stream, SrcLoc.srcLine = line, SrcLoc.srcColumn = column} getEndPosition :: SrcLoc.SrcSpan -> SrcLoc.SrcLoc getEndPosition portion = createPosition stream line column where stream = streamName portion line = Line $ SrcLoc.srcSpanEndLine portion column = Column $ SrcLoc.srcSpanEndColumn portion replaceNestedPortionLines :: (Line -> Line) -> SrcLoc.SrcSpanInfo -> SrcLoc.SrcSpanInfo replaceNestedPortionLines function nestedPortion = nestedPortion{SrcLoc.srcInfoSpan = parent', SrcLoc.srcInfoPoints = children'} where parent' = replace parent replace = replacePortionLines function parent = SrcLoc.srcInfoSpan nestedPortion children' = fmap replace children children = SrcLoc.srcInfoPoints nestedPortion replacePortionLines :: (Line -> Line) -> SrcLoc.SrcSpan -> SrcLoc.SrcSpan replacePortionLines function portion = portion{SrcLoc.srcSpanStartLine = start, SrcLoc.srcSpanEndLine = end} where Line start = function $ getStartLine portion Line end = function $ getEndLine portion stringPortion :: SrcLoc.SrcLoc -> String -> SrcLoc.SrcSpan stringPortion startPosition string = SrcLoc.mkSrcSpan startPosition endPosition where endPosition = createPosition stream endLine endColumn stream = streamName startPosition endLine = lastIndex lineCount startLine lastIndex difference = pred . plus difference lineCount = length stringLines stringLines = Newline.splitSeparatedLines string startLine = getStartLine startPosition endColumn = lastIndex lastLineLength lastLineStartColumn lastLineLength = maybe 0 length $ ListTool.maybeLast stringLines lastLineStartColumn = if hasSingleLine then startColumn else base hasSingleLine = lineCount == 1 startColumn = getStartColumn startPosition getStartLine :: SrcLoc.SrcInfo a => a -> Line getStartLine = getLine . SrcLoc.getPointLoc getStartColumn :: SrcLoc.SrcInfo a => a -> Column getStartColumn = getColumn . SrcLoc.getPointLoc getEndLine :: SrcLoc.SrcSpan -> Line getEndLine = getLine . getEndPosition getEndColumn :: SrcLoc.SrcSpan -> Column getEndColumn = getColumn . getEndPosition