haskell-tools-refactor-0.3.0.0: Language/Haskell/Tools/Refactor/Prepare.hs
{-# LANGUAGE StandaloneDeriving
, DeriveGeneric
, LambdaCase
, ScopedTypeVariables
, BangPatterns
, MultiWayIf
, FlexibleContexts
, TypeFamilies
, TupleSections
, TemplateHaskell
, ViewPatterns
#-}
-- | Defines utility methods that prepare Haskell modules for refactoring
module Language.Haskell.Tools.Refactor.Prepare where
import GHC hiding (loadModule)
import Panic (handleGhcException)
import Outputable
import BasicTypes
import Bag
import Var
import SrcLoc
import Module as GHC
import FastString
import HscTypes
import GHC.Paths ( libdir )
import CmdLineParser
import DynFlags
import StringBuffer
import Control.Monad.IO.Class
import System.FilePath
import Data.Maybe
import Data.List.Split
import Language.Haskell.Tools.AST as AST
import Language.Haskell.Tools.AST.FromGHC
import Language.Haskell.Tools.PrettyPrint
import Language.Haskell.Tools.Transform
import Language.Haskell.Tools.Refactor.RefactorBase
tryRefactor :: Refactoring IdDom -> String -> IO ()
tryRefactor refact moduleName
= runGhc (Just libdir) $ do
initGhcFlags
useDirs ["."]
mod <- loadModule "." moduleName >>= parseTyped
res <- runRefactor (toFileName "." moduleName, mod) [] refact
case res of Right r -> liftIO $ mapM_ (putStrLn . prettyPrint . snd . fromContentChanged) r
Left err -> liftIO $ putStrLn err
-- | Set the given flags for the GHC session
useFlags :: [String] -> Ghc [String]
useFlags args = do
let lArgs = map (L noSrcSpan) args
dynflags <- getSessionDynFlags
let ((leftovers, errors, warnings), newDynFlags) = (runCmdLine $ processArgs flagsAll lArgs) dynflags
setSessionDynFlags newDynFlags
return $ map unLoc leftovers
-- | Initialize GHC flags to default values that support refactoring
initGhcFlags :: Ghc ()
initGhcFlags = do
dflags <- getSessionDynFlags
setSessionDynFlags
$ flip gopt_set Opt_KeepRawTokenStream
$ flip gopt_set Opt_NoHsMain
$ dflags { importPaths = []
, hscTarget = HscAsm -- needed for static pointers
, ghcLink = LinkInMemory
, ghcMode = CompManager
, packageFlags = ExposePackage "template-haskell" (PackageArg "template-haskell") (ModRenaming True []) : packageFlags dflags
}
return ()
-- | Use the given source directories
useDirs :: [FilePath] -> Ghc ()
useDirs workingDirs = do
dynflags <- getSessionDynFlags
setSessionDynFlags dynflags { importPaths = importPaths dynflags ++ workingDirs }
return ()
-- | Translates module name and working directory into the name of the file where the given module should be defined
toFileName :: String -> String -> FilePath
toFileName workingDir mod = normalise $ workingDir </> map (\case '.' -> pathSeparator; c -> c) mod ++ ".hs"
-- | Translates module name and working directory into the name of the file where the boot module should be defined
toBootFileName :: String -> String -> FilePath
toBootFileName workingDir mod = normalise $ workingDir </> map (\case '.' -> pathSeparator; c -> c) mod ++ ".hs-boot"
-- | Load the summary of a module given by the working directory and module name.
loadModule :: String -> String -> Ghc ModSummary
loadModule workingDir moduleName
= do initGhcFlags
useDirs [workingDir]
target <- guessTarget moduleName Nothing
setTargets [target]
load LoadAllTargets
getModSummary $ mkModuleName moduleName
-- | The final version of our AST, with type infromation added
type TypedModule = Ann AST.UModule IdDom SrcTemplateStage
-- | Get the typed representation from a type-correct program.
parseTyped :: ModSummary -> Ghc TypedModule
parseTyped modSum = do
p <- parseModule modSum
tc <- typecheckModule p
let annots = pm_annotations p
srcBuffer = fromJust $ ms_hspp_buf $ pm_mod_summary p
prepareAST srcBuffer . placeComments (getNormalComments $ snd annots)
<$> (addTypeInfos (typecheckedSource tc)
=<< (do parseTrf <- runTrf (fst annots) (getPragmaComments $ snd annots) $ trfModule modSum (pm_parsed_source p)
runTrf (fst annots) (getPragmaComments $ snd annots)
$ trfModuleRename modSum parseTrf
(fromJust $ tm_renamed_source tc)
(pm_parsed_source p)))
data IsBoot = NormalHs | IsHsBoot deriving (Eq, Ord, Show)
readSrcSpan :: String -> String -> RealSrcSpan
readSrcSpan fileName s = case splitOn "-" s of
[from,to] -> mkRealSrcSpan (readSrcLoc fileName from) (readSrcLoc fileName to)
readSrcLoc :: String -> String -> RealSrcLoc
readSrcLoc fileName s = case splitOn ":" s of
[line,col] -> mkRealSrcLoc (mkFastString fileName) (read line) (read col)