{-# OPTIONS -Wall #-} module Language.Haskell.HBB.Inline ( inline, inlineM, showInlineResult, BufLoc(..), BufSpan(..), NonFirstLinesIndenting(..), InlineOptions(..), defaultInlineOptions ) where import Language.Haskell.HBB.Internal.InternalTTreeCreation import Language.Haskell.HBB.Internal.InterfaceTypes import Language.Haskell.HBB.Internal.InternalTTree import Language.Haskell.HBB.Internal.GHCHighlevel import Language.Haskell.HBB.Internal.TTreeColor import Language.Haskell.HBB.Internal.SrcSpan import Language.Haskell.HBB.Internal.TTree import Language.Haskell.HBB.Internal.GHC import Control.Monad.Reader hiding (liftIO) import FastString (unpackFS) import GHC.Paths (libdir) import GhcMonad (liftIO,GhcMonad) import SrcLoc -- | The data type InlineOptions is to alter the behviour of the function -- 'inline'. -- -- If 'showContext' is true 'inline' not only prints the inlined version of the -- function or value binding but also the file context. -- -- If 'showAnsiColored' is true 'inline' will use ANSI terminal colors to -- highlight different logical informations in the inlined version. Colors are -- used for areas that are identical with the original function or value -- binding (displays) and a bold grey is used for areas that have been added -- and do not occur in the original binding (additions). data InlineOptions = InlineOptions { showContext :: Bool , showAnsiColored :: Bool , adaptToTargetEnv :: NonFirstLinesIndenting } -- | This value defines the default options for inlining. -- -- Most text editors will need these settings (maybe adaptToTargetEnv should be -- adapted). The inlined version of the function or value binding is printed -- without ANSI colors and without context but with non-first lines being -- indented to a level that allows a text editor to replace the original name -- with the return value of mode 'inline'. defaultInlineOptions :: InlineOptions defaultInlineOptions = InlineOptions { showContext = False , showAnsiColored = False , adaptToTargetEnv = IgnoreIndOfTargetEnv } -- | This function implements the mode 'inline'. -- -- Inline takes a location or a span within a file which should be a function -- binding (as of 2014-09-16 excluding any parameter) and tries to produce an -- inlined version of the function. The inlined version of the function then is -- written to standard output. -- -- @ -- main :: IO () -- main = inline [\"-iexample\"] defaultInlineOptions \"example/Example.hs\" (BufLoc 14 13) -- @ -- -- It is important to know that the indentation of non-first lines (as of -- 2014-09-16) is always adapted to match the indentation of the location where -- the name should be replaced. -- If a second location isn't passed this function will use GHCs lexer to find -- out where the end of the variable or function name is. Consequently to -- inline a function and to simultaneously apply it to its arguments (which is -- not supported as of 2014-09-16) the second location must be passed. -- -- The first three command line parameters are: -- -- - The GHC options as string list (as they should appear on the command line) -- -- - Some options to the mode 'inline' that change the functions behaviour -- -- - The path to the GHC library folder (the module GHC.Paths exports 'libdir' -- which can be used here) inline :: [String] -> InlineOptions -> FilePath -> BufLoc -> Maybe BufLoc -> IO (BufSpan,String) inline ghcOptions iopts fn sl mbEndLoc = runGhcWithCmdLineFlags ghcOptions (Just libdir) $ inlineM iopts fn sl mbEndLoc -- | This function creates a string of the result returned by inline or -- inlineM. -- -- The string has exactly the format that should be understood by text editors -- that are using the mode inline. showInlineResult :: (BufSpan,String) -> String showInlineResult = snd -- | This is the monadic version of inline. -- -- Instead of taking command line flags to alter the GHC environment this -- function can be used with a custom GhcMonad instance which allows more -- control about GHCs behaviour. inlineM :: GhcMonad m => InlineOptions -> FilePath -> BufLoc -> Maybe BufLoc -> m (BufSpan,String) inlineM (InlineOptions { showContext = sc , showAnsiColored = sa , adaptToTargetEnv = ai }) occFileName startLoc mbEndLoc = do sti@(SearchedTokenInfo { result = (bindInfo,_) }) <- searchFunctionBindingM occFileName startLoc mbEndLoc occFileContent <- liftIO $ readFile occFileName (bindFileName,bindFileContent) <- do let (L (RealSrcSpan r) _) = bindInfo n = unpackFS $ srcSpanFile r content <- liftIO $ readFile n return (n,content) let tree@(TTree c childs) = let produceClientTTree :: FunBindInfo -> ClientTTree produceClientTTree sti'@(SearchedTokenInfo { result = (bi,_) }) = let richTTree = runReader (toTTree bi) ProduceLambda inlCol = ((srcLocCol $ realSrcSpanStart $ occSpan sti')) insSpan = pointBufSpan 1 (case ai of AdaptIndToTargetEnv -> inlCol IgnoreIndOfTargetEnv -> 1) in snd $ applyIndentation (IncInline insSpan,richTTree) in produceClientTTree sti fileCache = if occFileName == bindFileName then [( occFileName,str2LineBuf occFileContent)] else [( occFileName,str2LineBuf occFileContent) ,(bindFileName,str2LineBuf bindFileContent)] let alteredContent = case sc of True -> let spn = toBufSpan $ occSpan sti in case sa of True -> applyColoredTTree fileCache (spn,tree) (str2LineBuf occFileContent) False -> applyTTree fileCache (spn,tree) (str2LineBuf occFileContent) False -> let spn = pointBufSpan 1 1 in case sa of True -> applyColoredTTree fileCache (spn,(TTree c childs)) [""] False -> applyTTree fileCache (spn,(TTree c childs)) [""] return (toBufSpan $ occSpan sti,lineBuf2Str alteredContent)