packages feed

ghcide 1.1.0.0 → 1.2.0.0

raw patch · 109 files changed

+1822/−739 lines, 109 filesdep +aeson-prettydep +ghc-trace-eventsdep ~Diffdep ~base16-bytestringdep ~haddock-librarynew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: aeson-pretty, ghc-trace-events

Dependency ranges changed: Diff, base16-bytestring, haddock-library, hls-plugin-api, lsp, lsp-test, lsp-types, regex-tdfa

API changes (from Hackage documentation)

- Development.IDE.Core.Rules: getAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [Text]))
- Development.IDE.Core.Rules: getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])
- Development.IDE.Core.Rules: getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])
- Development.IDE.Core.Rules: highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight])
- Development.IDE.Core.Rules: refsAtPoint :: NormalizedFilePath -> Position -> Action [Location]
- Development.IDE.Core.Rules: useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)
- Development.IDE.Core.Rules: useNoFileE :: IdeRule k v => IdeState -> k -> MaybeT IdeAction v
- Development.IDE.Core.Rules: usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT IdeAction [(v, PositionMapping)]
- Development.IDE.Core.Rules: workspaceSymbols :: Text -> IdeAction (Maybe [SymbolInformation])
- Development.IDE.Main: [argFiles] :: Arguments -> Maybe [FilePath]
- Development.IDE.Plugin.CodeAction.ExactPrint: hideImplicitPreludeSymbol :: String -> ParsedSource -> Maybe Rewrite
- Development.IDE.Plugin.CodeAction.ExactPrint: newImport :: String -> Maybe String -> Maybe String -> Bool -> ParsedSource -> Maybe Rewrite
- Development.IDE.Plugin.CodeAction.ExactPrint: newImportAll :: String -> ParsedSource -> Maybe Rewrite
- Development.IDE.Plugin.CodeAction.ExactPrint: newUnqualImport :: String -> String -> Bool -> ParsedSource -> Maybe Rewrite
+ Development.IDE: useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)
+ Development.IDE: useNoFileE :: IdeRule k v => IdeState -> k -> MaybeT IdeAction v
+ Development.IDE: usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT IdeAction [(v, PositionMapping)]
+ Development.IDE.Core.Actions: getAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [Text]))
+ Development.IDE.Core.Actions: getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])
+ Development.IDE.Core.Actions: getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])
+ Development.IDE.Core.Actions: highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight])
+ Development.IDE.Core.Actions: refsAtPoint :: NormalizedFilePath -> Position -> Action [Location]
+ Development.IDE.Core.Actions: useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)
+ Development.IDE.Core.Actions: useNoFileE :: IdeRule k v => IdeState -> k -> MaybeT IdeAction v
+ Development.IDE.Core.Actions: usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT IdeAction [(v, PositionMapping)]
+ Development.IDE.Core.Actions: workspaceSymbols :: Text -> IdeAction (Maybe [SymbolInformation])
+ Development.IDE.Core.PositionMapping: composeDelta :: PositionDelta -> PositionDelta -> PositionDelta
+ Development.IDE.Core.Rules: usePropertyAction :: HasProperty s k t r => KeyNameProxy s -> PluginId -> Properties r -> Action (ToHsType t)
+ Development.IDE.Core.UseStale: Current :: Age
+ Development.IDE.Core.UseStale: Stale :: Type -> Age
+ Development.IDE.Core.UseStale: [TrackedStale] :: Tracked (Stale s) a -> PositionMap (Stale s) Current -> TrackedStale a
+ Development.IDE.Core.UseStale: class MapAge a
+ Development.IDE.Core.UseStale: data Age
+ Development.IDE.Core.UseStale: data PositionMap (from :: Age) (to :: Age)
+ Development.IDE.Core.UseStale: data Tracked (age :: Age) a
+ Development.IDE.Core.UseStale: data TrackedStale a
+ Development.IDE.Core.UseStale: dualPositionMap :: PositionMap from to -> PositionMap to from
+ Development.IDE.Core.UseStale: instance Control.Category.Category Development.IDE.Core.UseStale.PositionMap
+ Development.IDE.Core.UseStale: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance Data.Aeson.Types.FromJSON.FromJSON a => Data.Aeson.Types.FromJSON.FromJSON (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance Data.Aeson.Types.ToJSON.ToJSON a => Data.Aeson.Types.ToJSON.ToJSON (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance Data.Foldable.Foldable (Development.IDE.Core.UseStale.Tracked age)
+ Development.IDE.Core.UseStale: instance Data.Traversable.Traversable (Development.IDE.Core.UseStale.Tracked age)
+ Development.IDE.Core.UseStale: instance Development.IDE.Core.UseStale.MapAge Language.LSP.Types.Location.Range
+ Development.IDE.Core.UseStale: instance Development.IDE.Core.UseStale.MapAge SrcLoc.RealSrcSpan
+ Development.IDE.Core.UseStale: instance GHC.Base.Applicative (Development.IDE.Core.UseStale.Tracked age)
+ Development.IDE.Core.UseStale: instance GHC.Base.Functor (Development.IDE.Core.UseStale.Tracked age)
+ Development.IDE.Core.UseStale: instance GHC.Base.Functor Development.IDE.Core.UseStale.TrackedStale
+ Development.IDE.Core.UseStale: instance GHC.Base.Monad (Development.IDE.Core.UseStale.Tracked age)
+ Development.IDE.Core.UseStale: instance GHC.Classes.Eq a => GHC.Classes.Eq (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance GHC.Classes.Ord a => GHC.Classes.Ord (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance GHC.Read.Read a => GHC.Read.Read (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: instance GHC.Show.Show a => GHC.Show.Show (Development.IDE.Core.UseStale.Tracked age a)
+ Development.IDE.Core.UseStale: mapAgeFrom :: MapAge a => PositionMap from to -> Tracked to a -> Maybe (Tracked from a)
+ Development.IDE.Core.UseStale: mapAgeTo :: MapAge a => PositionMap from to -> Tracked from a -> Maybe (Tracked to a)
+ Development.IDE.Core.UseStale: unTrack :: Tracked age a -> a
+ Development.IDE.Core.UseStale: unsafeCopyAge :: Tracked age a -> b -> Tracked age b
+ Development.IDE.Core.UseStale: unsafeMkCurrent :: age -> Tracked 'Current age
+ Development.IDE.Core.UseStale: unsafeMkStale :: age -> Tracked (Stale s) age
+ Development.IDE.Core.UseStale: untrackedStaleValue :: TrackedStale a -> a
+ Development.IDE.Core.UseStale: useWithStale :: IdeRule k v => k -> NormalizedFilePath -> Action (Maybe (TrackedStale v))
+ Development.IDE.Core.UseStale: useWithStale_ :: IdeRule k v => k -> NormalizedFilePath -> Action (TrackedStale v)
+ Development.IDE.Main: Check :: [FilePath] -> Command
+ Development.IDE.Main: Custom :: FilePath -> IdeCommand -> Command
+ Development.IDE.Main: Db :: FilePath -> Options -> Command -> Command
+ Development.IDE.Main: IdeCommand :: (IdeState -> IO ()) -> IdeCommand
+ Development.IDE.Main: LSP :: Command
+ Development.IDE.Main: [argCommand] :: Arguments -> Command
+ Development.IDE.Main: [argsHandleIn] :: Arguments -> IO Handle
+ Development.IDE.Main: [argsHandleOut] :: Arguments -> IO Handle
+ Development.IDE.Main: [hieCommand] :: Command -> Command
+ Development.IDE.Main: [hieOptions] :: Command -> Options
+ Development.IDE.Main: [ideCommand] :: Command -> IdeCommand
+ Development.IDE.Main: [projectRoot] :: Command -> FilePath
+ Development.IDE.Main: commandP :: Parser Command
+ Development.IDE.Main: data Command
+ Development.IDE.Main: instance GHC.Show.Show Development.IDE.Main.Command
+ Development.IDE.Main: instance GHC.Show.Show Development.IDE.Main.IdeCommand
+ Development.IDE.Main: instance GHC.Show.Show HieDb.Run.Command
+ Development.IDE.Main: instance GHC.Show.Show HieDb.Run.Options
+ Development.IDE.Main: isLSP :: Command -> Bool
+ Development.IDE.Main: newtype IdeCommand
+ Development.IDE.Plugin.CodeAction: instance GHC.Classes.Eq Development.IDE.Plugin.CodeAction.NewImport
+ Development.IDE.Plugin.CodeAction: instance GHC.Classes.Ord Development.IDE.Plugin.CodeAction.NewImport
+ Development.IDE.Plugin.CodeAction: instance GHC.Show.Show Development.IDE.Plugin.CodeAction.NewImport
+ Development.IDE.Plugin.Completions.Types: CompletionsConfig :: Bool -> Bool -> CompletionsConfig
+ Development.IDE.Plugin.Completions.Types: [enableAutoExtend] :: CompletionsConfig -> Bool
+ Development.IDE.Plugin.Completions.Types: [enableSnippets] :: CompletionsConfig -> Bool
+ Development.IDE.Plugin.Completions.Types: data CompletionsConfig
+ Development.IDE.Plugin.Completions.Types: getCompletionsConfig :: MonadLsp Config m => PluginId -> m CompletionsConfig
+ Development.IDE.Plugin.Completions.Types: properties :: Properties '[ 'PropertyKey "autoExtendOn" 'TBoolean, 'PropertyKey "snippetsOn" 'TBoolean]
+ Development.IDE.Plugin.HLS: instance GHC.Base.Monoid Development.IDE.Plugin.HLS.IdeNotificationHandlers
+ Development.IDE.Plugin.HLS: instance GHC.Base.Semigroup Development.IDE.Plugin.HLS.IdeNotificationHandlers
+ Development.IDE.Plugin.TypeLenses: instance Data.Aeson.Types.ToJSON.ToJSON Development.IDE.Plugin.TypeLenses.Mode
+ Development.IDE.Types.Options: Explicit :: ProgressReportingStyle
+ Development.IDE.Types.Options: NoProgress :: ProgressReportingStyle
+ Development.IDE.Types.Options: Percentage :: ProgressReportingStyle
+ Development.IDE.Types.Options: [optProgressStyle] :: IdeOptions -> ProgressReportingStyle
+ Development.IDE.Types.Options: data ProgressReportingStyle
- Development.IDE: getClientConfig :: MonadLsp Config m => ShakeExtras -> m Config
+ Development.IDE: getClientConfig :: MonadLsp Config m => m Config
- Development.IDE: getPluginConfig :: MonadLsp Config m => ShakeExtras -> PluginId -> m PluginConfig
+ Development.IDE: getPluginConfig :: MonadLsp Config m => PluginId -> m PluginConfig
- Development.IDE.Core.Shake: getClientConfig :: MonadLsp Config m => ShakeExtras -> m Config
+ Development.IDE.Core.Shake: getClientConfig :: MonadLsp Config m => m Config
- Development.IDE.Core.Shake: getPluginConfig :: MonadLsp Config m => ShakeExtras -> PluginId -> m PluginConfig
+ Development.IDE.Core.Shake: getPluginConfig :: MonadLsp Config m => PluginId -> m PluginConfig
- Development.IDE.Core.Shake: type KnownTargets = HashMap Target [NormalizedFilePath]
+ Development.IDE.Core.Shake: type KnownTargets = HashMap Target (HashSet NormalizedFilePath)
- Development.IDE.LSP.LanguageServer: runLanguageServer :: forall config. Show config => Options -> (FilePath -> IO FilePath) -> (IdeState -> Value -> IO (Either Text config)) -> Handlers (ServerM config) -> (LanguageContextEnv config -> VFSHandle -> Maybe FilePath -> HieDb -> IndexQueue -> IO IdeState) -> IO ()
+ Development.IDE.LSP.LanguageServer: runLanguageServer :: forall config. Show config => Options -> Handle -> Handle -> (FilePath -> IO FilePath) -> config -> (config -> Value -> Either Text config) -> Handlers (ServerM config) -> (LanguageContextEnv config -> VFSHandle -> Maybe FilePath -> HieDb -> IndexQueue -> IO IdeState) -> IO ()
- Development.IDE.Main: Arguments :: Bool -> Maybe [FilePath] -> IO Logger -> Rules () -> IdePlugins IdeState -> Plugin Config -> SessionLoadingOptions -> (Maybe Config -> Action IdeGhcSession -> IdeOptions) -> Options -> Config -> (FilePath -> IO FilePath) -> IO (Debouncer NormalizedUri) -> Arguments
+ Development.IDE.Main: Arguments :: Bool -> Command -> IO Logger -> Rules () -> IdePlugins IdeState -> Plugin Config -> SessionLoadingOptions -> (Config -> Action IdeGhcSession -> IdeOptions) -> Options -> Config -> (FilePath -> IO FilePath) -> IO (Debouncer NormalizedUri) -> IO Handle -> IO Handle -> Arguments
- Development.IDE.Main: [argsIdeOptions] :: Arguments -> Maybe Config -> Action IdeGhcSession -> IdeOptions
+ Development.IDE.Main: [argsIdeOptions] :: Arguments -> Config -> Action IdeGhcSession -> IdeOptions
- Development.IDE.Plugin.HLS: asGhcIdePlugin :: Config -> IdePlugins IdeState -> Plugin Config
+ Development.IDE.Plugin.HLS: asGhcIdePlugin :: IdePlugins IdeState -> Plugin Config
- Development.IDE.Types.HscEnvEq: envImportPaths :: HscEnvEq -> Maybe [String]
+ Development.IDE.Types.HscEnvEq: envImportPaths :: HscEnvEq -> Maybe (Set FilePath)
- Development.IDE.Types.HscEnvEq: newHscEnvEqWithImportPaths :: Maybe [String] -> HscEnv -> [(InstalledUnitId, DynFlags)] -> IO HscEnvEq
+ Development.IDE.Types.HscEnvEq: newHscEnvEqWithImportPaths :: Maybe (Set FilePath) -> HscEnv -> [(InstalledUnitId, DynFlags)] -> IO HscEnvEq
- Development.IDE.Types.KnownTargets: type KnownTargets = HashMap Target [NormalizedFilePath]
+ Development.IDE.Types.KnownTargets: type KnownTargets = HashMap Target (HashSet NormalizedFilePath)
- Development.IDE.Types.Options: IdeOptions :: (ParsedSource -> IdePreprocessedSource) -> Action IdeGhcSession -> IdePkgLocationOptions -> [String] -> Maybe FilePath -> IdeOTMemoryProfiling -> IdeTesting -> IdeReportProgress -> String -> Bool -> [Text] -> IdeDefer -> IO Bool -> IO CheckParents -> OptHaddockParse -> (DynFlags -> DynFlags) -> ShakeOptions -> (forall a. Typeable a => a -> Bool) -> IdeOptions
+ Development.IDE.Types.Options: IdeOptions :: (ParsedSource -> IdePreprocessedSource) -> Action IdeGhcSession -> IdePkgLocationOptions -> [String] -> Maybe FilePath -> IdeOTMemoryProfiling -> IdeTesting -> IdeReportProgress -> String -> Bool -> [Text] -> IdeDefer -> IO Bool -> IO CheckParents -> OptHaddockParse -> (DynFlags -> DynFlags) -> ShakeOptions -> (forall a. Typeable a => a -> Bool) -> ProgressReportingStyle -> IdeOptions

Files

CHANGELOG.md view
@@ -1,3 +1,37 @@+### 1.2.0 (2021-04-11)+* Emit holes as diagnostics (#1653) - Sandy Maguire+* Fix ghcide and HLS enter lsp mode by default (#1692) - Potato Hatsue+* support custom Ide commands (#1666) - Pepe Iborra+* ghcide - enable ApplicativeDo everywhere (#1667) - Pepe Iborra+* Intelligent derivations of Semigroup and Monoid for Wingman (#1671) - Sandy Maguire+* Avoid creating IsFileOfInterest keys for non workspace files (#1661) - Pepe Iborra+* Fix a wingman bug caused by mismanaged stale data (#1657) - Sandy Maguire+* Skip tracing unless eventlog is enabled (#1658) - Pepe Iborra+* optimize ambiguity import suggestions (#1669) - Lei Zhu+* Replace Barrier with MVar in lsp main (#1668) - Potato Hatsue+* Add bounds for Diff (#1665) - Potato Hatsue+* log exceptions before killing the server (#1651) - Pepe Iborra+* Fix importing type operators (#1644) - Potato Hatsue+* Shut the Shake session on exit, instead of restarting it (#1655) - Pepe Iborra+* Do not override custom user commands (#1650) - Pepe Iborra+* Civilized indexing progress reporting (#1633) - Pepe Iborra+* Avoid reordering plugins (#1629) - Pepe Iborra+* Update to lsp-1.2 (#1631) - wz1000+* Use custom config for completions plugin (#1619) - Potato Hatsue+* Configurable I/O handles (#1617) - Pepe Iborra+* Add test data files to extra-source-files (#1605) - Javier Neira+* Allow for customizable Haskell views of Property types (#1608) - Sandy Maguire+* Extract hls-test-utils (#1606) - Potato Hatsue+* Add ability for plugins to handle file change notifications (#1588) - Pepe Iborra+* Bump haddock-library to 1.10.0 (#1598) - Potato Hatsue+* Use CiInterface/SkInterface for typeclass symbols (#1592) - FW+* Relax ghcides upper bound on base16-bytestring (#1595) - maralorn+* Regularize custom config of plugins (#1576) - Potato Hatsue+* Avoid duplicating known targets and import paths (#1590) - Pepe Iborra+* Update homepage and other urls for ghcide (#1580) - Felix Yan+* Add custom code action kinds for import related code actions (#1570) - Potato Hatsue+* Use TextEdit to insert new imports (#1554) - Potato Hatsue+ ### 1.1.0 (2021-03-09) * Add an option to control progress reporting (#1513) - Pepe Iborra * Fix missing parens of auto extending imports (#1526) - Potato Hatsue
exe/Arguments.hs view
@@ -1,26 +1,23 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -module Arguments(Arguments, Arguments'(..), getArguments, IdeCmd(..)) where+module Arguments(Arguments(..), getArguments) where -import           HieDb.Run+import           Development.IDE.Main (Command (..), commandP) import           Options.Applicative -type Arguments = Arguments' IdeCmd--data IdeCmd = Typecheck [FilePath] | DbCmd Options Command | LSP--data Arguments' a = Arguments-    {argLSP                :: Bool-    ,argsCwd               :: Maybe FilePath-    ,argsVersion           :: Bool-    ,argsShakeProfiling    :: Maybe FilePath-    ,argsOTMemoryProfiling :: Bool-    ,argsTesting           :: Bool-    ,argsDisableKick       :: Bool-    ,argsThreads           :: Int-    ,argsVerbose           :: Bool-    ,argFilesOrCmd         :: a+data Arguments = Arguments+    {argsCwd                   :: Maybe FilePath+    ,argsVersion               :: Bool+    ,argsVSCodeExtensionSchema :: Bool+    ,argsDefaultConfig         :: Bool+    ,argsShakeProfiling        :: Maybe FilePath+    ,argsOTMemoryProfiling     :: Bool+    ,argsTesting               :: Bool+    ,argsDisableKick           :: Bool+    ,argsThreads               :: Int+    ,argsVerbose               :: Bool+    ,argsCommand               :: Command     }  getArguments :: IO Arguments@@ -32,21 +29,17 @@  arguments :: Parser Arguments arguments = Arguments-      <$> switch (long "lsp" <> help "Start talking to an LSP client")-      <*> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")+      <$> optional (strOption $ long "cwd" <> metavar "DIR" <> help "Change to this directory")       <*> switch (long "version" <> help "Show ghcide and GHC versions")+      <*> switch (long "vscode-extension-schema" <> help "Print generic config schema for plugins (used in the package.json of haskell vscode extension)")+      <*> switch (long "generate-default-config" <> help "Print config supported by the server with default values")       <*> optional (strOption $ long "shake-profiling" <> metavar "DIR" <> help "Dump profiling reports to this directory")       <*> switch (long "ot-memory-profiling" <> help "Record OpenTelemetry info to the eventlog. Needs the -l RTS flag to have an effect")       <*> switch (long "test" <> help "Enable additional lsp messages used by the testsuite")       <*> switch (long "test-no-kick" <> help "Disable kick. Useful for testing cancellation")       <*> option auto (short 'j' <> help "Number of threads (0: automatic)" <> metavar "NUM" <> value 0 <> showDefault)       <*> switch (long "verbose" <> help "Include internal events in logging output")-      <*> ( hsubparser (command "typecheck" (info (Typecheck <$> fileCmd) fileInfo)-                   <> command "hiedb" (info (DbCmd <$> optParser "" True <*> cmdParser <**> helper) hieInfo)-                   <> command "lsp" (info (pure LSP <**> helper) lspInfo)  )-         <|> Typecheck <$> fileCmd )-  where-    fileCmd = many (argument str (metavar "FILES/DIRS..."))-    lspInfo = fullDesc <> progDesc "Start talking to an LSP client"-    fileInfo = fullDesc <> progDesc "Used as a test bed to check your IDE will work"-    hieInfo = fullDesc <> progDesc "Query .hie files"+      <*> (commandP <|> lspCommand <|> checkCommand)+      where+          checkCommand = Check <$> many (argument str (metavar "FILES/DIRS..."))+          lspCommand = LSP <$ flag' True (long "lsp" <> help "Start talking to an LSP client")
exe/Main.hs view
@@ -5,15 +5,17 @@  module Main(main) where -import           Arguments                         (Arguments' (..),-                                                    IdeCmd (..), getArguments)+import           Arguments                         (Arguments (..),+                                                    getArguments) import           Control.Concurrent.Extra          (newLock, withLock) import           Control.Monad.Extra               (unless, when, whenJust)+import qualified Data.Aeson.Encode.Pretty          as A 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.Text.Lazy.Encoding           (decodeUtf8)+import qualified Data.Text.Lazy.IO                 as LT import           Data.Version                      (showVersion) import           Development.GitRev                (gitHash) import           Development.IDE                   (Logger (Logger),@@ -23,18 +25,16 @@ import qualified Development.IDE.Main              as Main import qualified Development.IDE.Plugin.HLS.GhcIde as GhcIde import qualified Development.IDE.Plugin.Test       as Test-import           Development.IDE.Session           (getHieDbLoc,-                                                    setInitialDynFlags) import           Development.IDE.Types.Options import           Development.Shake                 (ShakeOptions (shakeThreads))-import           HieDb.Run                         (Options (..), runCommand) import           Ide.Plugin.Config                 (Config (checkParents, checkProject))+import           Ide.Plugin.ConfigUtils            (pluginsToDefaultConfig,+                                                    pluginsToVSCodeExtensionSchema) import           Ide.PluginUtils                   (pluginDescToIdePlugins) import           Paths_ghcide                      (version) import qualified System.Directory.Extra            as IO import           System.Environment                (getExecutablePath)-import           System.Exit                       (ExitCode (ExitFailure),-                                                    exitSuccess, exitWith)+import           System.Exit                       (exitSuccess) import           System.IO                         (hPutStrLn, stderr) import           System.Info                       (compilerVersion) @@ -58,6 +58,16 @@     if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess     else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion +    let hlsPlugins = pluginDescToIdePlugins GhcIde.descriptors++    when argsVSCodeExtensionSchema $ do+      LT.putStrLn $ decodeUtf8 $ A.encodePretty $ pluginsToVSCodeExtensionSchema hlsPlugins+      exitSuccess++    when argsDefaultConfig $ do+      LT.putStrLn $ decodeUtf8 $ A.encodePretty $ pluginsToDefaultConfig hlsPlugins+      exitSuccess+     whenJust argsCwd IO.setCurrentDirectory      -- lock to avoid overlapping output on stdout@@ -66,56 +76,37 @@             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.defaultMain def+        {Main.argCommand = argsCommand -            ,Main.argsLogger = pure logger+        ,Main.argsLogger = pure 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.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.argsHlsPlugins =+            pluginDescToIdePlugins $+            GhcIde.descriptors+            ++ [Test.blockCommandDescriptor "block-command" | argsTesting] -            ,Main.argsGhcidePlugin = if argsTesting-                then Test.plugin-                else mempty+        ,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-                  }-            }+        ,Main.argsIdeOptions = \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+                }+        } 
ghcide.cabal view
@@ -1,8 +1,8 @@-cabal-version:      2.2+cabal-version:      2.4 build-type:         Simple category:           Development name:               ghcide-version:            1.1.0.0+version:            1.2.0.0 license:            Apache-2.0 license-file:       LICENSE author:             Digital Asset and Ghcide contributors@@ -11,21 +11,19 @@ synopsis:           The core of an IDE description:     A library for building Haskell IDE's on top of the GHC API.-homepage:           https://github.com/haskell/ghcide#readme-bug-reports:        https://github.com/haskell/ghcide/issues+homepage:           https://github.com/haskell/haskell-language-server/tree/master/ghcide#readme+bug-reports:        https://github.com/haskell/haskell-language-server/issues tested-with:        GHC == 8.6.4 || == 8.6.5 || == 8.8.2 || == 8.8.3 || == 8.8.4 || == 8.10.2 || == 8.10.3 || == 8.10.4 extra-source-files: include/ghc-api-version.h README.md CHANGELOG.md-                    test/data/hover/*.hs-                    test/data/multi/cabal.project-                    test/data/multi/hie.yaml-                    test/data/multi/a/a.cabal-                    test/data/multi/a/*.hs-                    test/data/multi/b/b.cabal-                    test/data/multi/b/*.hs+                    test/data/**/*.project+                    test/data/**/*.cabal+                    test/data/**/*.yaml+                    test/data/**/*.hs+                    test/data/**/*.hs-boot  source-repository head     type:     git-    location: https://github.com/haskell/ghcide.git+    location: https://github.com/haskell/haskell-language-server.git  flag ghc-patched-unboxed-bytecode     description: The GHC version we link against supports unboxed sums and tuples in bytecode@@ -54,17 +52,19 @@         filepath,         fingertree,         ghc-exactprint,+        ghc-trace-events,         Glob,-        haddock-library >= 1.8 && < 1.10,+        haddock-library ^>= 1.10.0,         hashable,         hie-compat ^>= 0.1.0.0,-        hls-plugin-api ^>= 1.0.0.0,+        hls-plugin-api ^>= 1.1.0.0,         lens,         hiedb == 0.3.0.1,-        lsp-types == 1.1.*,-        lsp == 1.1.1.0,+        lsp-types == 1.2.*,+        lsp == 1.2.*,         mtl,         network-uri,+        optparse-applicative,         parallel,         prettyprinter-ansi-terminal,         prettyprinter-ansi-terminal,@@ -86,7 +86,7 @@         utf8-string,         vector,         hslogger,-        Diff,+        Diff ^>=0.4.0,         vector,         bytestring-encoding,         opentelemetry >=0.6.1,@@ -101,7 +101,7 @@         cryptohash-sha1 >=0.11.100 && <0.12,         hie-bios >= 0.7.1 && < 0.8.0,         implicit-hie-cradle >= 0.3.0.2 && < 0.4,-        base16-bytestring >=0.1.1 && <0.2+        base16-bytestring >=0.1.1 && <1.1     if os(windows)       build-depends:         Win32@@ -112,9 +112,12 @@         cbits/getmodtime.c      default-extensions:+        ApplicativeDo         BangPatterns         DeriveFunctor         DeriveGeneric+        DeriveFoldable+        DeriveTraversable         FlexibleContexts         GeneralizedNewtypeDeriving         LambdaCase@@ -139,6 +142,7 @@         Control.Concurrent.Strict         Development.IDE         Development.IDE.Main+        Development.IDE.Core.Actions         Development.IDE.Core.Debouncer         Development.IDE.Core.FileStore         Development.IDE.Core.IdeConfiguration@@ -150,6 +154,7 @@         Development.IDE.Core.Service         Development.IDE.Core.Shake         Development.IDE.Core.Tracing+        Development.IDE.Core.UseStale         Development.IDE.GHC.Compat         Development.IDE.Core.Compile         Development.IDE.GHC.Error@@ -284,7 +289,8 @@         optparse-applicative,         shake,         text,-        unordered-containers+        unordered-containers,+        aeson-pretty     other-modules:         Arguments         Paths_ghcide@@ -316,6 +322,7 @@         implicit-hie:gen-hie     build-depends:         aeson,+        async,         base,         binary,         bytestring,@@ -340,12 +347,13 @@         hls-plugin-api,         network-uri,         lens,-        lsp-test == 0.13.0.0,+        lsp-test == 0.14.0.0,         optparse-applicative,         process,         QuickCheck,         quickcheck-instances,         rope-utf16-splay,+        regex-tdfa ^>= 1.3.1,         safe,         safe-exceptions,         shake,@@ -397,7 +405,7 @@         extra,         filepath,         ghcide,-        lsp-test == 0.13.0.0,+        lsp-test == 0.14.0.0,         optparse-applicative,         process,         safe-exceptions,
session-loader/Development/IDE/Session.hs view
@@ -83,6 +83,7 @@  import           Control.Concurrent.STM               (atomically) import           Control.Concurrent.STM.TQueue+import qualified Data.HashSet                         as Set import           Database.SQLite.Simple import           HIE.Bios.Cradle                      (yamlConfig) import           HieDb.Create@@ -247,10 +248,10 @@                 found <- filterM (IO.doesFileExist . fromNormalizedFilePath) targetLocations                 return (targetTarget, found)           modifyVarIO' knownTargetsVar $ traverseHashed $ \known -> do-            let known' = HM.unionWith (<>) known $ HM.fromList knownTargets+            let known' = HM.unionWith (<>) known $ HM.fromList $ map (second Set.fromList) knownTargets             when (known /= known') $                 logDebug logger $ "Known files updated: " <>-                    T.pack(show $ (HM.map . map) fromNormalizedFilePath known')+                    T.pack(show $ (HM.map . Set.map) fromNormalizedFilePath known')             pure known'      -- Create a new HscEnv from a hieYaml root and a set of options
src/Development/IDE.hs view
@@ -6,6 +6,11 @@  ) where +import           Development.IDE.Core.Actions          as X (getAtPoint,+                                                             getDefinition,+                                                             getTypeDefinition,+                                                             useE, useNoFileE,+                                                             usesE) import           Development.IDE.Core.FileExists       as X (getFileExists) import           Development.IDE.Core.FileStore        as X (getFileContents) import           Development.IDE.Core.IdeConfiguration as X (IdeConfiguration (..),@@ -13,11 +18,8 @@ import           Development.IDE.Core.OfInterest       as X (getFilesOfInterest) import           Development.IDE.Core.RuleTypes        as X import           Development.IDE.Core.Rules            as X (IsHiFileStable (..),-                                                             getAtPoint,                                                              getClientConfigAction,-                                                             getDefinition,-                                                             getParsedModule,-                                                             getTypeDefinition)+                                                             getParsedModule) import           Development.IDE.Core.Service          as X (runAction) import           Development.IDE.Core.Shake            as X (FastResult (..),                                                              IdeAction (..),
+ src/Development/IDE/Core/Actions.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE NoApplicativeDo #-}+{-# LANGUAGE TypeFamilies    #-}+module Development.IDE.Core.Actions+( getAtPoint+, getDefinition+, getTypeDefinition+, highlightAtPoint+, refsAtPoint+, useE+, useNoFileE+, usesE+, workspaceSymbols+) where++import           Control.Monad.Reader+import           Control.Monad.Trans.Maybe+import qualified Data.HashMap.Strict                  as HM+import           Data.Maybe+import qualified Data.Text                            as T+import           Data.Tuple.Extra+import           Development.IDE.Core.OfInterest+import           Development.IDE.Core.PositionMapping+import           Development.IDE.Core.RuleTypes+import           Development.IDE.Core.Service+import           Development.IDE.Core.Shake+import           Development.IDE.GHC.Compat           hiding (TargetFile,+                                                       TargetModule,+                                                       parseModule,+                                                       typecheckModule,+                                                       writeHieFile)+import qualified Development.IDE.Spans.AtPoint        as AtPoint+import           Development.IDE.Types.Location+import           Development.Shake                    hiding (Diagnostic)+import qualified HieDb+import           Language.LSP.Types                   (DocumentHighlight (..),+                                                       SymbolInformation (..))+++-- | Eventually this will lookup/generate URIs for files in dependencies, but not in the+-- project. Right now, this is just a stub.+lookupMod+  :: HieDbWriter -- ^ access the database+  -> FilePath -- ^ The `.hie` file we got from the database+  -> ModuleName+  -> UnitId+  -> Bool -- ^ Is this file a boot file?+  -> MaybeT IdeAction Uri+lookupMod _dbchan _hie_f _mod _uid _boot = MaybeT $ pure Nothing+++-- IMPORTANT NOTE : make sure all rules `useE`d by these have a "Persistent Stale" rule defined,+-- so we can quickly answer as soon as the IDE is opened+-- Even if we don't have persistent information on disk for these rules, the persistent rule+-- should just return an empty result+-- It is imperative that the result of the persistent rule succeed in such a case, or we will+-- block waiting for the rule to be properly computed.++-- | Try to get hover text for the name under point.+getAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [T.Text]))+getAtPoint file pos = runMaybeT $ do+  ide <- ask+  opts <- liftIO $ getIdeOptionsIO ide++  (hf, mapping) <- useE GetHieAst file+  dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> (runMaybeT $ useE GetDocMap file)++  !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)+  MaybeT $ pure $ fmap (first (toCurrentRange mapping =<<)) $ AtPoint.atPoint opts hf dkMap pos'++toCurrentLocations :: PositionMapping -> [Location] -> [Location]+toCurrentLocations mapping = mapMaybe go+  where+    go (Location uri range) = Location uri <$> toCurrentRange mapping range++-- | useE is useful to implement functions that aren’t rules but need shortcircuiting+-- e.g. getDefinition.+useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)+useE k = MaybeT . useWithStaleFast k++useNoFileE :: IdeRule k v => IdeState -> k -> MaybeT IdeAction v+useNoFileE _ide k = fst <$> useE k emptyFilePath++usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT IdeAction [(v,PositionMapping)]+usesE k = MaybeT . fmap sequence . mapM (useWithStaleFast k)++-- | Goto Definition.+getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])+getDefinition file pos = runMaybeT $ do+    ide <- ask+    opts <- liftIO $ getIdeOptionsIO ide+    (HAR _ hf _ _ _, mapping) <- useE GetHieAst file+    (ImportMap imports, _) <- useE GetImportMap file+    !pos' <- MaybeT (pure $ fromCurrentPosition mapping pos)+    hiedb <- lift $ asks hiedb+    dbWriter <- lift $ asks hiedbWriter+    toCurrentLocations mapping <$> AtPoint.gotoDefinition hiedb (lookupMod dbWriter) opts imports hf pos'++getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])+getTypeDefinition file pos = runMaybeT $ do+    ide <- ask+    opts <- liftIO $ getIdeOptionsIO ide+    (hf, mapping) <- useE GetHieAst file+    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)+    hiedb <- lift $ asks hiedb+    dbWriter <- lift $ asks hiedbWriter+    toCurrentLocations mapping <$> AtPoint.gotoTypeDefinition hiedb (lookupMod dbWriter) opts hf pos'++highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight])+highlightAtPoint file pos = runMaybeT $ do+    (HAR _ hf rf _ _,mapping) <- useE GetHieAst file+    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)+    let toCurrentHighlight (DocumentHighlight range t) = flip DocumentHighlight t <$> toCurrentRange mapping range+    mapMaybe toCurrentHighlight <$>AtPoint.documentHighlight hf rf pos'++-- Refs are not an IDE action, so it is OK to be slow and (more) accurate+refsAtPoint :: NormalizedFilePath -> Position -> Action [Location]+refsAtPoint file pos = do+    ShakeExtras{hiedb} <- getShakeExtras+    fs <- HM.keys <$> getFilesOfInterest+    asts <- HM.fromList . mapMaybe sequence . zip fs <$> usesWithStale GetHieAst fs+    AtPoint.referencesAtPoint hiedb file pos (AtPoint.FOIReferences asts)++workspaceSymbols :: T.Text -> IdeAction (Maybe [SymbolInformation])+workspaceSymbols query = runMaybeT $ do+  hiedb <- lift $ asks hiedb+  res <- liftIO $ HieDb.searchDef hiedb $ T.unpack query+  pure $ mapMaybe AtPoint.defRowToSymbolInfo res
src/Development/IDE/Core/Compile.hs view
@@ -507,7 +507,9 @@ -- can just increment the 'indexCompleted' TVar and exit. -- indexHieFile :: ShakeExtras -> ModSummary -> NormalizedFilePath -> Fingerprint -> Compat.HieFile -> IO ()-indexHieFile se mod_summary srcPath hash hf = atomically $ do+indexHieFile se mod_summary srcPath hash hf = do+ IdeOptions{optProgressStyle} <- getIdeOptionsIO se+ atomically $ do   pending <- readTVar indexPending   case HashMap.lookup srcPath pending of     Just pendingHash | pendingHash == hash -> pure () -- An index is already scheduled@@ -523,7 +525,7 @@             -- If the hash in the pending list doesn't match the current hash, then skip             Just pendingHash -> pendingHash /= hash         unless newerScheduled $ do-          pre+          pre optProgressStyle           addRefsFromLoaded db targetPath (RealFile $ fromNormalizedFilePath srcPath) hash hf           post   where@@ -532,7 +534,7 @@     HieDbWriter{..} = hiedbWriter se      -- Get a progress token to report progress and update it for the current file-    pre = do+    pre style = do       tok <- modifyVar indexProgressToken $ fmap dupe . \case         x@(Just _) -> pure x         -- Create a token if we don't already have one@@ -545,7 +547,7 @@               _ <- LSP.sendRequest LSP.SWindowWorkDoneProgressCreate (LSP.WorkDoneProgressCreateParams u) (const $ pure ())               LSP.sendNotification LSP.SProgress $ LSP.ProgressParams u $                 LSP.Begin $ LSP.WorkDoneProgressBeginParams-                  { _title = "Indexing references from:"+                  { _title = "Indexing"                   , _cancellable = Nothing                   , _message = Nothing                   , _percentage = Nothing@@ -557,15 +559,26 @@         remaining <- HashMap.size <$> readTVar indexPending         pure (done, remaining) -      let progress = " (" <> T.pack (show done) <> "/" <> T.pack (show $ done + remaining) <> ")..."-       whenJust (lspEnv se) $ \env -> whenJust tok $ \tok -> LSP.runLspT env $         LSP.sendNotification LSP.SProgress $ LSP.ProgressParams tok $-          LSP.Report $ LSP.WorkDoneProgressReportParams-            { _cancellable = Nothing-            , _message = Just $ T.pack (fromNormalizedFilePath srcPath) <> progress-            , _percentage = Nothing-            }+          LSP.Report $+            case style of+                Percentage -> LSP.WorkDoneProgressReportParams+                    { _cancellable = Nothing+                    , _message = Nothing+                    , _percentage = Just (100 * fromIntegral done / fromIntegral (done + remaining) )+                    }+                Explicit -> LSP.WorkDoneProgressReportParams+                    { _cancellable = Nothing+                    , _message = Just $+                        T.pack " (" <> T.pack (show done) <> "/" <> T.pack (show $ done + remaining) <> ")..."+                    , _percentage = Nothing+                    }+                NoProgress -> LSP.WorkDoneProgressReportParams+                  { _cancellable = Nothing+                  , _message = Nothing+                  , _percentage = Nothing+                  }      -- Report the progress once we are done indexing this file     post = do
src/Development/IDE/Core/FileStore.hs view
@@ -67,6 +67,7 @@  import qualified Data.Binary                                  as B import qualified Data.ByteString.Lazy                         as LBS+import           Development.IDE.Core.IdeConfiguration        (isWorkspaceFile) import           Language.LSP.Server                          hiding                                                               (getVirtualFile) import qualified Language.LSP.Server                          as LSP@@ -127,16 +128,23 @@         let file' = fromNormalizedFilePath file         let wrap time@(l,s) = (Just $ LBS.toStrict $ B.encode time, ([], Just $ ModificationTime l s))         mbVirtual <- liftIO $ getVirtualFile vfs $ filePathToUri' file-        -- we use 'getVirtualFile' to discriminate FOIs so make that-        -- dependency explicit by using the IsFileOfInterest rule-        _ <- use_ IsFileOfInterest file         case mbVirtual of             Just (virtualFileVersion -> ver) -> do                 alwaysRerun                 pure (Just $ LBS.toStrict $ B.encode ver, ([], Just $ VFSVersion ver))             Nothing -> do                 isWF <- isWatched file-                unless (isWF || isInterface file) alwaysRerun+                if isWF+                    then -- the file is watched so we can rely on FileWatched notifications,+                         -- but also need a dependency on IsFileOfInterest to reinstall+                         -- alwaysRerun when the file becomes VFS+                        void (use_ IsFileOfInterest file)+                    else if isInterface file+                        then -- interface files are tracked specially using the closed world assumption+                            pure ()+                        else -- in all other cases we will need to freshly check the file system+                            alwaysRerun+                 liftIO $ fmap wrap (getModTime file')                     `catch` \(e :: IOException) -> do                         let err | isDoesNotExistError e = "File does not exist: " ++ file'
src/Development/IDE/Core/OfInterest.hs view
@@ -98,7 +98,7 @@     liftIO $ progressUpdate KickStarted      -- Update the exports map for FOIs-    (results, ()) <- par (uses GenerateCore files) (void $ uses GetHieAst files)+    results <- uses GenerateCore files <* uses GetHieAst files      -- Update the exports map for non FOIs     -- We can skip this if checkProject is True, assuming they never change under our feet.
src/Development/IDE/Core/PositionMapping.hs view
@@ -11,6 +11,7 @@   , PositionDelta(..)   , addDelta   , idDelta+  , composeDelta   , mkDelta   , toCurrentRange   , fromCurrentRange
src/Development/IDE/Core/Rules.hs view
@@ -18,21 +18,16 @@     priorityTypeCheck,     priorityGenerateCore,     priorityFilesOfInterest,-    runAction, useE, useNoFileE, usesE,+    runAction,     toIdeResult,     defineNoFile,     defineEarlyCutOffNoFile,     mainRule,-    getAtPoint,-    getDefinition,-    getTypeDefinition,-    highlightAtPoint,-    refsAtPoint,-    workspaceSymbols,     getDependencies,     getParsedModule,     getParsedModuleWithComments,     getClientConfigAction,+    usePropertyAction,     -- * Rules     CompiledLinkables(..),     IsHiFileStable(..),@@ -64,32 +59,51 @@     typeCheckRuleDefinition,     ) where -import           Fingerprint-+import           Control.Concurrent.Async                     (concurrently)+import           Control.Concurrent.Strict+import           Control.Exception.Safe import           Control.Monad.Extra-import           Control.Monad.Trans.Class+import           Control.Monad.Reader+import           Control.Monad.State+import           Control.Monad.Trans.Except                   (ExceptT, except,+                                                               runExceptT) import           Control.Monad.Trans.Maybe import           Data.Aeson                                   (Result (Success),                                                                toJSON)+import qualified Data.Aeson.Types                             as A import           Data.Binary                                  hiding (get, put)+import qualified Data.Binary                                  as B import qualified Data.ByteString                              as BS+import           Data.ByteString.Encoding                     as T+import qualified Data.ByteString.Lazy                         as LBS+import           Data.Coerce import           Data.Foldable+import qualified Data.HashMap.Strict                          as HM+import qualified Data.HashSet                                 as HashSet+import           Data.Hashable+import           Data.IORef import           Data.IntMap.Strict                           (IntMap) import qualified Data.IntMap.Strict                           as IntMap import           Data.List import qualified Data.Map                                     as M import           Data.Maybe+import qualified Data.Rope.UTF16                              as Rope import qualified Data.Set                                     as Set import qualified Data.Text                                    as T import qualified Data.Text.Encoding                           as T+import           Data.Time                                    (UTCTime (..)) import           Data.Tuple.Extra import           Development.IDE.Core.Compile import           Development.IDE.Core.FileExists import           Development.IDE.Core.FileStore               (getFileContents,-                                                               modificationTime, resetInterfaceStore)+                                                               modificationTime,+                                                               resetInterfaceStore)+import           Development.IDE.Core.IdeConfiguration import           Development.IDE.Core.OfInterest import           Development.IDE.Core.PositionMapping import           Development.IDE.Core.RuleTypes+import           Development.IDE.Core.Service+import           Development.IDE.Core.Shake import           Development.IDE.GHC.Compat                   hiding                                                               (TargetFile,                                                                TargetModule,@@ -101,57 +115,37 @@ import           Development.IDE.GHC.Util import           Development.IDE.Import.DependencyInformation import           Development.IDE.Import.FindImports+import qualified Development.IDE.Spans.AtPoint                as AtPoint import           Development.IDE.Spans.Documentation import           Development.IDE.Spans.LocalBindings import           Development.IDE.Types.Diagnostics            as Diag+import           Development.IDE.Types.HscEnvEq import           Development.IDE.Types.Location import qualified Development.IDE.Types.Logger                 as L import           Development.IDE.Types.Options import           Development.Shake                            hiding                                                               (Diagnostic)-import qualified Language.LSP.Server                          as LSP-import           Language.LSP.Types                           (DocumentHighlight (..),-                                                               SMethod (SCustomMethod),-                                                               SymbolInformation (..))-import           Language.LSP.VFS-+import           Development.Shake.Classes                    hiding (get, put)+import           Fingerprint import           GHC.Generics                                 (Generic)+import           GHC.IO.Encoding import qualified GHC.LanguageExtensions                       as LangExt+import qualified HieDb import           HscTypes                                     hiding                                                               (TargetFile,                                                                TargetModule)--import           Control.Concurrent.Async                     (concurrently)-import           Control.Exception.Safe-import           Control.Monad.Reader-import           Control.Monad.Trans.Except                   (ExceptT, except,-                                                               runExceptT)-import           Development.IDE.Core.IdeConfiguration-import           Development.IDE.Core.Service-import           Development.IDE.Core.Shake-import qualified Development.IDE.Spans.AtPoint                as AtPoint-import           Development.IDE.Types.HscEnvEq-import           Development.Shake.Classes                    hiding (get, put)--import           Control.Concurrent.Strict-import           Control.Monad.State-import           Data.ByteString.Encoding                     as T-import           Data.Coerce-import qualified Data.HashMap.Strict                          as HM-import qualified Data.HashSet                                 as HashSet-import           Data.Hashable-import           Data.IORef-import qualified Data.Rope.UTF16                              as Rope-import           Data.Time                                    (UTCTime (..))-import           GHC.IO.Encoding+import           Ide.Plugin.Config+import qualified Language.LSP.Server                          as LSP+import           Language.LSP.Types                           (SMethod (SCustomMethod))+import           Language.LSP.VFS import           Module import           TcRnMonad                                    (tcg_dependent_files) -import qualified Data.Aeson.Types                             as A-import qualified HieDb-import           Ide.Plugin.Config-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Binary as B+import           Ide.Plugin.Properties (HasProperty, KeyNameProxy, Properties, ToHsType, useProperty)+import           Ide.Types (PluginId)+import           Data.Default (def)+import           Ide.PluginUtils (configForPlugin)+import           Control.Applicative  -- | This is useful for rules to convert rules that can only produce errors or -- a result into the more general IdeResult type that supports producing@@ -159,17 +153,6 @@ toIdeResult :: Either [FileDiagnostic] v -> IdeResult v toIdeResult = either (, Nothing) (([],) . Just) --- | useE is useful to implement functions that aren’t rules but need shortcircuiting--- e.g. getDefinition.-useE :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)-useE k = MaybeT . useWithStaleFast k--useNoFileE :: IdeRule k v => IdeState -> k -> MaybeT IdeAction v-useNoFileE _ide k = fst <$> useE k emptyFilePath--usesE :: IdeRule k v => k -> [NormalizedFilePath] -> MaybeT IdeAction [(v,PositionMapping)]-usesE k = MaybeT . fmap sequence . mapM (useWithStaleFast k)- defineNoFile :: IdeRule k v => (k -> Action v) -> Rules () defineNoFile f = defineNoDiagnostics $ \k file -> do     if file == emptyFilePath then do res <- f k; return (Just res) else@@ -181,91 +164,8 @@         fail $ "Rule " ++ show k ++ " should always be called with the empty string for a file"  --------------------------------------------------------------- Core IDE features----------------------------------------------------------------- IMPORTANT NOTE : make sure all rules `useE`d by these have a "Persistent Stale" rule defined,--- so we can quickly answer as soon as the IDE is opened--- Even if we don't have persistent information on disk for these rules, the persistent rule--- should just return an empty result--- It is imperative that the result of the persistent rule succeed in such a case, or we will--- block waiting for the rule to be properly computed.---- | Try to get hover text for the name under point.-getAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe (Maybe Range, [T.Text]))-getAtPoint file pos = runMaybeT $ do-  ide <- ask-  opts <- liftIO $ getIdeOptionsIO ide--  (hf, mapping) <- useE GetHieAst file-  dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> (runMaybeT $ useE GetDocMap file)--  !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-  MaybeT $ pure $ fmap (first (toCurrentRange mapping =<<)) $ AtPoint.atPoint opts hf dkMap pos'--toCurrentLocations :: PositionMapping -> [Location] -> [Location]-toCurrentLocations mapping = mapMaybe go-  where-    go (Location uri range) = Location uri <$> toCurrentRange mapping range---- | Goto Definition.-getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])-getDefinition file pos = runMaybeT $ do-    ide <- ask-    opts <- liftIO $ getIdeOptionsIO ide-    (HAR _ hf _ _ _, mapping) <- useE GetHieAst file-    (ImportMap imports, _) <- useE GetImportMap file-    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-    hiedb <- lift $ asks hiedb-    dbWriter <- lift $ asks hiedbWriter-    toCurrentLocations mapping <$> AtPoint.gotoDefinition hiedb (lookupMod dbWriter) opts imports hf pos'--getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location])-getTypeDefinition file pos = runMaybeT $ do-    ide <- ask-    opts <- liftIO $ getIdeOptionsIO ide-    (hf, mapping) <- useE GetHieAst file-    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-    hiedb <- lift $ asks hiedb-    dbWriter <- lift $ asks hiedbWriter-    toCurrentLocations mapping <$> AtPoint.gotoTypeDefinition hiedb (lookupMod dbWriter) opts hf pos'--highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight])-highlightAtPoint file pos = runMaybeT $ do-    (HAR _ hf rf _ _,mapping) <- useE GetHieAst file-    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-    let toCurrentHighlight (DocumentHighlight range t) = flip DocumentHighlight t <$> toCurrentRange mapping range-    mapMaybe toCurrentHighlight <$>AtPoint.documentHighlight hf rf pos'---- Refs are not an IDE action, so it is OK to be slow and (more) accurate-refsAtPoint :: NormalizedFilePath -> Position -> Action [Location]-refsAtPoint file pos = do-    ShakeExtras{hiedb} <- getShakeExtras-    fs <- HM.keys <$> getFilesOfInterest-    asts <- HM.fromList . mapMaybe sequence . zip fs <$> usesWithStale GetHieAst fs-    AtPoint.referencesAtPoint hiedb file pos (AtPoint.FOIReferences asts)--workspaceSymbols :: T.Text -> IdeAction (Maybe [SymbolInformation])-workspaceSymbols query = runMaybeT $ do-  hiedb <- lift $ asks hiedb-  res <- liftIO $ HieDb.searchDef hiedb $ T.unpack query-  pure $ mapMaybe AtPoint.defRowToSymbolInfo res-------------------------------------------------------------- -- Exposed API ---------------------------------------------------------------- | Eventually this will lookup/generate URIs for files in dependencies, but not in the--- project. Right now, this is just a stub.-lookupMod-  :: HieDbWriter -- ^ access the database-  -> FilePath -- ^ The `.hie` file we got from the database-  -> ModuleName-  -> UnitId-  -> Bool -- ^ Is this file a boot file?-  -> MaybeT IdeAction Uri-lookupMod _dbchan _hie_f _mod _uid _boot = MaybeT $ pure Nothing- -- | Get all transitive file dependencies of a given module. -- Does not include the file itself. getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])@@ -398,7 +298,7 @@     let fp = fromNormalizedFilePath file     (diag, res) <- parseModule opt packageState fp ms     case res of-        Nothing -> pure (diag, Nothing)+        Nothing   -> pure (diag, Nothing)         Just modu -> pure (diag, Just modu)  getLocatedImportsRule :: Rules ()@@ -877,8 +777,8 @@                            else SourceUnmodified     return (Just (summarize sourceModified), Just sourceModified)   where-      summarize SourceModified = BS.singleton 1-      summarize SourceUnmodified = BS.singleton 2+      summarize SourceModified            = BS.singleton 1+      summarize SourceUnmodified          = BS.singleton 2       summarize SourceUnmodifiedAndStable = BS.singleton 3  getModSummaryRule :: Rules ()@@ -1046,6 +946,19 @@     Just (Success c) -> return c     _                -> return defValue +usePropertyAction ::+  (HasProperty s k t r) =>+  KeyNameProxy s ->+  PluginId ->+  Properties r ->+  Action (ToHsType t)+usePropertyAction kn plId p = do+  config <- getClientConfigAction def+  let pluginConfig = configForPlugin config plId+  pure $ useProperty kn p $ plcConfig pluginConfig++-- ---------------------------------------------------------------------+ -- | For now we always use bytecode unless something uses unboxed sums and tuples along with TH getLinkableType :: NormalizedFilePath -> Action (Maybe LinkableType) getLinkableType f = use_ NeedsCompilation f@@ -1070,8 +983,8 @@         -- again, this time keeping the object code.         -- A file needs to be compiled if any file that depends on it uses TemplateHaskell or needs to be compiled         ms <- msrModSummary . fst <$> useWithStale_ GetModSummaryWithoutTimestamps file-        (modsums,needsComps) <--            par (map (fmap (msrModSummary . fst)) <$> usesWithStale GetModSummaryWithoutTimestamps revdeps)+        (modsums,needsComps) <- liftA2+            (,) (map (fmap (msrModSummary . fst)) <$> usesWithStale GetModSummaryWithoutTimestamps revdeps)                 (uses NeedsCompilation revdeps)         pure $ computeLinkableType ms modsums (map join needsComps) 
src/Development/IDE/Core/Shake.hs view
@@ -50,7 +50,7 @@     getIdeOptions,     getIdeOptionsIO,     GlobalIdeOptions(..),-    getClientConfig,+    HLS.getClientConfig,     getPluginConfig,     garbageCollect,     knownTargets,@@ -230,14 +230,10 @@     Just x <- getShakeExtraRules @ShakeExtras     return x -getClientConfig :: LSP.MonadLsp Config m => ShakeExtras -> m Config-getClientConfig ShakeExtras { defaultConfig } =-    fromMaybe defaultConfig <$> HLS.getClientConfig- getPluginConfig-    :: LSP.MonadLsp Config m => ShakeExtras -> PluginId -> m PluginConfig-getPluginConfig extras plugin = do-    config <- getClientConfig extras+    :: LSP.MonadLsp Config m => PluginId -> m PluginConfig+getPluginConfig plugin = do+    config <- HLS.getClientConfig     return $ HLS.configForPlugin config plugin  -- | Register a function that will be called to get the "stale" result of a rule, possibly from disk@@ -503,7 +499,7 @@         let hiedbWriter = HieDbWriter{..}         progressAsync <- async $             when reportProgress $-                progressThread mostRecentProgressEvent inProgress+                progressThread optProgressStyle mostRecentProgressEvent inProgress         exportsMap <- newVar mempty          actionQueue <- newQueue@@ -521,7 +517,10 @@     shakeDatabaseProfile <- shakeDatabaseProfileIO shakeProfileDir     let ideState = IdeState{..} -    IdeOptions{ optOTMemoryProfiling = IdeOTMemoryProfiling otProfilingEnabled } <- getIdeOptionsIO shakeExtras+    IdeOptions+        { optOTMemoryProfiling = IdeOTMemoryProfiling otProfilingEnabled+        , optProgressStyle+        } <- getIdeOptionsIO shakeExtras     startTelemetry otProfilingEnabled logger $ state shakeExtras      return ideState@@ -532,7 +531,7 @@         -- And two transitions, modelled by 'ProgressEvent':         --   1. KickCompleted - transitions from Reporting into Idle         --   2. KickStarted - transitions from Idle into Reporting-        progressThread mostRecentProgressEvent inProgress = progressLoopIdle+        progressThread style mostRecentProgressEvent inProgress = progressLoopIdle           where             progressLoopIdle = do                 atomically $ do@@ -564,7 +563,7 @@                 bracket_                   (start u)                   (stop u)-                  (loop u Nothing)+                  (loop u 0)                 where                     start id = LSP.sendNotification LSP.SProgress $                         LSP.ProgressParams@@ -589,16 +588,27 @@                         current <- liftIO $ readVar inProgress                         let done = length $ filter (== 0) $ HMap.elems current                         let todo = HMap.size current-                        let next = Just $ T.pack $ show done <> "/" <> show todo+                        let next = 100 * fromIntegral done / fromIntegral todo                         when (next /= prev) $                           LSP.sendNotification LSP.SProgress $                           LSP.ProgressParams                               { _token = id-                              , _value = LSP.Report $ LSP.WorkDoneProgressReportParams-                                { _cancellable = Nothing-                                , _message = next-                                , _percentage = Nothing-                                }+                              , _value = LSP.Report $ case style of+                                  Explicit -> LSP.WorkDoneProgressReportParams+                                    { _cancellable = Nothing+                                    , _message = Just $ T.pack $ show done <> "/" <> show todo+                                    , _percentage = Nothing+                                    }+                                  Percentage -> LSP.WorkDoneProgressReportParams+                                    { _cancellable = Nothing+                                    , _message = Nothing+                                    , _percentage = Just next+                                    }+                                  NoProgress -> LSP.WorkDoneProgressReportParams+                                    { _cancellable = Nothing+                                    , _message = Nothing+                                    , _percentage = Nothing+                                    }                               }                         loop id next @@ -828,12 +838,14 @@         Nothing -> liftIO $ throwIO $ BadDependency (show key)         Just v  -> return v -newtype IdeAction a = IdeAction { runIdeActionT  :: (ReaderT ShakeExtras IO) a }-    deriving newtype (MonadReader ShakeExtras, MonadIO, Functor, Applicative, Monad)- -- | IdeActions are used when we want to return a result immediately, even if it -- is stale Useful for UI actions like hover, completion where we don't want to -- block.+--+-- Run via 'runIdeAction'.+newtype IdeAction a = IdeAction { runIdeActionT  :: (ReaderT ShakeExtras IO) a }+    deriving newtype (MonadReader ShakeExtras, MonadIO, Functor, Applicative, Monad)+ runIdeAction :: String -> ShakeExtras -> IdeAction a -> IO a runIdeAction _herald s i = runReaderT (runIdeActionT i) s 
src/Development/IDE/Core/Tracing.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE NoApplicativeDo #-} {-# LANGUAGE CPP #-} #include "ghc-api-version.h" module Development.IDE.Core.Tracing@@ -6,7 +7,9 @@     , startTelemetry     , measureMemory     , getInstrumentCached-    ,otTracedProvider,otSetUri)+    , otTracedProvider+    , otSetUri+    ) where  import           Control.Concurrent.Async       (Async, async)@@ -26,6 +29,7 @@                                                  readIORef, writeIORef) import           Data.String                    (IsString (fromString)) import           Data.Text.Encoding             (encodeUtf8)+import           Debug.Trace.Flags              (userTracingEnabled) import           Development.IDE.Core.RuleTypes (GhcSession (GhcSession),                                                  GhcSessionDeps (GhcSessionDeps),                                                  GhcSessionIO (GhcSessionIO))@@ -36,19 +40,17 @@                                                  Values) import           Development.Shake              (Action, actionBracket) import           Foreign.Storable               (Storable (sizeOf))-import           GHC.RTS.Flags import           HeapSize                       (recursiveSize, runHeapsize) import           Ide.PluginUtils                (installSigUsr1Handler) import           Ide.Types                      (PluginId (..)) import           Language.LSP.Types             (NormalizedFilePath,                                                  fromNormalizedFilePath) import           Numeric.Natural                (Natural)-import           OpenTelemetry.Eventlog         (Instrument, SpanInFlight,+import           OpenTelemetry.Eventlog         (Instrument, SpanInFlight (..),                                                  Synchronicity (Asynchronous),                                                  addEvent, beginSpan, endSpan,                                                  mkValueObserver, observe,                                                  setTag, withSpan, withSpan_)-import           System.IO.Unsafe               (unsafePerformIO)  -- | Trace a handler using OpenTelemetry. Adds various useful info into tags in the OpenTelemetry span. otTracedHandler@@ -57,27 +59,20 @@     -> String -- ^ Message label     -> (SpanInFlight -> m a)     -> m a-otTracedHandler requestType label act =-  let !name =-        if null label-          then requestType-          else requestType <> ":" <> show label-   -- Add an event so all requests can be quickly seen in the viewer without searching-   in do-     runInIO <- askRunInIO-     liftIO $ withSpan (fromString name) (\sp -> addEvent sp "" (fromString $ name <> " received") >> runInIO (act sp))+otTracedHandler requestType label act+  | userTracingEnabled = do+    let !name =+            if null label+            then requestType+            else requestType <> ":" <> show label+    -- Add an event so all requests can be quickly seen in the viewer without searching+    runInIO <- askRunInIO+    liftIO $ withSpan (fromString name) (\sp -> addEvent sp "" (fromString $ name <> " received") >> runInIO (act sp))+  | otherwise = act (SpanInFlight 0)  otSetUri :: SpanInFlight -> Uri -> IO () otSetUri sp (Uri t) = setTag sp "uri" (encodeUtf8 t) -{-# NOINLINE isTracingEnabled #-}-isTracingEnabled :: Bool-isTracingEnabled = unsafePerformIO $ do-    flags <- getTraceFlags-    case tracing flags of-        TraceNone -> return False-        _         -> return True- -- | Trace a Shake action using opentelemetry. otTracedAction     :: Show k@@ -87,7 +82,7 @@     -> Action a -- ^ The action     -> Action a otTracedAction key file success act-  | isTracingEnabled =+  | userTracingEnabled =     actionBracket         (do             sp <- beginSpan (fromString (show key))@@ -106,11 +101,13 @@ #else otTracedProvider :: MonadUnliftIO m => PluginId -> String -> m a -> m a #endif-otTracedProvider (PluginId pluginName) provider act = do-  runInIO <- askRunInIO-  liftIO $ withSpan (provider <> " provider") $ \sp -> do-    setTag sp "plugin" (encodeUtf8 pluginName)-    runInIO act+otTracedProvider (PluginId pluginName) provider act+  | userTracingEnabled = do+    runInIO <- askRunInIO+    liftIO $ withSpan (provider <> " provider") $ \sp -> do+        setTag sp "plugin" (encodeUtf8 pluginName)+        runInIO act+  | otherwise = act  startTelemetry :: Bool -> Logger -> Var Values -> IO () startTelemetry allTheTime logger stateRef = do
+ src/Development/IDE/Core/UseStale.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE DerivingVia    #-}+{-# LANGUAGE GADTs          #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE RankNTypes     #-}++module Development.IDE.Core.UseStale+  ( Age(..)+  , Tracked+  , unTrack+  , PositionMap+  , TrackedStale (..)+  , untrackedStaleValue+  , unsafeMkStale+  , unsafeMkCurrent+  , unsafeCopyAge+  , MapAge (..)+  , dualPositionMap+  , useWithStale+  , useWithStale_+  ) where++import           Control.Arrow+import           Control.Category (Category)+import qualified Control.Category as C+import           Control.DeepSeq (NFData)+import           Data.Aeson+import           Data.Coerce (coerce)+import           Data.Functor ((<&>))+import           Data.Functor.Identity (Identity(Identity))+import           Data.Kind (Type)+import           Data.String (fromString)+import           Development.IDE (NormalizedFilePath, IdeRule, Action, Range, rangeToRealSrcSpan, realSrcSpanToRange)+import qualified Development.IDE.Core.PositionMapping as P+import qualified Development.IDE.Core.Shake as IDE+import qualified FastString as FS+import           SrcLoc+++------------------------------------------------------------------------------+-- | A data kind for 'Tracked'.+data Age = Current | Stale Type+++------------------------------------------------------------------------------+-- | Some value, tagged with its age. All 'Current' ages are considered to be+-- the same thing, but 'Stale' values are protected by an untouchable variable+-- to ensure they can't be unified.+newtype Tracked (age :: Age) a  = UnsafeTracked+  { unTrack :: a+  }+  deriving stock (Functor, Foldable, Traversable)+  deriving newtype (Eq, Ord, Show, Read, ToJSON, FromJSON, NFData)+  deriving (Applicative, Monad) via Identity+++------------------------------------------------------------------------------+-- | Like 'P.PositionMapping', but with annotated ages for how 'Tracked' values+-- change. Use the 'Category' instance to compose 'PositionMapping's in order+-- to transform between values of different stale ages.+newtype PositionMap (from :: Age) (to :: Age) = PositionMap+  { getPositionMapping :: P.PositionMapping+  }++instance Category PositionMap where+  id  = coerce P.zeroMapping+  (.) = coerce P.composeDelta+++------------------------------------------------------------------------------+-- | Get a 'PositionMap' that runs in the opposite direction.+dualPositionMap :: PositionMap from to -> PositionMap to from+dualPositionMap (PositionMap (P.PositionMapping (P.PositionDelta from to))) =+  PositionMap $ P.PositionMapping $ P.PositionDelta to from+++------------------------------------------------------------------------------+-- | A pair containing a @'Tracked' 'Stale'@ value, as well as+-- a 'PositionMapping' that will fast-forward values to the current age.+data TrackedStale a where+  TrackedStale+      :: Tracked (Stale s) a+      -> PositionMap (Stale s) Current+      -> TrackedStale a++instance Functor TrackedStale where+  fmap f (TrackedStale t pm) = TrackedStale (fmap f t) pm+++untrackedStaleValue :: TrackedStale a -> a+untrackedStaleValue (TrackedStale ta _) = coerce ta+++------------------------------------------------------------------------------+-- | A class for which 'Tracked' values can be run across a 'PositionMapping'+-- to change their ages.+class MapAge a where+  {-# MINIMAL mapAgeFrom | mapAgeTo #-}+  mapAgeFrom :: PositionMap from to -> Tracked to   a -> Maybe (Tracked from a)+  mapAgeFrom = mapAgeTo . dualPositionMap++  mapAgeTo   :: PositionMap from to -> Tracked from a -> Maybe (Tracked to   a)+  mapAgeTo = mapAgeFrom . dualPositionMap+++instance MapAge Range where+  mapAgeFrom = coerce P.fromCurrentRange+  mapAgeTo   = coerce P.toCurrentRange+++instance MapAge RealSrcSpan where+  mapAgeFrom =+    invMapAge (\fs -> rangeToRealSrcSpan (fromString $ FS.unpackFS fs))+              (srcSpanFile &&& realSrcSpanToRange)+      .  mapAgeFrom+++------------------------------------------------------------------------------+-- | Helper function for deriving 'MapAge' for values in terms of other+-- instances.+invMapAge+    :: (c -> a -> b)+    -> (b -> (c, a))+    -> (Tracked from a -> Maybe (Tracked to a))+    -> Tracked from b+    -> Maybe (Tracked to b)+invMapAge to from f t =+  let (c, t') = unTrack $ fmap from t+   in fmap (fmap $ to c) $ f $ UnsafeTracked t'+++unsafeMkCurrent :: age ->  Tracked 'Current age+unsafeMkCurrent = coerce+++unsafeMkStale :: age -> Tracked (Stale s) age+unsafeMkStale = coerce+++unsafeCopyAge :: Tracked age a -> b -> Tracked age b+unsafeCopyAge _ = coerce+++-- | Request a Rule result, it not available return the last computed result, if any, which may be stale+useWithStale :: IdeRule k v+    => k -> NormalizedFilePath -> Action (Maybe (TrackedStale v))+useWithStale key file = do+  x <- IDE.useWithStale key file+  pure $ x <&> \(v, pm) ->+    TrackedStale (coerce v) (coerce pm)++-- | Request a Rule result, it not available return the last computed result which may be stale.+--   Errors out if none available.+useWithStale_ :: IdeRule k v+    => k -> NormalizedFilePath -> Action (TrackedStale v)+useWithStale_ key file = do+  (v, pm) <- IDE.useWithStale_ key file+  pure $ TrackedStale (coerce v) (coerce pm)+
src/Development/IDE/LSP/HoverDefinition.hs view
@@ -13,6 +13,7 @@     ) where  import           Control.Monad.IO.Class+import           Development.IDE.Core.Actions import           Development.IDE.Core.Rules import           Development.IDE.Core.Shake import           Development.IDE.LSP.Server
src/Development/IDE/LSP/LanguageServer.hs view
@@ -12,9 +12,6 @@     ( runLanguageServer     ) where -import           Control.Concurrent.Extra              (newBarrier,-                                                        signalBarrier,-                                                        waitBarrier) import           Control.Concurrent.STM import           Control.Monad.Extra import           Control.Monad.IO.Class@@ -23,10 +20,8 @@ import           Data.Maybe import qualified Data.Set                              as Set import qualified Data.Text                             as T-import qualified Development.IDE.GHC.Util              as Ghcide import           Development.IDE.LSP.Server import           Development.IDE.Session               (runWithDb)-import           GHC.IO.Handle                         (hDuplicate) import           Ide.Types                             (traceWithSpan) import qualified Language.LSP.Server                   as LSP import           Language.LSP.Types@@ -41,7 +36,6 @@ import           Development.IDE.Core.Shake import           Development.IDE.Core.Tracing import           Development.IDE.LSP.HoverDefinition-import           Development.IDE.LSP.Notifications import           Development.IDE.Types.Logger  import           System.IO.Unsafe                      (unsafeInterleaveIO)@@ -49,32 +43,21 @@ runLanguageServer     :: forall config. (Show config)     => LSP.Options+    -> Handle -- input+    -> Handle -- output     -> (FilePath -> IO FilePath) -- ^ Map root paths to the location of the hiedb for the project-    -> (IdeState -> Value -> IO (Either T.Text config))+    -> config+    -> (config -> Value -> Either T.Text config)     -> LSP.Handlers (ServerM config)     -> (LSP.LanguageContextEnv config -> VFSHandle -> Maybe FilePath -> HieDb -> IndexQueue -> IO IdeState)     -> IO ()-runLanguageServer options getHieDbLoc onConfigurationChange userHandlers getIdeState = do-    -- Move stdout to another file descriptor and duplicate stderr-    -- to stdout. This guards against stray prints from corrupting the JSON-RPC-    -- message stream.-    newStdout <- hDuplicate stdout-    stderr `Ghcide.hDuplicateTo'` stdout-    hSetBuffering stderr NoBuffering-    hSetBuffering stdout NoBuffering--    -- Print out a single space to assert that the above redirection works.-    -- This is interleaved with the logger, hence we just print a space here in-    -- order not to mess up the output too much. Verified that this breaks-    -- the language server tests without the redirection.-    putStr " " >> hFlush stdout+runLanguageServer options inH outH getHieDbLoc defaultConfig onConfigurationChange userHandlers getIdeState = do -    -- These barriers are signaled when the threads reading from these chans exit.-    -- This should not happen but if it does, we will make sure that the whole server-    -- dies and can be restarted instead of losing threads silently.-    clientMsgBarrier <- newBarrier+    -- This MVar becomes full when the server thread exits or we receive exit message from client.+    -- LSP loop will be canceled when it's full.+    clientMsgVar <- newEmptyMVar     -- Forcefully exit-    let exit = signalBarrier clientMsgBarrier ()+    let exit = void $ tryPutMVar clientMsgVar ()      -- The set of requests ids that we have received but not finished processing     pendingRequests <- newTVarIO Set.empty@@ -100,7 +83,6 @@     let ideHandlers = mconcat           [ setIdeHandlers           , userHandlers-          , setHandlersNotifications -- absolutely critical, join them with user notifications           ]      -- Send everything over a channel, since you need to wait until after initialise before@@ -117,9 +99,8 @@       let serverDefinition = LSP.ServerDefinition-            { LSP.onConfigurationChange = \v -> do-                (_chan, ide) <- ask-                liftIO $ onConfigurationChange ide v+            { LSP.onConfigurationChange = onConfigurationChange+            , LSP.defaultConfig = defaultConfig             , LSP.doInitialize = handleInit exit clearReqId waitForCancel clientMsgChan             , LSP.staticHandlers = asyncHandlers             , LSP.interpretHandler = \(env, st) -> LSP.Iso (LSP.runLspT env . flip runReaderT (clientMsgChan,st)) liftIO@@ -128,10 +109,10 @@      void $ waitAnyCancel =<< traverse async         [ void $ LSP.runServerWithHandles-            stdin-            newStdout+            inH+            outH             serverDefinition-        , void $ waitBarrier clientMsgBarrier+        , void $ readMVar clientMsgVar         ]      where@@ -156,7 +137,12 @@             logInfo (ideLogger ide) $ T.pack $ "Registering ide configuration: " <> show initConfig             registerIdeConfiguration (shakeExtras ide) initConfig -            _ <- flip forkFinally (const exitClientMsg) $ runWithDb dbLoc $ \hiedb hieChan -> do+            let handleServerException (Left e) = do+                    logError (ideLogger ide) $+                        T.pack $ "Fatal error in server thread: " <> show e+                    exitClientMsg+                handleServerException _ = pure ()+            _ <- flip forkFinally handleServerException $ runWithDb dbLoc $ \hiedb hieChan -> do               putMVar dbMVar (hiedb,hieChan)               forever $ do                 msg <- readChan clientMsgChan@@ -202,8 +188,9 @@ exitHandler :: IO () -> LSP.Handlers (ServerM c) exitHandler exit = LSP.notificationHandler SExit $ const $ do     (_, ide) <- ask+    liftIO $ logDebug (ideLogger ide) "Received exit message"     -- flush out the Shake session to record a Shake profile if applicable-    liftIO $ restartShakeSession (shakeExtras ide) []+    liftIO $ shakeShut ide     liftIO exit  modifyOptions :: LSP.Options -> LSP.Options
src/Development/IDE/LSP/Notifications.hs view
@@ -7,7 +7,8 @@ {-# LANGUAGE RankNTypes            #-}  module Development.IDE.LSP.Notifications-    ( setHandlersNotifications+    ( whenUriFile+    , descriptor     ) where  import qualified Language.LSP.Server                   as LSP@@ -18,7 +19,6 @@ import           Development.IDE.Core.IdeConfiguration import           Development.IDE.Core.Service import           Development.IDE.Core.Shake-import           Development.IDE.LSP.Server import           Development.IDE.Types.Location import           Development.IDE.Types.Logger import           Development.IDE.Types.Options@@ -37,15 +37,15 @@                                                         typecheckParents) import           Development.IDE.Core.OfInterest import           Ide.Plugin.Config                     (CheckParents (CheckOnClose))-+import           Ide.Types  whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO () whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath' -setHandlersNotifications :: LSP.Handlers (ServerM c)-setHandlersNotifications = mconcat-  [ notificationHandler LSP.STextDocumentDidOpen $-      \ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do+descriptor :: PluginId -> PluginDescriptor IdeState+descriptor plId = (defaultPluginDescriptor plId) { pluginNotificationHandlers = mconcat+  [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $+      \ide _ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do       updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])       whenUriFile _uri $ \file -> do           -- We don't know if the file actually exists, or if the contents match those on disk@@ -54,23 +54,23 @@           setFileModified ide False file           logDebug (ideLogger ide) $ "Opened text document: " <> getUri _uri -  , notificationHandler LSP.STextDocumentDidChange $-      \ide (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> liftIO $ do+  , mkPluginNotificationHandler LSP.STextDocumentDidChange $+      \ide _ (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> liftIO $ do         updatePositionMapping ide identifier changes         whenUriFile _uri $ \file -> do           modifyFilesOfInterest ide (M.insert file Modified{firstOpen=False})           setFileModified ide False file         logDebug (ideLogger ide) $ "Modified text document: " <> getUri _uri -  , notificationHandler LSP.STextDocumentDidSave $-      \ide (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do+  , mkPluginNotificationHandler LSP.STextDocumentDidSave $+      \ide _ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do         whenUriFile _uri $ \file -> do             modifyFilesOfInterest ide (M.insert file OnDisk)             setFileModified ide True file         logDebug (ideLogger ide) $ "Saved text document: " <> getUri _uri -  , notificationHandler LSP.STextDocumentDidClose $-        \ide (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do+  , mkPluginNotificationHandler LSP.STextDocumentDidClose $+        \ide _ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do           whenUriFile _uri $ \file -> do               modifyFilesOfInterest ide (M.delete file)               -- Refresh all the files that depended on this@@ -78,8 +78,8 @@               when (checkParents >= CheckOnClose) $ typecheckParents ide file               logDebug (ideLogger ide) $ "Closed text document: " <> getUri _uri -  , notificationHandler LSP.SWorkspaceDidChangeWatchedFiles $-      \ide (DidChangeWatchedFilesParams (List fileEvents)) -> liftIO $ do+  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeWatchedFiles $+      \ide _ (DidChangeWatchedFilesParams (List fileEvents)) -> liftIO $ do         -- See Note [File existence cache and LSP file watchers] which explains why we get these notifications and         -- what we do with them         let msg = Text.pack $ show fileEvents@@ -88,22 +88,22 @@         resetFileStore ide fileEvents         setSomethingModified ide -  , notificationHandler LSP.SWorkspaceDidChangeWorkspaceFolders $-      \ide (DidChangeWorkspaceFoldersParams events) -> liftIO $ do+  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeWorkspaceFolders $+      \ide _ (DidChangeWorkspaceFoldersParams events) -> liftIO $ do         let add       = S.union             substract = flip S.difference         modifyWorkspaceFolders ide           $ add       (foldMap (S.singleton . parseWorkspaceFolder) (_added   events))           . substract (foldMap (S.singleton . parseWorkspaceFolder) (_removed events)) -  , notificationHandler LSP.SWorkspaceDidChangeConfiguration $-      \ide (DidChangeConfigurationParams cfg) -> liftIO $ do+  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeConfiguration $+      \ide _ (DidChangeConfigurationParams cfg) -> liftIO $ do         let msg = Text.pack $ show cfg         logDebug (ideLogger ide) $ "Configuration changed: " <> msg         modifyClientSettings ide (const $ Just cfg)         setSomethingModified ide -  , notificationHandler LSP.SInitialized $ \ide _ -> do+  , mkPluginNotificationHandler LSP.SInitialized $ \ide _ _ -> do       clientCapabilities <- LSP.getClientCapabilities       let watchSupported = case () of             _ | LSP.ClientCapabilities{_workspace} <- clientCapabilities@@ -138,3 +138,4 @@         void $ LSP.sendRequest SClientRegisterCapability regParams (const $ pure ()) -- TODO handle response       else liftIO $ logDebug (ideLogger ide) "Warning: Client does not support watched files. Falling back to OS polling"   ]+    }
src/Development/IDE/LSP/Outline.hs view
@@ -19,7 +19,7 @@ import           Development.IDE.Core.Rules import           Development.IDE.Core.Shake import           Development.IDE.GHC.Compat-import           Development.IDE.GHC.Error      (realSrcSpanToRange)+import           Development.IDE.GHC.Error      (realSrcSpanToRange, rangeToRealSrcSpan) import           Development.IDE.Types.Location import           Language.LSP.Server            (LspM) import           Language.LSP.Types@@ -69,7 +69,7 @@                        t  -> " " <> t                      )     , _detail = Just $ pprText fdInfo-    , _kind   = SkClass+    , _kind   = SkFunction     } documentSymbolForDecl (L (RealSrcSpan l) (TyClD _ ClassDecl { tcdLName = L _ name, tcdSigs, tcdTyVars }))   = Just (defDocumentSymbol l :: DocumentSymbol)@@ -78,7 +78,7 @@                          "" -> ""                          t  -> " " <> t                        )-    , _kind     = SkClass+    , _kind     = SkInterface     , _detail   = Just "class"     , _children =       Just $ List@@ -183,12 +183,10 @@       mergeRanges xs = Range (minimum $ map _start xs) (maximum $ map _end xs)       importRange = mergeRanges $ map (_range :: DocumentSymbol -> Range) importSymbols     in-      Just (defDocumentSymbol empty :: DocumentSymbol)+      Just (defDocumentSymbol (rangeToRealSrcSpan "" importRange))           { _name = "imports"           , _kind = SkModule           , _children = Just (List importSymbols)-          , _range = importRange-          , _selectionRange = importRange           }  documentSymbolForImport :: Located (ImportDecl GhcPs) -> Maybe DocumentSymbol@@ -213,6 +211,7 @@   _range          = realSrcSpanToRange l   _selectionRange = realSrcSpanToRange l   _children       = Nothing+  _tags           = Nothing  showRdrName :: RdrName -> Text showRdrName = pprText
src/Development/IDE/Main.hs view
@@ -1,4 +1,12 @@-module Development.IDE.Main (Arguments(..), defaultMain) where+{-# OPTIONS_GHC -Wno-orphans #-}+module Development.IDE.Main+(Arguments(..)+,Command(..)+,IdeCommand(..)+,isLSP+,commandP+,defaultMain+) where import           Control.Concurrent.Extra              (newLock, readVar,                                                         withLock) import           Control.Exception.Safe                (Exception (displayException),@@ -14,7 +22,8 @@                                                         isJust) import qualified Data.Text                             as T import qualified Data.Text.IO                          as T-import           Development.IDE                       (Action, Rules)+import           Development.IDE                       (Action, Rules,+                                                        hDuplicateTo') import           Development.IDE.Core.Debouncer        (Debouncer,                                                         newAsyncDebouncer) import           Development.IDE.Core.FileStore        (makeVFSHandle)@@ -53,7 +62,10 @@                                                         defaultIdeOptions) import           Development.IDE.Types.Shake           (Key (Key)) import           Development.Shake                     (action)+import           GHC.IO.Encoding                       (setLocaleEncoding)+import           GHC.IO.Handle                         (hDuplicate) import           HIE.Bios.Cradle                       (findCradle)+import qualified HieDb.Run                             as HieDb import           Ide.Plugin.Config                     (CheckParents (NeverCheck),                                                         Config,                                                         getConfigFromNotification)@@ -62,39 +74,75 @@                                                         pluginDescToIdePlugins) import           Ide.Types                             (IdePlugins) import qualified Language.LSP.Server                   as LSP+import           Options.Applicative                   hiding (action) import qualified System.Directory.Extra                as IO import           System.Exit                           (ExitCode (ExitFailure),                                                         exitWith) import           System.FilePath                       (takeExtension,                                                         takeFileName)-import           System.IO                             (BufferMode (LineBuffering),+import           System.IO                             (BufferMode (LineBuffering, NoBuffering),+                                                        Handle, hFlush,                                                         hPutStrLn,                                                         hSetBuffering,                                                         hSetEncoding, stderr,-                                                        stdout, utf8)+                                                        stdin, stdout, utf8) import           System.Time.Extra                     (offsetTime,                                                         showDuration) import           Text.Printf                           (printf) +data Command+    = Check [FilePath]  -- ^ Typecheck some paths and print diagnostics. Exit code is the number of failures+    | Db {projectRoot :: FilePath, hieOptions ::  HieDb.Options, hieCommand :: HieDb.Command}+     -- ^ Run a command in the hiedb+    | LSP   -- ^ Run the LSP server+    | Custom {projectRoot :: FilePath, ideCommand :: IdeCommand} -- ^ User defined+    deriving Show++newtype IdeCommand = IdeCommand (IdeState -> IO ())++instance Show IdeCommand where show _ = "<ide command>"++-- TODO move these to hiedb+deriving instance Show HieDb.Command+deriving instance Show HieDb.Options++isLSP :: Command -> Bool+isLSP LSP = True+isLSP _   = False++commandP :: Parser Command+commandP = hsubparser (command "typecheck" (info (Check <$> fileCmd) fileInfo)+                    <> command "hiedb" (info (Db "." <$> HieDb.optParser "" True <*> HieDb.cmdParser <**> helper) hieInfo)+                    <> command "lsp" (info (pure LSP <**> helper) lspInfo)+                    )+  where+    fileCmd = many (argument str (metavar "FILES/DIRS..."))+    lspInfo = fullDesc <> progDesc "Start talking to an LSP client"+    fileInfo = fullDesc <> progDesc "Used as a test bed to check your IDE will work"+    hieInfo = fullDesc <> progDesc "Query .hie files"++ data Arguments = Arguments-    { argsOTMemoryProfiling :: Bool-    , argFiles :: Maybe [FilePath]   -- ^ Nothing: lsp server ;  Just: typecheck and exit-    , argsLogger :: IO Logger-    , argsRules :: Rules ()-    , argsHlsPlugins :: IdePlugins IdeState-    , argsGhcidePlugin :: Plugin Config  -- ^ Deprecated+    { argsOTMemoryProfiling     :: Bool+    , argCommand                :: Command+    , argsLogger                :: IO Logger+    , argsRules                 :: Rules ()+    , argsHlsPlugins            :: IdePlugins IdeState+    , argsGhcidePlugin          :: Plugin Config  -- ^ Deprecated     , argsSessionLoadingOptions :: SessionLoadingOptions-    , argsIdeOptions :: Maybe Config -> Action IdeGhcSession -> IdeOptions-    , argsLspOptions :: LSP.Options-    , argsDefaultHlsConfig :: Config-    , argsGetHieDbLoc :: FilePath -> IO FilePath -- ^ Map project roots to the location of the hiedb for the project-    , argsDebouncer :: IO (Debouncer NormalizedUri) -- ^ Debouncer used for diagnostics+    , argsIdeOptions            :: Config -> Action IdeGhcSession -> IdeOptions+    , argsLspOptions            :: LSP.Options+    , argsDefaultHlsConfig      :: Config+    , argsGetHieDbLoc           :: FilePath -> IO FilePath -- ^ Map project roots to the location of the hiedb for the project+    , argsDebouncer             :: IO (Debouncer NormalizedUri) -- ^ Debouncer used for diagnostics+    , argsHandleIn              :: IO Handle+    , argsHandleOut             :: IO Handle     }  instance Default Arguments where     def = Arguments         { argsOTMemoryProfiling = False-        , argFiles = Nothing+        , argCommand = LSP         , argsLogger = stderrLogger         , argsRules = mainRule >> action kick         , argsGhcidePlugin = mempty@@ -105,6 +153,21 @@         , argsDefaultHlsConfig = def         , argsGetHieDbLoc = getHieDbLoc         , argsDebouncer = newAsyncDebouncer+        , argsHandleIn = pure stdin+        , argsHandleOut = do+                -- Move stdout to another file descriptor and duplicate stderr+                -- to stdout. This guards against stray prints from corrupting the JSON-RPC+                -- message stream.+                newStdout <- hDuplicate stdout+                stderr `hDuplicateTo'` stdout+                hSetBuffering stdout NoBuffering++                -- Print out a single space to assert that the above redirection works.+                -- This is interleaved with the logger, hence we just print a space here in+                -- order not to mess up the output too much. Verified that this breaks+                -- the language server tests without the redirection.+                putStr " " >> hFlush stdout+                return newStdout         }  -- | Cheap stderr logger that relies on LineBuffering@@ -116,25 +179,28 @@  defaultMain :: Arguments -> IO () defaultMain Arguments{..} = do+    setLocaleEncoding utf8     pid <- T.pack . show <$> getProcessID     logger <- argsLogger     hSetBuffering stderr LineBuffering -    let hlsPlugin = asGhcIdePlugin argsDefaultHlsConfig argsHlsPlugins+    let hlsPlugin = asGhcIdePlugin argsHlsPlugins         hlsCommands = allLspCmdIds' pid argsHlsPlugins         plugins = hlsPlugin <> argsGhcidePlugin-        options = argsLspOptions { LSP.executeCommandCommands = Just hlsCommands }-        argsOnConfigChange _ide = pure . getConfigFromNotification argsDefaultHlsConfig+        options = argsLspOptions { LSP.executeCommandCommands = LSP.executeCommandCommands argsLspOptions <> Just hlsCommands }+        argsOnConfigChange = getConfigFromNotification         rules = argsRules >> pluginRules plugins      debouncer <- argsDebouncer+    inH <- argsHandleIn+    outH <- argsHandleOut -    case argFiles of-        Nothing -> do+    case argCommand of+        LSP -> do             t <- offsetTime             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!"-            runLanguageServer options argsGetHieDbLoc argsOnConfigChange (pluginHandlers plugins) $ \env vfs rootPath hiedb hieChan -> do+            hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run WITHOUT the --lsp option!"+            runLanguageServer options inH outH argsGetHieDbLoc argsDefaultHlsConfig argsOnConfigChange (pluginHandlers plugins) $ \env vfs rootPath hiedb hieChan -> do                 t <- t                 hPutStrLn stderr $ "Started LSP server in " ++ showDuration t @@ -164,7 +230,7 @@                     vfs                     hiedb                     hieChan-        Just argFiles -> do+        Check argFiles -> do           dir <- IO.getCurrentDirectory           dbLoc <- getHieDbLoc dir           runWithDb dbLoc $ \hiedb hieChan -> do@@ -190,7 +256,7 @@             putStrLn "\nStep 3/4: Initializing the IDE"             vfs <- makeVFSHandle             sessionLoader <- loadSessionWithOptions argsSessionLoadingOptions dir-            let options = (argsIdeOptions Nothing sessionLoader)+            let options = (argsIdeOptions argsDefaultHlsConfig sessionLoader)                         { optCheckParents = pure NeverCheck                         , optCheckProject = pure False                         }@@ -225,7 +291,29 @@                 measureMemory logger [keys] consoleObserver valuesRef              unless (null failed) (exitWith $ ExitFailure (length failed))+        Db dir opts cmd -> do+            dbLoc <- getHieDbLoc dir+            hPutStrLn stderr $ "Using hiedb at: " ++ dbLoc+            mlibdir <- setInitialDynFlags def+            case mlibdir of+                Nothing     -> exitWith $ ExitFailure 1+                Just libdir -> HieDb.runCommand libdir opts{HieDb.database = dbLoc} cmd+        Custom projectRoot (IdeCommand c) -> do+          dbLoc <- getHieDbLoc projectRoot+          runWithDb dbLoc $ \hiedb hieChan -> do+            vfs <- makeVFSHandle+            sessionLoader <- loadSessionWithOptions argsSessionLoadingOptions "."+            let options =+                  (argsIdeOptions argsDefaultHlsConfig sessionLoader)+                    { optCheckParents = pure NeverCheck,+                      optCheckProject = pure False+                    }+            ide <- initialise argsDefaultHlsConfig rules Nothing logger debouncer options vfs hiedb hieChan+            registerIdeConfiguration (shakeExtras ide) $ IdeConfiguration mempty (hashed Nothing)+            c ide+ {-# ANN defaultMain ("HLint: ignore Use nubOrd" :: String) #-}+  expandFiles :: [FilePath] -> IO [FilePath] expandFiles = concatMapM $ \x -> do
src/Development/IDE/Plugin/CodeAction.hs view
@@ -35,6 +35,7 @@ import qualified Data.Rope.UTF16                                   as Rope import qualified Data.Set                                          as S import qualified Data.Text                                         as T+import           Data.Tuple.Extra                                  (fst3) import           Development.IDE.Core.RuleTypes import           Development.IDE.Core.Rules import           Development.IDE.Core.Service@@ -43,7 +44,8 @@ import           Development.IDE.GHC.Error import           Development.IDE.GHC.ExactPrint import           Development.IDE.GHC.Util                          (prettyPrint,-                                                                    printRdrName)+                                                                    printRdrName,+                                                                    unsafePrintSDoc) import           Development.IDE.Plugin.CodeAction.Args import           Development.IDE.Plugin.CodeAction.ExactPrint import           Development.IDE.Plugin.CodeAction.PositionIndexed@@ -71,7 +73,8 @@ import           RdrName                                           (GlobalRdrElt (..),                                                                     lookupGlobalRdrEnv) import           Safe                                              (atMay)-import           SrcLoc                                            (realSrcSpanStart)+import           SrcLoc                                            (realSrcSpanEnd,+                                                                    realSrcSpanStart) import           TcRnTypes                                         (ImportAvails (..),                                                                     TcGblEnv (..)) import           Text.Regex.TDFA                                   (mrAfter,@@ -114,7 +117,7 @@       actions =         [ mkCA title kind isPreferred [x] edit         | x <- xs, (title, kind, isPreferred, tedit) <- suggestAction $ CodeActionArgs exportsMap ideOptions parsedModule text df annotatedPS tcM har bindings gblSigs x-        , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing+        , let edit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing         ]       actions' = caRemoveRedundantImports parsedModule text diag xs uri                <> actions@@ -123,7 +126,7 @@  mkCA :: T.Text -> Maybe CodeActionKind -> Maybe Bool -> [Diagnostic] -> WorkspaceEdit -> (Command |? CodeAction) mkCA title kind isPreferred diags edit =-  InR $ CodeAction title kind (Just $ List diags) isPreferred Nothing (Just edit) Nothing+  InR $ CodeAction title kind (Just $ List diags) isPreferred Nothing (Just edit) Nothing Nothing  suggestAction :: CodeActionArgs -> GhcideCodeActions suggestAction caa =@@ -179,8 +182,8 @@ --  imported from ‘Data.ByteString’ at B.hs:6:1-22 --  imported from ‘Data.ByteString.Lazy’ at B.hs:8:1-27 --  imported from ‘Data.Text’ at B.hs:7:1-16-suggestHideShadow :: ParsedSource -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T.Text, [Rewrite])]-suggestHideShadow pm@(L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_message, _range}+suggestHideShadow :: ParsedSource -> Maybe TcModuleResult -> Maybe HieAstResult -> Diagnostic -> [(T.Text, [Either TextEdit Rewrite])]+suggestHideShadow ps@(L _ HsModule {hsmodImports}) mTcM mHar Diagnostic {_message, _range}   | Just [identifier, modName, s] <-       matchRegexUnifySpaces         _message@@ -205,8 +208,8 @@         mDecl <- findImportDeclByModuleName hsmodImports $ T.unpack modName,         title <- "Hide " <> identifier <> " from " <> modName =         if modName == "Prelude" && null mDecl-          then [(title, maybeToList $ hideImplicitPreludeSymbol (T.unpack identifier) pm)]-          else maybeToList $ (title,) . pure . hideSymbol (T.unpack identifier) <$> mDecl+          then maybeToList $ (\(_, te) -> (title, [Left te])) <$> newImportToEdit (hideImplicitPreludeSymbol identifier) ps+          else maybeToList $ (title,) . pure . pure . hideSymbol (T.unpack identifier) <$> mDecl       | otherwise = []  findImportDeclByModuleName :: [LImportDecl GhcPs] -> String -> Maybe (LImportDecl GhcPs)@@ -279,6 +282,7 @@     removeSingle title tedit diagnostic = mkCA title (Just CodeActionQuickFix) Nothing [diagnostic] WorkspaceEdit{..} where         _changes = Just $ Map.singleton uri $ List tedit         _documentChanges = Nothing+        _changeAnnotations = Nothing     removeAll tedit = InR $ CodeAction{..} where         _changes = Just $ Map.singleton uri $ List tedit         _title = "Remove all redundant imports"@@ -289,6 +293,8 @@         _isPreferred = Nothing         _command = Nothing         _disabled = Nothing+        _xdata = Nothing+        _changeAnnotations = Nothing  caRemoveInvalidExports :: Maybe ParsedModule -> Maybe T.Text -> [Diagnostic] -> [Diagnostic] -> Uri -> [Command |? CodeAction] caRemoveInvalidExports m contents digs ctxDigs uri@@ -325,6 +331,8 @@         _command = Nothing         _isPreferred = Nothing         _disabled = Nothing+        _xdata = Nothing+        _changeAnnotations = Nothing     removeAll [] = Nothing     removeAll ranges = Just $ InR $ CodeAction{..} where         tedit = concatMap (\r -> [TextEdit r ""]) ranges@@ -337,6 +345,8 @@         _command = Nothing         _isPreferred = Nothing         _disabled = Nothing+        _xdata = Nothing+        _changeAnnotations = Nothing  suggestRemoveRedundantExport :: ParsedModule -> Diagnostic -> Maybe (T.Text, [Range]) suggestRemoveRedundantExport ParsedModule{pm_parsed_source = L _ HsModule{..}} Diagnostic{..}@@ -738,7 +748,7 @@ indentation :: T.Text -> Int indentation = T.length . T.takeWhile isSpace -suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, Rewrite)]+suggestExtendImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, Rewrite)] suggestExtendImport exportsMap (L _ HsModule {hsmodImports}) Diagnostic{_range=_range,..}     | Just [binding, mod, srcspan] <-       matchRegexUnifySpaces _message@@ -757,6 +767,7 @@             Just decl <- findImportDeclByRange decls range,             Just ident <- lookupExportMap binding mod           = [ ( "Add " <> renderImportStyle importStyle <> " to the import list of " <> mod+              , quickFixImportKind' "extend" importStyle               , uncurry extendImport (unImportStyle importStyle) decl               )             | importStyle <- NE.toList $ importStyles ident@@ -808,7 +819,7 @@     Maybe T.Text ->     ParsedSource ->     Diagnostic ->-    [(T.Text, [Rewrite])]+    [(T.Text, [Either TextEdit Rewrite])] suggestImportDisambiguation df (Just txt) ps@(L _ HsModule {hsmodImports}) diag@Diagnostic {..}     | Just [ambiguous] <-         matchRegexUnifySpaces@@ -837,31 +848,39 @@         toModuleTarget mName = ExistingImp <$> Map.lookup mName locDic         parensed =             "(" `T.isPrefixOf` T.strip (textInRange _range txt)+        -- > removeAllDuplicates [1, 1, 2, 3, 2] = [3]+        removeAllDuplicates = map head . filter ((==1) <$> length) . group . sort+        hasDuplicate xs = length xs /= length (S.fromList xs)         suggestions symbol mods-            | Just targets <- mapM toModuleTarget mods =-                sortOn fst-                [ ( renderUniquify mode modNameText symbol-                  , disambiguateSymbol ps diag symbol mode-                  )-                | (modTarget, restImports) <- oneAndOthers targets-                , let modName = targetModuleName modTarget-                      modNameText = T.pack $ moduleNameString modName-                , mode <--                    HideOthers restImports :-                    [ ToQualified parensed qual-                    | ExistingImp imps <- [modTarget]-                    , L _ qual <- nubOrd $ mapMaybe (ideclAs . unLoc)-                        $ NE.toList imps-                    ]-                    ++ [ToQualified parensed modName-                        | any (occursUnqualified symbol . unLoc)-                            (targetImports modTarget)-                        || case modTarget of-                            ImplicitPrelude{} -> True-                            _                 -> False-                        ]+          | hasDuplicate mods = case mapM toModuleTarget (removeAllDuplicates mods) of+                                  Just targets -> suggestionsImpl symbol (map (, []) targets)+                                  Nothing      -> []+          | otherwise         = case mapM toModuleTarget mods of+                                  Just targets -> suggestionsImpl symbol (oneAndOthers targets)+                                  Nothing      -> []+        suggestionsImpl symbol targetsWithRestImports = +            sortOn fst+            [ ( renderUniquify mode modNameText symbol+              , disambiguateSymbol ps diag symbol mode+              )+            | (modTarget, restImports) <- targetsWithRestImports+            , let modName = targetModuleName modTarget+                  modNameText = T.pack $ moduleNameString modName+            , mode <-+                [ ToQualified parensed qual+                | ExistingImp imps <- [modTarget]+                , L _ qual <- nubOrd $ mapMaybe (ideclAs . unLoc)+                    $ NE.toList imps                 ]-            | otherwise = []+                ++ [ToQualified parensed modName+                    | any (occursUnqualified symbol . unLoc)+                        (targetImports modTarget)+                    || case modTarget of+                        ImplicitPrelude{} -> True+                        _                 -> False+                    ]+                ++ [HideOthers restImports | not (null restImports)]+            ]         renderUniquify HideOthers {} modName symbol =             "Use " <> modName <> " for " <> symbol <> ", hiding other imports"         renderUniquify (ToQualified _ qual) _ symbol =@@ -897,23 +916,23 @@     Diagnostic ->     T.Text ->     HidingMode ->-    [Rewrite]+    [Either TextEdit Rewrite] disambiguateSymbol pm Diagnostic {..} (T.unpack -> symbol) = \case     (HideOthers hiddens0) ->-        [ hideSymbol symbol idecl+        [ Right $ hideSymbol symbol idecl         | ExistingImp idecls <- hiddens0         , idecl <- NE.toList idecls         ]             ++ mconcat                 [ if null imps-                    then maybeToList $ hideImplicitPreludeSymbol symbol pm-                    else hideSymbol symbol <$> imps+                    then maybeToList $ Left . snd <$> newImportToEdit (hideImplicitPreludeSymbol $ T.pack symbol) pm+                    else Right . hideSymbol symbol <$> imps                 | ImplicitPrelude imps <- hiddens0                 ]     (ToQualified parensed qualMod) ->         let occSym = mkVarOcc symbol             rdr = Qual qualMod occSym-         in [ if parensed+         in Right <$> [ if parensed                 then Rewrite (rangeToSrcSpan "<dummy>" _range) $ \df ->                     liftParseAST @(HsExpr GhcPs) df $                     prettyPrint $@@ -1136,7 +1155,7 @@  ------------------------------------------------------------------------------------------------- -suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, [Rewrite])]+suggestNewOrExtendImportForClassMethod :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, [Either TextEdit Rewrite])] suggestNewOrExtendImportForClassMethod packageExportsMap ps Diagnostic {_message}   | Just [methodName, className] <-       matchRegexUnifySpaces@@ -1155,22 +1174,25 @@           -- extend           Just decl ->             [ ( "Add " <> renderImportStyle style <> " to the import list of " <> moduleNameText,-                [uncurry extendImport (unImportStyle style) decl]+                quickFixImportKind' "extend" style,+                [Right $ uncurry extendImport (unImportStyle style) decl]               )               | style <- importStyle             ]           -- new-          _ ->-            [ ( "Import " <> moduleNameText <> " with " <> rendered,-                maybeToList $ newUnqualImport (T.unpack moduleNameText) (T.unpack rendered) False ps-              )+          _+            | Just (range, indent) <- newImportInsertRange ps+            ->+             (\(kind, unNewImport -> x) -> (x, kind, [Left $ TextEdit range (x <> "\n" <> T.replicate indent " ")])) <$>+            [ (quickFixImportKind' "new" style, newUnqualImport moduleNameText rendered False)               | style <- importStyle,                 let rendered = renderImportStyle style             ]-              <> maybeToList (("Import " <> moduleNameText,) <$> fmap pure (newImportAll (T.unpack moduleNameText) ps))+              <> [(quickFixImportKind "new.all", newImportAll moduleNameText)]+            | otherwise -> [] -suggestNewImport :: ExportsMap -> ParsedModule -> Diagnostic -> [(T.Text, TextEdit)]-suggestNewImport packageExportsMap ParsedModule {pm_parsed_source = L _ HsModule {..}} Diagnostic{_message}+suggestNewImport :: ExportsMap -> ParsedSource -> Diagnostic -> [(T.Text, CodeActionKind, TextEdit)]+suggestNewImport packageExportsMap ps@(L _ HsModule {..}) Diagnostic{_message}   | msg <- unifySpaces _message   , Just thingMissing <- extractNotInScopeName msg   , qual <- extractQualifiedModuleName msg@@ -1179,24 +1201,17 @@         >>= (findImportDeclByModuleName hsmodImports . T.unpack)         >>= ideclAs . unLoc         <&> T.pack . moduleNameString . unLoc-  , Just insertLine <- case hsmodImports of-        [] -> case srcSpanStart $ getLoc (head hsmodDecls) of-          RealSrcLoc s -> Just $ srcLocLine s - 1-          _            -> Nothing-        _ -> case srcSpanEnd $ getLoc (last hsmodImports) of-          RealSrcLoc s -> Just $ srcLocLine s-          _            -> Nothing-  , insertPos <- Position insertLine 0+  , Just (range, indent) <- newImportInsertRange ps   , extendImportSuggestions <- matchRegexUnifySpaces msg     "Perhaps you want to add ‘[^’]*’ to the import list in the import of ‘([^’]*)’"-  = [(imp, TextEdit (Range insertPos insertPos) (imp <> "\n"))-    | imp <- sort $ constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions+  = sortOn fst3 [(imp, kind, TextEdit range (imp <> "\n" <> T.replicate indent " "))+    | (kind, unNewImport -> imp) <- constructNewImportSuggestions packageExportsMap (qual <|> qual', thingMissing) extendImportSuggestions     ] suggestNewImport _ _ _ = []  constructNewImportSuggestions-  :: ExportsMap -> (Maybe T.Text, NotInScope) -> Maybe [T.Text] -> [T.Text]-constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrd+  :: ExportsMap -> (Maybe T.Text, NotInScope) -> Maybe [T.Text] -> [(CodeActionKind, NewImport)]+constructNewImportSuggestions exportsMap (qual, thingMissing) notTheseModules = nubOrdOn snd   [ suggestion   | Just name <- [T.stripPrefix (maybe "" (<> ".") qual) $ notInScope thingMissing]   , identInfo <- maybe [] Set.toList $ Map.lookup name (getExportsMap exportsMap)@@ -1205,18 +1220,74 @@   , suggestion <- renderNewImport identInfo   ]  where-  renderNewImport :: IdentInfo -> [T.Text]+  renderNewImport :: IdentInfo -> [(CodeActionKind, NewImport)]   renderNewImport identInfo     | Just q <- qual-    , asQ <- if q == m then "" else " as " <> q-    = ["import qualified " <> m <> asQ]+    = [(quickFixImportKind "new.qualified", newQualImport m q)]     | otherwise-    = ["import " <> m <> " (" <> renderImportStyle importStyle <> ")"+    = [(quickFixImportKind' "new" importStyle, newUnqualImport m (renderImportStyle importStyle) False)       | importStyle <- NE.toList $ importStyles identInfo] ++-      ["import " <> m ]+      [(quickFixImportKind "new.all", newImportAll m)]     where         m = moduleNameText identInfo +newtype NewImport = NewImport {unNewImport :: T.Text}+  deriving (Show, Eq, Ord)++newImportToEdit :: NewImport -> ParsedSource -> Maybe (T.Text, TextEdit)+newImportToEdit (unNewImport -> imp) ps+  | Just (range, indent) <- newImportInsertRange ps+  = Just (imp, TextEdit range (imp <> "\n" <> T.replicate indent " "))+  | otherwise = Nothing++newImportInsertRange :: ParsedSource -> Maybe (Range, Int)+newImportInsertRange (L _ HsModule {..})+  |  Just (uncurry Position -> insertPos, col) <- case hsmodImports of+      [] -> case getLoc (head hsmodDecls) of+        RealSrcSpan s -> let col = srcLocCol (realSrcSpanStart s) - 1+              in Just ((srcLocLine (realSrcSpanStart s) - 1, col), col)+        _            -> Nothing+      _ -> case  getLoc (last hsmodImports) of+        RealSrcSpan s -> let col = srcLocCol (realSrcSpanStart s) - 1+            in Just ((srcLocLine $ realSrcSpanEnd s,col), col)+        _            -> Nothing+    = Just (Range insertPos insertPos, col)+  | otherwise = Nothing++-- | Construct an import declaration with at most one symbol+newImport+  :: T.Text -- ^ module name+  -> Maybe T.Text -- ^  the symbol+  -> Maybe T.Text -- ^ qualified name+  -> Bool -- ^ the symbol is to be imported or hidden+  -> NewImport+newImport modName mSymbol mQual hiding = NewImport impStmt+  where+     symImp+            | Just symbol <- mSymbol+              , symOcc <- mkVarOcc $ T.unpack symbol =+              " (" <> T.pack (unsafePrintSDoc (parenSymOcc symOcc $ ppr symOcc)) <> ")"+            | otherwise = ""+     impStmt =+       "import "+         <> maybe "" (const "qualified ") mQual+         <> modName+         <> (if hiding then " hiding" else "")+         <> symImp+         <> maybe "" (\qual -> if modName == qual then "" else " as " <> qual) mQual++newQualImport :: T.Text -> T.Text -> NewImport+newQualImport modName qual = newImport modName Nothing (Just qual) False++newUnqualImport :: T.Text -> T.Text -> Bool -> NewImport+newUnqualImport modName symbol = newImport modName (Just symbol) Nothing++newImportAll :: T.Text -> NewImport+newImportAll modName = newImport modName Nothing Nothing False++hideImplicitPreludeSymbol :: T.Text -> NewImport+hideImplicitPreludeSymbol symbol = newUnqualImport "Prelude" symbol True+ canUseIdent :: NotInScope -> IdentInfo -> Bool canUseIdent NotInScopeDataConstructor{} = isDatacon canUseIdent _                           = const True@@ -1494,10 +1565,20 @@   | otherwise   = ImportTopLevel rendered :| [] +-- | Used for adding new imports renderImportStyle :: ImportStyle -> T.Text-renderImportStyle (ImportTopLevel x)    = x+renderImportStyle (ImportTopLevel x)   = x+renderImportStyle (ImportViaParent x p@(T.uncons -> Just ('(', _))) = "type " <> p <> "(" <> x <> ")" renderImportStyle (ImportViaParent x p) = p <> "(" <> x <> ")" +-- | Used for extending import lists unImportStyle :: ImportStyle -> (Maybe String, String) unImportStyle (ImportTopLevel x)    = (Nothing, T.unpack x) unImportStyle (ImportViaParent x y) = (Just $ T.unpack y, T.unpack x)++quickFixImportKind' :: T.Text -> ImportStyle -> CodeActionKind+quickFixImportKind' x (ImportTopLevel _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.topLevel"+quickFixImportKind' x (ImportViaParent _ _) = CodeActionUnknown $ "quickfix.import." <> x <> ".list.withParent"++quickFixImportKind :: T.Text -> CodeActionKind+quickFixImportKind x = CodeActionUnknown $ "quickfix.import." <> x
src/Development/IDE/Plugin/CodeAction/Args.hs view
@@ -25,8 +25,12 @@ import           Retrie                                       (Annotated (astA)) import           Retrie.ExactPrint                            (annsA) +type CodeActionTitle = T.Text++type CodeActionPreferred = Bool+ -- | A compact representation of 'Language.LSP.Types.CodeAction's-type GhcideCodeActions = [(T.Text, Maybe CodeActionKind, Maybe Bool, [TextEdit])]+type GhcideCodeActions = [(CodeActionTitle, Maybe CodeActionKind, Maybe CodeActionPreferred, [TextEdit])]  class ToTextEdit a where   toTextEdit :: CodeActionArgs -> a -> [TextEdit]@@ -105,16 +109,16 @@ instance ToCodeAction a => ToCodeAction (Maybe a) where   toCodeAction caa = maybe [] (toCodeAction caa) -instance ToTextEdit a => ToCodeAction (T.Text, a) where+instance ToTextEdit a => ToCodeAction (CodeActionTitle, a) where   toCodeAction caa (title, te) = [(title, Just CodeActionQuickFix, Nothing, toTextEdit caa te)] -instance ToTextEdit a => ToCodeAction (T.Text, CodeActionKind, a) where+instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionKind, a) where   toCodeAction caa (title, kind, te) = [(title, Just kind, Nothing, toTextEdit caa te)] -instance ToTextEdit a => ToCodeAction (T.Text, Bool, a) where-  toCodeAction caa (title, isPreferred, te) = [(title, Nothing, Just isPreferred, toTextEdit caa te)]+instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionPreferred, a) where+  toCodeAction caa (title, isPreferred, te) = [(title, Just CodeActionQuickFix, Just isPreferred, toTextEdit caa te)] -instance ToTextEdit a => ToCodeAction (T.Text, CodeActionKind, Bool, a) where+instance ToTextEdit a => ToCodeAction (CodeActionTitle, CodeActionKind, CodeActionPreferred, a) where   toCodeAction caa (title, kind, isPreferred, te) = [(title, Just kind, Just isPreferred, toTextEdit caa te)]  -------------------------------------------------------------------------------------------------
src/Development/IDE/Plugin/CodeAction/ExactPrint.hs view
@@ -12,12 +12,8 @@   -- * Utilities   appendConstraint,   extendImport,-  hideImplicitPreludeSymbol,   hideSymbol,   liftParseAST,-  newImport,-  newUnqualImport,-  newImportAll, ) where  import           Control.Applicative@@ -39,20 +35,14 @@ import           Development.IDE.Spans.Common import           FieldLabel                            (flLabel) import           GHC.Exts                              (IsList (fromList))-import           GhcPlugins                            (mkRealSrcLoc,-                                                        realSrcSpanStart,-                                                        sigPrec)+import           GhcPlugins                            (mkRdrUnqual, sigPrec) import           Language.Haskell.GHC.ExactPrint import           Language.Haskell.GHC.ExactPrint.Types (DeltaPos (DP),                                                         KeywordId (G), mkAnnKey) import           Language.LSP.Types import           OccName-import           Outputable                            (ppr, showSDoc,-                                                        showSDocUnsafe)-import           Retrie.GHC                            (mkRealSrcSpan,-                                                        rdrNameOcc,-                                                        realSrcSpanEnd,-                                                        unpackFS)+import           Outputable                            (ppr, showSDocUnsafe)+import           Retrie.GHC                            (rdrNameOcc, unpackFS)  ------------------------------------------------------------------------------ @@ -93,6 +83,7 @@     WorkspaceEdit       { _changes = Just (fromList [(uri, List edits)])       , _documentChanges = Nothing+      , _changeAnnotations = Nothing       }  ------------------------------------------------------------------------------@@ -209,44 +200,48 @@   Rewrite l $ \df -> do     case mparent of       Just parent -> extendImportViaParent df parent identifier lDecl-      _           -> extendImportTopLevel df identifier lDecl+      _           -> extendImportTopLevel identifier lDecl --- | Add an identifier to import list+-- | Add an identifier or a data type to import list -- -- extendImportTopLevel "foo" AST: -- -- import A --> Error -- import A (foo) --> Error -- import A (bar) --> import A (bar, foo)-extendImportTopLevel :: DynFlags -> String -> LImportDecl GhcPs -> TransformT (Either String) (LImportDecl GhcPs)-extendImportTopLevel df idnetifier (L l it@ImportDecl{..})+extendImportTopLevel ::+  -- | rendered+  String ->+  LImportDecl GhcPs ->+  TransformT (Either String) (LImportDecl GhcPs)+extendImportTopLevel thing (L l it@ImportDecl{..})   | Just (hide, L l' lies) <- ideclHiding     , hasSibling <- not $ null lies = do     src <- uniqueSrcSpanT     top <- uniqueSrcSpanT-    rdr <- liftParseAST df idnetifier+    let rdr = L src $ mkRdrUnqual $ mkVarOcc thing      let alreadyImported =           showNameWithoutUniques (occName (unLoc rdr))             `elem` map (showNameWithoutUniques @OccName) (listify (const True) lies)     when alreadyImported $-      lift (Left $ idnetifier <> " already imported")+      lift (Left $ thing <> " already imported")      let lie = L src $ IEName rdr         x = L top $ IEVar noExtField lie     if x `elem` lies-      then lift (Left $ idnetifier <> " already imported")+      then lift (Left $ thing <> " already imported")       else do         when hasSibling $           addTrailingCommaT (last lies)         addSimpleAnnT x (DP (0, if hasSibling then 1 else 0)) []-        addSimpleAnnT rdr dp00 $ unqalDP $ hasParen idnetifier+        addSimpleAnnT rdr dp00 [(G AnnVal, dp00)]         -- Parens are attachted to `lies`, so if `lies` was empty previously,         -- we need change the ann key from `[]` to `:` to keep parens and other anns.         unless hasSibling $           transferAnn (L l' lies) (L l' [x]) id         return $ L l it{ideclHiding = Just (hide, L l' $ lies ++ [x])}-extendImportTopLevel _ _ _ = lift $ Left "Unable to extend the import list"+extendImportTopLevel _ _ = lift $ Left "Unable to extend the import list"  -- | Add an identifier with its parent to import list --@@ -258,7 +253,14 @@ -- import A () --> import A (Bar(Cons)) -- import A (Foo, Bar) --> import A (Foo, Bar(Cons)) -- import A (Foo, Bar()) --> import A (Foo, Bar(Cons))-extendImportViaParent :: DynFlags -> String -> String -> LImportDecl GhcPs -> TransformT (Either String) (LImportDecl GhcPs)+extendImportViaParent ::+  DynFlags ->+  -- | parent (already parenthesized if needs)+  String ->+  -- | rendered child+  String ->+  LImportDecl GhcPs ->+  TransformT (Either String) (LImportDecl GhcPs) extendImportViaParent df parent child (L l it@ImportDecl{..})   | Just (hide, L l' lies) <- ideclHiding = go hide l' [] lies  where@@ -269,8 +271,8 @@     -- ThingAbs ie => ThingWith ie child     | parent == unIEWrappedName ie = do       srcChild <- uniqueSrcSpanT-      childRdr <- liftParseAST df child-      let childLIE = L srcChild $ IEName childRdr+      let childRdr = L srcChild $ mkRdrUnqual $ mkVarOcc child+          childLIE = L srcChild $ IEName childRdr           x :: LIE GhcPs = L ll' $ IEThingWith noExtField absIE NoIEWildcard [childLIE] []       -- take anns from ThingAbs, and attatch parens to it       transferAnn lAbs x $ \old -> old{annsDP = annsDP old ++ [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, dp00)]}@@ -282,7 +284,7 @@       , hasSibling <- not $ null lies' =       do         srcChild <- uniqueSrcSpanT-        childRdr <- liftParseAST df child+        let childRdr = L srcChild $ mkRdrUnqual $ mkVarOcc child          let alreadyImported =               showNameWithoutUniques (occName (unLoc childRdr))@@ -293,7 +295,7 @@         when hasSibling $           addTrailingCommaT (last lies')         let childLIE = L srcChild $ IEName childRdr-        addSimpleAnnT childRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen child+        addSimpleAnnT childRdr (DP (0, if hasSibling then 1 else 0)) [(G AnnVal, dp00)]         return $ L l it{ideclHiding = Just (hide, L l' $ reverse pre ++ [L l'' (IEThingWith noExtField twIE NoIEWildcard (lies' ++ [childLIE]) [])] ++ xs)}   go hide l' pre (x : xs) = go hide l' (x : pre) xs   go hide l' pre []@@ -303,14 +305,18 @@       srcParent <- uniqueSrcSpanT       srcChild <- uniqueSrcSpanT       parentRdr <- liftParseAST df parent-      childRdr <- liftParseAST df child+      let childRdr = L srcChild $ mkRdrUnqual $ mkVarOcc child+          isParentOperator = hasParen parent       when hasSibling $         addTrailingCommaT (head pre)-      let parentLIE = L srcParent $ IEName parentRdr+      let parentLIE = L srcParent $ (if isParentOperator then IEType else IEName) parentRdr           childLIE = L srcChild $ IEName childRdr           x :: LIE GhcPs = L l'' $ IEThingWith noExtField parentLIE NoIEWildcard [childLIE] []-      addSimpleAnnT parentRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP $ hasParen parent-      addSimpleAnnT childRdr (DP (0, 0)) $ unqalDP $ hasParen child+      -- Add AnnType for the parent if it's parenthesized (type operator)+      when isParentOperator $+        addSimpleAnnT parentLIE (DP (0, 0)) [(G AnnType, DP (0, 0))]+      addSimpleAnnT parentRdr (DP (0, if hasSibling then 1 else 0)) $ unqalDP 1 isParentOperator+      addSimpleAnnT childRdr (DP (0, 0)) [(G AnnVal, dp00)]       addSimpleAnnT x (DP (0, 0)) [(G AnnOpenP, DP (0, 1)), (G AnnCloseP, DP (0, 0))]       -- Parens are attachted to `pre`, so if `pre` was empty previously,       -- we need change the ann key from `[]` to `:` to keep parens and other anns.@@ -326,10 +332,10 @@ hasParen ('(' : _) = True hasParen _         = False -unqalDP :: Bool -> [(KeywordId, DeltaPos)]-unqalDP paren =+unqalDP :: Int -> Bool -> [(KeywordId, DeltaPos)]+unqalDP c paren =   ( if paren-      then \x -> (G AnnOpenP, dp00) : x : [(G AnnCloseP, dp00)]+      then \x -> (G AnnOpenP, DP (0, c)) : x : [(G AnnCloseP, dp00)]       else pure   )     (G AnnVal, dp00)@@ -373,7 +379,7 @@       , (G AnnCloseP, DP (0, 0))       ]   addSimpleAnnT x (DP (0, 0)) []-  addSimpleAnnT rdr dp00 $ unqalDP $ isOperator $ unLoc rdr+  addSimpleAnnT rdr dp00 $ unqalDP 0 $ isOperator $ unLoc rdr   if hasSibling     then when hasSibling $ do       addTrailingCommaT x@@ -432,69 +438,3 @@             (filter ((/= symbol) . unqualIEWrapName . unLoc) cons)             (filter ((/= symbol) . T.pack . unpackFS . flLabel . unLoc) flds)   killLie v = Just v---- | Insert a import declaration with at most one symbol---- newImport "A" (Just "Bar(Cons)") Nothing False --> import A (Bar(Cons))--- newImport "A" (Just "foo") Nothing True --> import A hiding (foo)--- newImport "A" Nothing (Just "Q") False --> import qualified A as Q------ Wrong combinations will result in parse error--- Returns Nothing if there is no imports and declarations-newImport ::-  -- | module name-  String ->-  -- | the symbol-  Maybe String ->-  -- | whether to be qualified-  Maybe String ->-  -- | the symbol is to be imported or hidden-  Bool ->-  ParsedSource ->-  Maybe Rewrite-newImport modName mSymbol mQual hiding (L _ HsModule{..}) = do-  -- TODO (berberman): if the previous line is module name and there is no other imports,-  -- 'AnnWhere' will be crowded out to the next line, which is a bug-  let predLine old =-        mkRealSrcLoc-          (srcLocFile old)-          (srcLocLine old - 1)-          (srcLocCol old)-      existingImpSpan = (fmap (realSrcSpanEnd,) . realSpan . getLoc) =<< lastMaybe hsmodImports-      existingDeclSpan = (fmap (predLine . realSrcSpanStart,) . realSpan . getLoc) =<< headMaybe hsmodDecls-  (f, s) <- existingImpSpan <|> existingDeclSpan-  let beg = f s-      indentation = srcSpanStartCol s-      ran = RealSrcSpan $ mkRealSrcSpan beg beg-  pure $-    Rewrite ran $ \df -> do-      let symImp-            | Just symbol <- mSymbol-              , symOcc <- mkVarOcc symbol =-              "(" <> showSDoc df (parenSymOcc symOcc $ ppr symOcc) <> ")"-            | otherwise = ""-          impStmt =-            "import "-              <> maybe "" (const "qualified ") mQual-              <> modName-              <> (if hiding then " hiding " else " ")-              <> symImp-              <> maybe "" (" as " <>) mQual-      -- Re-labeling is needed to reflect annotations correctly-      L _ idecl0 <- liftParseAST @(ImportDecl GhcPs) df impStmt-      let idecl = L ran idecl0-      addSimpleAnnT-        idecl-        (DP (1, indentation - 1))-        [(G AnnImport, DP (1, indentation - 1))]-      pure idecl--newUnqualImport :: String -> String -> Bool -> ParsedSource -> Maybe Rewrite-newUnqualImport modName symbol = newImport modName (Just symbol) Nothing--newImportAll :: String -> ParsedSource -> Maybe Rewrite-newImportAll modName = newImport modName Nothing Nothing False---- | Insert "import Prelude hiding (symbol)"-hideImplicitPreludeSymbol :: String -> ParsedSource -> Maybe Rewrite-hideImplicitPreludeSymbol symbol = newUnqualImport "Prelude" symbol True
src/Development/IDE/Plugin/Completions.hs view
@@ -35,7 +35,7 @@ import           Development.Shake.Classes import           GHC.Exts                                     (toList) import           GHC.Generics-import           Ide.Plugin.Config                            (Config (completionSnippetsOn))+import           Ide.Plugin.Config                            (Config) import           Ide.Types import qualified Language.LSP.Server                          as LSP import           Language.LSP.Types@@ -47,6 +47,7 @@   { pluginRules = produceCompletions   , pluginHandlers = mkPluginHandler STextDocumentCompletion getCompletionsLSP   , pluginCommands = [extendImportCommand]+  , pluginCustomConfig = mkCustomConfig properties   }  produceCompletions :: Rules ()@@ -135,9 +136,8 @@                 -> return (InL $ List [])               (Just pfix', _) -> do                 let clientCaps = clientCapabilities $ shakeExtras ide-                config <- getClientConfig $ shakeExtras ide-                let snippets = WithSnippets . completionSnippetsOn $ config-                allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps snippets+                config <- getCompletionsConfig plId+                allCompletions <- liftIO $ getCompletions plId ideOpts cci' parsedMod bindMap pfix' clientCaps config                 pure $ InL (List allCompletions)               _ -> return (InL $ List [])           _ -> return (InL $ List [])
src/Development/IDE/Plugin/Completions/Logic.hs view
@@ -9,7 +9,6 @@   CachedCompletions , cacheDataProducer , localCompletionsForParsedModule-, WithSnippets(..) , getCompletions ) where @@ -56,8 +55,7 @@ import           GhcPlugins                               (flLabel, unpackFS) import           Ide.PluginUtils                          (mkLspCommand) import           Ide.Types                                (CommandId (..),-                                                           PluginId,-                                                           WithSnippets (..))+                                                           PluginId) import           Language.LSP.Types import           Language.LSP.Types.Capabilities import qualified Language.LSP.VFS                         as VFS@@ -145,7 +143,7 @@   | isTcOcc   oc = case ty of                      Just t                        | "Constraint" `T.isSuffixOf` t-                       -> CiClass+                       -> CiInterface                      _ -> CiStruct   | isDataOcc oc = CiConstructor   | otherwise    = CiVariable@@ -188,6 +186,7 @@                   _filterText = Nothing,                   _insertText = Just insertText,                   _insertTextFormat = Just Snippet,+                  _insertTextMode = Nothing,                   _textEdit = Nothing,                   _additionalTextEdits = Nothing,                   _commitCharacters = Nothing,@@ -274,13 +273,13 @@ mkModCompl label =   CompletionItem label (Just CiModule) Nothing Nothing     Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing+    Nothing Nothing Nothing Nothing Nothing Nothing  mkImportCompl :: T.Text -> T.Text -> CompletionItem mkImportCompl enteredQual label =   CompletionItem m (Just CiModule) Nothing (Just label)     Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing+    Nothing Nothing Nothing Nothing Nothing Nothing   where     m = fromMaybe "" (T.stripPrefix enteredQual label) @@ -288,13 +287,13 @@ mkExtCompl label =   CompletionItem label (Just CiKeyword) Nothing Nothing     Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing+    Nothing Nothing Nothing Nothing Nothing Nothing  mkPragmaCompl :: T.Text -> T.Text -> CompletionItem mkPragmaCompl label insertText =   CompletionItem label (Just CiKeyword) Nothing Nothing     Nothing Nothing Nothing Nothing Nothing (Just insertText) (Just Snippet)-    Nothing Nothing Nothing Nothing Nothing+    Nothing Nothing Nothing Nothing Nothing Nothing   cacheDataProducer :: Uri -> HscEnvEq -> Module -> GlobalRdrEnv-> GlobalRdrEnv -> [LImportDecl GhcPs] -> IO CachedCompletions@@ -406,7 +405,7 @@                 [mkComp id CiVariable Nothing                 | VarPat _ id <- listify (\(_ :: Pat GhcPs) -> True) pat_lhs]             TyClD _ ClassDecl{tcdLName, tcdSigs} ->-                mkComp tcdLName CiClass Nothing :+                mkComp tcdLName CiInterface Nothing :                 [ mkComp id CiFunction (Just $ ppr typ)                 | L _ (TypeSig _ ids typ) <- tcdSigs                 , id <- ids]@@ -428,7 +427,7 @@         ]      mkComp n ctyp ty =-        CI ctyp pn (Right thisModName) ty pn Nothing doc (ctyp `elem` [CiStruct, CiClass]) Nothing+        CI ctyp pn (Right thisModName) ty pn Nothing doc (ctyp `elem` [CiStruct, CiInterface]) Nothing       where         pn = ppr n         doc = SpanDocText (getDocumentation [pm] n) (SpanDocUris Nothing Nothing)@@ -465,13 +464,17 @@ ppr :: Outputable a => a -> T.Text ppr = T.pack . prettyPrint -toggleSnippets :: ClientCapabilities -> WithSnippets -> CompletionItem -> CompletionItem-toggleSnippets ClientCapabilities {_textDocument} (WithSnippets with) =+toggleSnippets :: ClientCapabilities -> CompletionsConfig -> CompletionItem -> CompletionItem+toggleSnippets ClientCapabilities {_textDocument} (CompletionsConfig with _) =   removeSnippetsWhen (not $ with && supported)   where     supported =       Just True == (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport) +toggleAutoExtend :: CompletionsConfig -> CompItem -> CompItem+toggleAutoExtend (CompletionsConfig _ False) x = x {additionalTextEdits = Nothing}+toggleAutoExtend _ x = x+ removeSnippetsWhen :: Bool -> CompletionItem -> CompletionItem removeSnippetsWhen condition x =   if condition@@ -491,10 +494,10 @@     -> (Bindings, PositionMapping)     -> VFS.PosPrefixInfo     -> ClientCapabilities-    -> WithSnippets+    -> CompletionsConfig     -> IO [CompletionItem] getCompletions plId ideOpts CC {allModNamesAsNS, unqualCompls, qualCompls, importableModules}-               maybe_parsed (localBindings, bmapping) prefixInfo caps withSnippets = do+               maybe_parsed (localBindings, bmapping) prefixInfo caps config = do   let VFS.PosPrefixInfo { fullLine, prefixModule, prefixText } = prefixInfo       enteredQual = if T.null prefixModule then "" else prefixModule <> "."       fullPrefix  = enteredQual <> prefixText@@ -530,7 +533,7 @@                         Just ValueContext -> filter (not . isTypeCompl) compls                         Just _            -> filter (not . isTypeCompl) compls           -- Add whether the text to insert has backticks-          ctxCompls = map (\comp -> comp { isInfix = infixCompls }) ctxCompls'+          ctxCompls = map (\comp -> toggleAutoExtend config $ comp { isInfix = infixCompls }) ctxCompls'            infixCompls :: Maybe Backtick           infixCompls = isUsedAsInfix fullLine prefixModule prefixText pos@@ -562,7 +565,7 @@         ]        filtListWithSnippet f list suffix =-        [ toggleSnippets caps withSnippets (f label (snippet <> suffix))+        [ toggleSnippets caps config (f label (snippet <> suffix))         | (snippet, label) <- list         , Fuzzy.test fullPrefix label         ]@@ -596,7 +599,7 @@         compls <- mapM (mkCompl plId ideOpts) uniqueFiltCompls         return $ filtModNameCompls               ++ filtKeywordCompls-              ++ map ( toggleSnippets caps withSnippets) compls+              ++ map (toggleSnippets caps config) compls   -- ---------------------------------------------------------------------
src/Development/IDE/Plugin/Completions/Types.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE DeriveAnyClass     #-} {-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GADTs              #-}+{-# LANGUAGE OverloadedLabels   #-} module Development.IDE.Plugin.Completions.Types (   module Development.IDE.Plugin.Completions.Types ) where@@ -13,6 +15,11 @@ import           Data.Text                    (Text) import           Development.IDE.Spans.Common import           GHC.Generics                 (Generic)+import           Ide.Plugin.Config            (Config)+import           Ide.Plugin.Properties+import           Ide.PluginUtils              (usePropertyLsp)+import           Ide.Types                    (PluginId)+import           Language.LSP.Server          (MonadLsp) import           Language.LSP.Types           (CompletionItemKind, Uri)  -- From haskell-ide-engine/src/Haskell/Ide/Engine/LSP/Completions.hs@@ -22,6 +29,29 @@  extendImportCommandId :: Text extendImportCommandId = "extendImport"++properties :: Properties+  '[ 'PropertyKey "autoExtendOn" 'TBoolean,+     'PropertyKey "snippetsOn" 'TBoolean]+properties = emptyProperties+  & defineBooleanProperty #snippetsOn+    "Inserts snippets when using code completions"+    True+  & defineBooleanProperty #autoExtendOn+    "Extends the import list automatically when completing a out-of-scope identifier"+    True++getCompletionsConfig :: (MonadLsp Config m) => PluginId -> m CompletionsConfig+getCompletionsConfig pId =+  CompletionsConfig+    <$> usePropertyLsp #snippetsOn pId properties+    <*> usePropertyLsp #autoExtendOn pId properties+++data CompletionsConfig = CompletionsConfig {+  enableSnippets   :: Bool,+  enableAutoExtend :: Bool+}  data ExtendImport = ExtendImport   { doc         :: !Uri,
src/Development/IDE/Plugin/HLS.hs view
@@ -1,5 +1,7 @@-{-# LANGUAGE GADTs     #-}-{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE DataKinds         #-}+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds         #-}  module Development.IDE.Plugin.HLS     (@@ -8,6 +10,7 @@  import           Control.Exception            (SomeException) import           Control.Monad+import           Control.Monad.IO.Class import qualified Data.Aeson                   as J import           Data.Bifunctor import           Data.Dependent.Map           (DMap)@@ -17,13 +20,13 @@ import qualified Data.List                    as List import           Data.List.NonEmpty           (NonEmpty, nonEmpty, toList) import qualified Data.Map                     as Map-import           Data.Maybe                   (fromMaybe) import           Data.String import qualified Data.Text                    as T import           Development.IDE.Core.Shake import           Development.IDE.Core.Tracing import           Development.IDE.LSP.Server import           Development.IDE.Plugin+import           Development.IDE.Types.Logger import           Development.Shake            (Rules) import           Ide.Plugin.Config import           Ide.PluginUtils              (getClientConfig)@@ -40,13 +43,13 @@ --  -- | Map a set of plugins to the underlying ghcide engine.-asGhcIdePlugin :: Config -> IdePlugins IdeState -> Plugin Config-asGhcIdePlugin defaultConfig mp =+asGhcIdePlugin :: IdePlugins IdeState -> Plugin Config+asGhcIdePlugin (IdePlugins ls) =     mkPlugin rulesPlugins HLS.pluginRules <>     mkPlugin executeCommandPlugins HLS.pluginCommands <>-    mkPlugin (extensiblePlugins defaultConfig)  HLS.pluginHandlers+    mkPlugin extensiblePlugins HLS.pluginHandlers <>+    mkPlugin extensibleNotificationPlugins HLS.pluginNotificationHandlers     where-        ls = Map.toList (ipMap mp)          mkPlugin :: ([(PluginId, b)] -> Plugin Config) -> (PluginDescriptor IdeState -> b) -> Plugin Config         mkPlugin maker selector =@@ -128,8 +131,8 @@  -- --------------------------------------------------------------------- -extensiblePlugins :: Config -> [(PluginId, PluginHandlers IdeState)] -> Plugin Config-extensiblePlugins defaultConfig xs = Plugin mempty handlers+extensiblePlugins :: [(PluginId, PluginHandlers IdeState)] -> Plugin Config+extensiblePlugins xs = Plugin mempty handlers   where     IdeHandlers handlers' = foldMap bakePluginId xs     bakePluginId :: (PluginId, PluginHandlers IdeState) -> IdeHandlers@@ -139,7 +142,7 @@     handlers = mconcat $ do       (IdeMethod m :=> IdeHandler fs') <- DMap.assocs handlers'       pure $ requestHandler m $ \ide params -> do-        config <- fromMaybe defaultConfig <$> Ide.PluginUtils.getClientConfig+        config <- Ide.PluginUtils.getClientConfig         let fs = filter (\(pid,_) -> pluginEnabled m pid config) fs'         case nonEmpty fs of           Nothing -> pure $ Left $ ResponseError InvalidRequest@@ -154,7 +157,32 @@               Just xs -> do                 caps <- LSP.getClientCapabilities                 pure $ Right $ combineResponses m config caps params xs+-- --------------------------------------------------------------------- +extensibleNotificationPlugins :: [(PluginId, PluginNotificationHandlers IdeState)] -> Plugin Config+extensibleNotificationPlugins xs = Plugin mempty handlers+  where+    IdeNotificationHandlers handlers' = foldMap bakePluginId xs+    bakePluginId :: (PluginId, PluginNotificationHandlers IdeState) -> IdeNotificationHandlers+    bakePluginId (pid,PluginNotificationHandlers hs) = IdeNotificationHandlers $ DMap.map+      (\(PluginNotificationHandler f) -> IdeNotificationHandler [(pid,f pid)])+      hs+    handlers = mconcat $ do+      (IdeNotification m :=> IdeNotificationHandler fs') <- DMap.assocs handlers'+      pure $ notificationHandler m $ \ide params -> do+        config <- Ide.PluginUtils.getClientConfig+        let fs = filter (\(pid,_) -> plcGlobalOn $ configForPlugin config pid) fs'+        case nonEmpty fs of+          Nothing -> do+              liftIO $ logInfo (ideLogger ide) "extensibleNotificationPlugins no enabled plugins"+              pure ()+          Just fs -> do+            -- We run the notifications in order, so the core ghcide provider+            -- (which restarts the shake process) hopefully comes last+              mapM_ (\(pid,f) -> otTracedProvider pid (fromString $ show m) $ f ide params) fs++-- ---------------------------------------------------------------------+ runConcurrently   :: MonadUnliftIO m   => (SomeException -> PluginId -> T.Text)@@ -175,12 +203,25 @@ newtype IdeHandler (m :: J.Method FromClient Request)   = IdeHandler [(PluginId,IdeState -> MessageParams m -> LSP.LspM Config (NonEmpty (Either ResponseError (ResponseResult m))))] +-- | Combine the 'PluginHandler' for all plugins+newtype IdeNotificationHandler (m :: J.Method FromClient Notification)+  = IdeNotificationHandler [(PluginId, IdeState -> MessageParams m -> LSP.LspM Config ())]+-- type NotificationHandler (m :: Method FromClient Notification) = MessageParams m -> IO ()`+ -- | Combine the 'PluginHandlers' for all plugins-newtype IdeHandlers = IdeHandlers (DMap IdeMethod IdeHandler)+newtype IdeHandlers             = IdeHandlers             (DMap IdeMethod       IdeHandler)+newtype IdeNotificationHandlers = IdeNotificationHandlers (DMap IdeNotification IdeNotificationHandler)  instance Semigroup IdeHandlers where   (IdeHandlers a) <> (IdeHandlers b) = IdeHandlers $ DMap.unionWithKey go a b     where-      go _ (IdeHandler a) (IdeHandler b) = IdeHandler (a ++ b)+      go _ (IdeHandler a) (IdeHandler b) = IdeHandler (a <> b) instance Monoid IdeHandlers where   mempty = IdeHandlers mempty++instance Semigroup IdeNotificationHandlers where+  (IdeNotificationHandlers a) <> (IdeNotificationHandlers b) = IdeNotificationHandlers $ DMap.unionWithKey go a b+    where+      go _ (IdeNotificationHandler a) (IdeNotificationHandler b) = IdeNotificationHandler (a <> b)+instance Monoid IdeNotificationHandlers where+  mempty = IdeNotificationHandlers mempty
src/Development/IDE/Plugin/HLS/GhcIde.hs view
@@ -1,29 +1,31 @@ {-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings     #-}  -- | Exposes the ghcide features as an HLS plugin module Development.IDE.Plugin.HLS.GhcIde   (     descriptors   ) where-import Development.IDE-import Development.IDE.LSP.HoverDefinition-import Development.IDE.LSP.Outline-import Ide.Types-import Language.LSP.Types-import Language.LSP.Server (LspM)-import Text.Regex.TDFA.Text()-import qualified Development.IDE.Plugin.CodeAction as CodeAction-import qualified Development.IDE.Plugin.Completions as Completions-import qualified Development.IDE.Plugin.TypeLenses as TypeLenses-import Control.Monad.IO.Class+import           Control.Monad.IO.Class+import           Development.IDE+import           Development.IDE.LSP.HoverDefinition+import qualified Development.IDE.LSP.Notifications   as Notifications+import           Development.IDE.LSP.Outline+import qualified Development.IDE.Plugin.CodeAction   as CodeAction+import qualified Development.IDE.Plugin.Completions  as Completions+import qualified Development.IDE.Plugin.TypeLenses   as TypeLenses+import           Ide.Types+import           Language.LSP.Server                 (LspM)+import           Language.LSP.Types+import           Text.Regex.TDFA.Text                ()  descriptors :: [PluginDescriptor IdeState] descriptors =   [ descriptor "ghcide-hover-and-symbols",     CodeAction.descriptor "ghcide-code-actions",     Completions.descriptor "ghcide-completions",-    TypeLenses.descriptor "ghcide-type-lenses"+    TypeLenses.descriptor "ghcide-type-lenses",+    Notifications.descriptor "ghcide-core"   ]  -- ---------------------------------------------------------------------
src/Development/IDE/Plugin/TypeLenses.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE TypeFamilies   #-}+{-# LANGUAGE DeriveAnyClass   #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE TypeFamilies     #-}  -- | An HLS plugin to provide code lenses for type signatures module Development.IDE.Plugin.TypeLenses (@@ -13,16 +14,14 @@  import           Avail                               (availsToNameSet) import           Control.DeepSeq                     (rwhnf)-import           Control.Monad                       (join)+import           Control.Monad                       (mzero) import           Control.Monad.Extra                 (whenMaybe) import           Control.Monad.IO.Class              (MonadIO (liftIO))-import qualified Data.Aeson                          as A-import           Data.Aeson.Types                    (Value (..), toJSON) import qualified Data.Aeson.Types                    as A+import           Data.Aeson.Types                    (Value (..), toJSON) import qualified Data.HashMap.Strict                 as Map import           Data.List                           (find)-import           Data.Maybe                          (catMaybes, fromJust,-                                                      fromMaybe)+import           Data.Maybe                          (catMaybes, fromJust) import qualified Data.Text                           as T import           Development.IDE                     (GhcSession (..),                                                       HscEnvEq (hscEnv),@@ -52,16 +51,17 @@                                                       realSrcLocSpan,                                                       tidyOpenType) import           HscTypes                            (mkPrintUnqualified)-import           Ide.Plugin.Config                   (Config,-                                                      PluginConfig (plcConfig))-import           Ide.PluginUtils                     (getPluginConfig,-                                                      mkLspCommand)+import           Ide.Plugin.Config                   (Config)+import           Ide.Plugin.Properties+import           Ide.PluginUtils                     (mkLspCommand,+                                                      usePropertyLsp) import           Ide.Types                           (CommandFunction,                                                       CommandId (CommandId),                                                       PluginCommand (PluginCommand),                                                       PluginDescriptor (..),                                                       PluginId,                                                       defaultPluginDescriptor,+                                                      mkCustomConfig,                                                       mkPluginHandler) import qualified Language.LSP.Server                 as LSP import           Language.LSP.Types                  (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams),@@ -90,15 +90,24 @@     { pluginHandlers = mkPluginHandler STextDocumentCodeLens codeLensProvider     , pluginCommands = [PluginCommand (CommandId typeLensCommandId) "adds a signature" commandHandler]     , pluginRules = rules+    , pluginCustomConfig = mkCustomConfig properties     } +properties :: Properties '[ 'PropertyKey "mode" ('TEnum Mode)]+properties = emptyProperties+  & defineEnumProperty #mode "Control how type lenses are shown"+    [ (Always, "Always displays type lenses of global bindings")+    , (Exported, "Only display type lenses of exported global bindings")+    , (Diagnostics, "Follows error messages produced by GHC about missing signatures")+    ] Always+ codeLensProvider ::   IdeState ->   PluginId ->   CodeLensParams ->   LSP.LspM Config (Either ResponseError (List CodeLens)) codeLensProvider ideState pId CodeLensParams{_textDocument = TextDocumentIdentifier uri} = do-  (fromMaybe Always . join -> mode) <- fmap (parseCustomConfig . plcConfig) <$> getPluginConfig pId+  mode <- usePropertyLsp #mode pId properties   fmap (Right . List) $ case uriToFilePath' uri of     Just (toNormalizedFilePath' -> filePath) -> liftIO $ do       tmr <- runAction "codeLens.TypeCheck" ideState (use TypeCheck filePath)@@ -108,7 +117,7 @@       diag <- getDiagnostics ideState       hDiag <- getHiddenDiagnostics ideState -      let toWorkSpaceEdit tedit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing+      let toWorkSpaceEdit tedit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing           generateLensForGlobal sig@GlobalBindingTypeSig{..} = do             range <- srcSpanToRange $ gbSrcSpan sig             tedit <- gblBindingTypeSigToEdit sig@@ -202,13 +211,17 @@     Diagnostics   deriving (Eq, Ord, Show, Read, Enum) +instance A.ToJSON Mode where+  toJSON Always = "always"+  toJSON Exported = "exported"+  toJSON Diagnostics = "diagnostics"+ instance A.FromJSON Mode where-  parseJSON = A.withText "Mode" $ \s ->-    case T.toLower s of-      "always"      -> pure Always-      "exported"    -> pure Exported-      "diagnostics" -> pure Diagnostics-      _             -> A.unexpected (A.String s)+  parseJSON = A.withText "Mode" $ \case+    "always"      -> pure Always+    "exported"    -> pure Exported+    "diagnostics" -> pure Diagnostics+    _             -> mzero  -------------------------------------------------------------------------------- @@ -245,9 +258,6 @@     hsc <- use GhcSession nfp     result <- liftIO $ gblBindingType (hscEnv <$> hsc) (tmrTypechecked <$> tmr)     pure ([], result)--parseCustomConfig :: A.Object -> Maybe Mode-parseCustomConfig = A.parseMaybe (A..: "mode")  gblBindingType :: Maybe HscEnv -> Maybe TcGblEnv -> IO (Maybe GlobalBindingTypeSigsResult) gblBindingType (Just hsc) (Just gblEnv) = do
src/Development/IDE/Spans/AtPoint.hs view
@@ -344,7 +344,7 @@  defRowToSymbolInfo :: Res DefRow -> Maybe SymbolInformation defRowToSymbolInfo (DefRow{..}:.(modInfoSrcFile -> Just srcFile))-  = Just $ SymbolInformation (showGhc defNameOcc) kind Nothing loc Nothing+  = Just $ SymbolInformation (showGhc defNameOcc) kind Nothing Nothing loc Nothing   where     kind       | isVarOcc defNameOcc = SkVariable
src/Development/IDE/Spans/Common.hs view
@@ -121,8 +121,15 @@   = "`" ++ i ++ "`" haddockToMarkdown (H.DocIdentifierUnchecked i)   = "`" ++ i ++ "`"-haddockToMarkdown (H.DocModule i)+haddockToMarkdown (H.DocModule (H.ModLink i Nothing))   = "`" ++ escapeBackticks i ++ "`"+-- See https://github.com/haskell/haddock/pull/1315+-- Module references can be labeled in markdown style, e.g. [some label]("Some.Module")+-- However, we don't want to use the link markup here, since the module name would be covered+-- up by the label. Thus, we keep both the label and module name in the following style:+-- some label ( `Some.Module` )+haddockToMarkdown (H.DocModule (H.ModLink i (Just label)))+  = haddockToMarkdown label ++ " ( `" ++ escapeBackticks i ++ "` )" haddockToMarkdown (H.DocWarning w)   = haddockToMarkdown w haddockToMarkdown (H.DocEmphasis d)
src/Development/IDE/Types/Exports.hs view
@@ -56,21 +56,30 @@         -- deliberately skip the rendered field         rnf name `seq` rnf parent `seq` rnf isDatacon `seq` rnf moduleNameText +-- | Render an identifier as imported or exported style.+-- TODO: pattern synonym+renderIEWrapped :: Name -> Text+renderIEWrapped n+  | isTcOcc occ && isSymOcc occ = "type " <> pack (printName n)+  | otherwise = pack $ printName n+  where+    occ = occName n+ mkIdentInfos :: Text -> AvailInfo -> [IdentInfo] mkIdentInfos mod (Avail n) =-    [IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing (isDataConName n) mod]+    [IdentInfo (pack (prettyPrint n)) (renderIEWrapped n) Nothing (isDataConName n) mod] mkIdentInfos mod (AvailTC parent (n:nn) flds)     -- Following the GHC convention that parent == n if parent is exported     | n == parent-    = [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) (Just $! parentP) (isDataConName n) mod+    = [ IdentInfo (pack (prettyPrint n)) (renderIEWrapped n) (Just $! parentP) (isDataConName n) mod         | n <- nn ++ map flSelector flds       ] ++-      [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing (isDataConName n) mod]+      [ IdentInfo (pack (prettyPrint n)) (renderIEWrapped n) Nothing (isDataConName n) mod]     where         parentP = pack $ printName parent  mkIdentInfos mod (AvailTC _ nn flds)-    = [ IdentInfo (pack (prettyPrint n)) (pack (printName n)) Nothing (isDataConName n) mod+    = [ IdentInfo (pack (prettyPrint n)) (renderIEWrapped n) Nothing (isDataConName n) mod         | n <- nn ++ map flSelector flds       ] 
src/Development/IDE/Types/HscEnvEq.hs view
@@ -18,6 +18,8 @@ import           Control.Monad.Extra           (eitherM, join, mapMaybeM) import           Control.Monad.IO.Class import           Data.Either                   (fromRight)+import           Data.Set                      (Set)+import qualified Data.Set                      as Set import           Data.Unique import           Development.IDE.GHC.Compat import           Development.IDE.GHC.Error     (catchSrcErrors)@@ -48,7 +50,7 @@                -- ^ In memory components for this HscEnv                -- This is only used at the moment for the import dirs in                -- the DynFlags-    , envImportPaths        :: Maybe [String]+    , envImportPaths        :: Maybe (Set FilePath)         -- ^ If Just, import dirs originally configured in this env         --   If Nothing, the env import dirs are unaltered     , envPackageExports     :: IO ExportsMap@@ -69,9 +71,9 @@     importPathsCanon <-       mapM canonicalizePath $ relativeToCradle <$> importPaths (hsc_dflags hscEnv0) -    newHscEnvEqWithImportPaths (Just importPathsCanon) hscEnv deps+    newHscEnvEqWithImportPaths (Just $ Set.fromList importPathsCanon) hscEnv deps -newHscEnvEqWithImportPaths :: Maybe [String] -> HscEnv -> [(InstalledUnitId, DynFlags)] -> IO HscEnvEq+newHscEnvEqWithImportPaths :: Maybe (Set FilePath) -> HscEnv -> [(InstalledUnitId, DynFlags)] -> IO HscEnvEq newHscEnvEqWithImportPaths envImportPaths hscEnv deps = do      let dflags = hsc_dflags hscEnv@@ -121,7 +123,7 @@ hscEnvWithImportPaths :: HscEnvEq -> HscEnv hscEnvWithImportPaths HscEnvEq{..}     | Just imps <- envImportPaths-    = hscEnv{hsc_dflags = (hsc_dflags hscEnv){importPaths = imps}}+    = hscEnv{hsc_dflags = (hsc_dflags hscEnv){importPaths = Set.toList imps}}     | otherwise     = hscEnv 
src/Development/IDE/Types/KnownTargets.hs view
@@ -14,11 +14,11 @@ import           GHC.Generics  -- | A mapping of module name to known files-type KnownTargets = HashMap Target [NormalizedFilePath]+type KnownTargets = HashMap Target (HashSet NormalizedFilePath)  data Target = TargetModule ModuleName | TargetFile NormalizedFilePath   deriving ( Eq, Generic, Show )   deriving anyclass (Hashable, NFData)  toKnownFiles :: KnownTargets -> HashSet NormalizedFilePath-toKnownFiles = HSet.fromList . concat . HMap.elems+toKnownFiles = HSet.unions . HMap.elems
src/Development/IDE/Types/Options.hs view
@@ -16,6 +16,7 @@   , IdeResult   , IdeGhcSession(..)   , OptHaddockParse(..)+  , ProgressReportingStyle(..)   ,optShakeFiles) where  import qualified Data.Text                         as T@@ -78,6 +79,7 @@   , optShakeOptions       :: ShakeOptions   , optSkipProgress       :: forall a. Typeable a => a -> Bool       -- ^ Predicate to select which rule keys to exclude from progress reporting.+  , optProgressStyle      :: ProgressReportingStyle   }  optShakeFiles :: IdeOptions -> Maybe FilePath@@ -104,6 +106,12 @@ newtype IdeTesting           = IdeTesting        Bool newtype IdeOTMemoryProfiling = IdeOTMemoryProfiling    Bool +data ProgressReportingStyle+    = Percentage -- ^ Report using the LSP @_percentage@ field+    | Explicit   -- ^ Report using explicit 123/456 text+    | NoProgress -- ^ Do not report any percentage++ clientSupportsProgress :: LSP.ClientCapabilities -> IdeReportProgress clientSupportsProgress caps = IdeReportProgress $ Just True ==     (LSP._workDoneProgress =<< LSP._window (caps :: LSP.ClientCapabilities))@@ -131,6 +139,7 @@     ,optHaddockParse = HaddockParse     ,optCustomDynFlags = id     ,optSkipProgress = defaultSkipProgress+    ,optProgressStyle = Explicit     }  defaultSkipProgress :: Typeable a => a -> Bool
+ test/data/TH/THA.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE TemplateHaskell #-}+module THA where+import Language.Haskell.TH++th_a :: DecsQ+th_a = [d| a = () |]
+ test/data/TH/THB.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-}+module THB where+import THA++$th_a
+ test/data/TH/THC.hs view
@@ -0,0 +1,5 @@+module THC where+import THB++c ::()+c = a
+ test/data/TH/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["-Wmissing-signatures", "-package template-haskell", "THA", "THB", "THC"]}}
+ test/data/THNewName/A.hs view
@@ -0,0 +1,6 @@+module A (template) where++import Language.Haskell.TH++template :: DecsQ+template = (\consA -> [DataD [] (mkName "A") [] Nothing [NormalC consA []] []]) <$> newName "A"
+ test/data/THNewName/B.hs view
@@ -0,0 +1,5 @@+module B(A(A)) where++import A++template
+ test/data/THNewName/C.hs view
@@ -0,0 +1,4 @@+module C where+import B++a = A
+ test/data/THNewName/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["-XTemplateHaskell","-Wmissing-signatures","A", "B", "C"]}}
+ test/data/THUnboxed/THA.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE TemplateHaskell, UnboxedTuples #-}+module THA where+import Language.Haskell.TH++f :: Int -> (# Int, Int #)+f x = (# x , x+1 #)++th_a :: DecsQ+th_a = case f 1 of (# a , b #) -> [d| a = () |]
+ test/data/THUnboxed/THB.hs view
@@ -0,0 +1,5 @@+{-# LANGUAGE TemplateHaskell #-}+module THB where+import THA++$th_a
+ test/data/THUnboxed/THC.hs view
@@ -0,0 +1,5 @@+module THC where+import THB++c ::()+c = a
+ test/data/THUnboxed/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["-Wmissing-signatures", "-package template-haskell", "THA", "THB", "THC"]}}
+ test/data/boot/A.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE TemplateHaskell #-}+module A where++import B( TB(..) )++newtype TA = MkTA Int+    deriving Eq++f :: TB -> TA+f (MkTB x) = MkTA x
+ test/data/boot/A.hs-boot view
@@ -0,0 +1,3 @@+module A where+newtype TA = MkTA Int+instance Eq TA
+ test/data/boot/B.hs view
@@ -0,0 +1,7 @@+module B(TA(..), TB(..)) where+import {-# SOURCE #-} A( TA(..) )++data TB = MkTB !Int++g :: TA -> TB+g (MkTA x) = MkTB x
+ test/data/boot/C.hs view
@@ -0,0 +1,8 @@+module C where++import B+import A hiding (MkTA(..))++x = MkTA+y = MkTB+z = f
+ test/data/boot/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["A.hs", "A.hs-boot", "B.hs", "C.hs"]}}
+ test/data/cabal-exe/a/a.cabal view
@@ -0,0 +1,14 @@+cabal-version:       2.2++name:                a+version:             0.1.0.0+author:              Fendor+maintainer:          power.walross@gmail.com+build-type:          Simple++executable a+  main-is:             Main.hs+  hs-source-dirs:      src+  ghc-options:         -Wall+  build-depends:       base+  default-language:    Haskell2010
+ test/data/cabal-exe/a/src/Main.hs view
@@ -0,0 +1,3 @@+module Main where++main = putStrLn "Hello, Haskell!"
+ test/data/cabal-exe/cabal.project view
@@ -0,0 +1,1 @@+packages: ./a
+ test/data/cabal-exe/hie.yaml view
@@ -0,0 +1,3 @@+cradle:+  cabal:+    component: "exe:a"
+ test/data/hiding/AVec.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TypeOperators #-}+module AVec (Vec, type (@@@), (++), cons, fromList, snoc) where++import Prelude hiding ((++))++data Vec a++(++) :: Vec a -> Vec a -> Vec a+(++) = undefined++data (@@@) a b++fromList :: [a] -> Vec a+fromList = undefined++cons :: a -> Vec a -> Vec a+cons = undefined++snoc :: Vec a -> a -> Vec a+snoc = undefined
+ test/data/hiding/BVec.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TypeOperators #-}+module BVec (Vec, type (@@@), (++), cons, fromList, snoc) where++import Prelude hiding ((++))++data Vec a++(++) :: Vec a -> Vec a -> Vec a+(++) = undefined++data (@@@) a b++fromList :: [a] -> Vec a+fromList = undefined++cons :: a -> Vec a -> Vec a+cons = undefined++snoc :: Vec a -> a -> Vec a+snoc = undefined
+ test/data/hiding/CVec.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TypeOperators #-}+module CVec (Vec, type (@@@), (++), cons, fromList, snoc) where++import Prelude hiding ((++))++data Vec a++(++) :: Vec a -> Vec a -> Vec a+(++) = undefined++data (@@@) a b++fromList :: [a] -> Vec a+fromList = undefined++cons :: a -> Vec a -> Vec a+cons = undefined++snoc :: Vec a -> a -> Vec a+snoc = undefined
+ test/data/hiding/DVec.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TypeOperators #-}+module DVec (Vec, (++), type (@@@), cons, fromList, snoc) where++import Prelude hiding ((++))++data Vec a++(++) :: Vec a -> Vec a -> Vec a+(++) = undefined++data (@@@) a b++fromList :: [a] -> Vec a+fromList = undefined++cons :: a -> Vec a -> Vec a+cons = undefined++snoc :: Vec a -> a -> Vec a+snoc = undefined
+ test/data/hiding/EVec.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TypeOperators #-}+module EVec (Vec, (++), type (@@@), cons, fromList, snoc) where++import Prelude hiding ((++))++data Vec a++(++) :: Vec a -> Vec a -> Vec a+(++) = undefined++data (@@@) a b++fromList :: [a] -> Vec a+fromList = undefined++cons :: a -> Vec a -> Vec a+cons = undefined++snoc :: Vec a -> a -> Vec a+snoc = undefined
+ test/data/hiding/FVec.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE DuplicateRecordFields #-}++module FVec (RecA(..), RecB(..)) where++data Vec a++newtype RecA a = RecA { fromList :: [a] -> Vec a }++newtype RecB a = RecB { fromList :: [a] -> Vec a }
+ test/data/hiding/HideFunction.expected.append.E.hs view
@@ -0,0 +1,12 @@+module HideFunction where++import AVec (fromList)+import BVec (fromList)+import CVec hiding ((++), cons)+import DVec hiding ((++), cons, snoc)+import EVec as E+import Prelude hiding ((++))++theFun = fromList++theOp = (++)
+ test/data/hiding/HideFunction.expected.append.Prelude.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec (fromList)+import BVec (fromList)+import CVec hiding ((++), cons)+import DVec hiding ((++), cons, snoc)+import EVec as E hiding ((++))++theFun = fromList++theOp = (++)
+ test/data/hiding/HideFunction.expected.fromList.A.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec (fromList)+import BVec ( (++))+import CVec hiding (fromList, cons)+import DVec hiding (fromList, cons, snoc)+import EVec as E hiding (fromList)++theFun = fromList++theOp = (++)
+ test/data/hiding/HideFunction.expected.fromList.B.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec ()+import BVec (fromList, (++))+import CVec hiding (fromList, cons)+import DVec hiding (fromList, cons, snoc)+import EVec as E hiding (fromList)++theFun = fromList++theOp = (++)
+ test/data/hiding/HideFunction.expected.qualified.append.Prelude.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec (fromList)+import BVec (fromList, (++))+import CVec hiding (cons)+import DVec hiding (cons, snoc)+import EVec as E++theFun = fromList++theOp = (Prelude.++)
+ test/data/hiding/HideFunction.expected.qualified.fromList.E.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec (fromList)+import BVec (fromList, (++))+import CVec hiding (cons)+import DVec hiding (cons, snoc)+import EVec as E++theFun = E.fromList++theOp = (++)
+ test/data/hiding/HideFunction.hs view
@@ -0,0 +1,11 @@+module HideFunction where++import AVec (fromList)+import BVec (fromList, (++))+import CVec hiding (cons)+import DVec hiding (cons, snoc)+import EVec as E++theFun = fromList++theOp = (++)
+ test/data/hiding/HidePreludeIndented.expected.hs view
@@ -0,0 +1,5 @@+module HidePreludeIndented where++   import AVec+   import Prelude hiding ((++))+   op = (++)
+ test/data/hiding/HidePreludeIndented.hs view
@@ -0,0 +1,4 @@+module HidePreludeIndented where++   import AVec+   op = (++)
+ test/data/hiding/HideQualifyDuplicateRecordFields.expected.hs view
@@ -0,0 +1,10 @@+module HideQualifyDuplicateRecordFields where++import AVec+import BVec+import CVec+import DVec+import EVec+import FVec++theFun = AVec.fromList
+ test/data/hiding/HideQualifyDuplicateRecordFields.hs view
@@ -0,0 +1,10 @@+module HideQualifyDuplicateRecordFields where++import AVec+import BVec+import CVec+import DVec+import EVec+import FVec++theFun = fromList
+ test/data/hiding/HideQualifyDuplicateRecordFieldsSelf.hs view
@@ -0,0 +1,5 @@+module HideQualifyDuplicateRecordFieldsSelf where++import FVec++x = fromList
+ test/data/hiding/HideQualifyInfix.expected.hs view
@@ -0,0 +1,5 @@+module HideQualifyInfix where++import AVec++infixed xs ys = xs Prelude.++ ys
+ test/data/hiding/HideQualifyInfix.hs view
@@ -0,0 +1,5 @@+module HideQualifyInfix where++import AVec++infixed xs ys = xs ++ ys
+ test/data/hiding/HideQualifySectionLeft.expected.hs view
@@ -0,0 +1,5 @@+module HideQualifySectionLeft where++import AVec++sectLeft xs = (Prelude.++ xs)
+ test/data/hiding/HideQualifySectionLeft.hs view
@@ -0,0 +1,5 @@+module HideQualifySectionLeft where++import AVec++sectLeft xs = (++ xs)
+ test/data/hiding/HideQualifySectionRight.expected.hs view
@@ -0,0 +1,5 @@+module HideQualifySectionRight where++import AVec++sectLeft xs = (xs Prelude.++)
+ test/data/hiding/HideQualifySectionRight.hs view
@@ -0,0 +1,5 @@+module HideQualifySectionRight where++import AVec++sectLeft xs = (xs ++)
+ test/data/hiding/HideType.expected.A.hs view
@@ -0,0 +1,9 @@+module HideType where++import AVec (Vec, fromList)+import BVec (fromList, (++))+import CVec hiding (Vec, cons)+import DVec hiding (Vec, cons, snoc)+import EVec as E hiding (Vec)++type TheType = Vec
+ test/data/hiding/HideType.expected.E.hs view
@@ -0,0 +1,9 @@+module HideType where++import AVec ( fromList)+import BVec (fromList, (++))+import CVec hiding (Vec, cons)+import DVec hiding (Vec, cons, snoc)+import EVec as E++type TheType = Vec
+ test/data/hiding/HideType.hs view
@@ -0,0 +1,9 @@+module HideType where++import AVec (Vec, fromList)+import BVec (fromList, (++))+import CVec hiding (cons)+import DVec hiding (cons, snoc)+import EVec as E++type TheType = Vec
+ test/data/hiding/hie.yaml view
@@ -0,0 +1,10 @@+cradle:+    direct:+        arguments:+        - -Wall+        - HideFunction.hs+        - AVec.hs+        - BVec.hs+        - CVec.hs+        - DVec.hs+        - EVec.hs
+ test/data/hover/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["Foo", "Bar", "GotoHover"]}}
+ test/data/ignore-fatal/IgnoreFatal.hs view
@@ -0,0 +1,8 @@+-- "missing signature" is declared a fatal warning in the cabal file,+-- but is ignored in this module.++{-# OPTIONS_GHC -fno-warn-missing-signatures #-}++module IgnoreFatal where++a = 'a'
+ test/data/ignore-fatal/cabal.project view
@@ -0,0 +1,1 @@+packages: ignore-fatal.cabal
+ test/data/ignore-fatal/hie.yaml view
@@ -0,0 +1,4 @@+cradle:+  cabal:+    - path: "."+      component: "lib:ignore-fatal"
+ test/data/ignore-fatal/ignore-fatal.cabal view
@@ -0,0 +1,10 @@+name: ignore-fatal+version: 1.0.0+build-type: Simple+cabal-version: >= 1.2++library+  build-depends: base+  exposed-modules: IgnoreFatal+  hs-source-dirs: .+  ghc-options: -Werror=missing-signatures
+ test/data/plugin-knownnat/KnownNat.hs view
@@ -0,0 +1,10 @@+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}+{-# LANGUAGE DataKinds, ScopedTypeVariables, TypeOperators #-}+module KnownNat where+import Data.Proxy+import GHC.TypeLits++f :: forall n. KnownNat n => Proxy n -> Integer+f _ = natVal (Proxy :: Proxy n) + natVal (Proxy :: Proxy (n+2))+foo :: Int -> Int -> Int+foo a _b = a + c
+ test/data/plugin-knownnat/cabal.project view
@@ -0,0 +1,1 @@+packages: .
+ test/data/plugin-knownnat/plugin.cabal view
@@ -0,0 +1,9 @@+cabal-version:  1.18+name: plugin+version: 1.0.0+build-type: Simple++library+  build-depends: base, ghc-typelits-knownnat+  exposed-modules: KnownNat+  hs-source-dirs: .
+ test/data/plugin-recorddot/RecordDot.hs view
@@ -0,0 +1,6 @@+{-# LANGUAGE DuplicateRecordFields, TypeApplications, TypeFamilies, UndecidableInstances, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}+module RecordDot (Company(..), display) where+data Company = Company {name :: String}+display :: Company -> String+display c = c.name
+ test/data/plugin-recorddot/cabal.project view
@@ -0,0 +1,1 @@+packages: .
+ test/data/plugin-recorddot/plugin.cabal view
@@ -0,0 +1,9 @@+cabal-version:  1.18+name: plugin+version: 1.0.0+build-type: Simple++library+  build-depends: base, record-dot-preprocessor, record-hasfield+  exposed-modules: RecordDot+  hs-source-dirs: .
+ test/data/recomp/A.hs view
@@ -0,0 +1,6 @@+module A(x) where++import B++x :: Int+x = y
+ test/data/recomp/B.hs view
@@ -0,0 +1,4 @@+module B(y) where++y :: Int+y = undefined
+ test/data/recomp/P.hs view
@@ -0,0 +1,5 @@+module P() where+import A+import B++bar = x :: Int
+ test/data/recomp/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["-Wmissing-signatures","B", "A", "P"]}}
+ test/data/references/Main.hs view
@@ -0,0 +1,14 @@+module Main where++import References++main :: IO ()+main = return ()++++a = 2 :: Int+b = a + 1++acc :: Account+acc = Savings
+ test/data/references/OtherModule.hs view
@@ -0,0 +1,9 @@+module OtherModule (symbolDefinedInOtherModule, symbolDefinedInOtherOtherModule) where++import OtherOtherModule++symbolDefinedInOtherModule = 1++symbolLocalToOtherModule = 2++someFxn x = x + symbolLocalToOtherModule
+ test/data/references/OtherOtherModule.hs view
@@ -0,0 +1,3 @@+module OtherOtherModule where++symbolDefinedInOtherOtherModule = "asdf"
+ test/data/references/References.hs view
@@ -0,0 +1,25 @@+module References where++import OtherModule++foo = bar++bar = let x = bar 42 in const "hello"++baz = do+  x <- bar 23+  return $ bar 14++data Account =+  Checking+  | Savings++bobsAccount = Checking++bobHasChecking = case bobsAccount of+                     Checking -> True+                     Savings -> False++x = symbolDefinedInOtherModule++y = symbolDefinedInOtherOtherModule
+ test/data/references/hie.yaml view
@@ -0,0 +1,1 @@+cradle: {direct: {arguments: ["Main","OtherModule","OtherOtherModule","References"]}}
+ test/data/rootUri/dirA/Foo.hs view
@@ -0,0 +1,3 @@+module Foo () where++foo = ()
+ test/data/rootUri/dirA/foo.cabal view
@@ -0,0 +1,9 @@+name: foo+version: 1.0.0+build-type: Simple+cabal-version: >= 1.2++library+  build-depends: base+  exposed-modules: Foo+  hs-source-dirs: .
+ test/data/rootUri/dirB/Foo.hs view
@@ -0,0 +1,3 @@+module Foo () where++foo = ()
+ test/data/rootUri/dirB/foo.cabal view
@@ -0,0 +1,9 @@+name: foo+version: 1.0.0+build-type: Simple+cabal-version: >= 1.2++library+  build-depends: base+  exposed-modules: Foo+  hs-source-dirs: .
test/exe/Main.hs view
@@ -36,6 +36,7 @@                                                            positionResultToMaybe,                                                            toCurrent) import           Development.IDE.Core.Shake               (Q (..))+import qualified Development.IDE.Main                     as IDE import           Development.IDE.GHC.Util import           Development.IDE.Plugin.Completions.Types (extendImportCommandId) import           Development.IDE.Plugin.TypeLenses        (typeLensCommandId)@@ -75,7 +76,7 @@ import           System.Info.Extra                        (isWindows) import           System.Process.Extra                     (CreateProcess (cwd),                                                            proc,-                                                           readCreateProcessWithExitCode)+                                                           readCreateProcessWithExitCode, createPipe) import           Test.QuickCheck -- import Test.QuickCheck.Instances () import           Control.Lens                             ((^.))@@ -92,6 +93,15 @@ import           Test.Tasty.HUnit import           Test.Tasty.Ingredients.Rerun import           Test.Tasty.QuickCheck+import           Data.IORef+import           Ide.PluginUtils (pluginDescToIdePlugins)+import           Control.Concurrent.Async+import           Ide.Types+import           Data.String                              (IsString(fromString))+import qualified Language.LSP.Types                       as LSP+import           Data.IORef.Extra                         (atomicModifyIORef_)+import qualified Development.IDE.Plugin.HLS.GhcIde        as Ghcide+import           Text.Regex.TDFA                          ((=~))  waitForProgressBegin :: Session () waitForProgressBegin = skipManyTill anyMessage $ satisfyMaybe $ \case@@ -179,7 +189,7 @@     , chk "NO doc link"               _documentLinkProvider Nothing     , chk "NO color"                         _colorProvider (Just $ InL False)     , chk "NO folding range"          _foldingRangeProvider (Just $ InL False)-    , che "   execute command"      _executeCommandProvider [blockCommandId, extendImportCommandId, typeLensCommandId]+    , che "   execute command"      _executeCommandProvider [extendImportCommandId, typeLensCommandId, blockCommandId]     , chk "   workspace"                         _workspace (Just $ WorkspaceServerCapabilities (Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )}))     , chk "NO experimental"                   _experimental Nothing     ] where@@ -846,7 +856,7 @@             ]       doc <- createDoc "Testing.hs" "haskell" content       _ <- waitForDiagnostics-      actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))+      actionsOrCommands <- getAllCodeActions doc       let [addSignature] = [action | InR action@CodeAction { _title = actionTitle } <- actionsOrCommands                                    , "Use type signature" `T.isInfixOf` actionTitle                            ]@@ -866,7 +876,7 @@             ]       doc <- createDoc "Testing.hs" "haskell" content       _ <- waitForDiagnostics-      actionsOrCommands <- getCodeActions doc (Range (Position 2 1) (Position 2 10))+      actionsOrCommands <- getAllCodeActions doc       let [addSignature] = [action | InR action@CodeAction { _title = actionTitle } <- actionsOrCommands                                     , "Use type signature" `T.isInfixOf` actionTitle                               ]@@ -889,7 +899,7 @@             ]       doc <- createDoc "Testing.hs" "haskell" content       _ <- waitForDiagnostics-      actionsOrCommands <- getCodeActions doc (Range (Position 4 1) (Position 4 10))+      actionsOrCommands <- getAllCodeActions doc       let [addSignature] = [action | InR action@CodeAction { _title = actionTitle } <- actionsOrCommands                                     , "Use type signature" `T.isInfixOf` actionTitle                               ]@@ -906,6 +916,7 @@       liftIO $ expectedContentAfterAction @=? contentAfterAction   ] +{-# HLINT ignore "Use nubOrd" #-} removeImportTests :: TestTree removeImportTests = testGroup "remove import actions"   [ testSession "redundant" $ do@@ -1111,7 +1122,7 @@       doc <- createDoc "ModuleC.hs" "haskell" content       _ <- waitForDiagnostics       [_, _, _, _, InR action@CodeAction { _title = actionTitle }]-          <- getCodeActions doc (Range (Position 2 0) (Position 2 5))+          <- nub <$> getAllCodeActions doc       liftIO $ "Remove all redundant imports" @=? actionTitle       executeCodeAction action       contentAfterAction <- documentContents doc@@ -1149,7 +1160,7 @@                     , "import ModuleA as A (stuffB)"                     , "main = print (stuffA, stuffB)"                     ])-            (Range (Position 3 17) (Position 3 18))+            (Range (Position 2 17) (Position 2 18))             ["Add stuffA to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1169,7 +1180,7 @@                     , "import ModuleA as A (stuffB)"                     , "main = print (stuffB .* stuffB)"                     ])-            (Range (Position 3 17) (Position 3 18))+            (Range (Position 2 17) (Position 2 18))             ["Add (.*) to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1206,7 +1217,7 @@                     , "b :: A"                     , "b = Constructor"                     ])-            (Range (Position 2 5) (Position 2 5))+            (Range (Position 3 5) (Position 3 5))             ["Add A(Constructor) to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1225,7 +1236,7 @@                     , "b :: A"                     , "b = Constructor"                     ])-            (Range (Position 2 5) (Position 2 5))+            (Range (Position 3 5) (Position 3 5))             ["Add A(Constructor) to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1245,7 +1256,7 @@                     , "b :: A"                     , "b = ConstructorFoo"                     ])-            (Range (Position 2 5) (Position 2 5))+            (Range (Position 3 5) (Position 3 5))             ["Add A(ConstructorFoo) to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1266,7 +1277,7 @@                     , "import qualified ModuleA as A (stuffB)"                     , "main = print (A.stuffA, A.stuffB)"                     ])-            (Range (Position 3 17) (Position 3 18))+            (Range (Position 2 17) (Position 2 18))             ["Add stuffA to the import list of ModuleA"]             (T.unlines                     [ "module ModuleB where"@@ -1370,13 +1381,33 @@                     , "x = Refl"                     ])             (Range (Position 3 17) (Position 3 18))-            ["Add (:~:)(Refl) to the import list of Data.Type.Equality"]+            ["Add type (:~:)(Refl) to the import list of Data.Type.Equality"]             (T.unlines                     [ "module ModuleA where"                     , "import Data.Type.Equality ((:~:) (Refl))"                     , "x :: (:~:) [] []"                     , "x = Refl"                     ])+        , expectFailBecause "importing pattern synonyms is unsupported"+          $ testSession "extend import list with pattern synonym" $ template+            [("ModuleA.hs", T.unlines+                    [ "{-# LANGUAGE PatternSynonyms #-}"+                      , "module ModuleA where"+                      , "pattern Some x = Just x"+                    ])+            ]+            ("ModuleB.hs", T.unlines+                    [ "module ModuleB where"+                    , "import A ()"+                    , "k (Some x) = x"+                    ])+            (Range (Position 2 3) (Position 2 7))+            ["Add pattern Some to the import list of A"]+            (T.unlines+                    [ "module ModuleB where"+                    , "import A (pattern Some)"+                    , "k (Some x) = x"+                    ])         ]       where         codeActionTitle CodeAction{_title=x} = x@@ -1434,38 +1465,17 @@     [ testGroup         "new"         [ testSession "via parent" $-            template-              [ "module A where",-                ""-              ]-              (Range (Position 5 2) (Position 5 8))-              "Import Data.Semigroup with Semigroup(stimes)"-              [ "module A where",-                "",-                "import Data.Semigroup (Semigroup(stimes))"-              ],+            template'+            "import Data.Semigroup (Semigroup(stimes))"+            (Range (Position 5 2) (Position 5 8)),           testSession "top level" $-            template-              [ "module A where",-                ""-              ]-              (Range (Position 5 2) (Position 5 8))-              "Import Data.Semigroup with stimes"-              [ "module A where",-                "",-                "import Data.Semigroup (stimes)"-              ],+            template'+              "import Data.Semigroup (stimes)"+              (Range (Position 5 2) (Position 5 8)),           testSession "all" $-            template-              [ "module A where",-                ""-              ]+            template'+              "import Data.Semigroup"               (Range (Position 5 2) (Position 5 8))-              "Import Data.Semigroup"-              [ "module A where",-                "",-                "import Data.Semigroup"-              ]         ],       testGroup         "extend"@@ -1513,6 +1523,7 @@       executeCodeAction $ fromJust $ find (\CodeAction {_title} -> _title == executeTitle) actions'       content <- documentContents doc       liftIO $ T.unlines (expectedContent <> decls) @=? content+    template' executeTitle range = let c = ["module A where", ""] in template c range executeTitle $ c <> [executeTitle]  suggestImportTests :: TestTree suggestImportTests = testGroup "suggest import actions"@@ -1559,6 +1570,7 @@     , test True []          "f = (&) [] id"               []                "import Data.Function ((&))"     , test True []          "f = (.|.)"                   []                "import Data.Bits (Bits((.|.)))"     , test True []          "f = (.|.)"                   []                "import Data.Bits ((.|.))"+    , test True []          "f :: a ~~ b"                 []                "import Data.Type.Equality (type (~~))"     , test True       ["qualified Data.Text as T"       ]                     "f = T.putStrLn"              []                "import qualified Data.Text.IO as T"@@ -1573,6 +1585,7 @@       , "qualified Data.Data as T"       ]                     "f = T.putStrLn"              []                "import qualified Data.Text.IO as T"     ]+    , expectFailBecause "importing pattern synonyms is unsupported" $ test True [] "k (Some x) = x" [] "import B (pattern Some)"   ]   where     test = test' False@@ -1580,8 +1593,9 @@     test' waitForCheckProject wanted imps def other newImp = testSessionWithExtraFiles "hover" (T.unpack def) $ \dir -> do       let before = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ def : other           after  = T.unlines $ "module A where" : ["import " <> x | x <- imps] ++ [newImp] ++ def : other-          cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -, A, Bar, Foo]}}"+          cradle = "cradle: {direct: {arguments: [-hide-all-packages, -package, base, -package, text, -package-env, -, A, Bar, Foo, B]}}"       liftIO $ writeFileUTF8 (dir </> "hie.yaml") cradle+      liftIO $ writeFileUTF8 (dir </> "B.hs") $ unlines ["{-# LANGUAGE PatternSynonyms #-}", "module B where", "pattern Some x = Just x"]       doc <- createDoc "Test.hs" "haskell" before       waitForProgressDone       _diags <- waitForDiagnostics@@ -1660,6 +1674,23 @@             compareHideFunctionTo [(8,9),(10,8)]                 "Replace with qualified: E.fromList"                 "HideFunction.expected.qualified.fromList.E.hs"+        , testCase "Hide DuplicateRecordFields" $+            compareTwo+                "HideQualifyDuplicateRecordFields.hs" [(9, 9)]+                "Replace with qualified: AVec.fromList"+                "HideQualifyDuplicateRecordFields.expected.hs"+        , testCase "Duplicate record fields should not be imported" $ do+          withTarget ("HideQualifyDuplicateRecordFields" <.> ".hs") [(9, 9)] $+            \_ actions -> do+              liftIO $+                assertBool "Hidings should not be presented while DuplicateRecordFields exists" $+                  all not [ actionTitle =~ T.pack "Use ([A-Za-z][A-Za-z0-9]*) for fromList, hiding other imports"+                      | InR CodeAction { _title = actionTitle } <- actions]+          withTarget ("HideQualifyDuplicateRecordFieldsSelf" <.> ".hs") [(4, 4)] $+            \_ actions -> do+              liftIO $+                assertBool "ambiguity from DuplicateRecordFields should not be imported" $+                  null actions         ]     , testGroup "(++)"         [ testCase "Prelude, parensed" $@@ -1695,16 +1726,14 @@             contentAfterAction <- documentContents doc             liftIO $ T.replace "\r\n" "\n" expected @=? contentAfterAction     compareHideFunctionTo = compareTwo "HideFunction.hs"-    auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs"]+    auxFiles = ["AVec.hs", "BVec.hs", "CVec.hs", "DVec.hs", "EVec.hs", "FVec.hs"]     withTarget file locs k = withTempDir $ \dir -> runInDir dir $ do         liftIO $ mapM_ (\fp -> copyFile (hidingDir </> fp) $ dir </> fp)             $ file : auxFiles         doc <- openDoc file "haskell"         waitForProgressDone         void $ expectDiagnostics [(file, [(DsError, loc, "Ambiguous occurrence") | loc <- locs])]-        contents <- documentContents doc-        let range = Range (Position 0 0) (Position (length $ T.lines contents) 0)-        actions <- getCodeActions doc range+        actions <- getAllCodeActions doc         k doc actions     withHideFunction = withTarget ("HideFunction" <.> "hs") @@ -1911,7 +1940,7 @@       _ <- waitForDiagnostics       InR action@CodeAction { _title = actionTitle } : _                   <- sortOn (\(InR CodeAction{_title=x}) -> x) <$>-                     getCodeActions docB (R 1 0 1 50)+                     getCodeActions docB (R 0 0 0 50)       liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool"       executeCodeAction action       contentAfterAction <- documentContents docB@@ -1935,7 +1964,7 @@       _ <- waitForDiagnostics       InR action@CodeAction { _title = actionTitle } : _                   <- sortOn (\(InR CodeAction{_title=x}) -> x) <$>-                     getCodeActions docB (R 1 0 1 50)+                     getCodeActions docB (R 0 0 0 50)       liftIO $ actionTitle @?= "Define select :: [Bool] -> Bool"       executeCodeAction action       contentAfterAction <- documentContents docB@@ -2083,15 +2112,15 @@       docId <- createDoc "A.hs" "haskell" source       expectDiagnostics [ ("A.hs", [(DsWarning, pos, "not used")]) ] -      (action, title) <- extractCodeAction docId "Delete"+      (action, title) <- extractCodeAction docId "Delete" pos        liftIO $ title @?= expectedTitle       executeCodeAction action       contentAfterAction <- documentContents docId       liftIO $ contentAfterAction @?= expectedResult -    extractCodeAction docId actionPrefix = do-      [action@CodeAction { _title = actionTitle }]  <- findCodeActionsByPrefix docId (R 0 0 0 0) [actionPrefix]+    extractCodeAction docId actionPrefix (l, c) = do+      [action@CodeAction { _title = actionTitle }]  <- findCodeActionsByPrefix docId (R l c l c) [actionPrefix]       return (action, actionTitle)  addTypeAnnotationsToLiteralsTest :: TestTree@@ -2216,15 +2245,16 @@       docId <- createDoc "A.hs" "haskell" source       expectDiagnostics [ ("A.hs", diag) ] -      (action, title) <- extractCodeAction docId "Add type annotation"+      let cursors = map snd3 diag+      (action, title) <- extractCodeAction docId "Add type annotation" (minimum cursors) (maximum cursors)        liftIO $ title @?= expectedTitle       executeCodeAction action       contentAfterAction <- documentContents docId       liftIO $ contentAfterAction @?= expectedResult -    extractCodeAction docId actionPrefix = do-      [action@CodeAction { _title = actionTitle }]  <- findCodeActionsByPrefix docId (R 0 0 0 0) [actionPrefix]+    extractCodeAction docId actionPrefix (l,c) (l', c')= do+      [action@CodeAction { _title = actionTitle }]  <- findCodeActionsByPrefix docId (R l c l' c') [actionPrefix]       return (action, actionTitle)  @@ -2270,7 +2300,7 @@             ]       doc <- createDoc "Testing.hs" "haskell" content       _ <- waitForDiagnostics-      actionsOrCommands <- getCodeActions doc (Range (Position 2 8) (Position 2 16))+      actionsOrCommands <- getCodeActions doc (Range (Position 1 8) (Position 1 16))       let [changeToMap] = [action | InR action@CodeAction{ _title = actionTitle } <- actionsOrCommands, ("Data." <> modname) `T.isInfixOf` actionTitle ]       executeCodeAction changeToMap       contentAfterAction <- documentContents doc@@ -2400,7 +2430,7 @@   check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do     doc <- createDoc "Testing.hs" "haskell" originalCode     _ <- waitForDiagnostics-    actionsOrCommands <- getCodeActions doc (Range (Position 6 0) (Position 6 68))+    actionsOrCommands <- getAllCodeActions doc     chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands     executeCodeAction chosenAction     modifiedCode <- documentContents doc@@ -2552,7 +2582,7 @@ checkCodeAction testName actionTitle originalCode expectedCode = testSession testName $ do   doc <- createDoc "Testing.hs" "haskell" originalCode   _ <- waitForDiagnostics-  actionsOrCommands <- getCodeActions doc (Range (Position 6 0) (Position 6 maxBound))+  actionsOrCommands <- getAllCodeActions doc   chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands   executeCodeAction chosenAction   modifiedCode <- documentContents doc@@ -2635,7 +2665,7 @@   check actionTitle originalCode expectedCode = testSession (T.unpack actionTitle) $ do     doc <- createDoc "Testing.hs" "haskell" originalCode     _ <- waitForDiagnostics-    actionsOrCommands <- getCodeActions doc (Range (Position 4 0) (Position 4 maxBound))+    actionsOrCommands <- getAllCodeActions doc     chosenAction <- liftIO $ pickActionWithTitle actionTitle actionsOrCommands     executeCodeAction chosenAction     modifiedCode <- documentContents doc@@ -2645,7 +2675,7 @@   checkPeculiarFormatting title code = testSession title $ do     doc <- createDoc "Testing.hs" "haskell" code     _ <- waitForDiagnostics-    actionsOrCommands <- getCodeActions doc (Range (Position 4 0) (Position 4 maxBound))+    actionsOrCommands <- getAllCodeActions doc     liftIO $ assertBool "Found some actions" (null actionsOrCommands)    in testGroup "remove redundant function constraints"@@ -2789,7 +2819,7 @@               , "  ) where"               , "foo = id"               , "bar = foo"])-        (R 4 0 4 3)+        (R 5 0 5 3)         "Export ‘bar’"         (Just $ T.unlines               [ "{-# OPTIONS_GHC -Wunused-top-binds #-}"@@ -3788,7 +3818,7 @@         "class"         ["bar :: Xx", "xxx = ()", "-- | haddock", "class Xxx a"]         (Position 0 9)-        [("Xxx", CiClass, "Xxx", False, True, Nothing)],+        [("Xxx", CiInterface, "Xxx", False, True, Nothing)],     completionTest         "records"         ["data Person = Person { _personName:: String, _personAge:: Int}", "bar = Person { _pers }" ]@@ -3882,7 +3912,7 @@       "type"       ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Bo", "f = True"]       (Position 2 7)-      [ ("Bounded", CiClass, "Bounded ${1:*}", True, True, Nothing),+      [ ("Bounded", CiInterface, "Bounded ${1:*}", True, True, Nothing),         ("Bool", CiStruct, "Bool ", True, True, Nothing)       ],     completionTest@@ -3998,7 +4028,7 @@             ["module A where", "import Data.Type.Equality ()", "f = Ref"]             (Position 2 8)             "Refl"-            ["module A where", "import Data.Type.Equality ((:~:) (Refl))", "f = Ref"]+            ["module A where", "import Data.Type.Equality (type (:~:) (Refl))", "f = Ref"]         ]       , testGroup "Record completion"         [ completionCommandTest@@ -4183,7 +4213,7 @@     let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]     docId   <- createDoc "A.hs" "haskell" source     symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkClass (R 1 0 1 13)]+    liftIO $ symbols @?= Left [docSymbolD "A" "type family" SkFunction (R 1 0 1 13)]   , testSessionWait "type family instance " $ do     let source = T.unlines           [ "{-# language TypeFamilies #-}"@@ -4193,14 +4223,14 @@     docId   <- createDoc "A.hs" "haskell" source     symbols <- getDocumentSymbols docId     liftIO $ symbols @?= Left-      [ docSymbolD "A a"   "type family" SkClass     (R 1 0 1 15)+      [ docSymbolD "A a"   "type family" SkFunction     (R 1 0 1 15)       , docSymbol "A ()" SkInterface (R 2 0 2 23)       ]   , testSessionWait "data family" $ do     let source = T.unlines ["{-# language TypeFamilies #-}", "data family A"]     docId   <- createDoc "A.hs" "haskell" source     symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkClass (R 1 0 1 11)]+    liftIO $ symbols @?= Left [docSymbolD "A" "data family" SkFunction (R 1 0 1 11)]   , testSessionWait "data family instance " $ do     let source = T.unlines           [ "{-# language TypeFamilies #-}"@@ -4210,7 +4240,7 @@     docId   <- createDoc "A.hs" "haskell" source     symbols <- getDocumentSymbols docId     liftIO $ symbols @?= Left-      [ docSymbolD "A a"   "data family" SkClass     (R 1 0 1 11)+      [ docSymbolD "A a"   "data family" SkFunction     (R 1 0 1 11)       , docSymbol "A ()" SkInterface (R 2 0 2 25)       ]   , testSessionWait "constant" $ do@@ -4306,26 +4336,28 @@   ]  where   docSymbol name kind loc =-    DocumentSymbol name Nothing kind Nothing loc loc Nothing+    DocumentSymbol name Nothing kind Nothing Nothing loc loc Nothing   docSymbol' name kind loc selectionLoc =-    DocumentSymbol name Nothing kind Nothing loc selectionLoc Nothing+    DocumentSymbol name Nothing kind Nothing Nothing loc selectionLoc Nothing   docSymbolD name detail kind loc =-    DocumentSymbol name (Just detail) kind Nothing loc loc Nothing+    DocumentSymbol name (Just detail) kind Nothing Nothing loc loc Nothing   docSymbolWithChildren name kind loc cc =-    DocumentSymbol name Nothing kind Nothing loc loc (Just $ List cc)+    DocumentSymbol name Nothing kind Nothing Nothing loc loc (Just $ List cc)   docSymbolWithChildren' name kind loc selectionLoc cc =-    DocumentSymbol name Nothing kind Nothing loc selectionLoc (Just $ List cc)+    DocumentSymbol name Nothing kind Nothing Nothing loc selectionLoc (Just $ List cc)   moduleSymbol name loc cc = DocumentSymbol name                                             Nothing                                             SkFile                                             Nothing+                                            Nothing                                             (R 0 0 maxBound 0)                                             loc                                             (Just $ List cc)   classSymbol name loc cc = DocumentSymbol name                                            (Just "class")-                                           SkClass+                                           SkInterface                                            Nothing+                                           Nothing                                            loc                                            loc                                            (Just $ List cc)@@ -4881,7 +4913,7 @@               , "foo = id"               ]             void waitForDiagnostics-            actions <- getCodeActions doc (Range (Position 0 0) (Position 0 0))+            actions <- getCodeActions doc (Range (Position 1 0) (Position 1 0))             liftIO $ [ _title | InR CodeAction{_title} <- actions] @=?               [ "add signature: foo :: a -> a" ]     ]@@ -5163,21 +5195,26 @@   -- HIE calls getXgdDirectory which assumes that HOME is set.   -- Only sets HOME if it wasn't already set.   setEnv "HOME" "/homeless-shelter" False-  let lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities $ Just True }+  conf <- getConfigFromEnv+  runSessionWithConfig conf cmd lspTestCaps projDir s++getConfigFromEnv :: IO SessionConfig+getConfigFromEnv = do   logColor <- fromMaybe True <$> checkEnv "LSP_TEST_LOG_COLOR"   timeoutOverride <- fmap read <$> getEnv "LSP_TIMEOUT"-  let conf = defaultConfig{messageTimeout = fromMaybe (messageTimeout defaultConfig) timeoutOverride}-            -- uncomment this or set LSP_TEST_LOG_STDERR=1 to see all logging-            --   { logStdErr = True }-            --   uncomment this or set LSP_TEST_LOG_MESSAGES=1 to see all messages-            --   { logMessages = True }-  runSessionWithConfig conf{logColor} cmd lspTestCaps projDir s+  return defaultConfig+    { messageTimeout = fromMaybe (messageTimeout defaultConfig) timeoutOverride+    , logColor+    }   where     checkEnv :: String -> IO (Maybe Bool)     checkEnv s = fmap convertVal <$> getEnv s     convertVal "0" = False     convertVal _   = True +lspTestCaps :: ClientCapabilities+lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities $ Just True }+ openTestDataDoc :: FilePath -> Session TextDocumentIdentifier openTestDataDoc path = do   source <- liftIO $ readFileUtf8 $ "test/data" </> path@@ -5245,7 +5282,38 @@          let expected = "1:2-3:4"          assertBool (unwords ["expected to find range", expected, "in diagnostic", shown]) $              expected `isInfixOf` shown+     , testCase "notification handlers run sequentially" $ do+        orderRef <- newIORef []+        let plugins = pluginDescToIdePlugins $+                [ (defaultPluginDescriptor $ fromString $ show i)+                    { pluginNotificationHandlers = mconcat+                        [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $ \_ _ _ ->+                            liftIO $ atomicModifyIORef_ orderRef (i:)+                        ]+                    }+                    | i <- [(1::Int)..20]+                ] ++ Ghcide.descriptors++        testIde def{IDE.argsHlsPlugins = plugins} $ do+            _ <- createDoc "haskell" "A.hs" "module A where"+            waitForProgressDone+            actualOrder <- liftIO $ readIORef orderRef++            liftIO $ actualOrder @?= reverse [(1::Int)..20]      ]++testIde :: IDE.Arguments -> Session () -> IO ()+testIde arguments session = do+    config <- getConfigFromEnv+    (hInRead, hInWrite) <- createPipe+    (hOutRead, hOutWrite) <- createPipe+    let server = IDE.defaultMain arguments+            { IDE.argsHandleIn = pure hInRead+            , IDE.argsHandleOut = pure hOutWrite+            }++    withAsync server $ \_ ->+        runSessionWithHandles hInWrite hOutRead config lspTestCaps "." session  positionMappingTests :: TestTree positionMappingTests =