nri-prelude-0.6.0.3: src/NriPrelude/Plugin.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE GADTs #-}
{-# OPTIONS_GHC -fno-warn-incomplete-record-updates #-}
-- | A GHC plugin for a more Elm-like Haskell experience. It automatically
-- adds an unqualified import of the NriPrelude module, and qualified imports of
-- other base modules such as List and Maybe.
--
-- To use it make sure your project has @nri-prelude@ listed as a dependency,
-- then add the follwing ghc option to your cabal or package yaml file:
--
-- > -fplugin=NriPrelude.Plugin
module NriPrelude.Plugin
( plugin,
)
where
-- Useful documentation
-- - Elm's default imports: https://package.elm-lang.org/packages/elm/core/latest/
-- - GHC user guide on compiler plugins: https://ghc.gitlab.haskell.org/ghc/doc/users_guide/extending_ghc.html#compiler-plugins
-- - Module providing API for creating plugins: https://www.stackage.org/haddock/lts-17.4/ghc-lib-8.10.4.20210206/GhcPlugins.html
import Data.Function ((&))
import qualified Data.List
#if __GLASGOW_HASKELL__ >= 900
import qualified GHC.Plugins as GhcPlugins
#else
import GhcPlugins
#endif
import NriPrelude.Plugin.GhcVersionDependent
( hsmodImports,
hsmodName,
ideclImplicit,
ideclName,
ideclQualified,
isQualified,
mkQualified,
simpleImportDecl,
)
import qualified Set
import Prelude
-- | adds an unqualified import of the NriPrelude module, and qualified imports of
-- other base modules such as List and Maybe.
--
-- To use it make sure your project has @nri-prelude@ listed as a dependency,
-- then add the follwing ghc option to your cabal or package yaml file:
--
-- > -fplugin=NriPrelude.Plugin
plugin :: GhcPlugins.Plugin
plugin =
GhcPlugins.defaultPlugin
{ GhcPlugins.parsedResultAction = addImplicitImports,
-- Let GHC know this plugin doesn't perform arbitrary IO. Given the same
-- input file it will make the same changes. Without this GHC will
-- recompile modules using this plugin every time which is expensive.
GhcPlugins.pluginRecompile = GhcPlugins.purePlugin
}
addImplicitImports ::
[GhcPlugins.CommandLineOption] ->
GhcPlugins.ModSummary ->
GhcPlugins.HsParsedModule ->
GhcPlugins.Hsc GhcPlugins.HsParsedModule
addImplicitImports _ _ parsed =
Prelude.pure
parsed
{ GhcPlugins.hpm_module =
fmap addImportsWhenNotPath (GhcPlugins.hpm_module parsed)
}
where
addImportsWhenNotPath hsModule =
case fmap unLocate (hsmodName hsModule) of
Nothing -> addImports hsModule
Just modName ->
if Data.List.isPrefixOf "Paths_" modName
then hsModule
else addImports hsModule
addImports hsModule =
hsModule
{ hsmodImports =
-- Add default Elm-like imports when the user hasn't imported them
-- explicitly yet, in order to avoid duplicate import warnings.
hsmodImports hsModule
++ ( Set.diff extraImports (existingImports hsModule)
& Set.toList
& fmap
( \imp ->
case imp of
Unqualified name -> unqualified name
Qualified name -> qualified name
)
)
}
existingImports hsModule =
hsmodImports hsModule
& fmap
( \(GhcPlugins.L _ imp) ->
case (isQualified imp, unLocate (ideclName imp)) of
(True, name) -> Qualified name
(False, name) -> Unqualified name
)
& Set.fromList
unLocate (GhcPlugins.L _ x) = GhcPlugins.moduleNameString x
unqualified name =
GhcPlugins.noLoc (simpleImportDecl (GhcPlugins.mkModuleName name))
& fmap (\qual -> qual {ideclImplicit = True})
qualified name =
fmap (\qual -> qual {ideclQualified = mkQualified}) (unqualified name)
data Import
= Unqualified String
| Qualified String
deriving (Eq, Ord)
-- taken from https://package.elm-lang.org/packages/elm/core/latest/
extraImports :: Set.Set Import
extraImports =
Set.fromList
[ Unqualified "NriPrelude", -- Elm exports types from withi these modules. We re-export them from NriPrelude. Same effect.
Qualified "Basics",
Qualified "Char",
Qualified "Debug",
Qualified "List",
Qualified "Maybe",
Qualified "Platform",
Qualified "Result",
Qualified "Text", -- equivalent to Elm's String
Qualified "Tuple",
-- Additionally Task and Log because we use them everywhere
Qualified "Log",
Qualified "Task"
]