-- Copyright (c) 2019 The DAML Authors. All rights reserved.

-- SPDX-License-Identifier: Apache-2.0


-- | Options

module Development.IDE.Types.Options
  ( IdeOptions(..)
  , IdePreprocessedSource(..)
  , IdeReportProgress(..)
  , IdeDefer(..)
  , IdeTesting(..)
  , IdeOTMemoryProfiling(..)
  , clientSupportsProgress
  , IdePkgLocationOptions(..)
  , defaultIdeOptions
  , IdeResult
  , IdeGhcSession(..)
  , OptHaddockParse(..)
  ) where

import Data.Default
import Development.Shake
import Development.IDE.GHC.Util
import           GHC hiding (parseModule, typecheckModule)
import           GhcPlugins                     as GHC hiding (fst3, (<>))
import qualified Language.Haskell.LSP.Types.Capabilities as LSP
import qualified Data.Text as T
import Development.IDE.Types.Diagnostics
import Control.DeepSeq (NFData(..))
import Ide.Plugin.Config

data IdeGhcSession = IdeGhcSession
  { IdeGhcSession -> FilePath -> IO (IdeResult HscEnvEq, [FilePath])
loadSessionFun :: FilePath -> IO (IdeResult HscEnvEq, [FilePath])
  -- ^ Returns the Ghc session and the cradle dependencies

  , IdeGhcSession -> Int
sessionVersion :: !Int
  -- ^ Used as Shake key, versions must be unique and not reused

  }

instance Show IdeGhcSession where show :: IdeGhcSession -> FilePath
show IdeGhcSession
_ = FilePath
"IdeGhcSession"
instance NFData IdeGhcSession where rnf :: IdeGhcSession -> ()
rnf !IdeGhcSession
_ = ()

