packages feed

ghcide-1.0.0.0: exe/Main.hs

-- Copyright (c) 2019 The DAML Authors. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
{-# OPTIONS_GHC -Wno-dodgy-imports #-} -- GHC no longer exports def in GHC 8.6 and above
{-# LANGUAGE TemplateHaskell #-}

module Main(main) where

import Arguments ( Arguments'(..), IdeCmd(..), getArguments )
import Control.Concurrent.Extra ( newLock, withLock )
import Control.Monad.Extra ( unless, when, whenJust )
import Data.Default ( Default(def) )
import Data.List.Extra ( upper )
import Data.Maybe (fromMaybe)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Version ( showVersion )
import Development.GitRev ( gitHash )
import Development.IDE ( Logger(Logger), Priority(Info), action )
import Development.IDE.Core.OfInterest (kick)
import Development.IDE.Core.Rules (mainRule)
import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde
import qualified Development.IDE.Plugin.Test as Test
import Development.IDE.Session (setInitialDynFlags, getHieDbLoc)
import Development.IDE.Types.Options
import qualified Development.IDE.Main as Main
import Development.Shake (ShakeOptions(shakeThreads))
import Ide.Plugin.Config (Config(checkParents, checkProject))
import Ide.PluginUtils (pluginDescToIdePlugins)
import HieDb.Run (Options(..), runCommand)
import Paths_ghcide ( version )
import qualified System.Directory.Extra as IO
import System.Environment ( getExecutablePath )
import System.Exit ( ExitCode(ExitFailure), exitSuccess, exitWith )
import System.Info ( compilerVersion )
import System.IO ( stderr, hPutStrLn )

ghcideVersion :: IO String
ghcideVersion = do
  path <- getExecutablePath
  let gitHashSection = case $(gitHash) of
        x | x == "UNKNOWN" -> ""
        x -> " (GIT hash: " <> x <> ")"
  return $ "ghcide version: " <> showVersion version
             <> " (GHC: " <> showVersion compilerVersion
             <> ") (PATH: " <> path <> ")"
             <> gitHashSection

main :: IO ()
main = do
    -- WARNING: If you write to stdout before runLanguageServer
    --          then the language server will not work
    Arguments{..} <- getArguments

    if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess
    else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion

    whenJust argsCwd IO.setCurrentDirectory

    -- lock to avoid overlapping output on stdout
    lock <- newLock
    let logger = Logger $ \pri msg -> when (pri >= logLevel) $ withLock lock $
            T.putStrLn $ T.pack ("[" ++ upper (show pri) ++ "] ") <> msg
        logLevel = if argsVerbose then minBound else Info

    case argFilesOrCmd of
      DbCmd opts cmd -> do
        dir <- IO.getCurrentDirectory
        dbLoc <- getHieDbLoc dir
        mlibdir <- setInitialDynFlags def
        case mlibdir of
          Nothing -> exitWith $ ExitFailure 1
          Just libdir -> runCommand libdir opts{database = dbLoc} cmd

      _ -> do

          case argFilesOrCmd of
              LSP -> do
                hPutStrLn stderr "Starting LSP server..."
                hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
              _ -> return ()

          Main.defaultMain def
            {Main.argFiles = case argFilesOrCmd of
                Typecheck x | not argLSP -> Just x
                _ -> Nothing

            ,Main.argsLogger = logger

            ,Main.argsRules = do
                -- install the main and ghcide-plugin rules
                mainRule
                -- install the kick action, which triggers a typecheck on every
                -- Shake database restart, i.e. on every user edit.
                unless argsDisableKick $
                    action kick

            ,Main.argsHlsPlugins =
                pluginDescToIdePlugins $
                GhcIde.descriptors
                ++ [Test.blockCommandDescriptor "block-command" | argsTesting]

            ,Main.argsGhcidePlugin = if argsTesting
                then Test.plugin
                else mempty

            ,Main.argsIdeOptions = \(fromMaybe def -> config) sessionLoader ->
                let defOptions = defaultIdeOptions sessionLoader
                in defOptions
                  { optShakeProfiling = argsShakeProfiling
                  , optOTMemoryProfiling = IdeOTMemoryProfiling argsOTMemoryProfiling
                  , optTesting = IdeTesting argsTesting
                  , optShakeOptions = (optShakeOptions defOptions){shakeThreads = argsThreads}
                  , optCheckParents = pure $ checkParents config
                  , optCheckProject = pure $ checkProject config
                  }
            }