data IdeOptions = IdeOptions
  { IdeOptions -> ParsedSource -> IdePreprocessedSource
optPreprocessor :: GHC.ParsedSource -> IdePreprocessedSource
    -- ^ Preprocessor to run over all parsed source trees, generating a list of warnings

    --   and a list of errors, along with a new parse tree.

  , IdeOptions -> Action IdeGhcSession
optGhcSession :: Action IdeGhcSession
    -- ^ Setup a GHC session for a given file, e.g. @Foo.hs@.

    --   For the same 'ComponentOptions' from hie-bios, the resulting function will be applied once per file.

    --   It is desirable that many files get the same 'HscEnvEq', so that more IDE features work.

  , IdeOptions -> IdePkgLocationOptions
optPkgLocationOpts :: IdePkgLocationOptions
    -- ^ How to locate source and @.hie@ files given a module name.

  , IdeOptions -> [FilePath]
optExtensions :: [String]
    -- ^ File extensions to search for code, defaults to Haskell sources (including @.hs@)


  , IdeOptions -> Int
optThreads :: Int
    -- ^ Number of threads to use. Use 0 for number of threads on the machine.

  , IdeOptions -> Maybe FilePath
optShakeFiles :: Maybe FilePath
  -- ^ Directory where the shake database should be stored. For ghcide this is always set to `Nothing` for now

  -- meaning we keep everything in memory but the daml CLI compiler uses this for incremental builds.

  , IdeOptions -> Maybe FilePath
optShakeProfiling :: Maybe FilePath
    -- ^ Set to 'Just' to create a directory of profiling reports.

  , IdeOptions -> IdeOTMemoryProfiling
optOTMemoryProfiling :: IdeOTMemoryProfiling
    -- ^ Whether to record profiling information with OpenTelemetry. You must

    --   also enable the -l RTS flag for this to have any effect

  , IdeOptions -> IdeTesting
optTesting :: IdeTesting
    -- ^ Whether to enable additional lsp messages used by the test suite for checking invariants

  , IdeOptions -> IdeReportProgress
optReportProgress :: IdeReportProgress
    -- ^ Whether to report progress during long operations.

  , IdeOptions -> FilePath
optLanguageSyntax :: String
    -- ^ the ```language to use

  , IdeOptions -> Bool
optNewColonConvention :: Bool
    -- ^ whether to use new colon convention

  , IdeOptions -> [Text]
optKeywords :: [T.Text]
    -- ^ keywords used for completions. These are customizable

    -- since DAML has a different set of keywords than Haskell.

  , IdeOptions -> IdeDefer
optDefer :: IdeDefer
    -- ^ Whether to defer type errors, typed holes and out of scope

    --   variables. Deferral allows the IDE to continue to provide

    --   features such as diagnostics and go-to-definition, in

    --   situations in which they would become unavailable because of

    --   the presence of type errors, holes or unbound variables.

  , IdeOptions -> Bool
optCheckProject :: !Bool
    -- ^ Whether to typecheck the entire project on load

  , IdeOptions -> CheckParents
optCheckParents :: CheckParents
    -- ^ When to typecheck reverse dependencies of a file

  , IdeOptions -> OptHaddockParse
optHaddockParse :: OptHaddockParse
    -- ^ Whether to return result of parsing module with Opt_Haddock.

    --   Otherwise, return the result of parsing without Opt_Haddock, so

    --   that the parsed module contains the result of Opt_KeepRawTokenStream,

    --   which might be necessary for hlint.

  , IdeOptions -> DynFlags -> DynFlags
optCustomDynFlags :: DynFlags -> DynFlags
    -- ^ Will be called right after setting up a new cradle,

    --   allowing to customize the Ghc options used

  }

data OptHaddockParse = HaddockParse | NoHaddockParse
  deriving (OptHaddockParse -> OptHaddockParse -> Bool
(OptHaddockParse -> OptHaddockParse -> Bool)
-> (OptHaddockParse -> OptHaddockParse -> Bool)
-> Eq OptHaddockParse
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OptHaddockParse -> OptHaddockParse -> Bool
$c/= :: OptHaddockParse -> OptHaddockParse -> Bool
== :: OptHaddockParse -> OptHaddockParse -> Bool
$c== :: OptHaddockParse -> OptHaddockParse -> Bool
Eq,Eq OptHaddockParse
Eq OptHaddockParse
-> (OptHaddockParse -> OptHaddockParse -> Ordering)
-> (OptHaddockParse -> OptHaddockParse -> Bool)
-> (OptHaddockParse -> OptHaddockParse -> Bool)
-> (OptHaddockParse -> OptHaddockParse -> Bool)
-> (OptHaddockParse -> OptHaddockParse -> Bool)
-> (OptHaddockParse -> OptHaddockParse -> OptHaddockParse)
-> (OptHaddockParse -> OptHaddockParse -> OptHaddockParse)
-> Ord OptHaddockParse
OptHaddockParse -> OptHaddockParse -> Bool
OptHaddockParse -> OptHaddockParse -> Ordering
OptHaddockParse -> OptHaddockParse -> OptHaddockParse
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: OptHaddockParse -> OptHaddockParse -> OptHaddockParse
$cmin :: OptHaddockParse -> OptHaddockParse -> OptHaddockParse
max :: OptHaddockParse -> OptHaddockParse -> OptHaddockParse
$cmax :: OptHaddockParse -> OptHaddockParse -> OptHaddockParse
>= :: OptHaddockParse -> OptHaddockParse -> Bool
$c>= :: OptHaddockParse -> OptHaddockParse -> Bool
> :: OptHaddockParse -> OptHaddockParse -> Bool
$c> :: OptHaddockParse -> OptHaddockParse -> Bool
<= :: OptHaddockParse -> OptHaddockParse -> Bool
$c<= :: OptHaddockParse -> OptHaddockParse -> Bool
< :: OptHaddockParse -> OptHaddockParse -> Bool
$c< :: OptHaddockParse -> OptHaddockParse -> Bool
compare :: OptHaddockParse -> OptHaddockParse -> Ordering
$ccompare :: OptHaddockParse -> OptHaddockParse -> Ordering
$cp1Ord :: Eq OptHaddockParse
Ord,Int -> OptHaddockParse -> ShowS
[OptHaddockParse] -> ShowS
OptHaddockParse -> FilePath
(Int -> OptHaddockParse -> ShowS)
-> (OptHaddockParse -> FilePath)
-> ([OptHaddockParse] -> ShowS)
-> Show OptHaddockParse
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [OptHaddockParse] -> ShowS
$cshowList :: [OptHaddockParse] -> ShowS
show :: OptHaddockParse -> FilePath
$cshow :: OptHaddockParse -> FilePath
showsPrec :: Int -> OptHaddockParse -> ShowS
$cshowsPrec :: Int -> OptHaddockParse -> ShowS
Show,Int -> OptHaddockParse
OptHaddockParse -> Int
OptHaddockParse -> [OptHaddockParse]
OptHaddockParse -> OptHaddockParse
OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
OptHaddockParse
-> OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
(OptHaddockParse -> OptHaddockParse)
-> (OptHaddockParse -> OptHaddockParse)
-> (Int -> OptHaddockParse)
-> (OptHaddockParse -> Int)
-> (OptHaddockParse -> [OptHaddockParse])
-> (OptHaddockParse -> OptHaddockParse -> [OptHaddockParse])
-> (OptHaddockParse -> OptHaddockParse -> [OptHaddockParse])
-> (OptHaddockParse
    -> OptHaddockParse -> OptHaddockParse -> [OptHaddockParse])
-> Enum OptHaddockParse
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: OptHaddockParse
-> OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
$cenumFromThenTo :: OptHaddockParse
-> OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
enumFromTo :: OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
$cenumFromTo :: OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
enumFromThen :: OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
$cenumFromThen :: OptHaddockParse -> OptHaddockParse -> [OptHaddockParse]
enumFrom :: OptHaddockParse -> [OptHaddockParse]
$cenumFrom :: OptHaddockParse -> [OptHaddockParse]
fromEnum :: OptHaddockParse -> Int
$cfromEnum :: OptHaddockParse -> Int
toEnum :: Int -> OptHaddockParse
$ctoEnum :: Int -> OptHaddockParse
pred :: OptHaddockParse -> OptHaddockParse
$cpred :: OptHaddockParse -> OptHaddockParse
succ :: OptHaddockParse -> OptHaddockParse
$csucc :: OptHaddockParse -> OptHaddockParse
Enum)

data IdePreprocessedSource = IdePreprocessedSource
  { IdePreprocessedSource -> [(SrcSpan, FilePath)]
preprocWarnings :: [(GHC.SrcSpan, String)]
    -- ^ Warnings emitted by the preprocessor.

  , IdePreprocessedSource -> [(SrcSpan, FilePath)]
preprocErrors :: [(GHC.SrcSpan, String)]
    -- ^ Errors emitted by the preprocessor.

  , IdePreprocessedSource -> ParsedSource
preprocSource :: GHC.ParsedSource
    -- ^ New parse tree emitted by the preprocessor.

  }

newtype IdeReportProgress    = IdeReportProgress Bool
newtype IdeDefer             = IdeDefer          Bool
newtype IdeTesting           = IdeTesting        Bool
newtype IdeOTMemoryProfiling = IdeOTMemoryProfiling    Bool

clientSupportsProgress :: LSP.ClientCapabilities -> IdeReportProgress
clientSupportsProgress :: ClientCapabilities -> IdeReportProgress
clientSupportsProgress ClientCapabilities
caps = Bool -> IdeReportProgress
IdeReportProgress (Bool -> IdeReportProgress) -> Bool -> IdeReportProgress
forall a b. (a -> b) -> a -> b
$ Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
==
    (WindowClientCapabilities -> Maybe Bool
LSP._workDoneProgress (WindowClientCapabilities -> Maybe Bool)
-> Maybe WindowClientCapabilities -> Maybe Bool
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ClientCapabilities -> Maybe WindowClientCapabilities
LSP._window (ClientCapabilities
caps :: LSP.ClientCapabilities))

defaultIdeOptions :: Action IdeGhcSession -> IdeOptions
defaultIdeOptions :: Action IdeGhcSession -> IdeOptions
defaultIdeOptions Action IdeGhcSession
session = IdeOptions :: (ParsedSource -> IdePreprocessedSource)
-> Action IdeGhcSession
-> IdePkgLocationOptions
-> [FilePath]
-> Int
-> Maybe FilePath
-> Maybe FilePath
-> IdeOTMemoryProfiling
-> IdeTesting
-> IdeReportProgress
-> FilePath
-> Bool
-> [Text]
-> IdeDefer
-> Bool
-> CheckParents
-> OptHaddockParse
-> (DynFlags -> DynFlags)
-> IdeOptions
IdeOptions
    {optPreprocessor :: ParsedSource -> IdePreprocessedSource
optPreprocessor = [(SrcSpan, FilePath)]
-> [(SrcSpan, FilePath)] -> ParsedSource -> IdePreprocessedSource
IdePreprocessedSource [] []
    ,optGhcSession :: Action IdeGhcSession
optGhcSession = Action IdeGhcSession
session
    ,optExtensions :: [FilePath]
optExtensions = [FilePath
"hs", FilePath
"lhs"]
    ,optPkgLocationOpts :: IdePkgLocationOptions
optPkgLocationOpts = IdePkgLocationOptions
defaultIdePkgLocationOptions
    ,optThreads :: Int
optThreads = Int
0
    ,optShakeFiles :: Maybe FilePath
optShakeFiles = Maybe FilePath
forall a. Maybe a
Nothing
    ,optShakeProfiling :: Maybe FilePath
optShakeProfiling = Maybe FilePath
forall a. Maybe a
Nothing
    ,optOTMemoryProfiling :: IdeOTMemoryProfiling
optOTMemoryProfiling = Bool -> IdeOTMemoryProfiling
IdeOTMemoryProfiling Bool
False
    ,optReportProgress :: IdeReportProgress
optReportProgress = Bool -> IdeReportProgress
IdeReportProgress Bool
False
    ,optLanguageSyntax :: FilePath
optLanguageSyntax = FilePath
"haskell"
    ,optNewColonConvention :: Bool
optNewColonConvention = Bool
False
    ,optKeywords :: [Text]
optKeywords = [Text]
haskellKeywords
    ,optDefer :: IdeDefer
optDefer = Bool -> IdeDefer
IdeDefer Bool
True
    ,optTesting :: IdeTesting
optTesting = Bool -> IdeTesting
IdeTesting Bool
False
    ,optCheckProject :: Bool
optCheckProject = Config -> Bool
checkProject Config
forall a. Default a => a
def
    ,optCheckParents :: CheckParents
optCheckParents = Config -> CheckParents
checkParents Config
forall a. Default a => a
def
    ,optHaddockParse :: OptHaddockParse
optHaddockParse = OptHaddockParse
HaddockParse
    ,optCustomDynFlags :: DynFlags -> DynFlags
optCustomDynFlags = DynFlags -> DynFlags
forall a. a -> a
id
    }


-- | The set of options used to locate files belonging to external packages.

data IdePkgLocationOptions = IdePkgLocationOptions
  { IdePkgLocationOptions
-> PackageConfig -> Module -> IO (Maybe FilePath)
optLocateHieFile :: PackageConfig -> Module -> IO (Maybe FilePath)
  -- ^ Locate the HIE file for the given module. The PackageConfig can be

  -- used to lookup settings like importDirs.

  , IdePkgLocationOptions
-> PackageConfig -> Module -> IO (Maybe FilePath)
optLocateSrcFile :: PackageConfig -> Module -> IO (Maybe FilePath)
  -- ^ Locate the source file for the given module. The PackageConfig can be

  -- used to lookup settings like importDirs. For DAML, we place them in the package DB.

  -- For cabal this could point somewhere in ~/.cabal/packages.

  }

defaultIdePkgLocationOptions :: IdePkgLocationOptions
defaultIdePkgLocationOptions :: IdePkgLocationOptions
defaultIdePkgLocationOptions = (PackageConfig -> Module -> IO (Maybe FilePath))
-> (PackageConfig -> Module -> IO (Maybe FilePath))
-> IdePkgLocationOptions
IdePkgLocationOptions PackageConfig -> Module -> IO (Maybe FilePath)
forall (m :: * -> *) p p a. Monad m => p -> p -> m (Maybe a)
f PackageConfig -> Module -> IO (Maybe FilePath)
forall (m :: * -> *) p p a. Monad m => p -> p -> m (Maybe a)
f
    where f :: p -> p -> m (Maybe a)
f p
_ p
_ = Maybe a -> m (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing

-- | From https://wiki.haskell.org/Keywords

haskellKeywords :: [T.Text]
haskellKeywords :: [Text]
haskellKeywords =
  [ Text
"as"
  , Text
"case", Text
"of"
  , Text
"class", Text
"instance", Text
"type"
  , Text
"data", Text
"family", Text
"newtype"
  , Text
"default"
  , Text
"deriving"
  , Text
"do", Text
"mdo", Text
"proc", Text
"rec"
  , Text
"forall"
  , Text
"foreign"
  , Text
"hiding"
  , Text
"if", Text
"then", Text
"else"
  , Text
"import", Text
"qualified", Text
"hiding"
  , Text
"infix", Text
"infixl", Text
"infixr"
  , Text
"let", Text
"in", Text
"where"
  , Text
"module"
  ]