packages feed

ghcide 2.0.0.1 → 2.1.0.0

raw patch · 88 files changed

+5761/−4833 lines, 88 filesdep +row-typesdep ~hls-graphdep ~hls-plugin-apidep ~lspPVP ok

version bump matches the API change (PVP)

Dependencies added: row-types

Dependency ranges changed: hls-graph, hls-plugin-api, lsp, lsp-test, lsp-types

API changes (from Hackage documentation)

- Development.IDE: DsError :: DiagnosticSeverity
- Development.IDE: DsHint :: DiagnosticSeverity
- Development.IDE: DsInfo :: DiagnosticSeverity
- Development.IDE: DsWarning :: DiagnosticSeverity
- Development.IDE: GetDependencyInformation :: GetDependencyInformation
- Development.IDE: List :: [a] -> List a
- Development.IDE: [_character] :: Position -> UInt
- Development.IDE: [_end] :: Range -> Position
- Development.IDE: [_line] :: Position -> UInt
- Development.IDE: [_range] :: Location -> Range
- Development.IDE: [_start] :: Range -> Position
- Development.IDE: [_uri] :: Location -> Uri
- Development.IDE: [getUri] :: Uri -> Text
- Development.IDE: data GetDependencyInformation
- Development.IDE: newtype List a
- 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: withDefaultRecorder :: MonadUnliftIO m => Maybe FilePath -> Maybe [LoggingColumn] -> (Recorder (WithPriority (Doc d)) -> m a) -> m a
- 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.RuleTypes: GetDependencyInformation :: GetDependencyInformation
- Development.IDE.Core.RuleTypes: data GetDependencyInformation
- Development.IDE.Core.RuleTypes: instance Control.DeepSeq.NFData Development.IDE.Core.RuleTypes.GetDependencyInformation
- Development.IDE.Core.RuleTypes: instance Data.Hashable.Class.Hashable Development.IDE.Core.RuleTypes.GetDependencyInformation
- Development.IDE.Core.RuleTypes: instance GHC.Classes.Eq Development.IDE.Core.RuleTypes.GetDependencyInformation
- Development.IDE.Core.RuleTypes: instance GHC.Generics.Generic Development.IDE.Core.RuleTypes.GetDependencyInformation
- Development.IDE.Core.RuleTypes: instance GHC.Show.Show Development.IDE.Core.RuleTypes.GetDependencyInformation
- Development.IDE.Core.Rules: [$sel:checkForImportCycles:GhcSessionDepsConfig] :: GhcSessionDepsConfig -> Bool
- Development.IDE.Core.Rules: [$sel:checkForImportCycles:RulesConfig] :: RulesConfig -> Bool
- Development.IDE.Core.Rules: getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])
- Development.IDE.Core.Rules: getDependencyInformationRule :: Recorder (WithPriority Log) -> Rules ()
- Development.IDE.Core.UseStale: instance Development.IDE.Core.UseStale.MapAge Language.LSP.Types.Location.Range
- Development.IDE.Import.DependencyInformation: ShowableModuleName :: ModuleName -> ShowableModuleName
- Development.IDE.Import.DependencyInformation: [depModuleNames] :: DependencyInformation -> !FilePathIdMap ShowableModuleName
- Development.IDE.Import.DependencyInformation: [rawBootMap] :: RawDependencyInformation -> !BootIdMap
- Development.IDE.Import.DependencyInformation: [rawModuleNameMap] :: RawDependencyInformation -> !FilePathIdMap ShowableModuleName
- Development.IDE.Import.DependencyInformation: [showableModuleName] :: ShowableModuleName -> ModuleName
- Development.IDE.Import.DependencyInformation: instance Control.DeepSeq.NFData Development.IDE.Import.DependencyInformation.ShowableModuleName
- Development.IDE.Import.DependencyInformation: instance GHC.Show.Show Development.IDE.Import.DependencyInformation.ShowableModuleName
- Development.IDE.Import.DependencyInformation: newtype ShowableModuleName
- Development.IDE.LSP.Server: [$sel:unServerM:ServerM] :: ServerM c a -> ReaderT (ReactorChan, IdeState) (LspM c) a
- Development.IDE.Types.Diagnostics: DsError :: DiagnosticSeverity
- Development.IDE.Types.Diagnostics: DsHint :: DiagnosticSeverity
- Development.IDE.Types.Diagnostics: DsInfo :: DiagnosticSeverity
- Development.IDE.Types.Diagnostics: DsWarning :: DiagnosticSeverity
- Development.IDE.Types.Diagnostics: List :: [a] -> List a
- Development.IDE.Types.Diagnostics: newtype List a
- Development.IDE.Types.Location: [_character] :: Position -> UInt
- Development.IDE.Types.Location: [_end] :: Range -> Position
- Development.IDE.Types.Location: [_line] :: Position -> UInt
- Development.IDE.Types.Location: [_range] :: Location -> Range
- Development.IDE.Types.Location: [_start] :: Range -> Position
- Development.IDE.Types.Location: [_uri] :: Location -> Uri
- Development.IDE.Types.Location: [getUri] :: Uri -> Text
- Development.IDE.Types.Logger: DataColumn :: LoggingColumn
- Development.IDE.Types.Logger: Debug :: Priority
- Development.IDE.Types.Logger: Error :: Priority
- Development.IDE.Types.Logger: Info :: Priority
- Development.IDE.Types.Logger: Logger :: (Priority -> Text -> IO ()) -> Logger
- Development.IDE.Types.Logger: PriorityColumn :: LoggingColumn
- Development.IDE.Types.Logger: Recorder :: (forall m. MonadIO m => msg -> m ()) -> Recorder msg
- Development.IDE.Types.Logger: SourceLocColumn :: LoggingColumn
- Development.IDE.Types.Logger: ThreadIdColumn :: LoggingColumn
- Development.IDE.Types.Logger: TimeColumn :: LoggingColumn
- Development.IDE.Types.Logger: Warning :: Priority
- Development.IDE.Types.Logger: WithPriority :: Priority -> CallStack -> a -> WithPriority a
- Development.IDE.Types.Logger: [callStack_] :: WithPriority a -> CallStack
- Development.IDE.Types.Logger: [logPriority] :: Logger -> Priority -> Text -> IO ()
- Development.IDE.Types.Logger: [logger_] :: Recorder msg -> forall m. MonadIO m => msg -> m ()
- Development.IDE.Types.Logger: [payload] :: WithPriority a -> a
- Development.IDE.Types.Logger: [priority] :: WithPriority a -> Priority
- Development.IDE.Types.Logger: cfilter :: (a -> Bool) -> Recorder a -> Recorder a
- Development.IDE.Types.Logger: cmap :: (a -> b) -> Recorder b -> Recorder a
- Development.IDE.Types.Logger: cmapIO :: (a -> IO b) -> Recorder b -> Recorder a
- Development.IDE.Types.Logger: cmapWithPrio :: (a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
- Development.IDE.Types.Logger: data LoggingColumn
- Development.IDE.Types.Logger: data Priority
- Development.IDE.Types.Logger: data WithPriority a
- Development.IDE.Types.Logger: instance Data.Functor.Contravariant.Contravariant Development.IDE.Types.Logger.Recorder
- Development.IDE.Types.Logger: instance GHC.Base.Functor Development.IDE.Types.Logger.WithPriority
- Development.IDE.Types.Logger: instance GHC.Base.Monoid (Development.IDE.Types.Logger.Recorder msg)
- Development.IDE.Types.Logger: instance GHC.Base.Monoid Development.IDE.Types.Logger.Logger
- Development.IDE.Types.Logger: instance GHC.Base.Semigroup (Development.IDE.Types.Logger.Recorder msg)
- Development.IDE.Types.Logger: instance GHC.Base.Semigroup Development.IDE.Types.Logger.Logger
- Development.IDE.Types.Logger: instance GHC.Classes.Eq Development.IDE.Types.Logger.Priority
- Development.IDE.Types.Logger: instance GHC.Classes.Ord Development.IDE.Types.Logger.Priority
- Development.IDE.Types.Logger: instance GHC.Enum.Bounded Development.IDE.Types.Logger.Priority
- Development.IDE.Types.Logger: instance GHC.Enum.Enum Development.IDE.Types.Logger.Priority
- Development.IDE.Types.Logger: instance GHC.Show.Show Development.IDE.Types.Logger.Priority
- Development.IDE.Types.Logger: logDebug :: Logger -> Text -> IO ()
- Development.IDE.Types.Logger: logError :: Logger -> Text -> IO ()
- Development.IDE.Types.Logger: logInfo :: Logger -> Text -> IO ()
- Development.IDE.Types.Logger: logWarning :: Logger -> Text -> IO ()
- Development.IDE.Types.Logger: logWith :: (HasCallStack, MonadIO m) => Recorder (WithPriority msg) -> Priority -> msg -> m ()
- Development.IDE.Types.Logger: lspClientLogRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)
- Development.IDE.Types.Logger: lspClientMessageRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)
- Development.IDE.Types.Logger: makeDefaultHandleRecorder :: MonadIO m => Maybe [LoggingColumn] -> Lock -> Handle -> m (Recorder (WithPriority (Doc a)))
- Development.IDE.Types.Logger: makeDefaultStderrRecorder :: MonadIO m => Maybe [LoggingColumn] -> m (Recorder (WithPriority (Doc a)))
- Development.IDE.Types.Logger: newtype Logger
- Development.IDE.Types.Logger: newtype Recorder msg
- Development.IDE.Types.Logger: noLogging :: Logger
- Development.IDE.Types.Logger: renderStrict :: SimpleDocStream ann -> Text
- Development.IDE.Types.Logger: toCologActionWithPrio :: (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg)
- Development.IDE.Types.Logger: withBacklog :: (v -> Recorder a) -> IO (Recorder a, v -> IO ())
- Development.IDE.Types.Logger: withDefaultRecorder :: MonadUnliftIO m => Maybe FilePath -> Maybe [LoggingColumn] -> (Recorder (WithPriority (Doc d)) -> m a) -> m a
+ Development.IDE: DiagnosticSeverity_Error :: DiagnosticSeverity
+ Development.IDE: DiagnosticSeverity_Hint :: DiagnosticSeverity
+ Development.IDE: DiagnosticSeverity_Information :: DiagnosticSeverity
+ Development.IDE: DiagnosticSeverity_Warning :: DiagnosticSeverity
+ Development.IDE: [$sel:_character:Position] :: Position -> UInt
+ Development.IDE: [$sel:_codeDescription:Diagnostic] :: Diagnostic -> Maybe CodeDescription
+ Development.IDE: [$sel:_data_:Diagnostic] :: Diagnostic -> Maybe Value
+ Development.IDE: [$sel:_end:Range] :: Range -> Position
+ Development.IDE: [$sel:_line:Position] :: Position -> UInt
+ Development.IDE: [$sel:_range:Location] :: Location -> Range
+ Development.IDE: [$sel:_start:Range] :: Range -> Position
+ Development.IDE: [$sel:_uri:Location] :: Location -> Uri
+ Development.IDE: [$sel:getUri:Uri] :: Uri -> Text
+ Development.IDE: withFileRecorder :: MonadUnliftIO m => FilePath -> Maybe [LoggingColumn] -> (Either IOException (Recorder (WithPriority (Doc d))) -> m a) -> m a
+ Development.IDE.Core.Compile: shareUsages :: ModIface -> ModIface
+ Development.IDE.Core.Compile: sourceParser :: Text
+ Development.IDE.Core.Compile: sourceTypecheck :: Text
+ Development.IDE.Core.PluginUtils: fromCurrentPositionE :: Monad m => PositionMapping -> Position -> ExceptT PluginError m Position
+ Development.IDE.Core.PluginUtils: fromCurrentPositionMT :: Monad m => PositionMapping -> Position -> MaybeT m Position
+ Development.IDE.Core.PluginUtils: fromCurrentRangeE :: Monad m => PositionMapping -> Range -> ExceptT PluginError m Range
+ Development.IDE.Core.PluginUtils: fromCurrentRangeMT :: Monad m => PositionMapping -> Range -> MaybeT m Range
+ Development.IDE.Core.PluginUtils: runActionE :: MonadIO m => String -> IdeState -> ExceptT e Action a -> ExceptT e m a
+ Development.IDE.Core.PluginUtils: runActionMT :: MonadIO m => String -> IdeState -> MaybeT Action a -> MaybeT m a
+ Development.IDE.Core.PluginUtils: runIdeActionE :: MonadIO m => String -> ShakeExtras -> ExceptT e IdeAction a -> ExceptT e m a
+ Development.IDE.Core.PluginUtils: runIdeActionMT :: MonadIO m => String -> ShakeExtras -> MaybeT IdeAction a -> MaybeT m a
+ Development.IDE.Core.PluginUtils: toCurrentPositionE :: Monad m => PositionMapping -> Position -> ExceptT PluginError m Position
+ Development.IDE.Core.PluginUtils: toCurrentPositionMT :: Monad m => PositionMapping -> Position -> MaybeT m Position
+ Development.IDE.Core.PluginUtils: toCurrentRangeE :: Monad m => PositionMapping -> Range -> ExceptT PluginError m Range
+ Development.IDE.Core.PluginUtils: toCurrentRangeMT :: Monad m => PositionMapping -> Range -> MaybeT m Range
+ Development.IDE.Core.PluginUtils: uriToFilePathE :: Monad m => Uri -> ExceptT PluginError m FilePath
+ Development.IDE.Core.PluginUtils: uriToFilePathMT :: Monad m => Uri -> MaybeT m FilePath
+ Development.IDE.Core.PluginUtils: useE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError Action v
+ Development.IDE.Core.PluginUtils: useMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v
+ Development.IDE.Core.PluginUtils: useWithStaleE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError Action (v, PositionMapping)
+ Development.IDE.Core.PluginUtils: useWithStaleFastE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError IdeAction (v, PositionMapping)
+ Development.IDE.Core.PluginUtils: useWithStaleFastMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)
+ Development.IDE.Core.PluginUtils: useWithStaleMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action (v, PositionMapping)
+ Development.IDE.Core.PluginUtils: usesE :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> ExceptT PluginError Action (f v)
+ Development.IDE.Core.PluginUtils: usesMT :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> MaybeT Action (f v)
+ Development.IDE.Core.Rules: [$sel:fullModuleGraph:GhcSessionDepsConfig] :: GhcSessionDepsConfig -> Bool
+ Development.IDE.Core.Rules: [$sel:fullModuleGraph:RulesConfig] :: RulesConfig -> Bool
+ Development.IDE.Core.UseStale: instance Development.IDE.Core.UseStale.MapAge Language.LSP.Protocol.Internal.Types.Range.Range
+ Development.IDE.GHC.Compat.Core: readIface :: Module -> FilePath -> TcRnIf gbl lcl (MaybeErr SDoc ModIface)
+ Development.IDE.GHC.Compat.Units: findImportedModule :: HscEnv -> ModuleName -> IO (Maybe Module)
+ Development.IDE.GHC.Orphans: instance Control.DeepSeq.NFData GHC.Unit.Module.Graph.ModuleGraph
+ Development.IDE.GHC.Orphans: instance GHC.Show.Show GHC.Unit.Module.Graph.ModuleGraph
+ Development.IDE.Import.DependencyInformation: PathIdMap :: !FilePathIdMap ArtifactsLocation -> !HashMap NormalizedFilePath FilePathId -> !Int -> PathIdMap
+ Development.IDE.Import.DependencyInformation: ShowableModule :: Module -> ShowableModule
+ Development.IDE.Import.DependencyInformation: ShowableModuleEnv :: ModuleEnv a -> ShowableModuleEnv a
+ Development.IDE.Import.DependencyInformation: [depModuleFiles] :: DependencyInformation -> !ShowableModuleEnv FilePathId
+ Development.IDE.Import.DependencyInformation: [depModuleGraph] :: DependencyInformation -> !ModuleGraph
+ Development.IDE.Import.DependencyInformation: [depModules] :: DependencyInformation -> !FilePathIdMap ShowableModule
+ Development.IDE.Import.DependencyInformation: [idToPathMap] :: PathIdMap -> !FilePathIdMap ArtifactsLocation
+ Development.IDE.Import.DependencyInformation: [nextFreshId] :: PathIdMap -> !Int
+ Development.IDE.Import.DependencyInformation: [pathToIdMap] :: PathIdMap -> !HashMap NormalizedFilePath FilePathId
+ Development.IDE.Import.DependencyInformation: [rawModuleMap] :: RawDependencyInformation -> !FilePathIdMap ShowableModule
+ Development.IDE.Import.DependencyInformation: [showableModuleEnv] :: ShowableModuleEnv a -> ModuleEnv a
+ Development.IDE.Import.DependencyInformation: [showableModule] :: ShowableModule -> Module
+ Development.IDE.Import.DependencyInformation: instance Control.DeepSeq.NFData Development.IDE.Import.DependencyInformation.ShowableModule
+ Development.IDE.Import.DependencyInformation: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Development.IDE.Import.DependencyInformation.ShowableModuleEnv a)
+ Development.IDE.Import.DependencyInformation: instance GHC.Show.Show Development.IDE.Import.DependencyInformation.ShowableModule
+ Development.IDE.Import.DependencyInformation: instance GHC.Show.Show a => GHC.Show.Show (Development.IDE.Import.DependencyInformation.ShowableModuleEnv a)
+ Development.IDE.Import.DependencyInformation: lookupModuleFile :: Module -> DependencyInformation -> Maybe NormalizedFilePath
+ Development.IDE.Import.DependencyInformation: newtype ShowableModule
+ Development.IDE.Import.DependencyInformation: newtype ShowableModuleEnv a
+ Development.IDE.LSP.Server: [unServerM] :: ServerM c a -> ReaderT (ReactorChan, IdeState) (LspM c) a
+ Development.IDE.Plugin.HLS: ExceptionInPlugin :: PluginId -> Some SMethod -> SomeException -> Log
+ Development.IDE.Plugin.HLS: LogResponseError :: PluginId -> ResponseError -> Log
+ Development.IDE.Plugin.HLS: toResponseError :: (PluginId, PluginError) -> ResponseError
+ Development.IDE.Types.Diagnostics: DiagnosticSeverity_Error :: DiagnosticSeverity
+ Development.IDE.Types.Diagnostics: DiagnosticSeverity_Hint :: DiagnosticSeverity
+ Development.IDE.Types.Diagnostics: DiagnosticSeverity_Information :: DiagnosticSeverity
+ Development.IDE.Types.Diagnostics: DiagnosticSeverity_Warning :: DiagnosticSeverity
+ Development.IDE.Types.Diagnostics: [$sel:_codeDescription:Diagnostic] :: Diagnostic -> Maybe CodeDescription
+ Development.IDE.Types.Diagnostics: [$sel:_data_:Diagnostic] :: Diagnostic -> Maybe Value
+ Development.IDE.Types.HscEnvEq: updateHscEnvEq :: HscEnvEq -> HscEnv -> IO HscEnvEq
+ Development.IDE.Types.Location: [$sel:_character:Position] :: Position -> UInt
+ Development.IDE.Types.Location: [$sel:_end:Range] :: Range -> Position
+ Development.IDE.Types.Location: [$sel:_line:Position] :: Position -> UInt
+ Development.IDE.Types.Location: [$sel:_range:Location] :: Location -> Range
+ Development.IDE.Types.Location: [$sel:_start:Range] :: Range -> Position
+ Development.IDE.Types.Location: [$sel:_uri:Location] :: Location -> Uri
+ Development.IDE.Types.Location: [$sel:getUri:Uri] :: Uri -> Text
+ Text.Fuzzy.Parallel: defChunkSize :: Int
+ Text.Fuzzy.Parallel: defMaxResults :: Int
+ Text.Fuzzy.Parallel: filter' :: Int -> Int -> Text -> [t] -> (t -> Text) -> (Text -> Text -> Maybe Int) -> [Scored t]
+ Text.Fuzzy.Parallel: simpleFilter' :: Int -> Int -> Text -> [Text] -> (Text -> Text -> Maybe Int) -> [Scored Text]
- Development.IDE: Diagnostic :: Range -> Maybe DiagnosticSeverity -> Maybe (Int32 |? Text) -> Maybe DiagnosticSource -> Text -> Maybe (List DiagnosticTag) -> Maybe (List DiagnosticRelatedInformation) -> Diagnostic
+ Development.IDE: Diagnostic :: Range -> Maybe DiagnosticSeverity -> Maybe (Int32 |? Text) -> Maybe CodeDescription -> Maybe Text -> Text -> Maybe [DiagnosticTag] -> Maybe [DiagnosticRelatedInformation] -> Maybe Value -> Diagnostic
- Development.IDE: Recorder :: (forall m. MonadIO m => msg -> m ()) -> Recorder msg
+ Development.IDE: Recorder :: (forall (m :: Type -> Type). MonadIO m => msg -> m ()) -> Recorder msg
- Development.IDE: [$sel:_relatedInformation:Diagnostic] :: Diagnostic -> Maybe (List DiagnosticRelatedInformation)
+ Development.IDE: [$sel:_relatedInformation:Diagnostic] :: Diagnostic -> Maybe [DiagnosticRelatedInformation]
- Development.IDE: [$sel:_source:Diagnostic] :: Diagnostic -> Maybe DiagnosticSource
+ Development.IDE: [$sel:_source:Diagnostic] :: Diagnostic -> Maybe Text
- Development.IDE: [$sel:_tags:Diagnostic] :: Diagnostic -> Maybe (List DiagnosticTag)
+ Development.IDE: [$sel:_tags:Diagnostic] :: Diagnostic -> Maybe [DiagnosticTag]
- Development.IDE: [logger_] :: Recorder msg -> forall m. MonadIO m => msg -> m ()
+ Development.IDE: [logger_] :: Recorder msg -> forall (m :: Type -> Type). MonadIO m => msg -> m ()
- Development.IDE: ideErrorWithSource :: Maybe DiagnosticSource -> Maybe DiagnosticSeverity -> a -> Text -> (a, ShowDiagnostic, Diagnostic)
+ Development.IDE: ideErrorWithSource :: Maybe Text -> Maybe DiagnosticSeverity -> a -> Text -> (a, ShowDiagnostic, Diagnostic)
- Development.IDE: toCologActionWithPrio :: (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg)
+ Development.IDE: toCologActionWithPrio :: forall (m :: Type -> Type) msg. (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg)
- Development.IDE.Core.Compile: mergeEnvs :: HscEnv -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv
+ Development.IDE.Core.Compile: mergeEnvs :: HscEnv -> ModuleGraph -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv
- Development.IDE.Core.Service: updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> STM ()
+ Development.IDE.Core.Service: updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> [TextDocumentContentChangeEvent] -> STM ()
- Development.IDE.Core.Shake: addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v, PositionDelta, TextDocumentVersion))) -> Rules ()
+ Development.IDE.Core.Shake: addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v, PositionDelta, Maybe Int32))) -> Rules ()
- Development.IDE.Core.Shake: updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> STM ()
+ Development.IDE.Core.Shake: updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> [TextDocumentContentChangeEvent] -> STM ()
- Development.IDE.Import.DependencyInformation: DependencyInformation :: !FilePathIdMap (NonEmpty NodeError) -> !FilePathIdMap ShowableModuleName -> !FilePathIdMap FilePathIdSet -> !IntMap IntSet -> !PathIdMap -> !BootIdMap -> DependencyInformation
+ Development.IDE.Import.DependencyInformation: DependencyInformation :: !FilePathIdMap (NonEmpty NodeError) -> !FilePathIdMap ShowableModule -> !FilePathIdMap FilePathIdSet -> !IntMap IntSet -> !PathIdMap -> !BootIdMap -> !ShowableModuleEnv FilePathId -> !ModuleGraph -> DependencyInformation
- Development.IDE.Import.DependencyInformation: RawDependencyInformation :: !FilePathIdMap (Either ModuleParseError ModuleImports) -> !PathIdMap -> !BootIdMap -> !FilePathIdMap ShowableModuleName -> RawDependencyInformation
+ Development.IDE.Import.DependencyInformation: RawDependencyInformation :: !FilePathIdMap (Either ModuleParseError ModuleImports) -> !PathIdMap -> !FilePathIdMap ShowableModule -> RawDependencyInformation
- Development.IDE.Import.DependencyInformation: pathToId :: PathIdMap -> NormalizedFilePath -> FilePathId
+ Development.IDE.Import.DependencyInformation: pathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId
- Development.IDE.Import.DependencyInformation: processDependencyInformation :: RawDependencyInformation -> DependencyInformation
+ Development.IDE.Import.DependencyInformation: processDependencyInformation :: RawDependencyInformation -> BootIdMap -> ModuleGraph -> DependencyInformation
- Development.IDE.LSP.HoverDefinition: documentHighlight :: IdeState -> TextDocumentPositionParams -> LspM c (Either ResponseError (List DocumentHighlight))
+ Development.IDE.LSP.HoverDefinition: documentHighlight :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LspM c) ([DocumentHighlight] |? Null)
- Development.IDE.LSP.HoverDefinition: gotoDefinition :: IdeState -> TextDocumentPositionParams -> LspM c (Either ResponseError (ResponseResult TextDocumentDefinition))
+ Development.IDE.LSP.HoverDefinition: gotoDefinition :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LspM c) (MessageResult Method_TextDocumentDefinition)
- Development.IDE.LSP.HoverDefinition: gotoTypeDefinition :: IdeState -> TextDocumentPositionParams -> LspM c (Either ResponseError (ResponseResult TextDocumentTypeDefinition))
+ Development.IDE.LSP.HoverDefinition: gotoTypeDefinition :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LspM c) (MessageResult Method_TextDocumentTypeDefinition)
- Development.IDE.LSP.HoverDefinition: hover :: IdeState -> TextDocumentPositionParams -> LspM c (Either ResponseError (Maybe Hover))
+ Development.IDE.LSP.HoverDefinition: hover :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LspM c) (Hover |? Null)
- Development.IDE.LSP.HoverDefinition: references :: IdeState -> ReferenceParams -> LspM c (Either ResponseError (List Location))
+ Development.IDE.LSP.HoverDefinition: references :: PluginMethodHandler IdeState 'Method_TextDocumentReferences
- Development.IDE.LSP.HoverDefinition: wsSymbols :: IdeState -> WorkspaceSymbolParams -> LspM c (Either ResponseError (List SymbolInformation))
+ Development.IDE.LSP.HoverDefinition: wsSymbols :: PluginMethodHandler IdeState 'Method_WorkspaceSymbol
- Development.IDE.LSP.LanguageServer: runLanguageServer :: forall config a m. Show config => Recorder (WithPriority Log) -> Options -> Handle -> Handle -> config -> (config -> Value -> Either Text config) -> (MVar () -> IO (LanguageContextEnv config -> RequestMessage Initialize -> IO (Either ResponseError (LanguageContextEnv config, a)), Handlers (m config), (LanguageContextEnv config, a) -> m config <~> IO)) -> IO ()
+ Development.IDE.LSP.LanguageServer: runLanguageServer :: forall config a m. Show config => Recorder (WithPriority Log) -> Options -> Handle -> Handle -> config -> (config -> Value -> Either Text config) -> (MVar () -> IO (LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either ResponseError (LanguageContextEnv config, a)), Handlers (m config), (LanguageContextEnv config, a) -> m config <~> IO)) -> IO ()
- Development.IDE.LSP.LanguageServer: setupLSP :: forall config err. Recorder (WithPriority Log) -> (FilePath -> IO FilePath) -> Handlers (ServerM config) -> (LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState) -> MVar () -> IO (LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LanguageContextEnv config, IdeState)), Handlers (ServerM config), (LanguageContextEnv config, IdeState) -> ServerM config <~> IO)
+ Development.IDE.LSP.LanguageServer: setupLSP :: forall config err. Recorder (WithPriority Log) -> (FilePath -> IO FilePath) -> Handlers (ServerM config) -> (LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState) -> MVar () -> IO (LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either err (LanguageContextEnv config, IdeState)), Handlers (ServerM config), (LanguageContextEnv config, IdeState) -> ServerM config <~> IO)
- Development.IDE.LSP.Outline: moduleOutline :: IdeState -> DocumentSymbolParams -> LspM c (Either ResponseError (List DocumentSymbol |? List SymbolInformation))
+ Development.IDE.LSP.Outline: moduleOutline :: PluginMethodHandler IdeState 'Method_TextDocumentDocumentSymbol
- Development.IDE.LSP.Server: notificationHandler :: forall (m :: Method FromClient Notification) c. HasTracing (MessageParams m) => SMethod m -> (IdeState -> VFS -> MessageParams m -> LspM c ()) -> Handlers (ServerM c)
+ Development.IDE.LSP.Server: notificationHandler :: forall (m :: Method ClientToServer Notification) c. HasTracing (MessageParams m) => SMethod m -> (IdeState -> VFS -> MessageParams m -> LspM c ()) -> Handlers (ServerM c)
- Development.IDE.LSP.Server: requestHandler :: forall (m :: Method FromClient Request) c. HasTracing (MessageParams m) => SMethod m -> (IdeState -> MessageParams m -> LspM c (Either ResponseError (ResponseResult m))) -> Handlers (ServerM c)
+ Development.IDE.LSP.Server: requestHandler :: forall (m :: Method ClientToServer Request) c. HasTracing (MessageParams m) => SMethod m -> (IdeState -> MessageParams m -> LspM c (Either ResponseError (MessageResult m))) -> Handlers (ServerM c)
- Development.IDE.Plugin.HLS: LogPluginError :: PluginId -> ResponseError -> Log
+ Development.IDE.Plugin.HLS: LogPluginError :: PluginId -> PluginError -> Log
- Development.IDE.Spans.AtPoint: atPoint :: IdeOptions -> HieAstResult -> DocAndKindMap -> HscEnv -> Position -> Maybe (Maybe Range, [Text])
+ Development.IDE.Spans.AtPoint: atPoint :: IdeOptions -> HieAstResult -> DocAndKindMap -> HscEnv -> Position -> IO (Maybe (Maybe Range, [Text]))
- Development.IDE.Spans.Pragmas: getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo
+ Development.IDE.Spans.Pragmas: getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError m NextPragmaInfo
- Development.IDE.Types.Diagnostics: Diagnostic :: Range -> Maybe DiagnosticSeverity -> Maybe (Int32 |? Text) -> Maybe DiagnosticSource -> Text -> Maybe (List DiagnosticTag) -> Maybe (List DiagnosticRelatedInformation) -> Diagnostic
+ Development.IDE.Types.Diagnostics: Diagnostic :: Range -> Maybe DiagnosticSeverity -> Maybe (Int32 |? Text) -> Maybe CodeDescription -> Maybe Text -> Text -> Maybe [DiagnosticTag] -> Maybe [DiagnosticRelatedInformation] -> Maybe Value -> Diagnostic
- Development.IDE.Types.Diagnostics: [$sel:_relatedInformation:Diagnostic] :: Diagnostic -> Maybe (List DiagnosticRelatedInformation)
+ Development.IDE.Types.Diagnostics: [$sel:_relatedInformation:Diagnostic] :: Diagnostic -> Maybe [DiagnosticRelatedInformation]
- Development.IDE.Types.Diagnostics: [$sel:_source:Diagnostic] :: Diagnostic -> Maybe DiagnosticSource
+ Development.IDE.Types.Diagnostics: [$sel:_source:Diagnostic] :: Diagnostic -> Maybe Text
- Development.IDE.Types.Diagnostics: [$sel:_tags:Diagnostic] :: Diagnostic -> Maybe (List DiagnosticTag)
+ Development.IDE.Types.Diagnostics: [$sel:_tags:Diagnostic] :: Diagnostic -> Maybe [DiagnosticTag]
- Development.IDE.Types.Diagnostics: ideErrorWithSource :: Maybe DiagnosticSource -> Maybe DiagnosticSeverity -> a -> Text -> (a, ShowDiagnostic, Diagnostic)
+ Development.IDE.Types.Diagnostics: ideErrorWithSource :: Maybe Text -> Maybe DiagnosticSeverity -> a -> Text -> (a, ShowDiagnostic, Diagnostic)

Files

exe/Main.hs view
@@ -22,7 +22,9 @@ import qualified Development.IDE.Monitoring.EKG           as EKG import qualified Development.IDE.Monitoring.OpenTelemetry as OpenTelemetry import qualified Development.IDE.Plugin.HLS.GhcIde        as GhcIde-import           Development.IDE.Types.Logger             (Logger (Logger),+import           Development.IDE.Types.Options+import           GHC.Stack                                (emptyCallStack)+import           Ide.Logger                               (Logger (Logger),                                                            LoggingColumn (DataColumn, PriorityColumn),                                                            Pretty (pretty),                                                            Priority (Debug, Error, Info),@@ -33,16 +35,14 @@                                                            layoutPretty,                                                            makeDefaultStderrRecorder,                                                            renderStrict)-import qualified Development.IDE.Types.Logger             as Logger-import           Development.IDE.Types.Options-import           GHC.Stack                                (emptyCallStack)+import qualified Ide.Logger                               as Logger import           Ide.Plugin.Config                        (Config (checkParents, checkProject)) import           Ide.PluginUtils                          (pluginDescToIdePlugins) import           Ide.Types                                (PluginDescriptor (pluginNotificationHandlers),                                                            defaultPluginDescriptor,                                                            mkPluginNotificationHandler)+import           Language.LSP.Protocol.Message            as LSP import           Language.LSP.Server                      as LSP-import           Language.LSP.Types                       as LSP import           Paths_ghcide                             (version) import qualified System.Directory.Extra                   as IO import           System.Environment                       (getExecutablePath)@@ -101,7 +101,7 @@     -- This plugin just installs a handler for the `initialized` notification, which then     -- picks up the LSP environment and feeds it to our recorders     let lspRecorderPlugin = (defaultPluginDescriptor "LSPRecorderCallback")-          { pluginNotificationHandlers = mkPluginNotificationHandler LSP.SInitialized $ \_ _ _ _ -> do+          { pluginNotificationHandlers = mkPluginNotificationHandler LSP.SMethod_Initialized $ \_ _ _ _ -> do               env <- LSP.getLspEnv               liftIO $ (cb1 <> cb2) env           }
ghcide.cabal view
@@ -2,7 +2,7 @@ build-type:         Simple category:           Development name:               ghcide-version:            2.0.0.1+version:            2.1.0.0 license:            Apache-2.0 license-file:       LICENSE author:             Digital Asset and Ghcide contributors@@ -65,12 +65,12 @@         haddock-library >= 1.8 && < 1.12,         hashable,         hie-compat ^>= 0.3.0.0,-        hls-plugin-api == 2.0.0.1,+        hls-plugin-api == 2.1.0.0,         lens,         list-t,         hiedb == 0.4.3.*,-        lsp-types ^>= 1.6.0.0,-        lsp ^>= 1.6.0.0 ,+        lsp-types ^>= 2.0.1.0,+        lsp ^>= 2.1.0.0 ,         mtl,         optparse-applicative,         parallel,@@ -78,9 +78,10 @@         prettyprinter >= 1.7,         random,         regex-tdfa >= 1.3.1.0,+        row-types,         text-rope,         safe-exceptions,-        hls-graph == 2.0.0.1,+        hls-graph == 2.1.0.0,         sorted-list,         sqlite-simple,         stm,@@ -153,6 +154,7 @@         Development.IDE.Core.FileUtils         Development.IDE.Core.IdeConfiguration         Development.IDE.Core.OfInterest+        Development.IDE.Core.PluginUtils         Development.IDE.Core.PositionMapping         Development.IDE.Core.Preprocessor         Development.IDE.Core.ProgressReporting@@ -196,7 +198,6 @@         Development.IDE.Types.HscEnvEq         Development.IDE.Types.KnownTargets         Development.IDE.Types.Location-        Development.IDE.Types.Logger         Development.IDE.Types.Monitoring         Development.IDE.Monitoring.OpenTelemetry         Development.IDE.Types.Options@@ -345,7 +346,8 @@         hls-plugin-api,         lens,         list-t,-        lsp-test ^>= 0.14,+        lsp-test ^>= 0.15.0.1,+        mtl,         monoid-subclasses,         network-uri,         QuickCheck,@@ -363,6 +365,7 @@         text,         text-rope,         unordered-containers,+        row-types     if impl(ghc < 9.2)       build-depends:           record-dot-preprocessor,@@ -381,6 +384,39 @@         HieDbRetry         Development.IDE.Test         Development.IDE.Test.Diagnostic+        ExceptionTests+        -- Tests that have been pulled out of the main file+        BootTests+        CodeLensTests+        CompletionTests+        CPPTests+        CradleTests+        DependentFileTest+        DiagnosticTests+        FindDefinitionAndHoverTests+        HaddockTests+        HighlightTests+        IfaceTests+        InitializeResponseTests+        LogType+        NonLspCommandLine+        OutlineTests+        PluginParsedResultTests+        PluginSimpleTests+        PositionMappingTests+        PreprocessorTests+        RootUriTests+        SafeTests+        SymlinkTests+        TestUtils+        THTests+        UnitTests+        WatchedFileTests+        AsyncTests+        ClientSettingsTests+        ReferenceTests+        GarbageCollectionTests+        OpenCloseTest     default-extensions:         BangPatterns         DeriveFunctor
session-loader/Development/IDE/Session.hs view
@@ -43,6 +43,7 @@ import           Data.List import qualified Data.Map.Strict                      as Map import           Data.Maybe+import           Data.Proxy import qualified Data.Text                            as T import           Data.Time.Clock import           Data.Version@@ -64,13 +65,6 @@ import           Development.IDE.Types.HscEnvEq       (HscEnvEq, newHscEnvEq,                                                        newHscEnvEqPreserveImportPaths) import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger         (Pretty (pretty),-                                                       Priority (Debug, Error, Info, Warning),-                                                       Recorder, WithPriority,-                                                       cmapWithPrio, logWith,-                                                       nest,-                                                       toCologActionWithPrio,-                                                       vcat, viaShow, (<+>)) import           Development.IDE.Types.Options import           GHC.Check import qualified HIE.Bios                             as HieBios@@ -78,8 +72,15 @@ import           HIE.Bios.Types                       hiding (Log) import qualified HIE.Bios.Types                       as HieBios import           Hie.Implicit.Cradle                  (loadImplicitHieCradle)+import           Ide.Logger                           (Pretty (pretty),+                                                       Priority (Debug, Error, Info, Warning),+                                                       Recorder, WithPriority,+                                                       cmapWithPrio, logWith,+                                                       nest,+                                                       toCologActionWithPrio,+                                                       vcat, viaShow, (<+>))+import           Language.LSP.Protocol.Message import           Language.LSP.Server-import           Language.LSP.Types import           System.Directory import qualified System.Directory.Extra               as IO import           System.FilePath@@ -483,7 +484,25 @@         packageSetup (hieYaml, cfp, opts, libDir) = do           -- Parse DynFlags for the newly discovered component           hscEnv <- emptyHscEnv ideNc libDir-          (df, targets) <- evalGhcEnv hscEnv $ setOptions opts (hsc_dflags hscEnv)+          (df', targets) <- evalGhcEnv hscEnv $ setOptions opts (hsc_dflags hscEnv)+          let df =+#if MIN_VERSION_ghc(9,3,0)+                case unitIdString (homeUnitId_ df') of+                     -- cabal uses main for the unit id of all executable packages+                     -- This makes multi-component sessions confused about what+                     -- options to use for that component.+                     -- Solution: hash the options and use that as part of the unit id+                     -- This works because there won't be any dependencies on the+                     -- executable unit.+                     "main" ->+                       let hash = B.unpack $ B16.encode $ H.finalize $ H.updates H.init (map B.pack $ componentOptions opts)+                           hashed_uid = Compat.toUnitId (Compat.stringToUnit ("main-"++hash))+                       in setHomeUnitId_ hashed_uid df'+                     _ -> df'+#else+                df'+#endif+           let deps = componentDependencies opts ++ maybeToList hieYaml           dep_info <- getDependencyInfo deps           -- Now lookup to see whether we are combining with an existing HscEnv@@ -498,6 +517,7 @@                   -- We will modify the unitId and DynFlags used for                   -- compilation but these are the true source of                   -- information.+                                     new_deps = RawComponentInfo (homeUnitId_ df) df targets cfp opts dep_info                                 : maybe [] snd oldDeps                   -- Get all the unit-ids for things in this component@@ -632,7 +652,7 @@            lfp <- flip makeRelative cfp <$> getCurrentDirectory             when optTesting $ mRunLspT lspEnv $-            sendNotification (SCustomMethod "ghcide/cradle/loaded") (toJSON cfp)+            sendNotification (SMethod_CustomMethod (Proxy @"ghcide/cradle/loaded")) (toJSON cfp)             -- Display a user friendly progress message here: They probably don't know what a cradle is            let progMsg = "Setting up " <> T.pack (takeBaseName (cradleRootDir cradle))@@ -906,7 +926,7 @@  renderCradleError :: NormalizedFilePath -> CradleError -> FileDiagnostic renderCradleError nfp (CradleError _ _ec t) =-  ideErrorWithSource (Just "cradle") (Just DsError) nfp (T.unlines (map T.pack t))+  ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) nfp (T.unlines (map T.pack t))  -- See Note [Multi Cradle Dependency Info] type DependencyInfo = Map.Map FilePath (Maybe UTCTime)@@ -1120,4 +1140,4 @@  renderPackageSetupException :: FilePath -> PackageSetupException -> (NormalizedFilePath, ShowDiagnostic, Diagnostic) renderPackageSetupException fp e =-    ideErrorWithSource (Just "cradle") (Just DsError) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException e)+    ideErrorWithSource (Just "cradle") (Just DiagnosticSeverity_Error) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException e)
src/Development/IDE.hs view
@@ -8,9 +8,7 @@  import           Development.IDE.Core.Actions          as X (getAtPoint,                                                              getDefinition,-                                                             getTypeDefinition,-                                                             useE, useNoFileE,-                                                             usesE)+                                                             getTypeDefinition) import           Development.IDE.Core.FileExists       as X (getFileExists) import           Development.IDE.Core.FileStore        as X (getFileContents) import           Development.IDE.Core.IdeConfiguration as X (IdeConfiguration (..),@@ -55,4 +53,4 @@                                                              hscEnv,                                                              hscEnvWithImportPaths) import           Development.IDE.Types.Location        as X-import           Development.IDE.Types.Logger          as X+import           Ide.Logger                            as X
src/Development/IDE/Core/Actions.hs view
@@ -6,13 +6,11 @@ , getTypeDefinition , highlightAtPoint , refsAtPoint-, useE-, useNoFileE-, usesE , workspaceSymbols , lookupMod ) where +import           Control.Monad.Extra                  (mapMaybeM) import           Control.Monad.Reader import           Control.Monad.Trans.Maybe import qualified Data.HashMap.Strict                  as HM@@ -20,6 +18,7 @@ import qualified Data.Text                            as T import           Data.Tuple.Extra import           Development.IDE.Core.OfInterest+import           Development.IDE.Core.PluginUtils import           Development.IDE.Core.PositionMapping import           Development.IDE.Core.RuleTypes import           Development.IDE.Core.Service@@ -30,8 +29,10 @@ import           Development.IDE.Types.HscEnvEq       (hscEnv) import           Development.IDE.Types.Location import qualified HieDb-import           Language.LSP.Types                   (DocumentHighlight (..),-                                                       SymbolInformation (..))+import           Language.LSP.Protocol.Types          (DocumentHighlight (..),+                                                       SymbolInformation (..),+                                                       normalizedFilePathToUri,+                                                       uriToNormalizedFilePath)   -- | Eventually this will lookup/generate URIs for files in dependencies, but not in the@@ -46,7 +47,7 @@ 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,+-- IMPORTANT NOTE : make sure all rules `useWithStaleFastMT`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@@ -59,50 +60,67 @@   ide <- ask   opts <- liftIO $ getIdeOptionsIO ide -  (hf, mapping) <- useE GetHieAst file-  env <- hscEnv . fst <$> useE GhcSession file-  dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> runMaybeT (useE GetDocMap file)+  (hf, mapping) <- useWithStaleFastMT GetHieAst file+  env <- hscEnv . fst <$> useWithStaleFastMT GhcSession file+  dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> runMaybeT (useWithStaleFastMT GetDocMap file)    !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-  MaybeT $ pure $ first (toCurrentRange mapping =<<) <$> AtPoint.atPoint opts hf dkMap env pos'+  MaybeT $ liftIO $ fmap (first (toCurrentRange mapping =<<)) <$> AtPoint.atPoint opts hf dkMap env pos' -toCurrentLocations :: PositionMapping -> [Location] -> [Location]-toCurrentLocations mapping = mapMaybe go+-- | For each Loacation, determine if we have the PositionMapping+-- for the correct file. If not, get the correct position mapping+-- and then apply the position mapping to the location.+toCurrentLocations+  :: PositionMapping+  -> NormalizedFilePath+  -> [Location]+  -> IdeAction [Location]+toCurrentLocations mapping file = mapMaybeM 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)+    go :: Location -> IdeAction (Maybe Location)+    go (Location uri range) =+      -- The Location we are going to might be in a different+      -- file than the one we are calling gotoDefinition from.+      -- So we check that the location file matches the file+      -- we are in.+      if nUri == normalizedFilePathToUri file+      -- The Location matches the file, so use the PositionMapping+      -- we have.+      then pure $ Location uri <$> toCurrentRange mapping range+      -- The Location does not match the file, so get the correct+      -- PositionMapping and use that instead.+      else do+        otherLocationMapping <- fmap (fmap snd) $ runMaybeT $ do+          otherLocationFile <- MaybeT $ pure $ uriToNormalizedFilePath nUri+          useWithStaleFastMT GetHieAst otherLocationFile+        pure $ Location uri <$> (flip toCurrentRange range =<< otherLocationMapping)+      where+        nUri :: NormalizedUri+        nUri = toNormalizedUri uri  -- | Goto Definition. getDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) getDefinition file pos = runMaybeT $ do     ide@ShakeExtras{ withHieDb, hiedbWriter } <- ask     opts <- liftIO $ getIdeOptionsIO ide-    (HAR _ hf _ _ _, mapping) <- useE GetHieAst file-    (ImportMap imports, _) <- useE GetImportMap file+    (HAR _ hf _ _ _, mapping) <- useWithStaleFastMT GetHieAst file+    (ImportMap imports, _) <- useWithStaleFastMT GetImportMap file     !pos' <- MaybeT (pure $ fromCurrentPosition mapping pos)-    toCurrentLocations mapping <$> AtPoint.gotoDefinition withHieDb (lookupMod hiedbWriter) opts imports hf pos'+    locations <- AtPoint.gotoDefinition withHieDb (lookupMod hiedbWriter) opts imports hf pos'+    MaybeT $ Just <$> toCurrentLocations mapping file locations  getTypeDefinition :: NormalizedFilePath -> Position -> IdeAction (Maybe [Location]) getTypeDefinition file pos = runMaybeT $ do     ide@ShakeExtras{ withHieDb, hiedbWriter } <- ask     opts <- liftIO $ getIdeOptionsIO ide-    (hf, mapping) <- useE GetHieAst file+    (hf, mapping) <- useWithStaleFastMT GetHieAst file     !pos' <- MaybeT (return $ fromCurrentPosition mapping pos)-    toCurrentLocations mapping <$> AtPoint.gotoTypeDefinition withHieDb (lookupMod hiedbWriter) opts hf pos'+    locations <- AtPoint.gotoTypeDefinition withHieDb (lookupMod hiedbWriter) opts hf pos'+    MaybeT $ Just <$> toCurrentLocations mapping file locations  highlightAtPoint :: NormalizedFilePath -> Position -> IdeAction (Maybe [DocumentHighlight]) highlightAtPoint file pos = runMaybeT $ do-    (HAR _ hf rf _ _,mapping) <- useE GetHieAst file+    (HAR _ hf rf _ _,mapping) <- useWithStaleFastMT 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'
src/Development/IDE/Core/Compile.hs view
@@ -34,6 +34,9 @@   , ml_core_file   , coreFileToLinkable   , TypecheckHelpers(..)+  , sourceTypecheck+  , sourceParser+  , shareUsages   ) where  import           Control.Monad.IO.Class@@ -64,6 +67,7 @@ import           Data.List.Extra import           Data.Map                          (Map) import qualified Data.Map.Strict                   as Map+import           Data.Proxy                        (Proxy(Proxy)) import qualified Data.Set                          as Set import           Data.Maybe import qualified Data.Text                         as T@@ -98,8 +102,9 @@ import           GHC.Serialized import           HieDb import qualified Language.LSP.Server               as LSP-import           Language.LSP.Types                (DiagnosticTag (..))-import qualified Language.LSP.Types                as LSP+import           Language.LSP.Protocol.Types                (DiagnosticTag (..))+import qualified Language.LSP.Protocol.Types                as LSP+import qualified Language.LSP.Protocol.Message            as LSP import           System.Directory import           System.FilePath import           System.IO.Extra                   (fixIO, newTempFileWithin)@@ -132,6 +137,7 @@ import qualified GHC                               as G import           GHC.Hs                            (LEpaComment) import qualified GHC.Types.Error                   as Error+import Development.IDE.Import.DependencyInformation #endif  #if MIN_VERSION_ghc(9,5,0)@@ -139,6 +145,12 @@ import GHC.Core.Lint.Interactive #endif +--Simple constansts to make sure the source is consistently named+sourceTypecheck :: T.Text+sourceTypecheck = "typecheck"+sourceParser :: T.Text+sourceParser = "parser"+ -- | Given a string buffer, return the string (after preprocessing) and the 'ParsedModule'. parseModule     :: IdeOptions@@ -182,13 +194,13 @@         case initialized of           Left errs -> return (errs, Nothing)           Right (modSummary', hsc) -> do-            (warnings, etcm) <- withWarnings "typecheck" $ \tweak ->+            (warnings, etcm) <- withWarnings sourceTypecheck $ \tweak ->                 let                   session = tweak (hscSetFlags dflags hsc)                    -- TODO: maybe settings ms_hspp_opts is unnecessary?                   mod_summary'' = modSummary' { ms_hspp_opts = hsc_dflags session}                 in-                  catchSrcErrors (hsc_dflags hsc) "typecheck" $ do+                  catchSrcErrors (hsc_dflags hsc) sourceTypecheck $ do                     tcRnModule session tc_helpers $ demoteIfDefer pm{pm_mod_summary = mod_summary''}             let errorPipeline = unDefer . hideDiag dflags . tagDiag                 diags = map errorPipeline warnings@@ -351,6 +363,10 @@ #if MIN_VERSION_ghc(9,3,0)     -- TODO: support backpack     nodeKeyToInstalledModule :: NodeKey -> Maybe InstalledModule+    -- We shouldn't get boot files here, but to be safe, never map them to an installed module+    -- because boot files don't have linkables we can load, and we will fail if we try to look+    -- for them+    nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB mod IsBoot) uid)) = Nothing     nodeKeyToInstalledModule (NodeKey_Module (ModNodeKeyWithUid (GWIB mod _) uid)) = Just $ mkModule uid mod     nodeKeyToInstalledModule _ = Nothing     moduleToNodeKey :: Module -> NodeKey@@ -458,6 +474,8 @@ #endif  -- | Mitigation for https://gitlab.haskell.org/ghc/ghc/-/issues/22744+-- Important to do this immediately after reading the unit before+-- anything else has a chance to read `mi_usages` shareUsages :: ModIface -> ModIface shareUsages iface = iface {mi_usages = usages}   where usages = map go (mi_usages iface)@@ -611,7 +629,7 @@     source = "compile"     catchErrs x = x `catches`       [ Handler $ return . (,Nothing) . diagFromGhcException source dflags-      , Handler $ return . (,Nothing) . diagFromString source DsError (noSpan "<internal>")+      , Handler $ return . (,Nothing) . diagFromString source DiagnosticSeverity_Error (noSpan "<internal>")       . (("Error during " ++ T.unpack source) ++) . show @SomeException       ] @@ -741,7 +759,7 @@  upgradeWarningToError :: FileDiagnostic -> FileDiagnostic upgradeWarningToError (nfp, sh, fd) =-  (nfp, sh, fd{_severity = Just DsError, _message = warn2err $ _message fd}) where+  (nfp, sh, fd{_severity = Just DiagnosticSeverity_Error, _message = warn2err $ _message fd}) where   warn2err :: T.Text -> T.Text   warn2err = T.intercalate ": error:" . T.splitOn ": warning:" @@ -780,18 +798,15 @@ tagDiag (w@(Reason warning), (nfp, sh, fd)) #endif   | Just tag <- requiresTag warning-  = (w, (nfp, sh, fd { _tags = addTag tag (_tags fd) }))+  = (w, (nfp, sh, fd { _tags = Just $ tag : concat (_tags fd) }))   where     requiresTag :: WarningFlag -> Maybe DiagnosticTag     requiresTag Opt_WarnWarningsDeprecations-      = Just DtDeprecated+      = Just DiagnosticTag_Deprecated     requiresTag wflag  -- deprecation was already considered above       | wflag `elem` unnecessaryDeprecationWarningFlags-      = Just DtUnnecessary+      = Just DiagnosticTag_Unnecessary     requiresTag _ = Nothing-    addTag :: DiagnosticTag -> Maybe (List DiagnosticTag) -> Maybe (List DiagnosticTag)-    addTag t Nothing          = Just (List [t])-    addTag t (Just (List ts)) = Just (List (t : ts)) -- other diagnostics are left unaffected tagDiag t = t @@ -901,9 +916,10 @@             -- If the hash in the pending list doesn't match the current hash, then skip             Just pendingHash -> pendingHash /= hash         unless newerScheduled $ do-          pre optProgressStyle-          withHieDb (\db -> HieDb.addRefsFromLoaded db targetPath (HieDb.RealFile $ fromNormalizedFilePath srcPath) hash hf')-          post+          -- Using bracket, so even if an exception happen during withHieDb call,+          -- the `post` (which clean the progress indicator) will still be called.+          bracket_ (pre optProgressStyle) post $+            withHieDb (\db -> HieDb.addRefsFromLoaded db targetPath (HieDb.RealFile $ fromNormalizedFilePath srcPath) hash hf')   where     mod_location    = ms_location mod_summary     targetPath      = Compat.ml_hie_file mod_location@@ -918,12 +934,13 @@           case lspEnv se of             Nothing -> pure Nothing             Just env -> LSP.runLspT env $ do-              u <- LSP.ProgressTextToken . T.pack . show . hashUnique <$> liftIO Unique.newUnique+              u <- LSP.ProgressToken . LSP.InR . T.pack . show . hashUnique <$> liftIO Unique.newUnique               -- TODO: Wait for the progress create response to use the token-              _ <- LSP.sendRequest LSP.SWindowWorkDoneProgressCreate (LSP.WorkDoneProgressCreateParams u) (const $ pure ())-              LSP.sendNotification LSP.SProgress $ LSP.ProgressParams u $-                LSP.Begin $ LSP.WorkDoneProgressBeginParams-                  { _title = "Indexing"+              _ <- LSP.sendRequest LSP.SMethod_WindowWorkDoneProgressCreate (LSP.WorkDoneProgressCreateParams u) (const $ pure ())+              LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams u $+                toJSON $ LSP.WorkDoneProgressBegin+                  { _kind = LSP.AString @"begin"+                  ,  _title = "Indexing"                   , _cancellable = Nothing                   , _message = Nothing                   , _percentage = Nothing@@ -941,22 +958,25 @@         progressPct = floor $ 100 * progressFrac        whenJust (lspEnv se) $ \env -> whenJust tok $ \tok -> LSP.runLspT env $-        LSP.sendNotification LSP.SProgress $ LSP.ProgressParams tok $-          LSP.Report $+        LSP.sendNotification LSP.SMethod_Progress $ LSP.ProgressParams tok $+          toJSON $             case style of-                Percentage -> LSP.WorkDoneProgressReportParams-                    { _cancellable = Nothing+                Percentage -> LSP.WorkDoneProgressReport+                    { _kind = LSP.AString @"report"+                    , _cancellable = Nothing                     , _message = Nothing                     , _percentage = Just progressPct                     }-                Explicit -> LSP.WorkDoneProgressReportParams-                    { _cancellable = Nothing+                Explicit -> LSP.WorkDoneProgressReport+                    { _kind = LSP.AString @"report"+                    , _cancellable = Nothing                     , _message = Just $                         T.pack " (" <> T.pack (show done) <> "/" <> T.pack (show $ done + remaining) <> ")..."                     , _percentage = Nothing                     }-                NoProgress -> LSP.WorkDoneProgressReportParams-                  { _cancellable = Nothing+                NoProgress -> LSP.WorkDoneProgressReport+                  { _kind = LSP.AString @"report"+                  , _cancellable = Nothing                   , _message = Nothing                   , _percentage = Nothing                   }@@ -973,15 +993,17 @@           swapTVar indexCompleted 0       whenJust (lspEnv se) $ \env -> LSP.runLspT env $         when (coerce $ ideTesting se) $-          LSP.sendNotification (LSP.SCustomMethod "ghcide/reference/ready") $+          LSP.sendNotification (LSP.SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $             toJSON $ fromNormalizedFilePath srcPath       whenJust mdone $ \done ->         modifyVar_ indexProgressToken $ \tok -> do           whenJust (lspEnv se) $ \env -> LSP.runLspT env $             whenJust tok $ \tok ->-              LSP.sendNotification LSP.SProgress $ LSP.ProgressParams tok $-                LSP.End $ LSP.WorkDoneProgressEndParams-                  { _message = Just $ "Finished indexing " <> T.pack (show done) <> " files"+              LSP.sendNotification LSP.SMethod_Progress  $ LSP.ProgressParams tok $+                toJSON $+                LSP.WorkDoneProgressEnd+                  { _kind = LSP.AString @"end"+                  , _message = Just $ "Finished indexing " <> T.pack (show done) <> " files"                   }           -- We are done with the current indexing cycle, so destroy the token           pure Nothing@@ -1013,7 +1035,7 @@ handleGenerationErrors dflags source action =   action >> return [] `catches`     [ Handler $ return . diagFromGhcException source dflags-    , Handler $ return . diagFromString source DsError (noSpan "<internal>")+    , Handler $ return . diagFromString source DiagnosticSeverity_Error (noSpan "<internal>")     . (("Error during " ++ T.unpack source) ++) . show @SomeException     ] @@ -1021,7 +1043,7 @@ handleGenerationErrors' dflags source action =   fmap ([],) action `catches`     [ Handler $ return . (,Nothing) . diagFromGhcException source dflags-    , Handler $ return . (,Nothing) . diagFromString source DsError (noSpan "<internal>")+    , Handler $ return . (,Nothing) . diagFromString source DiagnosticSeverity_Error (noSpan "<internal>")     . (("Error during " ++ T.unpack source) ++) . show @SomeException     ] @@ -1031,25 +1053,19 @@ -- Add the current ModSummary to the graph, along with the -- HomeModInfo's of all direct dependencies (by induction hypothesis all -- transitive dependencies will be contained in envs)+mergeEnvs :: HscEnv -> ModuleGraph -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv+mergeEnvs env mg ms extraMods envs = do #if MIN_VERSION_ghc(9,3,0)-mergeEnvs :: HscEnv -> (ModSummary, [NodeKey]) -> [HomeModInfo] -> [HscEnv] -> IO HscEnv-mergeEnvs env (ms, deps) extraMods envs = do     let im  = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))         ifr = InstalledFound (ms_location ms) im         curFinderCache = Compat.extendInstalledModuleEnv Compat.emptyInstalledModuleEnv im ifr-        -- Very important to force this as otherwise the hsc_mod_graph field is not-        -- forced and ends up retaining a reference to all the old hsc_envs we have merged to get-        -- this new one, which in turn leads to the EPS referencing the HPT.-        module_graph_nodes =-          nubOrdOn mkNodeKey (ModuleNode deps ms : concatMap (mgModSummaries' . hsc_mod_graph) envs)-     newFinderCache <- concatFC curFinderCache (map hsc_FC envs)-    liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $+    return $! loadModulesHome extraMods $       let newHug = foldl' mergeHUG (hsc_HUG env) (map hsc_HUG envs) in       (hscUpdateHUG (const newHug) env){           hsc_FC = newFinderCache,-          hsc_mod_graph = mkModuleGraph module_graph_nodes-      })+          hsc_mod_graph = mg+      }      where         mergeHUG (UnitEnvGraph a) (UnitEnvGraph b) = UnitEnvGraph $ Map.unionWith mergeHUE a b@@ -1059,39 +1075,32 @@         combineModules a b           | HsSrcFile <- mi_hsc_src (hm_iface a) = a           | otherwise = b++        -- Prefer non-boot files over non-boot files+        -- otherwise we can get errors like https://gitlab.haskell.org/ghc/ghc/-/issues/19816+        -- if a boot file shadows over a non-boot file+        combineModuleLocations a@(InstalledFound ml m) b | Just fp <- ml_hs_file ml, not ("boot" `isSuffixOf` fp) = a+        combineModuleLocations _ b = b+         concatFC :: FinderCacheState -> [FinderCache] -> IO FinderCache         concatFC cur xs = do           fcModules <- mapM (readIORef . fcModuleCache) xs           fcFiles <- mapM (readIORef . fcFileCache) xs-          fcModules' <- newIORef $! foldl' (plusInstalledModuleEnv const) cur fcModules+          fcModules' <- newIORef $! foldl' (plusInstalledModuleEnv combineModuleLocations) cur fcModules           fcFiles' <- newIORef $! Map.unions fcFiles           pure $ FinderCache fcModules' fcFiles'  #else-mergeEnvs :: HscEnv -> ModSummary -> [HomeModInfo] -> [HscEnv] -> IO HscEnv-mergeEnvs env ms extraMods envs = do     prevFinderCache <- concatFC <$> mapM (readIORef . hsc_FC) envs     let im  = Compat.installedModule (toUnitId $ moduleUnit $ ms_mod ms) (moduleName (ms_mod ms))         ifr = InstalledFound (ms_location ms) im-        -- Very important to force this as otherwise the hsc_mod_graph field is not-        -- forced and ends up retaining a reference to all the old hsc_envs we have merged to get-        -- this new one, which in turn leads to the EPS referencing the HPT.-        module_graph_nodes =-#if MIN_VERSION_ghc(9,2,0)-        -- We don't do any instantiation for backpack at this point of time, so it is OK to use-        -- 'extendModSummaryNoDeps'.-        -- This may have to change in the future.-          map extendModSummaryNoDeps $-#endif-          nubOrdOn ms_mod (ms : concatMap (mgModSummaries . hsc_mod_graph) envs)-     newFinderCache <- newIORef $! Compat.extendInstalledModuleEnv prevFinderCache im ifr-    liftRnf rwhnf module_graph_nodes `seq` (return $ loadModulesHome extraMods $+    return $! loadModulesHome extraMods $       env{           hsc_HPT = foldMapBy mergeUDFM emptyUDFM hsc_HPT envs,           hsc_FC = newFinderCache,-          hsc_mod_graph = mkModuleGraph module_graph_nodes-      })+          hsc_mod_graph = mg+      }      where         mergeUDFM = plusUDFM_C combineModules@@ -1248,7 +1257,7 @@    let loc  = mkRealSrcLoc (Util.mkFastString filename) 1 1    case unP Compat.parseHeader (initParserState (initParserOpts dflags) contents loc) of      PFailedWithErrorMessages msgs ->-        throwE $ diagFromErrMsgs "parser" dflags $ msgs dflags+        throwE $ diagFromErrMsgs sourceParser dflags $ msgs dflags      POk pst rdr_module -> do         let (warns, errs) = renderMessages $ getPsMessages pst dflags @@ -1262,9 +1271,9 @@         -- errors are those from which a parse tree just can't         -- be produced.         unless (null errs) $-            throwE $ diagFromErrMsgs "parser" dflags errs+            throwE $ diagFromErrMsgs sourceParser dflags errs -        let warnings = diagFromErrMsgs "parser" dflags warns+        let warnings = diagFromErrMsgs sourceParser dflags warns         return (warnings, rdr_module)  -- | Given a buffer, flags, and file path, produce a@@ -1281,7 +1290,7 @@        dflags = ms_hspp_opts ms        contents = fromJust $ ms_hspp_buf ms    case unP Compat.parseModule (initParserState (initParserOpts dflags) contents loc) of-     PFailedWithErrorMessages msgs -> throwE $ diagFromErrMsgs "parser" dflags $ msgs dflags+     PFailedWithErrorMessages msgs -> throwE $ diagFromErrMsgs sourceParser dflags $ msgs dflags      POk pst rdr_module ->          let              hpm_annotations = mkApiAnns pst@@ -1291,9 +1300,9 @@                let IdePreprocessedSource preproc_warns errs parsed = customPreprocessor rdr_module                 unless (null errs) $-                  throwE $ diagFromStrings "parser" DsError errs+                  throwE $ diagFromStrings sourceParser DiagnosticSeverity_Error errs -               let preproc_warnings = diagFromStrings "parser" DsWarning preproc_warns+               let preproc_warnings = diagFromStrings sourceParser DiagnosticSeverity_Warning preproc_warns                (parsed', msgs) <- liftIO $ applyPluginsParsedResultAction env dflags ms hpm_annotations parsed psMessages                let (warns, errs) = renderMessages msgs @@ -1307,7 +1316,7 @@                -- errors are those from which a parse tree just can't                -- be produced.                unless (null errs) $-                 throwE $ diagFromErrMsgs "parser" dflags errs+                 throwE $ diagFromErrMsgs sourceParser dflags errs                  -- To get the list of extra source files, we take the list@@ -1342,7 +1351,7 @@                srcs2 <- liftIO $ filterM doesFileExist srcs1                 let pm = ParsedModule ms parsed' srcs2 hpm_annotations-                   warnings = diagFromErrMsgs "parser" dflags warns+                   warnings = diagFromErrMsgs sourceParser dflags warns                pure (warnings ++ preproc_warnings, pm)  loadHieFile :: Compat.NameCacheUpdater -> FilePath -> IO GHC.HieFile@@ -1465,11 +1474,28 @@             | source_version <= dest_version -> SourceUnmodified             | otherwise -> SourceModified +    old_iface <- case mb_old_iface of+      Just iface -> pure (Just iface)+      Nothing -> do+        let ncu = hsc_NC sessionWithMsDynFlags+            read_dflags = hsc_dflags sessionWithMsDynFlags+#if MIN_VERSION_ghc(9,3,0)+        read_result <- liftIO $ readIface read_dflags ncu mod iface_file+#else+        read_result <- liftIO $ initIfaceCheck (text "readIface") sessionWithMsDynFlags+                              $ readIface mod iface_file+#endif+        case read_result of+          Util.Failed{} -> return Nothing+          -- important to call `shareUsages` here before checkOldIface+          -- consults `mi_usages`+          Util.Succeeded iface -> return $ Just (shareUsages iface)+     -- If mb_old_iface is nothing then checkOldIface will load it for us     -- given that the source is unmodified     (recomp_iface_reqd, mb_checked_iface) #if MIN_VERSION_ghc(9,3,0)-      <- liftIO $ checkOldIface sessionWithMsDynFlags ms mb_old_iface >>= \case+      <- liftIO $ checkOldIface sessionWithMsDynFlags ms old_iface >>= \case         UpToDateItem x -> pure (UpToDate, Just x)         OutOfDateItem reason x -> pure (NeedsRecompile reason, x) #else@@ -1483,15 +1509,14 @@           regenerate linkableNeeded      case (mb_checked_iface, recomp_iface_reqd) of-      (Just iface', UpToDate) -> do-             let iface = shareUsages iface'+      (Just iface, UpToDate) -> do              details <- liftIO $ mkDetailsFromIface sessionWithMsDynFlags iface              -- parse the runtime dependencies from the annotations              let runtime_deps                    | not (mi_used_th iface) = emptyModuleEnv                    | otherwise = parseRuntimeDeps (md_anns details)-             -- Perform the fine grained recompilation check for TH-             maybe_recomp <- checkLinkableDependencies get_linkable_hashes (hsc_mod_graph sessionWithMsDynFlags) runtime_deps+             -- Peform the fine grained recompilation check for TH+             maybe_recomp <- checkLinkableDependencies session get_linkable_hashes runtime_deps              case maybe_recomp of                Just msg -> do_regenerate msg                Nothing@@ -1528,13 +1553,21 @@ -- the runtime dependencies of the module, to check if any of them are out of date -- Hopefully 'runtime_deps' will be empty if the module didn't actually use TH -- See Note [Recompilation avoidance in the presence of TH]-checkLinkableDependencies :: MonadIO m => ([NormalizedFilePath] -> m [BS.ByteString]) -> ModuleGraph -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired)-checkLinkableDependencies get_linkable_hashes graph runtime_deps = do-  let hs_files = mapM go (moduleEnvToList runtime_deps)-      go (mod, hash) = do-        ms <- mgLookupModule graph mod-        let hs = fromJust $ ml_hs_file $ ms_location ms-        pure (toNormalizedFilePath' hs, hash)+checkLinkableDependencies :: MonadIO m => HscEnv -> ([NormalizedFilePath] -> m [BS.ByteString]) -> ModuleEnv BS.ByteString -> m (Maybe RecompileRequired)+checkLinkableDependencies hsc_env get_linkable_hashes runtime_deps = do+#if MIN_VERSION_ghc(9,3,0)+  moduleLocs <- liftIO $ readIORef (fcModuleCache $ hsc_FC hsc_env)+#else+  moduleLocs <- liftIO $ readIORef (hsc_FC hsc_env)+#endif+  let go (mod, hash) = do+        ifr <- lookupInstalledModuleEnv moduleLocs $ Compat.installedModule (toUnitId $ moduleUnit mod) (moduleName mod)+        case ifr of+          InstalledFound loc _ -> do+            hs <- ml_hs_file loc+            pure (toNormalizedFilePath' hs,hash)+          _ -> Nothing+      hs_files = mapM go (moduleEnvToList runtime_deps)   case hs_files of     Nothing -> error "invalid module graph"     Just fs -> do
src/Development/IDE/Core/FileExists.hs view
@@ -27,14 +27,14 @@ import qualified Development.IDE.Core.Shake            as Shake import           Development.IDE.Graph import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger          (Pretty (pretty),-                                                        Recorder, WithPriority,-                                                        cmapWithPrio) import           Development.IDE.Types.Options import qualified Focus+import           Ide.Logger                            (Pretty (pretty),+                                                        Recorder, WithPriority,+                                                        cmapWithPrio) import           Ide.Plugin.Config                     (Config)+import           Language.LSP.Protocol.Types import           Language.LSP.Server                   hiding (getVirtualFile)-import           Language.LSP.Types import qualified StmContainers.Map                     as STM import qualified System.Directory                      as Dir import qualified System.FilePath.Glob                  as Glob@@ -117,16 +117,16 @@     -- See Note [Invalidating file existence results]     -- flush previous values     let (fileModifChanges, fileExistChanges) =-            partition ((== FcChanged) . snd) changes+            partition ((== FileChangeType_Changed) . snd) changes     mapM_ (deleteValue (shakeExtras state) GetFileExists . fst) fileExistChanges     io1 <- recordDirtyKeys (shakeExtras state) GetFileExists $ map fst fileExistChanges     io2 <- recordDirtyKeys (shakeExtras state) GetModificationTime $ map fst fileModifChanges     return (io1 <> io2)  fromChange :: FileChangeType -> Maybe Bool-fromChange FcCreated = Just True-fromChange FcDeleted = Just False-fromChange FcChanged = Nothing+fromChange FileChangeType_Created = Just True+fromChange FileChangeType_Deleted = Just False+fromChange FileChangeType_Changed = Nothing  ------------------------------------------------------------------------------------- 
src/Development/IDE/Core/FileStore.hs view
@@ -54,29 +54,29 @@ #else #endif -import qualified Development.IDE.Types.Logger                 as L+import qualified Ide.Logger                                   as L +import           Data.Aeson                                   (ToJSON (toJSON)) import qualified Data.Binary                                  as B import qualified Data.ByteString.Lazy                         as LBS import           Data.List                                    (foldl') import qualified Data.Text                                    as Text import           Development.IDE.Core.IdeConfiguration        (isWorkspaceFile) import qualified Development.IDE.Core.Shake                   as Shake-import           Development.IDE.Types.Logger                 (Pretty (pretty),+import           Ide.Logger                                   (Pretty (pretty),                                                                Priority (Info),                                                                Recorder,                                                                WithPriority,                                                                cmapWithPrio,                                                                logWith, viaShow,                                                                (<+>))-import qualified Language.LSP.Server                          as LSP-import           Language.LSP.Types                           (DidChangeWatchedFilesRegistrationOptions (DidChangeWatchedFilesRegistrationOptions),-                                                               FileChangeType (FcChanged),+import           Language.LSP.Protocol.Message                (toUntypedRegistration)+import qualified Language.LSP.Protocol.Message                as LSP+import           Language.LSP.Protocol.Types                  (DidChangeWatchedFilesRegistrationOptions (DidChangeWatchedFilesRegistrationOptions),                                                                FileSystemWatcher (..),-                                                               WatchKind (..),                                                                _watchers)-import qualified Language.LSP.Types                           as LSP-import qualified Language.LSP.Types.Capabilities              as LSP+import qualified Language.LSP.Protocol.Types                  as LSP+import qualified Language.LSP.Server                          as LSP import           Language.LSP.VFS import           System.FilePath import           System.IO.Unsafe@@ -162,14 +162,14 @@  -- | Reset the GetModificationTime state of watched files --   Assumes the list does not include any FOIs-resetFileStore :: IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO ()+resetFileStore :: IdeState -> [(NormalizedFilePath, LSP.FileChangeType)] -> IO () resetFileStore ideState changes = mask $ \_ -> do     -- we record FOIs document versions in all the stored values     -- so NEVER reset FOIs to avoid losing their versions     -- FOI filtering is done by the caller (LSP Notification handler)     forM_ changes $ \(nfp, c) -> do         case c of-            FcChanged+            LSP.FileChangeType_Changed             --  already checked elsewhere |  not $ HM.member nfp fois               -> atomically $                deleteValue (shakeExtras ideState) GetModificationTime nfp@@ -268,26 +268,27 @@       if watchSupported       then do         let-          regParams    = LSP.RegistrationParams (List [LSP.SomeRegistration registration])+          regParams    = LSP.RegistrationParams  [toUntypedRegistration registration]           -- The registration ID is arbitrary and is only used in case we want to deregister (which we won't).           -- We could also use something like a random UUID, as some other servers do, but this works for           -- our purposes.-          registration = LSP.Registration "globalFileWatches"-                                           LSP.SWorkspaceDidChangeWatchedFiles-                                           regOptions+          registration = LSP.TRegistration { _id ="globalFileWatches"+                                           , _method = LSP.SMethod_WorkspaceDidChangeWatchedFiles+                                           , _registerOptions = Just $ regOptions}           regOptions =-            DidChangeWatchedFilesRegistrationOptions { _watchers = List watchers }+            DidChangeWatchedFilesRegistrationOptions { _watchers = watchers }           -- See Note [File existence cache and LSP file watchers] for why this exists, and the choice of watch kind-          watchKind = WatchKind { _watchCreate = True, _watchChange = True, _watchDelete = True}+          -- WatchKind_Custom 7 is for create, change, and delete+          watchKind = LSP.WatchKind_Custom 7           -- See Note [Which files should we watch?] for an explanation of why the pattern is the way that it is           -- The patterns will be something like "**/.hs", i.e. "any number of directory segments,           -- followed by a file with an extension 'hs'.           watcher glob = FileSystemWatcher { _globPattern = glob, _kind = Just watchKind }           -- We use multiple watchers instead of one using '{}' because lsp-test doesn't           -- support that: https://github.com/bubba/lsp-test/issues/77-          watchers = [ watcher (Text.pack glob) | glob <- globs ]+          watchers = [ watcher (LSP.GlobPattern (LSP.InL (LSP.Pattern (Text.pack glob)))) | glob <- globs ] -        void $ LSP.sendRequest LSP.SClientRegisterCapability regParams (const $ pure ()) -- TODO handle response+        void $ LSP.sendRequest LSP.SMethod_ClientRegisterCapability regParams (const $ pure ()) -- TODO handle response         return True       else return False @@ -311,7 +312,7 @@   atomicModifyIORef' filePathMap $ \km ->     let new_key = HashMap.lookup k km     in case new_key of-          Just v -> (km, v)+          Just v  -> (km, v)           Nothing -> (HashMap.insert k k km, k) {-# NOINLINE shareFilePath  #-}- +
src/Development/IDE/Core/IdeConfiguration.hs view
@@ -13,6 +13,7 @@ where  import           Control.Concurrent.Strict+import           Control.Lens                   ((^.)) import           Control.Monad import           Control.Monad.IO.Class import           Data.Aeson.Types               (Value)@@ -22,7 +23,7 @@ import           Development.IDE.Core.Shake import           Development.IDE.Graph import           Development.IDE.Types.Location-import           Language.LSP.Types+import           Language.LSP.Protocol.Types import           System.FilePath                (isRelative)  -- | Lsp client relevant configuration details@@ -49,15 +50,15 @@   IdeConfiguration {..}  where   workspaceFolders =-    foldMap (singleton . toNormalizedUri) _rootUri+    foldMap (singleton . toNormalizedUri) (nullToMaybe _rootUri)       <> (foldMap . foldMap)            (singleton . parseWorkspaceFolder)-           _workspaceFolders+           (nullToMaybe =<< _workspaceFolders)   clientSettings = hashed _initializationOptions  parseWorkspaceFolder :: WorkspaceFolder -> NormalizedUri parseWorkspaceFolder WorkspaceFolder{_uri} =-  toNormalizedUri (Uri _uri)+  toNormalizedUri _uri  modifyWorkspaceFolders   :: IdeState -> (HashSet NormalizedUri -> HashSet NormalizedUri) -> IO ()
src/Development/IDE/Core/OfInterest.hs view
@@ -24,6 +24,7 @@ import           Control.Monad.IO.Class import           Data.HashMap.Strict                      (HashMap) import qualified Data.HashMap.Strict                      as HashMap+import           Data.Proxy import qualified Data.Text                                as T import           Development.IDE.Graph @@ -39,14 +40,15 @@ import           Development.IDE.Plugin.Completions.Types import           Development.IDE.Types.Exports import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger             (Pretty (pretty),+import           Development.IDE.Types.Options            (IdeTesting (..))+import           GHC.TypeLits                             (KnownSymbol)+import           Ide.Logger                               (Pretty (pretty),                                                            Recorder,                                                            WithPriority,                                                            cmapWithPrio,                                                            logDebug)-import           Development.IDE.Types.Options            (IdeTesting (..))+import qualified Language.LSP.Protocol.Message            as LSP import qualified Language.LSP.Server                      as LSP-import qualified Language.LSP.Types                       as LSP  data Log = LogShake Shake.Log   deriving Show@@ -130,12 +132,13 @@ kick = do     files <- HashMap.keys <$> getFilesOfInterestUntracked     ShakeExtras{exportsMap, ideTesting = IdeTesting testing, lspEnv, progress} <- getShakeExtras-    let signal msg = when testing $ liftIO $+    let signal :: KnownSymbol s => Proxy s -> Action ()+        signal msg = when testing $ liftIO $             mRunLspT lspEnv $-                LSP.sendNotification (LSP.SCustomMethod msg) $+                LSP.sendNotification (LSP.SMethod_CustomMethod msg) $                 toJSON $ map fromNormalizedFilePath files -    signal "kick/start"+    signal (Proxy @"kick/start")     liftIO $ progressUpdate progress KickStarted      -- Update the exports map@@ -155,4 +158,4 @@         void garbageCollectDirtyKeys         liftIO $ writeVar var False -    signal "kick/done"+    signal (Proxy @"kick/done")
+ src/Development/IDE/Core/PluginUtils.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE GADTs #-}+module Development.IDE.Core.PluginUtils where++import           Control.Monad.Extra+import           Control.Monad.IO.Class+import           Control.Monad.Reader                 (runReaderT)+import           Control.Monad.Trans.Except+import           Control.Monad.Trans.Maybe+import           Data.Functor.Identity+import qualified Data.Text                            as T+import           Development.IDE.Core.PositionMapping+import           Development.IDE.Core.Shake           (IdeAction, IdeRule,+                                                       IdeState (shakeExtras),+                                                       mkDelayedAction,+                                                       shakeEnqueue)+import qualified Development.IDE.Core.Shake           as Shake+import           Development.IDE.GHC.Orphans          ()+import           Development.IDE.Graph                hiding (ShakeValue)+import           Development.IDE.Types.Location       (NormalizedFilePath)+import qualified Development.IDE.Types.Location       as Location+import qualified Ide.Logger                           as Logger+import           Ide.Plugin.Error+import qualified Language.LSP.Protocol.Types          as LSP++-- ----------------------------------------------------------------------------+-- Action wrappers+-- ----------------------------------------------------------------------------++-- |ExceptT version of `runAction`, takes a ExceptT Action+runActionE :: MonadIO m => String -> IdeState -> ExceptT e Action a -> ExceptT e m a+runActionE herald ide act =+  mapExceptT liftIO . ExceptT $+    join $ shakeEnqueue (shakeExtras ide) (mkDelayedAction herald Logger.Debug $ runExceptT act)++-- |MaybeT version of `runAction`, takes a MaybeT Action+runActionMT :: MonadIO m => String -> IdeState -> MaybeT Action a -> MaybeT m a+runActionMT herald ide act =+  mapMaybeT liftIO . MaybeT $+    join $ shakeEnqueue (shakeExtras ide) (mkDelayedAction herald Logger.Debug $ runMaybeT act)++-- |ExceptT version of `use` that throws a PluginRuleFailed upon failure+useE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError Action v+useE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . useMT k++-- |MaybeT version of `use`+useMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT Action v+useMT k = MaybeT . Shake.use k++-- |ExceptT version of `uses` that throws a PluginRuleFailed upon failure+usesE :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> ExceptT PluginError Action (f v)+usesE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . usesMT k++-- |MaybeT version of `uses`+usesMT :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> MaybeT Action (f v)+usesMT k xs = MaybeT $ sequence <$> Shake.uses k xs++-- |ExceptT version of `useWithStale` that throws a PluginRuleFailed upon+-- failure+useWithStaleE :: IdeRule k v+    => k -> NormalizedFilePath -> ExceptT PluginError Action (v, PositionMapping)+useWithStaleE key = maybeToExceptT (PluginRuleFailed (T.pack $ show key)) . useWithStaleMT key++-- |MaybeT version of `useWithStale`+useWithStaleMT :: IdeRule k v+    => k -> NormalizedFilePath -> MaybeT Action (v, PositionMapping)+useWithStaleMT key file = MaybeT $ runIdentity <$> Shake.usesWithStale key (Identity file)++-- ----------------------------------------------------------------------------+-- IdeAction wrappers+-- ----------------------------------------------------------------------------++-- |ExceptT version of `runIdeAction`, takes a ExceptT IdeAction+runIdeActionE :: MonadIO m => String -> Shake.ShakeExtras -> ExceptT e IdeAction a -> ExceptT e m a+runIdeActionE _herald s i = ExceptT $ liftIO $ runReaderT (Shake.runIdeActionT $ runExceptT i) s++-- |MaybeT version of `runIdeAction`, takes a MaybeT IdeAction+runIdeActionMT :: MonadIO m => String -> Shake.ShakeExtras -> MaybeT IdeAction a -> MaybeT m a+runIdeActionMT _herald s i = MaybeT $ liftIO $ runReaderT (Shake.runIdeActionT $ runMaybeT i) s++-- |ExceptT version of `useWithStaleFast` that throws a PluginRuleFailed upon+-- failure+useWithStaleFastE :: IdeRule k v => k -> NormalizedFilePath -> ExceptT PluginError IdeAction (v, PositionMapping)+useWithStaleFastE k = maybeToExceptT (PluginRuleFailed (T.pack $ show k)) . useWithStaleFastMT k++-- |MaybeT version of `useWithStaleFast`+useWithStaleFastMT :: IdeRule k v => k -> NormalizedFilePath -> MaybeT IdeAction (v, PositionMapping)+useWithStaleFastMT k = MaybeT . Shake.useWithStaleFast k++-- ----------------------------------------------------------------------------+-- Location wrappers+-- ----------------------------------------------------------------------------++-- |ExceptT version of `uriToFilePath` that throws a PluginInvalidParams upon+-- failure+uriToFilePathE :: Monad m => LSP.Uri -> ExceptT PluginError m FilePath+uriToFilePathE uri = maybeToExceptT (PluginInvalidParams (T.pack $ "uriToFilePath' failed. Uri:" <>  show uri)) $ uriToFilePathMT uri++-- |MaybeT version of `uriToFilePath`+uriToFilePathMT :: Monad m => LSP.Uri -> MaybeT m FilePath+uriToFilePathMT = MaybeT . pure . Location.uriToFilePath'++-- ----------------------------------------------------------------------------+-- PositionMapping wrappers+-- ----------------------------------------------------------------------------++-- |ExceptT version of `toCurrentPosition` that throws a PluginInvalidUserState+-- upon failure+toCurrentPositionE :: Monad m => PositionMapping -> LSP.Position -> ExceptT PluginError m LSP.Position+toCurrentPositionE mapping = maybeToExceptT (PluginInvalidUserState "toCurrentPosition"). toCurrentPositionMT mapping++-- |MaybeT version of `toCurrentPosition`+toCurrentPositionMT :: Monad m => PositionMapping -> LSP.Position -> MaybeT m LSP.Position+toCurrentPositionMT mapping = MaybeT . pure . toCurrentPosition mapping++-- |ExceptT version of `fromCurrentPosition` that throws a+-- PluginInvalidUserState upon failure+fromCurrentPositionE :: Monad m => PositionMapping -> LSP.Position -> ExceptT PluginError m LSP.Position+fromCurrentPositionE mapping = maybeToExceptT (PluginInvalidUserState "fromCurrentPosition") . fromCurrentPositionMT mapping++-- |MaybeT version of `fromCurrentPosition`+fromCurrentPositionMT :: Monad m => PositionMapping -> LSP.Position -> MaybeT m LSP.Position+fromCurrentPositionMT mapping = MaybeT . pure . fromCurrentPosition mapping++-- |ExceptT version of `toCurrentRange` that throws a PluginInvalidUserState+-- upon failure+toCurrentRangeE :: Monad m => PositionMapping -> LSP.Range -> ExceptT PluginError m LSP.Range+toCurrentRangeE mapping = maybeToExceptT (PluginInvalidUserState "toCurrentRange") . toCurrentRangeMT mapping++-- |MaybeT version of `toCurrentRange`+toCurrentRangeMT :: Monad m => PositionMapping -> LSP.Range -> MaybeT m LSP.Range+toCurrentRangeMT mapping = MaybeT . pure . toCurrentRange mapping++-- |ExceptT version of `fromCurrentRange` that throws a PluginInvalidUserState+-- upon failure+fromCurrentRangeE :: Monad m => PositionMapping -> LSP.Range -> ExceptT PluginError m LSP.Range+fromCurrentRangeE mapping = maybeToExceptT (PluginInvalidUserState "fromCurrentRange") . fromCurrentRangeMT mapping++-- |MaybeT version of `fromCurrentRange`+fromCurrentRangeMT :: Monad m => PositionMapping -> LSP.Range -> MaybeT m LSP.Range+fromCurrentRangeMT mapping = MaybeT . pure . fromCurrentRange mapping
src/Development/IDE/Core/PositionMapping.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedLabels #-} -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 module Development.IDE.Core.PositionMapping@@ -28,11 +29,13 @@ import           Data.Algorithm.Diff import           Data.Bifunctor import           Data.List-import qualified Data.Text           as T-import qualified Data.Vector.Unboxed as V-import           Language.LSP.Types  (Position (Position), Range (Range),-                                      TextDocumentContentChangeEvent (TextDocumentContentChangeEvent),-                                      UInt)+import           Data.Row+import qualified Data.Text                   as T+import qualified Data.Vector.Unboxed         as V+import           Language.LSP.Protocol.Types (Position (Position),+                                              Range (Range),+                                              TextDocumentContentChangeEvent (TextDocumentContentChangeEvent),+                                              UInt, type (|?) (InL))  -- | Either an exact position, or the range of text that was substituted data PositionResult a@@ -120,10 +123,12 @@ addDelta :: PositionDelta -> PositionMapping -> PositionMapping addDelta delta (PositionMapping pm) = PositionMapping (composeDelta delta pm) +-- TODO: We currently ignore the right hand side (if there is only text), as+-- that was what was done with lsp* 1.6 packages applyChange :: PositionDelta -> TextDocumentContentChangeEvent -> PositionDelta-applyChange PositionDelta{..} (TextDocumentContentChangeEvent (Just r) _ t) = PositionDelta-    { toDelta = toCurrent r t <=< toDelta-    , fromDelta = fromDelta <=< fromCurrent r t+applyChange PositionDelta{..} (TextDocumentContentChangeEvent (InL x)) = PositionDelta+    { toDelta = toCurrent (x .! #range) (x .! #text) <=< toDelta+    , fromDelta = fromDelta <=< fromCurrent (x .! #range) (x .! #text)     } applyChange posMapping _ = posMapping 
src/Development/IDE/Core/Preprocessor.hs view
@@ -8,9 +8,9 @@  import           Development.IDE.GHC.Compat import qualified Development.IDE.GHC.Compat.Util   as Util-import qualified Development.IDE.GHC.Util          as Util import           Development.IDE.GHC.CPP import           Development.IDE.GHC.Orphans       ()+import qualified Development.IDE.GHC.Util          as Util  import           Control.DeepSeq                   (NFData (rnf)) import           Control.Exception                 (evaluate)@@ -133,7 +133,9 @@           _source = Just "CPP",           _message = T.unlines $ cdMessage d,           _relatedInformation = Nothing,-          _tags = Nothing+          _tags = Nothing,+          _codeDescription = Nothing,+          _data_ = Nothing         }  
src/Development/IDE/Core/ProgressReporting.hs view
@@ -21,6 +21,7 @@ import           Control.Monad.Extra import           Control.Monad.IO.Class import           Control.Monad.Trans.Class      (lift)+import           Data.Aeson                     (ToJSON (toJSON)) import           Data.Foldable                  (for_) import           Data.Functor                   (($>)) import qualified Data.Text                      as T@@ -30,9 +31,10 @@ import           Development.IDE.Types.Location import           Development.IDE.Types.Options import qualified Focus+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types+import qualified Language.LSP.Protocol.Types    as LSP import qualified Language.LSP.Server            as LSP-import           Language.LSP.Types-import qualified Language.LSP.Types             as LSP import qualified StmContainers.Map              as STM import           System.Time.Extra import           UnliftIO.Exception             (bracket_)@@ -125,30 +127,32 @@             -- first sleep a bit, so we only show progress messages if it's going to take             -- a "noticable amount of time" (we often expect a thread kill to arrive before the sleep finishes)             liftIO $ sleep before-            u <- ProgressTextToken . T.pack . show . hashUnique <$> liftIO newUnique+            u <- ProgressToken . InR . T.pack . show . hashUnique <$> liftIO newUnique              b <- liftIO newBarrier-            void $ LSP.runLspT lspEnv $ LSP.sendRequest LSP.SWindowWorkDoneProgressCreate+            void $ LSP.runLspT lspEnv $ LSP.sendRequest SMethod_WindowWorkDoneProgressCreate                 LSP.WorkDoneProgressCreateParams { _token = u } $ liftIO . signalBarrier b             liftIO $ async $ do                 ready <- waitBarrier b                 LSP.runLspT lspEnv $ for_ ready $ const $ bracket_ (start u) (stop u) (loop u 0)             where-                start id = LSP.sendNotification LSP.SProgress $+                start id = LSP.sendNotification SMethod_Progress $                     LSP.ProgressParams                         { _token = id-                        , _value = LSP.Begin $ WorkDoneProgressBeginParams-                          { _title = "Processing"+                        , _value = toJSON $ WorkDoneProgressBegin+                          { _kind = AString @"begin"+                          ,  _title = "Processing"                           , _cancellable = Nothing                           , _message = Nothing                           , _percentage = Nothing                           }                         }-                stop id = LSP.sendNotification LSP.SProgress+                stop id = LSP.sendNotification SMethod_Progress                     LSP.ProgressParams                         { _token = id-                        , _value = LSP.End WorkDoneProgressEndParams-                          { _message = Nothing+                        , _value = toJSON $ WorkDoneProgressEnd+                          { _kind = AString @"end"+                           , _message = Nothing                           }                         }                 loop _ _ | optProgressStyle == NoProgress =@@ -164,17 +168,19 @@                             nextPct :: UInt                             nextPct = floor $ 100 * nextFrac                         when (nextPct /= prevPct) $-                          LSP.sendNotification LSP.SProgress $+                          LSP.sendNotification SMethod_Progress $                           LSP.ProgressParams                               { _token = id-                              , _value = LSP.Report $ case optProgressStyle of-                                  Explicit -> LSP.WorkDoneProgressReportParams-                                    { _cancellable = Nothing+                              , _value = case optProgressStyle of+                                  Explicit -> toJSON $ WorkDoneProgressReport+                                    { _kind = AString @"report"+                                    , _cancellable = Nothing                                     , _message = Just $ T.pack $ show done <> "/" <> show todo                                     , _percentage = Nothing                                     }-                                  Percentage -> LSP.WorkDoneProgressReportParams-                                    { _cancellable = Nothing+                                  Percentage -> toJSON $ WorkDoneProgressReport+                                    { _kind = AString @"report"+                                    , _cancellable = Nothing                                     , _message = Nothing                                     , _percentage = Just nextPct                                     }
src/Development/IDE/Core/RuleTypes.hs view
@@ -42,7 +42,7 @@ import           Development.IDE.Spans.LocalBindings import           Development.IDE.Types.Diagnostics import           GHC.Serialized                               (Serialized)-import           Language.LSP.Types                           (Int32,+import           Language.LSP.Protocol.Types                  (Int32,                                                                NormalizedFilePath)  data LinkableType = ObjectLinkable | BCOLinkable@@ -69,11 +69,6 @@ -- all comments included using Opt_KeepRawTokenStream type instance RuleResult GetParsedModuleWithComments = ParsedModule --- | The dependency information produced by following the imports recursively.--- This rule will succeed even if there is an error, e.g., a module could not be located,--- a module could not be parsed or an import cycle.-type instance RuleResult GetDependencyInformation = DependencyInformation- type instance RuleResult GetModuleGraph = DependencyInformation  data GetKnownTargets = GetKnownTargets@@ -262,8 +257,8 @@ -- | Resolve the imports in a module to the file path of a module in the same package type instance RuleResult GetLocatedImports = [(Located ModuleName, Maybe ArtifactsLocation)] --- | This rule is used to report import cycles. It depends on GetDependencyInformation.--- We cannot report the cycles directly from GetDependencyInformation since+-- | This rule is used to report import cycles. It depends on GetModuleGraph.+-- We cannot report the cycles directly from GetModuleGraph since -- we can only report diagnostics for the current file. type instance RuleResult ReportImportCycles = () @@ -400,11 +395,6 @@     deriving (Eq, Show, Typeable, Generic) instance Hashable NeedsCompilation instance NFData   NeedsCompilation--data GetDependencyInformation = GetDependencyInformation-    deriving (Eq, Show, Typeable, Generic)-instance Hashable GetDependencyInformation-instance NFData   GetDependencyInformation  data GetModuleGraph = GetModuleGraph     deriving (Eq, Show, Typeable, Generic)
src/Development/IDE/Core/Rules.hs view
@@ -23,7 +23,6 @@     defineEarlyCutOffNoFile,     mainRule,     RulesConfig(..),-    getDependencies,     getParsedModule,     getParsedModuleWithComments,     getClientConfigAction,@@ -34,7 +33,6 @@     getParsedModuleRule,     getParsedModuleWithCommentsRule,     getLocatedImportsRule,-    getDependencyInformationRule,     reportImportCyclesRule,     typeCheckRule,     getDocMapRule,@@ -68,6 +66,7 @@ import           Control.Concurrent.Strict import           Control.DeepSeq import           Control.Exception.Safe+import           Control.Exception                            (evaluate) import           Control.Monad.Extra import           Control.Monad.Reader import           Control.Monad.State@@ -90,8 +89,10 @@ import           Data.IntMap.Strict                           (IntMap) import qualified Data.IntMap.Strict                           as IntMap import           Data.List+import           Data.List.Extra                              (nubOrdOn) import qualified Data.Map                                     as M import           Data.Maybe+import           Data.Proxy import qualified Data.Text.Utf16.Rope                         as Rope import qualified Data.Set                                     as Set import qualified Data.Text                                    as T@@ -135,7 +136,8 @@ import qualified HieDb import           Ide.Plugin.Config import qualified Language.LSP.Server                          as LSP-import           Language.LSP.Types                           (SMethod (SCustomMethod, SWindowShowMessage), ShowMessageParams (ShowMessageParams), MessageType (MtInfo))+import           Language.LSP.Protocol.Types                  (ShowMessageParams (ShowMessageParams), MessageType (MessageType_Info))+import           Language.LSP.Protocol.Message                (SMethod (SMethod_CustomMethod, SMethod_WindowShowMessage)) import           Language.LSP.VFS import           System.Directory                             (makeAbsolute, doesFileExist) import           Data.Default                                 (def, Default)@@ -151,13 +153,14 @@ import Language.LSP.Server (LspT) import System.Info.Extra (isWindows) import HIE.Bios.Ghc.Gap (hostIsDynamic)-import Development.IDE.Types.Logger (Recorder, logWith, cmapWithPrio, WithPriority, Pretty (pretty), (<+>), nest, vcat)+import Ide.Logger (Recorder, logWith, cmapWithPrio, WithPriority, Pretty (pretty), (<+>), nest, vcat) import qualified Development.IDE.Core.Shake as Shake-import qualified Development.IDE.Types.Logger as Logger+import qualified Ide.Logger as Logger import qualified Development.IDE.Types.Shake as Shake import           Development.IDE.GHC.CoreFile import           Data.Time.Clock.POSIX             (posixSecondsToUTCTime) import Control.Monad.IO.Unlift+import qualified Data.IntMap as IM #if MIN_VERSION_ghc(9,3,0) import GHC.Unit.Module.Graph import GHC.Unit.Env@@ -165,6 +168,8 @@ #if MIN_VERSION_ghc(9,5,0) import GHC.Unit.Home.ModInfo #endif+import GHC (mgModSummaries)+import GHC.Fingerprint  data Log   = LogShake Shake.Log@@ -210,12 +215,6 @@ ------------------------------------------------------------ -- Exposed API --------------------------------------------------------------- | Get all transitive file dependencies of a given module.--- Does not include the file itself.-getDependencies :: NormalizedFilePath -> Action (Maybe [NormalizedFilePath])-getDependencies file =-    fmap transitiveModuleDeps . (`transitiveDeps` file) <$> use_ GetDependencyInformation file- getSourceFileSource :: NormalizedFilePath -> Action BS.ByteString getSourceFileSource nfp = do     (_, msource) <- getFileContents nfp@@ -314,7 +313,7 @@ --   Ignore Haddock errors that are in both. Demote Haddock-only errors to warnings. mergeParseErrorsHaddock :: [FileDiagnostic] -> [FileDiagnostic] -> [FileDiagnostic] mergeParseErrorsHaddock normal haddock = normal ++-    [ (a,b,c{_severity = Just DsWarning, _message = fixMessage $ _message c})+    [ (a,b,c{_severity = Just DiagnosticSeverity_Warning, _message = fixMessage $ _message c})     | (a,b,c) <- haddock, Diag._range c `Set.notMember` locations]   where     locations = Set.fromList $ map (Diag._range . thd3) normal@@ -420,17 +419,17 @@ execRawDepM :: Monad m => StateT (RawDependencyInformation, IntMap a1) m a2 -> m (RawDependencyInformation, IntMap a1) execRawDepM act =     execStateT act-        ( RawDependencyInformation IntMap.empty emptyPathIdMap IntMap.empty IntMap.empty+        ( RawDependencyInformation IntMap.empty emptyPathIdMap IntMap.empty         , IntMap.empty         )  -- | Given a target file path, construct the raw dependency results by following -- imports recursively.-rawDependencyInformation :: [NormalizedFilePath] -> Action RawDependencyInformation+rawDependencyInformation :: [NormalizedFilePath] -> Action (RawDependencyInformation, BootIdMap) rawDependencyInformation fs = do     (rdi, ss) <- execRawDepM (goPlural fs)     let bm = IntMap.foldrWithKey (updateBootMap rdi) IntMap.empty ss-    return (rdi { rawBootMap = bm })+    return (rdi, bm)   where     goPlural ff = do         mss <- lift $ (fmap.fmap) msrModSummary <$> uses GetModSummaryWithoutTimestamps ff@@ -449,9 +448,9 @@           fId <- getFreshFid al           -- Record this module and its location           whenJust msum $ \ms ->-            modifyRawDepInfo (\rd -> rd { rawModuleNameMap = IntMap.insert (getFilePathId fId)-                                                                           (ShowableModuleName (moduleName $ ms_mod ms))-                                                                           (rawModuleNameMap rd)})+            modifyRawDepInfo (\rd -> rd { rawModuleMap = IntMap.insert (getFilePathId fId)+                                                                           (ShowableModule $ ms_mod ms)+                                                                           (rawModuleMap rd)})           -- Adding an edge to the bootmap so we can make sure to           -- insert boot nodes before the real files.           addBootMap al fId@@ -523,38 +522,37 @@     dropBootSuffix :: FilePath -> FilePath     dropBootSuffix hs_src = reverse . drop (length @[] "-boot") . reverse $ hs_src -getDependencyInformationRule :: Recorder (WithPriority Log) -> Rules ()-getDependencyInformationRule recorder =-    define (cmapWithPrio LogShake recorder) $ \GetDependencyInformation file -> do-       rawDepInfo <- rawDependencyInformation [file]-       pure ([], Just $ processDependencyInformation rawDepInfo)- reportImportCyclesRule :: Recorder (WithPriority Log) -> Rules () reportImportCyclesRule recorder =-    define (cmapWithPrio LogShake recorder) $ \ReportImportCycles file -> fmap (\errs -> if null errs then ([], Just ()) else (errs, Nothing)) $ do-        DependencyInformation{..} <- use_ GetDependencyInformation file-        let fileId = pathToId depPathIdMap file-        case IntMap.lookup (getFilePathId fileId) depErrorNodes of-            Nothing -> pure []-            Just errs -> do-                let cycles = mapMaybe (cycleErrorInFile fileId) (toList errs)-                -- Convert cycles of files into cycles of module names-                forM cycles $ \(imp, files) -> do-                    modNames <- forM files $ \fileId -> do-                        let file = idToPath depPathIdMap fileId-                        getModuleName file-                    pure $ toDiag imp $ sort modNames+    defineEarlyCutoff (cmapWithPrio LogShake recorder) $ Rule $ \ReportImportCycles file -> fmap (\errs -> if null errs then (Just "1",([], Just ())) else (Nothing, (errs, Nothing))) $ do+        DependencyInformation{..} <- useNoFile_ GetModuleGraph+        case pathToId depPathIdMap file of+          -- The header of the file does not parse, so it can't be part of any import cycles.+          Nothing -> pure []+          Just fileId ->+            case IntMap.lookup (getFilePathId fileId) depErrorNodes of+              Nothing -> pure []+              Just errs -> do+                  let cycles = mapMaybe (cycleErrorInFile fileId) (toList errs)+                  -- Convert cycles of files into cycles of module names+                  forM cycles $ \(imp, files) -> do+                      modNames <- forM files $ \fileId -> do+                          let file = idToPath depPathIdMap fileId+                          getModuleName file+                      pure $ toDiag imp $ sort modNames     where cycleErrorInFile f (PartOfCycle imp fs)             | f `elem` fs = Just (imp, fs)           cycleErrorInFile _ _ = Nothing           toDiag imp mods = (fp , ShowDiag , ) $ Diagnostic             { _range = rng-            , _severity = Just DsError+            , _severity = Just DiagnosticSeverity_Error             , _source = Just "Import cycle detection"             , _message = "Cyclic module dependency between " <> showCycle mods             , _code = Nothing             , _relatedInformation = Nothing             , _tags = Nothing+            , _codeDescription = Nothing+            , _data_ = Nothing             }             where rng = fromMaybe noRange $ srcSpanToRange (getLoc imp)                   fp = toNormalizedFilePath' $ fromMaybe noFilePath $ srcSpanToFilename (getLoc imp)@@ -591,7 +589,7 @@   diagsWrite <- case isFoi of     IsFOI Modified{firstOpen = False} -> do       when (coerce $ ideTesting se) $ liftIO $ mRunLspT (lspEnv se) $-        LSP.sendNotification (SCustomMethod "ghcide/reference/ready") $+        LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $           toJSON $ fromNormalizedFilePath f       pure []     _ | Just asts <- masts -> do@@ -677,11 +675,38 @@   pure (LBS.toStrict $ B.encode $ hash fs, unhashed fs)  getModuleGraphRule :: Recorder (WithPriority Log) -> Rules ()-getModuleGraphRule recorder = defineNoFile (cmapWithPrio LogShake recorder) $ \GetModuleGraph -> do+getModuleGraphRule recorder = defineEarlyCutOffNoFile (cmapWithPrio LogShake recorder) $ \GetModuleGraph -> do   fs <- toKnownFiles <$> useNoFile_ GetKnownTargets-  rawDepInfo <- rawDependencyInformation (HashSet.toList fs)-  pure $ processDependencyInformation rawDepInfo+  dependencyInfoForFiles (HashSet.toList fs) +dependencyInfoForFiles :: [NormalizedFilePath] -> Action (BS.ByteString, DependencyInformation)+dependencyInfoForFiles fs = do+  (rawDepInfo, bm) <- rawDependencyInformation fs+  let (all_fs, _all_ids) = unzip $ HM.toList $ pathToIdMap $ rawPathIdMap rawDepInfo+  msrs <- uses GetModSummaryWithoutTimestamps all_fs+  let mss = map (fmap msrModSummary) msrs+#if MIN_VERSION_ghc(9,3,0)+  let deps = map (\i -> IM.lookup (getFilePathId i) (rawImports rawDepInfo)) _all_ids+      nodeKeys = IM.fromList $ catMaybes $ zipWith (\fi mms -> (getFilePathId fi,) . NodeKey_Module . msKey <$> mms) _all_ids mss+      mns = catMaybes $ zipWith go mss deps+      go (Just ms) (Just (Right (ModuleImports xs))) = Just $ ModuleNode this_dep_keys ms+        where this_dep_ids = mapMaybe snd xs+              this_dep_keys = mapMaybe (\fi -> IM.lookup (getFilePathId fi) nodeKeys) this_dep_ids+      go (Just ms) _ = Just $ ModuleNode [] ms+      go _ _ = Nothing+      mg = mkModuleGraph mns+#else+  let mg = mkModuleGraph $+#if MIN_VERSION_ghc(9,2,0)+        -- We don't do any instantiation for backpack at this point of time, so it is OK to use+        -- 'extendModSummaryNoDeps'.+        -- This may have to change in the future.+          map extendModSummaryNoDeps $+#endif+          (catMaybes mss)+#endif+  pure (fingerprintToBS $ Util.fingerprintFingerprints $ map (maybe fingerprint0 msrFingerprint) msrs, processDependencyInformation rawDepInfo bm mg)+ -- This is factored out so it can be directly called from the GetModIface -- rule. Directly calling this rule means that on the initial load we can -- garbage collect all the intermediate typechecked modules rather than@@ -750,11 +775,11 @@         ghcSessionDepsDefinition fullModSummary ghcSessionDepsConfig env file  newtype GhcSessionDepsConfig = GhcSessionDepsConfig-    { checkForImportCycles :: Bool+    { fullModuleGraph :: Bool     } instance Default GhcSessionDepsConfig where   def = GhcSessionDepsConfig-    { checkForImportCycles = True+    { fullModuleGraph = True     }  -- | Note [GhcSessionDeps]@@ -773,7 +798,7 @@     case mbdeps of         Nothing -> return Nothing         Just deps -> do-            when checkForImportCycles $ void $ uses_ ReportImportCycles deps+            when fullModuleGraph $ void $ use_ ReportImportCycles file             ms <- msrModSummary <$> if fullModSummary                 then use_ GetModSummary file                 else use_ GetModSummaryWithoutTimestamps file@@ -781,21 +806,40 @@             depSessions <- map hscEnv <$> uses_ (GhcSessionDeps_ fullModSummary) deps             ifaces <- uses_ GetModIface deps             let inLoadOrder = map (\HiFileResult{..} -> HomeModInfo hirModIface hirModDetails emptyHomeModInfoLinkable) ifaces+            mg <- do+              if fullModuleGraph+              then depModuleGraph <$> useNoFile_ GetModuleGraph+              else do+                let mgs = map hsc_mod_graph depSessions #if MIN_VERSION_ghc(9,3,0)-            -- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph-            -- also points to all the direct descendants of the current module. To get the keys for the descendants-            -- we must get their `ModSummary`s-            !final_deps <- do-              dep_mss <- map msrModSummary <$> uses_ GetModSummaryWithoutTimestamps deps-             -- Don't want to retain references to the entire ModSummary when just the key will do-              return $!! map (NodeKey_Module . msKey) dep_mss-            let moduleNode = (ms, final_deps)+                -- On GHC 9.4+, the module graph contains not only ModSummary's but each `ModuleNode` in the graph+                -- also points to all the direct descendants of the current module. To get the keys for the descendants+                -- we must get their `ModSummary`s+                !final_deps <- do+                  dep_mss <- map msrModSummary <$> uses_ GetModSummaryWithoutTimestamps deps+                  return $!! map (NodeKey_Module . msKey) dep_mss+                let module_graph_nodes =+                      nubOrdOn mkNodeKey (ModuleNode final_deps ms : concatMap mgModSummaries' mgs) #else-            let moduleNode = ms+                let module_graph_nodes =+#if MIN_VERSION_ghc(9,2,0)+                      -- We don't do any instantiation for backpack at this point of time, so it is OK to use+                      -- 'extendModSummaryNoDeps'.+                      -- This may have to change in the future.+                      map extendModSummaryNoDeps $ #endif-            session' <- liftIO $ mergeEnvs hsc moduleNode inLoadOrder depSessions+                      nubOrdOn ms_mod (ms : concatMap mgModSummaries mgs)+#endif+                liftIO $ evaluate $ liftRnf rwhnf module_graph_nodes+                return $ mkModuleGraph module_graph_nodes+            session' <- liftIO $ mergeEnvs hsc mg ms inLoadOrder depSessions -            Just <$> liftIO (newHscEnvEqWithImportPaths (envImportPaths env) session' [])+            -- Here we avoid a call to to `newHscEnvEqWithImportPaths`, which creates a new+            -- ExportsMap when it is called. We only need to create the ExportsMap once per+            -- session, while `ghcSessionDepsDefinition` will be called for each file we need+            -- to compile. `updateHscEnvEq` will refresh the HscEnv (session') and also+            -- generate a new Unique.+            Just <$> liftIO (updateHscEnvEq env session')  -- | Load a iface from disk, or generate it if there isn't one or it is out of date -- This rule also ensures that the `.hie` and `.o` (if needed) files are written out.@@ -855,7 +899,7 @@       -> do       -- All good, the db has indexed the file       when (coerce $ ideTesting se) $ liftIO $ mRunLspT (lspEnv se) $-        LSP.sendNotification (SCustomMethod "ghcide/reference/ready") $+        LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/reference/ready")) $           toJSON $ fromNormalizedFilePath f     -- Not in db, must re-index     _ -> do@@ -1192,8 +1236,16 @@ instance IsIdeGlobal CompiledLinkables  data RulesConfig = RulesConfig-    { -- | Disable import cycle checking for improved performance in large codebases-      checkForImportCycles :: Bool+    { -- | Share the computation for the entire module graph+      -- We usually compute the full module graph for the project+      -- and share it for all files.+      -- However, in large projects it might not be desirable to wait+      -- for computing the entire module graph before starting to+      -- typecheck a particular file.+      -- Disabling this drastically decreases sharing and is likely to+      -- increase memory usage if you have multiple files open+      -- Disabling this also disables checking for import cycles+      fullModuleGraph :: Bool     -- | Disable TH for improved performance in large codebases     , enableTemplateHaskell :: Bool     -- | Warning to show when TH is not supported by the current HLS binary@@ -1206,8 +1258,8 @@         displayTHWarning :: LspT c IO ()         displayTHWarning             | not isWindows && not hostIsDynamic = do-                LSP.sendNotification SWindowShowMessage $-                    ShowMessageParams MtInfo thWarningMessage+                LSP.sendNotification SMethod_WindowShowMessage $+                    ShowMessageParams MessageType_Info thWarningMessage             | otherwise = return ()  thWarningMessage :: T.Text@@ -1227,11 +1279,10 @@     getParsedModuleRule recorder     getParsedModuleWithCommentsRule recorder     getLocatedImportsRule recorder-    getDependencyInformationRule recorder     reportImportCyclesRule recorder     typeCheckRule recorder     getDocMapRule recorder-    loadGhcSession recorder def{checkForImportCycles}+    loadGhcSession recorder def{fullModuleGraph}     getModIfaceFromDiskRule recorder     getModIfaceFromDiskAndIndexRule recorder     getModIfaceRule recorder
src/Development/IDE/Core/Service.hs view
@@ -23,16 +23,16 @@ import           Development.IDE.Core.FileExists  (fileExistsRules) import           Development.IDE.Core.OfInterest  hiding (Log, LogShake) import           Development.IDE.Graph-import           Development.IDE.Types.Logger     as Logger (Logger,+import           Development.IDE.Types.Options    (IdeOptions (..))+import           Ide.Logger                       as Logger (Logger,                                                              Pretty (pretty),                                                              Priority (Debug),                                                              Recorder,                                                              WithPriority,                                                              cmapWithPrio)-import           Development.IDE.Types.Options    (IdeOptions (..)) import           Ide.Plugin.Config+import qualified Language.LSP.Protocol.Types      as LSP import qualified Language.LSP.Server              as LSP-import qualified Language.LSP.Types               as LSP  import           Control.Monad import qualified Development.IDE.Core.FileExists  as FileExists
src/Development/IDE/Core/Shake.hs view
@@ -77,7 +77,7 @@     garbageCollectDirtyKeys,     garbageCollectDirtyKeysOlderThan,     Log(..),-    VFSModified(..), getClientConfigAction+    VFSModified(..), getClientConfigAction,     ) where  import           Control.Concurrent.Async@@ -152,8 +152,6 @@ import qualified Development.IDE.Types.Exports          as ExportsMap import           Development.IDE.Types.KnownTargets import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger           hiding (Priority)-import qualified Development.IDE.Types.Logger           as Logger import           Development.IDE.Types.Monitoring       (Monitoring (..)) import           Development.IDE.Types.Options import           Development.IDE.Types.Shake@@ -161,16 +159,18 @@ import           GHC.Fingerprint import           GHC.Stack                              (HasCallStack) import           HieDb.Types+import           Ide.Logger                             hiding (Priority)+import qualified Ide.Logger                             as Logger import           Ide.Plugin.Config import qualified Ide.PluginUtils                        as HLS import           Ide.Types                              (IdePlugins (IdePlugins),                                                          PluginDescriptor (pluginId),                                                          PluginId) import           Language.LSP.Diagnostics+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types+import qualified Language.LSP.Protocol.Types            as LSP import qualified Language.LSP.Server                    as LSP-import           Language.LSP.Types-import qualified Language.LSP.Types                     as LSP-import           Language.LSP.Types.Capabilities import           Language.LSP.VFS                       hiding (start) import qualified "list-t" ListT import           OpenTelemetry.Eventlog@@ -303,7 +303,7 @@ type WithIndefiniteProgressFunc = forall a.     T.Text -> LSP.ProgressCancellable -> IO a -> IO a -type GetStalePersistent = NormalizedFilePath -> IdeAction (Maybe (Dynamic,PositionDelta,TextDocumentVersion))+type GetStalePersistent = NormalizedFilePath -> IdeAction (Maybe (Dynamic,PositionDelta,Maybe Int32))  getShakeExtras :: Action ShakeExtras getShakeExtras = do@@ -344,7 +344,7 @@ -- This is called when we don't already have a result, or computing the rule failed. -- The result of this function will always be marked as 'stale', and a 'proper' rebuild of the rule will -- be queued if the rule hasn't run before.-addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,TextDocumentVersion))) -> Rules ()+addPersistentRule :: IdeRule k v => k -> (NormalizedFilePath -> IdeAction (Maybe (v,PositionDelta,Maybe Int32))) -> Rules () addPersistentRule k getVal = do   ShakeExtras{persistentKeys} <- getShakeExtrasRules   void $ liftIO $ atomically $ modifyTVar' persistentKeys $ insertKeyMap (newKey k) (fmap (fmap (first3 toDyn)) . getVal)@@ -639,7 +639,6 @@         actionQueue <- newQueue          let clientCapabilities = maybe def LSP.resClientCapabilities lspEnv-         dirtyKeys <- newTVarIO mempty         -- Take one VFS snapshot at the start         vfsVar <- newTVarIO =<< vfsSnapshot lspEnv@@ -900,7 +899,7 @@         logDebug logger $ T.pack $             label <> " of " <> show n <> " keys (took " <> showDuration t <> ")"     when (coerce ideTesting) $ liftIO $ mRunLspT lspEnv $-        LSP.sendNotification (SCustomMethod "ghcide/GC")+        LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/GC"))                              (toJSON $ mapMaybe (fmap showKey . fromKeyType) garbage)     return garbage @@ -962,13 +961,23 @@     => k -> NormalizedFilePath -> Action (Maybe (v, PositionMapping)) useWithStale key file = runIdentity <$> usesWithStale key (Identity file) --- | Request a Rule result, it not available return the last computed result which may be stale.---   Errors out if none available.+-- |Request a Rule result, it not available return the last computed result+--  which may be stale.+--+-- Throws an `BadDependency` exception which is caught by the rule system if+-- none available.+--+-- WARNING: Not suitable for PluginHandlers. Use `useWithStaleE` instead. useWithStale_ :: IdeRule k v     => k -> NormalizedFilePath -> Action (v, PositionMapping) useWithStale_ key file = runIdentity <$> usesWithStale_ key (Identity file) --- | Plural version of 'useWithStale_'+-- |Plural version of 'useWithStale_'+--+-- Throws an `BadDependency` exception which is caught by the rule system if+-- none available.+--+-- WARNING: Not suitable for PluginHandlers. usesWithStale_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f (v, PositionMapping)) usesWithStale_ key files = do     res <- usesWithStale key files@@ -1042,12 +1051,24 @@ useNoFile :: IdeRule k v => k -> Action (Maybe v) useNoFile key = use key emptyFilePath +-- Requests a rule if available.+--+-- Throws an `BadDependency` exception which is caught by the rule system if+-- none available.+--+-- WARNING: Not suitable for PluginHandlers. Use `useE` instead. use_ :: IdeRule k v => k -> NormalizedFilePath -> Action v use_ key file = runIdentity <$> uses_ key (Identity file)  useNoFile_ :: IdeRule k v => k -> Action v useNoFile_ key = use_ key emptyFilePath +-- |Plural version of `use_`+--+-- Throws an `BadDependency` exception which is caught by the rule system if+-- none available.+--+-- WARNING: Not suitable for PluginHandlers. Use `usesE` instead. uses_ :: (Traversable f, IdeRule k v) => k -> f NormalizedFilePath -> Action (f v) uses_ key files = do     res <- uses key files@@ -1128,7 +1149,7 @@  defineEarlyCutoff'     :: forall k v. IdeRule k v-    => (TextDocumentVersion -> [FileDiagnostic] -> Action ()) -- ^ update diagnostics+    => (Maybe Int32 -> [FileDiagnostic] -> Action ()) -- ^ update diagnostics     -- | compare current and previous for freshness     -> (BS.ByteString -> BS.ByteString -> Bool)     -> k@@ -1220,7 +1241,7 @@ updateFileDiagnostics :: MonadIO m   => Recorder (WithPriority Log)   -> NormalizedFilePath-  -> TextDocumentVersion+  -> Maybe Int32   -> Key   -> ShakeExtras   -> [(ShowDiagnostic,Diagnostic)] -- ^ current results@@ -1254,15 +1275,15 @@                         Just env -> LSP.runLspT env $ do                             liftIO $ tag "count" (show $ Prelude.length newDiags)                             liftIO $ tag "key" (show k)-                            LSP.sendNotification LSP.STextDocumentPublishDiagnostics $-                                LSP.PublishDiagnosticsParams (fromNormalizedUri uri) (fmap fromIntegral ver) (List newDiags)+                            LSP.sendNotification SMethod_TextDocumentPublishDiagnostics $+                                LSP.PublishDiagnosticsParams (fromNormalizedUri uri) (fmap fromIntegral ver) ( newDiags)                  return action     where         diagsFromRule :: Diagnostic -> Diagnostic         diagsFromRule c@Diagnostic{_range}             | coerce ideTesting = c                 {_relatedInformation =-                    Just $ List [+                    Just $  [                         DiagnosticRelatedInformation                             (Location                                 (filePathToUri $ fromNormalizedFilePath fp)@@ -1297,7 +1318,7 @@   (forall a. String -> String -> a -> a) ->   STMDiagnosticStore ->   NormalizedUri ->-  TextDocumentVersion ->+  Maybe Int32 ->   DiagnosticsBySource ->   STM [LSP.Diagnostic] updateSTMDiagnostics addTag store uri mv newDiagsBySource =@@ -1314,7 +1335,7 @@ setStageDiagnostics     :: (forall a. String -> String -> a -> a)     -> NormalizedUri-    -> TextDocumentVersion -- ^ the time that the file these diagnostics originate from was last edited+    -> Maybe Int32 -- ^ the time that the file these diagnostics originate from was last edited     -> T.Text     -> [LSP.Diagnostic]     -> STMDiagnosticStore@@ -1329,8 +1350,8 @@ getAllDiagnostics =     fmap (concatMap (\(k,v) -> map (fromUri k,ShowDiag,) $ getDiagnosticsFromStore v)) . ListT.toList . STM.listT -updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> List TextDocumentContentChangeEvent -> STM ()-updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} (List changes) =+updatePositionMapping :: IdeState -> VersionedTextDocumentIdentifier -> [TextDocumentContentChangeEvent] -> STM ()+updatePositionMapping IdeState{shakeExtras = ShakeExtras{positionMapping}} VersionedTextDocumentIdentifier{..} changes =     STM.focus (Focus.alter f) uri positionMapping       where         uri = toNormalizedUri _uri@@ -1341,8 +1362,5 @@                 -- used which is evident in long running sessions.                 EM.mapAccumRWithKey (\acc _k (delta, _) -> let new = addDelta delta acc in (new, (delta, acc)))                   zeroMapping-                  (EM.insert actual_version (shared_change, zeroMapping) mappingForUri)+                  (EM.insert _version (shared_change, zeroMapping) mappingForUri)         shared_change = mkDelta changes-        actual_version = case _version of-          Nothing -> error "Nothing version from server" -- This is a violation of the spec-          Just v  -> v
src/Development/IDE/Core/Tracing.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE PackageImports  #-} {-# LANGUAGE PatternSynonyms #-} {-# HLINT ignore #-}+ module Development.IDE.Core.Tracing     ( otTracedHandler     , otTracedAction@@ -29,9 +30,9 @@ import           Development.IDE.Types.Diagnostics (FileDiagnostic,                                                     showDiagnostics) import           Development.IDE.Types.Location    (Uri (..))-import           Development.IDE.Types.Logger      (Logger (Logger))+import           Ide.Logger                        (Logger (Logger)) import           Ide.Types                         (PluginId (..))-import           Language.LSP.Types                (NormalizedFilePath,+import           Language.LSP.Protocol.Types       (NormalizedFilePath,                                                     fromNormalizedFilePath) import           OpenTelemetry.Eventlog            (SpanInFlight (..), addEvent,                                                     beginSpan, endSpan, setTag,
src/Development/IDE/GHC/Compat/Core.hs view
@@ -39,6 +39,7 @@     lookupType,     needWiredInHomeIface,     loadWiredInHomeIface,+    readIface,     loadSysInterface,     importDecl, #if MIN_VERSION_ghc(8,8,0)
src/Development/IDE/GHC/Compat/Units.hs view
@@ -50,50 +50,54 @@     filterInplaceUnits,     FinderCache,     showSDocForUser',+    findImportedModule,     ) where  import           Control.Monad-import qualified Data.List.NonEmpty              as NE-import qualified Data.Map.Strict                 as Map+import qualified Data.List.NonEmpty                    as NE+import qualified Data.Map.Strict                       as Map #if MIN_VERSION_ghc(9,3,0) import           GHC.Unit.Home.ModInfo #endif #if MIN_VERSION_ghc(9,0,0) #if MIN_VERSION_ghc(9,2,0)-import qualified GHC.Data.ShortText              as ST+import qualified GHC.Data.ShortText                    as ST #if !MIN_VERSION_ghc(9,3,0)-import           GHC.Driver.Env                  (hsc_unit_dbs)+import           GHC.Driver.Env                        (hsc_unit_dbs) #endif import           GHC.Driver.Ppr import           GHC.Unit.Env import           GHC.Unit.External-import           GHC.Unit.Finder+import           GHC.Unit.Finder                       hiding+                                                       (findImportedModule) #else import           GHC.Driver.Types #endif import           GHC.Data.FastString-import qualified GHC.Driver.Session              as DynFlags+import qualified GHC.Driver.Session                    as DynFlags import           GHC.Types.Unique.Set-import qualified GHC.Unit.Info                   as UnitInfo-import           GHC.Unit.State                  (LookupResult, UnitInfo,-                                                  UnitState (unitInfoMap))-import qualified GHC.Unit.State                  as State-import           GHC.Unit.Types                  hiding (moduleUnit, toUnitId)-import qualified GHC.Unit.Types                  as Unit+import qualified GHC.Unit.Info                         as UnitInfo+import           GHC.Unit.State                        (LookupResult, UnitInfo,+                                                        UnitState (unitInfoMap))+import qualified GHC.Unit.State                        as State+import           GHC.Unit.Types                        hiding (moduleUnit,+                                                        toUnitId)+import qualified GHC.Unit.Types                        as Unit import           GHC.Utils.Outputable #else import qualified DynFlags import           FastString-import           GhcPlugins                      (SDoc, showSDocForUser)+import           GhcPlugins                            (SDoc, showSDocForUser) import           HscTypes-import           Module                          hiding (moduleUnitId)+import           Module                                hiding (moduleUnitId) import qualified Module-import           Packages                        (InstalledPackageInfo (haddockInterfaces, packageName),-                                                  LookupResult, PackageConfig,-                                                  PackageConfigMap,-                                                  PackageState,-                                                  getPackageConfigMap,-                                                  lookupPackage')+import           Packages                              (InstalledPackageInfo (haddockInterfaces, packageName),+                                                        LookupResult,+                                                        PackageConfig,+                                                        PackageConfigMap,+                                                        PackageState,+                                                        getPackageConfigMap,+                                                        lookupPackage') import qualified Packages #endif @@ -101,12 +105,23 @@ import           Development.IDE.GHC.Compat.Env import           Development.IDE.GHC.Compat.Outputable #if MIN_VERSION_ghc(9,0,0) && !MIN_VERSION_ghc(9,2,0)-import           Data.Map                        (Map)+import           Data.Map                              (Map) #endif import           Data.Either import           Data.Version import qualified GHC +#if MIN_VERSION_ghc(9,3,0)+import           GHC.Types.PkgQual                     (PkgQual (NoPkgQual))+#endif+#if MIN_VERSION_ghc(9,1,0)+import qualified GHC.Unit.Finder                       as GHC+#elif MIN_VERSION_ghc(9,0,0)+import qualified GHC.Driver.Finder                     as GHC+#else+import qualified Finder                                as GHC+#endif+ #if MIN_VERSION_ghc(9,0,0) type PreloadUnitClosure = UniqSet UnitId #if MIN_VERSION_ghc(9,2,0)@@ -407,3 +422,14 @@ #else showSDocForUser' env = showSDocForUser (hsc_dflags env) #endif++findImportedModule :: HscEnv -> ModuleName -> IO (Maybe Module)+findImportedModule env mn = do+#if MIN_VERSION_ghc(9,3,0)+    res <- GHC.findImportedModule env mn NoPkgQual+#else+    res <- GHC.findImportedModule env mn Nothing+#endif+    case res of+        Found _ mod -> pure . pure $ mod+        _           -> pure Nothing
src/Development/IDE/GHC/Error.hs view
@@ -44,7 +44,7 @@ import           Development.IDE.Types.Diagnostics as D import           Development.IDE.Types.Location import           GHC-import           Language.LSP.Types                (isSubrangeOf)+import           Language.LSP.Protocol.Types       (isSubrangeOf)   diagFromText :: T.Text -> D.DiagnosticSeverity -> SrcSpan -> T.Text -> FileDiagnostic@@ -57,6 +57,8 @@     , _code     = Nothing     , _relatedInformation = Nothing     , _tags     = Nothing+    , _codeDescription = Nothing+    , _data_   = Nothing     }  -- | Produce a GHC-style error from a source span and a message.@@ -132,13 +134,13 @@ toDSeverity SevOutput      = Nothing toDSeverity SevInteractive = Nothing toDSeverity SevDump        = Nothing-toDSeverity SevInfo        = Just DsInfo-toDSeverity SevFatal       = Just DsError+toDSeverity SevInfo        = Just DiagnosticSeverity_Information+toDSeverity SevFatal       = Just DiagnosticSeverity_Error #else toDSeverity SevIgnore      = Nothing #endif-toDSeverity SevWarning     = Just DsWarning-toDSeverity SevError       = Just DsError+toDSeverity SevWarning     = Just DiagnosticSeverity_Warning+toDSeverity SevError       = Just DiagnosticSeverity_Error   -- | Produce a bag of GHC-style errors (@ErrorMessages@) from the given@@ -186,7 +188,7 @@   diagFromGhcException :: T.Text -> DynFlags -> GhcException -> [FileDiagnostic]-diagFromGhcException diagSource dflags exc = diagFromString diagSource DsError (noSpan "<Internal>") (showGHCE dflags exc)+diagFromGhcException diagSource dflags exc = diagFromString diagSource DiagnosticSeverity_Error (noSpan "<Internal>") (showGHCE dflags exc)  showGHCE :: DynFlags -> GhcException -> String showGHCE dflags exc = case exc of
src/Development/IDE/GHC/Orphans.hs view
@@ -40,6 +40,7 @@ import           Data.Text                  (unpack) #if MIN_VERSION_ghc(9,0,0) import           GHC.ByteCode.Types+import GHC (ModuleGraph) #else import           ByteCodeTypes #endif@@ -215,6 +216,9 @@ instance Hashable OccName where hashWithSalt s n = hashWithSalt s (getKey $ getUnique n)  instance Show HomeModInfo where show = show . mi_module . hm_iface++instance Show ModuleGraph where show _ = "ModuleGraph {..}"+instance NFData ModuleGraph where rnf = rwhnf  instance NFData HomeModInfo where   rnf (HomeModInfo iface dets link) = rwhnf iface `seq` rnf dets `seq` rnf link
src/Development/IDE/GHC/Warnings.hs view
@@ -12,7 +12,7 @@ import           Development.IDE.GHC.Compat import           Development.IDE.GHC.Error import           Development.IDE.Types.Diagnostics-import           Language.LSP.Types                (type (|?) (..))+import           Language.LSP.Protocol.Types       (type (|?) (..))   -- | Take a GHC monadic action (e.g. @typecheckModule pm@ for some
src/Development/IDE/Import/DependencyInformation.hs view
@@ -10,8 +10,9 @@   , TransitiveDependencies(..)   , FilePathId(..)   , NamedModuleDep(..)-  , ShowableModuleName(..)-  , PathIdMap+  , ShowableModule(..)+  , ShowableModuleEnv(..)+  , PathIdMap (..)   , emptyPathIdMap   , getPathId   , lookupPathToId@@ -23,7 +24,7 @@   , transitiveDeps   , transitiveReverseDependencies   , immediateReverseDependencies-+  , lookupModuleFile   , BootIdMap   , insertBootId   ) where@@ -53,6 +54,7 @@ import           Development.IDE.Types.Location  import           GHC+import Development.IDE.GHC.Compat  -- | The imports for a given module. newtype ModuleImports = ModuleImports@@ -103,8 +105,8 @@ insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) } -pathToId :: PathIdMap -> NormalizedFilePath -> FilePathId-pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.! path+pathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId+pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.!? path  lookupPathToId :: PathIdMap -> NormalizedFilePath -> Maybe FilePathId lookupPathToId PathIdMap{pathToIdMap} path = HMS.lookup path pathToIdMap@@ -128,15 +130,14 @@     -- corresponding hs file. It is used when topologically sorting as we     -- need to add edges between .hs-boot and .hs so that the .hs files     -- appear later in the sort.-    , rawBootMap   :: !BootIdMap-    , rawModuleNameMap :: !(FilePathIdMap ShowableModuleName)+    , rawModuleMap :: !(FilePathIdMap ShowableModule)     } deriving Show  data DependencyInformation =   DependencyInformation     { depErrorNodes        :: !(FilePathIdMap (NonEmpty NodeError))     -- ^ Nodes that cannot be processed correctly.-    , depModuleNames       :: !(FilePathIdMap ShowableModuleName)+    , depModules       :: !(FilePathIdMap ShowableModule)     , depModuleDeps        :: !(FilePathIdMap FilePathIdSet)     -- ^ For a non-error node, this contains the set of module immediate dependencies     -- in the same package.@@ -146,14 +147,25 @@     -- ^ Map from FilePath to FilePathId     , depBootMap           :: !BootIdMap     -- ^ Map from hs-boot file to the corresponding hs file+    , depModuleFiles       :: !(ShowableModuleEnv FilePathId)+    -- ^ Map from Module to the corresponding non-boot hs file+    , depModuleGraph       :: !ModuleGraph     } deriving (Show, Generic) -newtype ShowableModuleName =-  ShowableModuleName {showableModuleName :: ModuleName}+newtype ShowableModule =+  ShowableModule {showableModule :: Module}   deriving NFData -instance Show ShowableModuleName where show = moduleNameString . showableModuleName+newtype ShowableModuleEnv a =+  ShowableModuleEnv {showableModuleEnv :: ModuleEnv a} +instance Show a => Show (ShowableModuleEnv a) where+  show (ShowableModuleEnv x) = show (moduleEnvToList x)+instance NFData a => NFData (ShowableModuleEnv a) where+  rnf = rwhnf++instance Show ShowableModule where show = moduleNameString . moduleName . showableModule+ reachableModules :: DependencyInformation -> [NormalizedFilePath] reachableModules DependencyInformation{..} =     map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps@@ -215,15 +227,17 @@    SuccessNode _ <> ErrorNode errs   = ErrorNode errs    SuccessNode a <> SuccessNode _    = SuccessNode a -processDependencyInformation :: RawDependencyInformation -> DependencyInformation-processDependencyInformation RawDependencyInformation{..} =+processDependencyInformation :: RawDependencyInformation -> BootIdMap -> ModuleGraph -> DependencyInformation+processDependencyInformation RawDependencyInformation{..} rawBootMap mg =   DependencyInformation     { depErrorNodes = IntMap.fromList errorNodes     , depModuleDeps = moduleDeps     , depReverseModuleDeps = reverseModuleDeps-    , depModuleNames = rawModuleNameMap+    , depModules = rawModuleMap     , depPathIdMap = rawPathIdMap     , depBootMap = rawBootMap+    , depModuleFiles = ShowableModuleEnv reverseModuleMap+    , depModuleGraph = mg     }   where resultGraph = buildResultGraph rawImports         (errorNodes, successNodes) = partitionNodeResults $ IntMap.toList resultGraph@@ -240,6 +254,7 @@           foldr (\(p, cs) res ->             let new = IntMap.fromList (map (, IntSet.singleton (coerce p)) (coerce cs))             in IntMap.unionWith IntSet.union new res ) IntMap.empty successEdges+        reverseModuleMap = mkModuleEnv $ map (\(i,sm) -> (showableModule sm, FilePathId i)) $ IntMap.toList rawModuleMap   -- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:@@ -328,7 +343,7 @@ -- | returns all transitive dependencies in topological order. transitiveDeps :: DependencyInformation -> NormalizedFilePath -> Maybe TransitiveDependencies transitiveDeps DependencyInformation{..} file = do-  let !fileId = pathToId depPathIdMap file+  !fileId <- pathToId depPathIdMap file   reachableVs <-       -- Delete the starting node       IntSet.delete (getFilePathId fileId) .@@ -350,6 +365,10 @@     boot_edge f = [getFilePathId f' | Just f' <- [IntMap.lookup f depBootMap]]      vs = topSort g++lookupModuleFile :: Module -> DependencyInformation -> Maybe NormalizedFilePath+lookupModuleFile mod DependencyInformation{..}+  = idToPath depPathIdMap <$> lookupModuleEnv (showableModuleEnv depModuleFiles) mod  newtype TransitiveDependencies = TransitiveDependencies   { transitiveModuleDeps :: [NormalizedFilePath]
src/Development/IDE/Import/FindImports.hs view
@@ -29,6 +29,7 @@ import           System.FilePath #if MIN_VERSION_ghc(9,3,0) import           GHC.Types.PkgQual+import           GHC.Unit.State #endif  data Import@@ -135,25 +136,45 @@ #else     Nothing -> do #endif++      mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : other_imports) exts targetFor isSource $ unLoc modName+      case mbFile of+        Nothing          -> lookupInPackageDB env+        Just (uid, file) -> toModLocation uid file+  where+    dflags = hsc_dflags env+    import_paths = mapMaybe (mkImportDirs env) comp_info+    other_imports =+#if MIN_VERSION_ghc(9,4,0)+      -- On 9.4+ instead of bringing all the units into scope, only bring into scope the units+      -- this one depends on+      -- This way if you have multiple units with the same module names, we won't get confused+      -- For example if unit a imports module M from unit B, when there is also a module M in unit C,+      -- and unit a only depends on unit b, without this logic there is the potential to get confused+      -- about which module unit a imports.+      -- Without multi-component support it is hard to recontruct the dependency environment so+      -- unit a will have both unit b and unit c in scope.+      map (\uid -> (uid, importPaths (homeUnitEnv_dflags (ue_findHomeUnitEnv uid ue)))) hpt_deps+    ue = hsc_unit_env env+    units = homeUnitEnv_units $ ue_findHomeUnitEnv (homeUnitId_ dflags) ue+    hpt_deps :: [UnitId]+    hpt_deps = homeUnitDepends units+#else+      import_paths'+#endif+       -- first try to find the module as a file. If we can't find it try to find it in the package       -- database.       -- Here the importPaths for the current modules are added to the front of the import paths from the other components.       -- This is particularly important for Paths_* modules which get generated for every component but unless you use it in       -- each component will end up being found in the wrong place and cause a multi-cradle match failure.-      let import_paths' =+    import_paths' = #if MIN_VERSION_ghc(9,3,0)             import_paths #else             map snd import_paths #endif -      mbFile <- locateModuleFile ((homeUnitId_ dflags, importPaths dflags) : import_paths') exts targetFor isSource $ unLoc modName-      case mbFile of-        Nothing          -> lookupInPackageDB env-        Just (uid, file) -> toModLocation uid file-  where-    dflags = hsc_dflags env-    import_paths = mapMaybe (mkImportDirs env) comp_info     toModLocation uid file = liftIO $ do         loc <- mkHomeModLocation dflags (unLoc modName) (fromNormalizedFilePath file) #if MIN_VERSION_ghc(9,0,0)@@ -180,7 +201,7 @@   mkError' $ ppr' $ cannotFindModule env modName0 $ lookupToFindResult reason   where     dfs = hsc_dflags env-    mkError' = diagFromString "not found" DsError (Compat.getLoc modName)+    mkError' = diagFromString "not found" DiagnosticSeverity_Error (Compat.getLoc modName)     modName0 = unLoc modName     ppr' = showSDoc dfs     -- We convert the lookup result to a find result to reuse GHC's cannotFindModule pretty printer.
src/Development/IDE/LSP/HoverDefinition.hs view
@@ -15,45 +15,47 @@     , wsSymbols     ) where +import           Control.Monad.Except           (ExceptT) import           Control.Monad.IO.Class+import           Data.Maybe                     (fromMaybe) import           Development.IDE.Core.Actions import           Development.IDE.Core.Rules import           Development.IDE.Core.Shake import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger+import           Ide.Logger+import           Ide.Plugin.Error+import           Ide.Types+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import qualified Language.LSP.Server            as LSP-import           Language.LSP.Types  import qualified Data.Text                      as T -gotoDefinition :: IdeState -> TextDocumentPositionParams -> LSP.LspM c (Either ResponseError (ResponseResult TextDocumentDefinition))-hover          :: IdeState -> TextDocumentPositionParams -> LSP.LspM c (Either ResponseError (Maybe Hover))-gotoTypeDefinition :: IdeState -> TextDocumentPositionParams -> LSP.LspM c (Either ResponseError (ResponseResult TextDocumentTypeDefinition))-documentHighlight :: IdeState -> TextDocumentPositionParams -> LSP.LspM c (Either ResponseError (List DocumentHighlight))-gotoDefinition = request "Definition" getDefinition (InR $ InL $ List []) (InR . InL . List)-gotoTypeDefinition = request "TypeDefinition" getTypeDefinition (InR $ InL $ List []) (InR . InL . List)-hover          = request "Hover"      getAtPoint     Nothing      foundHover-documentHighlight = request "DocumentHighlight" highlightAtPoint (List []) List+gotoDefinition :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LSP.LspM c) (MessageResult Method_TextDocumentDefinition)+hover          :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LSP.LspM c) (Hover |? Null)+gotoTypeDefinition :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LSP.LspM c) (MessageResult Method_TextDocumentTypeDefinition)+documentHighlight :: IdeState -> TextDocumentPositionParams -> ExceptT PluginError (LSP.LspM c) ([DocumentHighlight] |? Null)+gotoDefinition = request "Definition" getDefinition (InR $ InR Null) (InL . Definition. InR)+gotoTypeDefinition = request "TypeDefinition" getTypeDefinition (InR $ InR Null) (InL . Definition. InR)+hover          = request "Hover"      getAtPoint     (InR Null)     foundHover+documentHighlight = request "DocumentHighlight" highlightAtPoint (InR Null) InL -references :: IdeState -> ReferenceParams -> LSP.LspM c (Either ResponseError (List Location))-references ide (ReferenceParams (TextDocumentIdentifier uri) pos _ _ _) = liftIO $-  case uriToFilePath' uri of-    Just path -> do-      let filePath = toNormalizedFilePath' path-      logDebug (ideLogger ide) $+references :: PluginMethodHandler IdeState 'Method_TextDocumentReferences+references ide _ (ReferenceParams (TextDocumentIdentifier uri) pos _ _ _) = do+  nfp <- getNormalizedFilePathE uri+  liftIO $ logDebug (ideLogger ide) $         "References request at position " <> T.pack (showPosition pos) <>-        " in file: " <> T.pack path-      Right . List <$> (runAction "references" ide $ refsAtPoint filePath pos)-    Nothing -> pure $ Left $ ResponseError InvalidParams ("Invalid URI " <> T.pack (show uri)) Nothing+        " in file: " <> T.pack (show nfp)+  InL <$> (liftIO $ runAction "references" ide $ refsAtPoint nfp pos) -wsSymbols :: IdeState -> WorkspaceSymbolParams -> LSP.LspM c (Either ResponseError (List SymbolInformation))-wsSymbols ide (WorkspaceSymbolParams _ _ query) = liftIO $ do+wsSymbols :: PluginMethodHandler IdeState 'Method_WorkspaceSymbol+wsSymbols ide _ (WorkspaceSymbolParams _ _ query) = liftIO $ do   logDebug (ideLogger ide) $ "Workspace symbols request: " <> query-  runIdeAction "WorkspaceSymbols" (shakeExtras ide) $ Right . maybe (List []) List <$> workspaceSymbols query+  runIdeAction "WorkspaceSymbols" (shakeExtras ide) $ InL . fromMaybe [] <$> workspaceSymbols query -foundHover :: (Maybe Range, [T.Text]) -> Maybe Hover+foundHover :: (Maybe Range, [T.Text]) -> Hover |? Null foundHover (mbRange, contents) =-  Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents) mbRange+  InL $ Hover (InL $ MarkupContent MarkupKind_Markdown $ T.intercalate sectionSeparator contents) mbRange  -- | Respond to and log a hover or go-to-definition request request@@ -63,12 +65,12 @@   -> (a -> b)   -> IdeState   -> TextDocumentPositionParams-  -> LSP.LspM c (Either ResponseError b)+  -> ExceptT PluginError (LSP.LspM c) b request label getResults notFound found ide (TextDocumentPositionParams (TextDocumentIdentifier uri) pos) = liftIO $ do     mbResult <- case uriToFilePath' uri of         Just path -> logAndRunRequest label getResults ide pos path         Nothing   -> pure Nothing-    pure $ Right $ maybe notFound found mbResult+    pure $ maybe notFound found mbResult  logAndRunRequest :: T.Text -> (NormalizedFilePath -> Position -> IdeAction b) -> IdeState -> Position -> String -> IO b logAndRunRequest label getResults ide pos path = do
src/Development/IDE/LSP/LanguageServer.hs view
@@ -1,12 +1,12 @@       -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 +{-# LANGUAGE DuplicateRecordFields     #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GADTs                     #-} {-# LANGUAGE PolyKinds                 #-} {-# LANGUAGE RankNTypes                #-} {-# LANGUAGE StarIsType                #-}- -- WARNING: A copy of DA.Daml.LanguageServer, try to keep them in sync -- This version removes the daml: handling module Development.IDE.LSP.LanguageServer@@ -26,8 +26,9 @@ import           Development.IDE.LSP.Server import           Development.IDE.Session               (runWithDb) import           Ide.Types                             (traceWithSpan)+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import qualified Language.LSP.Server                   as LSP-import           Language.LSP.Types import           System.IO import           UnliftIO.Async import           UnliftIO.Concurrent@@ -40,14 +41,13 @@ import           Development.IDE.Core.Shake            hiding (Log, Priority) import           Development.IDE.Core.Tracing import qualified Development.IDE.Session               as Session-import           Development.IDE.Types.Logger-import qualified Development.IDE.Types.Logger          as Logger import           Development.IDE.Types.Shake           (WithHieDb)+import           Ide.Logger+import qualified Ide.Logger                            as Logger import           Language.LSP.Server                   (LanguageContextEnv,                                                         LspServerLog,                                                         type (<~>)) import           System.IO.Unsafe                      (unsafeInterleaveIO)- data Log   = LogRegisteringIdeConfig !IdeConfiguration   | LogReactorThreadException !SomeException@@ -92,7 +92,7 @@     -> config     -> (config -> Value -> Either T.Text config)     -> (MVar ()-        -> IO (LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either ResponseError (LSP.LanguageContextEnv config, a)),+        -> IO (LSP.LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either ResponseError (LSP.LanguageContextEnv config, a)),                LSP.Handlers (m config),                (LanguageContextEnv config, a) -> m config <~> IO))     -> IO ()@@ -107,7 +107,7 @@             { LSP.onConfigurationChange = onConfigurationChange             , LSP.defaultConfig = defaultConfig             , LSP.doInitialize = doInitialize-            , LSP.staticHandlers = staticHandlers+            , LSP.staticHandlers = (const staticHandlers)             , LSP.interpretHandler = interpretHandler             , LSP.options = modifyOptions options             }@@ -133,7 +133,7 @@   -> LSP.Handlers (ServerM config)   -> (LSP.LanguageContextEnv config -> Maybe FilePath -> WithHieDb -> IndexQueue -> IO IdeState)   -> MVar ()-  -> IO (LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState)),+  -> IO (LSP.LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState)),          LSP.Handlers (ServerM config),          (LanguageContextEnv config, IdeState) -> ServerM config <~> IO) setupLSP  recorder getHieDbLoc userHandlers getIdeState clientMsgVar = do@@ -195,8 +195,8 @@     -> (SomeLspId -> IO ())     -> (SomeLspId -> IO ())     -> Chan ReactorMessage-    -> LSP.LanguageContextEnv config -> RequestMessage Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState))-handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (RequestMessage _ _ m params) = otTracedHandler "Initialize" (show m) $ \sp -> do+    -> LSP.LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either err (LSP.LanguageContextEnv config, IdeState))+handleInit recorder getHieDbLoc getIdeState lifetime exitClientMsg clearReqId waitForCancel clientMsgChan env (TRequestMessage _ _ m params) = otTracedHandler "Initialize" (show m) $ \sp -> do     traceWithSpan sp params     let root = LSP.resRootPath env     dir <- maybe getCurrentDirectory return root@@ -233,11 +233,11 @@                     case cancelOrRes of                         Left () -> do                             log Debug $ LogCancelledRequest _id-                            k $ ResponseError RequestCancelled "" Nothing+                            k $ ResponseError (InL LSPErrorCodes_RequestCancelled) "" Nothing                         Right res -> pure res                 ) $ \(e :: SomeException) -> do                     exceptionInHandler e-                    k $ ResponseError InternalError (T.pack $ show e) Nothing+                    k $ ResponseError (InR ErrorCodes_InternalError) (T.pack $ show e) Nothing     _ <- flip forkFinally handleServerException $ do         untilMVar lifetime $ runWithDb (cmapWithPrio LogSession recorder) dbLoc $ \withHieDb hieChan -> do             putMVar dbMVar (WithHieDbShield withHieDb,hieChan)@@ -263,27 +263,30 @@     waitAnyCancel =<< traverse async [ io , readMVar mvar ]  cancelHandler :: (SomeLspId -> IO ()) -> LSP.Handlers (ServerM c)-cancelHandler cancelRequest = LSP.notificationHandler SCancelRequest $ \NotificationMessage{_params=CancelParams{_id}} ->-  liftIO $ cancelRequest (SomeLspId _id)+cancelHandler cancelRequest = LSP.notificationHandler SMethod_CancelRequest $ \TNotificationMessage{_params=CancelParams{_id}} ->+  liftIO $ cancelRequest (SomeLspId (toLspId _id))+  where toLspId :: (Int32 |? T.Text) -> LspId a+        toLspId (InL x) = IdInt x+        toLspId (InR y) = IdString y  shutdownHandler :: IO () -> LSP.Handlers (ServerM c)-shutdownHandler stopReactor = LSP.requestHandler SShutdown $ \_ resp -> do+shutdownHandler stopReactor = LSP.requestHandler SMethod_Shutdown $ \_ resp -> do     (_, ide) <- ask     liftIO $ logDebug (ideLogger ide) "Received shutdown message"     -- stop the reactor to free up the hiedb connection     liftIO stopReactor     -- flush out the Shake session to record a Shake profile if applicable     liftIO $ shakeShut ide-    resp $ Right Empty+    resp $ Right Null  exitHandler :: IO () -> LSP.Handlers (ServerM c)-exitHandler exit = LSP.notificationHandler SExit $ const $ liftIO exit+exitHandler exit = LSP.notificationHandler SMethod_Exit $ const $ liftIO exit  modifyOptions :: LSP.Options -> LSP.Options-modifyOptions x = x{ LSP.textDocumentSync   = Just $ tweakTDS origTDS+modifyOptions x = x{ LSP.optTextDocumentSync   = Just $ tweakTDS origTDS                    }     where-        tweakTDS tds = tds{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ InR $ SaveOptions Nothing}-        origTDS = fromMaybe tdsDefault $ LSP.textDocumentSync x+        tweakTDS tds = tds{_openClose=Just True, _change=Just TextDocumentSyncKind_Incremental, _save=Just $ InR $ SaveOptions Nothing}+        origTDS = fromMaybe tdsDefault $ LSP.optTextDocumentSync x         tdsDefault = TextDocumentSyncOptions Nothing Nothing Nothing Nothing Nothing 
src/Development/IDE/LSP/Notifications.hs view
@@ -13,8 +13,9 @@     , ghcideNotificationsPluginPriority     ) where -import           Language.LSP.Types-import qualified Language.LSP.Types                    as LSP+import qualified Language.LSP.Protocol.Message         as LSP+import           Language.LSP.Protocol.Types+import qualified Language.LSP.Protocol.Types           as LSP  import           Control.Concurrent.STM.Stats          (atomically) import           Control.Monad.Extra@@ -36,8 +37,8 @@ import           Development.IDE.Core.Shake            hiding (Log, Priority) import qualified Development.IDE.Core.Shake            as Shake import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger import           Development.IDE.Types.Shake           (toKey)+import           Ide.Logger import           Ide.Types import           Numeric.Natural @@ -56,9 +57,9 @@  descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId) { pluginNotificationHandlers = mconcat-  [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $+  [ mkPluginNotificationHandler LSP.SMethod_TextDocumentDidOpen $       \ide vfs _ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> liftIO $ do-      atomically $ updatePositionMapping ide (VersionedTextDocumentIdentifier _uri (Just _version)) (List [])+      atomically $ updatePositionMapping ide (VersionedTextDocumentIdentifier _uri _version) []       whenUriFile _uri $ \file -> do           -- We don't know if the file actually exists, or if the contents match those on disk           -- For example, vscode restores previously unsaved contents on open@@ -66,7 +67,7 @@           setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide False file           logDebug (ideLogger ide) $ "Opened text document: " <> getUri _uri -  , mkPluginNotificationHandler LSP.STextDocumentDidChange $+  , mkPluginNotificationHandler LSP.SMethod_TextDocumentDidChange $       \ide vfs _ (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> liftIO $ do         atomically $ updatePositionMapping ide identifier changes         whenUriFile _uri $ \file -> do@@ -74,14 +75,14 @@           setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide False file         logDebug (ideLogger ide) $ "Modified text document: " <> getUri _uri -  , mkPluginNotificationHandler LSP.STextDocumentDidSave $+  , mkPluginNotificationHandler LSP.SMethod_TextDocumentDidSave $       \ide vfs _ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> liftIO $ do         whenUriFile _uri $ \file -> do             addFileOfInterest ide file OnDisk             setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide True file         logDebug (ideLogger ide) $ "Saved text document: " <> getUri _uri -  , mkPluginNotificationHandler LSP.STextDocumentDidClose $+  , mkPluginNotificationHandler LSP.SMethod_TextDocumentDidClose $         \ide vfs _ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> liftIO $ do           whenUriFile _uri $ \file -> do               deleteFileOfInterest ide file@@ -90,8 +91,8 @@               setSomethingModified (VFSModified vfs) ide [] $ Text.unpack msg               logDebug (ideLogger ide) msg -  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeWatchedFiles $-      \ide vfs _ (DidChangeWatchedFilesParams (List fileEvents)) -> liftIO $ do+  , mkPluginNotificationHandler LSP.SMethod_WorkspaceDidChangeWatchedFiles $+      \ide vfs _ (DidChangeWatchedFilesParams 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         -- filter out files of interest, since we already know all about those@@ -110,7 +111,7 @@             resetFileStore ide fileEvents'             setSomethingModified (VFSModified vfs) ide [] msg -  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeWorkspaceFolders $+  , mkPluginNotificationHandler LSP.SMethod_WorkspaceDidChangeWorkspaceFolders $       \ide _ _ (DidChangeWorkspaceFoldersParams events) -> liftIO $ do         let add       = S.union             substract = flip S.difference@@ -118,14 +119,14 @@           $ add       (foldMap (S.singleton . parseWorkspaceFolder) (_added   events))           . substract (foldMap (S.singleton . parseWorkspaceFolder) (_removed events)) -  , mkPluginNotificationHandler LSP.SWorkspaceDidChangeConfiguration $+  , mkPluginNotificationHandler LSP.SMethod_WorkspaceDidChangeConfiguration $       \ide vfs _ (DidChangeConfigurationParams cfg) -> liftIO $ do         let msg = Text.pack $ show cfg         logDebug (ideLogger ide) $ "Configuration changed: " <> msg         modifyClientSettings ide (const $ Just cfg)         setSomethingModified (VFSModified vfs) ide [toKey GetClientSettings emptyFilePath] "config change" -  , mkPluginNotificationHandler LSP.SInitialized $ \ide _ _ _ -> do+  , mkPluginNotificationHandler LSP.SMethod_Initialized $ \ide _ _ _ -> do       --------- Initialize Shake session --------------------------------------------------------------------       liftIO $ shakeSessionInit (cmapWithPrio LogShake recorder) ide 
src/Development/IDE/LSP/Outline.hs view
@@ -22,26 +22,25 @@                                                  realSrcSpanToRange) import           Development.IDE.Types.Location import           Development.IDE.GHC.Util       (printOutputable)-import           Language.LSP.Server            (LspM)-import           Language.LSP.Types             (DocumentSymbol (..),+import           Ide.Types+import           Language.LSP.Protocol.Types             (DocumentSymbol (..),                                                  DocumentSymbolParams (DocumentSymbolParams, _textDocument),-                                                 List (..), ResponseError,-                                                 SymbolInformation,-                                                 SymbolKind (SkConstructor, SkField, SkFile, SkFunction, SkInterface, SkMethod, SkModule, SkObject, SkStruct, SkTypeParameter, SkUnknown),+                                                 SymbolKind (..),                                                  TextDocumentIdentifier (TextDocumentIdentifier),-                                                 type (|?) (InL), uriToFilePath)+                                                 type (|?) (InL, InR), uriToFilePath)+import          Language.LSP.Protocol.Message #if MIN_VERSION_ghc(9,2,0) import Data.List.NonEmpty (nonEmpty) #endif  moduleOutline-  :: IdeState -> DocumentSymbolParams -> LspM c (Either ResponseError (List DocumentSymbol |? List SymbolInformation))-moduleOutline ideState DocumentSymbolParams{ _textDocument = TextDocumentIdentifier uri }+  :: PluginMethodHandler IdeState 'Method_TextDocumentDocumentSymbol+moduleOutline ideState _ DocumentSymbolParams{ _textDocument = TextDocumentIdentifier uri }   = liftIO $ case uriToFilePath uri of     Just (toNormalizedFilePath' -> fp) -> do       mb_decls <- fmap fst <$> runAction "Outline" ideState (useWithStale GetParsedModule fp)-      pure $ Right $ case mb_decls of-        Nothing -> InL (List [])+      pure $ case mb_decls of+        Nothing -> InL []         Just ParsedModule { pm_parsed_source = L _ltop HsModule { hsmodName, hsmodDecls, hsmodImports } }           -> let                declSymbols  = mapMaybe documentSymbolForDecl hsmodDecls@@ -49,7 +48,7 @@                  (L (locA -> (RealSrcSpan l _)) m) -> Just $                    (defDocumentSymbol l :: DocumentSymbol)                      { _name  = printOutputable m-                     , _kind  = SkFile+                     , _kind  = SymbolKind_File                      , _range = Range (Position 0 0) (Position maxBound 0) -- _ltop is 0 0 0 0                      }                  _ -> Nothing@@ -59,14 +58,14 @@                allSymbols    = case moduleSymbol of                  Nothing -> importSymbols <> declSymbols                  Just x ->-                   [ x { _children = Just (List (importSymbols <> declSymbols))+                   [ x { _children = Just (importSymbols <> declSymbols)                        }                    ]              in-               InL (List allSymbols)+               InR (InL allSymbols)  -    Nothing -> pure $ Right $ InL (List [])+    Nothing -> pure $ InL []  documentSymbolForDecl :: LHsDecl GhcPs -> Maybe DocumentSymbol documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ FamDecl { tcdFam = FamilyDecl { fdLName = L _ n, fdInfo, fdTyVars } }))@@ -77,7 +76,7 @@                        t  -> " " <> t                      )     , _detail = Just $ printOutputable fdInfo-    , _kind   = SkFunction+    , _kind   = SymbolKind_Function     } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ ClassDecl { tcdLName = L _ name, tcdSigs, tcdTyVars }))   = Just (defDocumentSymbol l :: DocumentSymbol)@@ -86,13 +85,13 @@                          "" -> ""                          t  -> " " <> t                        )-    , _kind     = SkInterface+    , _kind     = SymbolKind_Interface     , _detail   = Just "class"     , _children =-      Just $ List+      Just $         [ (defDocumentSymbol l :: DocumentSymbol)             { _name           = printOutputable n-            , _kind           = SkMethod+            , _kind           = SymbolKind_Method             , _selectionRange = realSrcSpanToRange l'             }         | L (locA -> (RealSrcSpan l _))  (ClassOpSig _ False names _) <- tcdSigs@@ -102,15 +101,15 @@ documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ DataDecl { tcdLName = L _ name, tcdDataDefn = HsDataDefn { dd_cons } }))   = Just (defDocumentSymbol l :: DocumentSymbol)     { _name     = printOutputable name-    , _kind     = SkStruct+    , _kind     = SymbolKind_Struct     , _children =-      Just $ List+      Just $         [ (defDocumentSymbol l :: DocumentSymbol)             { _name           = printOutputable n-            , _kind           = SkConstructor+            , _kind           = SymbolKind_Constructor             , _selectionRange = realSrcSpanToRange l' #if MIN_VERSION_ghc(9,2,0)-            , _children       = List . toList <$> nonEmpty childs+            , _children       = toList <$> nonEmpty childs             }         | con <- extract_cons dd_cons         , let (cs, flds) = hsConDeclsBinders con@@ -133,7 +132,7 @@ #else                 { _name = printOutputable (unLoc (rdrNameFieldOcc n)) #endif-                , _kind = SkField+                , _kind = SymbolKind_Field                 }     cvtFld _  = Nothing #else@@ -145,10 +144,10 @@     }   where     -- | Extract the record fields of a constructor-    conArgRecordFields (RecCon (L _ lcdfs)) = Just $ List+    conArgRecordFields (RecCon (L _ lcdfs)) = Just       [ (defDocumentSymbol l :: DocumentSymbol)           { _name = printOutputable n-          , _kind = SkField+          , _kind = SymbolKind_Field           }       | L _ cdf <- lcdfs       , L (locA -> (RealSrcSpan l _)) n <- rdrNameFieldOcc . unLoc <$> cd_fld_names cdf@@ -157,12 +156,12 @@ #endif documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (TyClD _ SynDecl { tcdLName = L (locA -> (RealSrcSpan l' _)) n })) = Just   (defDocumentSymbol l :: DocumentSymbol) { _name           = printOutputable n-                                          , _kind           = SkTypeParameter+                                          , _kind           = SymbolKind_TypeParameter                                           , _selectionRange = realSrcSpanToRange l'                                           } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (InstD _ ClsInstD { cid_inst = ClsInstDecl { cid_poly_ty } }))   = Just (defDocumentSymbol l :: DocumentSymbol) { _name = printOutputable cid_poly_ty-                                                 , _kind = SkInterface+                                                 , _kind = SymbolKind_Interface                                                  } #if MIN_VERSION_ghc(9,2,0) documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (InstD _ DataFamInstD { dfid_inst = DataFamInstDecl FamEqn { feqn_tycon, feqn_pats } }))@@ -177,7 +176,7 @@         printOutputable (unLoc feqn_tycon) <> " " <> T.unwords                 (map printOutputable feqn_pats) #endif-    , _kind = SkInterface+    , _kind = SymbolKind_Interface     } #if MIN_VERSION_ghc(9,2,0) documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (InstD _ TyFamInstD { tfid_inst = TyFamInstDecl _ FamEqn { feqn_tycon, feqn_pats } }))@@ -192,23 +191,23 @@         printOutputable (unLoc feqn_tycon) <> " " <> T.unwords                 (map printOutputable feqn_pats) #endif-    , _kind = SkInterface+    , _kind = SymbolKind_Interface     } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (DerivD _ DerivDecl { deriv_type })) =   gfindtype deriv_type <&> \(L (_ :: SrcSpan) name) ->     (defDocumentSymbol l :: DocumentSymbol) { _name = printOutputable @(HsType GhcPs)                                               name-                                            , _kind = SkInterface+                                            , _kind = SymbolKind_Interface                                             } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (ValD _ FunBind{fun_id = L _ name})) = Just     (defDocumentSymbol l :: DocumentSymbol)       { _name   = printOutputable name-      , _kind   = SkFunction+      , _kind   = SymbolKind_Function       } documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (ValD _ PatBind{pat_lhs})) = Just     (defDocumentSymbol l :: DocumentSymbol)       { _name   = printOutputable pat_lhs-      , _kind   = SkFunction+      , _kind   = SymbolKind_Function       }  documentSymbolForDecl (L (locA -> (RealSrcSpan l _)) (ForD _ x)) = Just@@ -217,7 +216,7 @@                   ForeignImport{} -> name                   ForeignExport{} -> name                   XForeignDecl{}  -> "?"-    , _kind   = SkObject+    , _kind   = SymbolKind_Object     , _detail = case x of                   ForeignImport{} -> Just "import"                   ForeignExport{} -> Just "export"@@ -240,15 +239,15 @@     in       Just (defDocumentSymbol (rangeToRealSrcSpan "" importRange))           { _name = "imports"-          , _kind = SkModule-          , _children = Just (List importSymbols)+          , _kind = SymbolKind_Module+          , _children = Just importSymbols           }  documentSymbolForImport :: LImportDecl GhcPs -> Maybe DocumentSymbol documentSymbolForImport (L (locA -> (RealSrcSpan l _)) ImportDecl { ideclName, ideclQualified }) = Just   (defDocumentSymbol l :: DocumentSymbol)     { _name   = "import " <> printOutputable ideclName-    , _kind   = SkModule+    , _kind   = SymbolKind_Module     , _detail = case ideclQualified of { NotQualified -> Nothing; _ -> Just "qualified" }     } documentSymbolForImport _ = Nothing@@ -258,7 +257,9 @@   _detail         = Nothing   _deprecated     = Nothing   _name           = ""-  _kind           = SkUnknown 0+  -- This used to be SkUnknown 0, which is invalid, as SymbolKinds start at 1,+  -- therefore, I am replacing it with SymbolKind_File, which is the type for 1+  _kind           = SymbolKind_File   _range          = realSrcSpanToRange l   _selectionRange = realSrcSpanToRange l   _children       = Nothing
src/Development/IDE/LSP/Server.hs view
@@ -1,12 +1,8 @@-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE UndecidableInstances  #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE UndecidableInstances #-} -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE KindSignatures        #-}-{-# LANGUAGE RankNTypes            #-} module Development.IDE.LSP.Server   ( ReactorMessage(..)   , ReactorChan@@ -14,15 +10,14 @@   , requestHandler   , notificationHandler   ) where--import           Control.Monad.IO.Unlift      (MonadUnliftIO)+import           Control.Monad.IO.Unlift       (MonadUnliftIO) import           Control.Monad.Reader import           Development.IDE.Core.Shake import           Development.IDE.Core.Tracing-import           Ide.Types                    (HasTracing, traceWithSpan)-import           Language.LSP.Server          (Handlers, LspM)-import qualified Language.LSP.Server          as LSP-import           Language.LSP.Types+import           Ide.Types                     (HasTracing, traceWithSpan)+import           Language.LSP.Protocol.Message+import           Language.LSP.Server           (Handlers, LspM)+import qualified Language.LSP.Server           as LSP import           Language.LSP.VFS import           UnliftIO.Chan @@ -35,25 +30,26 @@   deriving (Functor, Applicative, Monad, MonadReader (ReactorChan, IdeState), MonadIO, MonadUnliftIO, LSP.MonadLsp c)  requestHandler-  :: forall (m :: Method FromClient Request) c. (HasTracing (MessageParams m)) =>+  :: forall (m :: Method ClientToServer Request) c. (HasTracing (MessageParams m)) =>      SMethod m-  -> (IdeState -> MessageParams m -> LspM c (Either ResponseError (ResponseResult m)))+  -> (IdeState -> MessageParams m -> LspM c (Either ResponseError (MessageResult m)))   -> Handlers (ServerM c)-requestHandler m k = LSP.requestHandler m $ \RequestMessage{_method,_id,_params} resp -> do+requestHandler m k = LSP.requestHandler m $ \TRequestMessage{_method,_id,_params} resp -> do   st@(chan,ide) <- ask   env <- LSP.getLspEnv-  let resp' = flip (runReaderT . unServerM) st . resp+  let resp' :: Either ResponseError (MessageResult m) -> LspM c ()+      resp' = flip (runReaderT . unServerM) st . resp       trace x = otTracedHandler "Request" (show _method) $ \sp -> do         traceWithSpan sp _params         x   writeChan chan $ ReactorRequest (SomeLspId _id) (trace $ LSP.runLspT env $ resp' =<< k ide _params) (LSP.runLspT env . resp' . Left)  notificationHandler-  :: forall (m :: Method FromClient Notification) c. (HasTracing (MessageParams m)) =>+  :: forall (m :: Method ClientToServer Notification) c. (HasTracing (MessageParams m)) =>      SMethod m   -> (IdeState -> VFS -> MessageParams m -> LspM c ())   -> Handlers (ServerM c)-notificationHandler m k = LSP.notificationHandler m $ \NotificationMessage{_params,_method}-> do+notificationHandler m k = LSP.notificationHandler m $ \TNotificationMessage{_params,_method}-> do   (chan,ide) <- ask   env <- LSP.getLspEnv   -- Take a snapshot of the VFS state on every notification
src/Development/IDE/Main.hs view
@@ -77,7 +77,7 @@ import qualified Development.IDE.Session                  as Session import           Development.IDE.Types.Location           (NormalizedUri,                                                            toNormalizedFilePath')-import           Development.IDE.Types.Logger             (Logger,+import           Ide.Logger             (Logger,                                                            Pretty (pretty),                                                            Priority (Info, Warning),                                                            Recorder,@@ -235,7 +235,7 @@             { optCheckProject = pure $ checkProject config             , optCheckParents = pure $ checkParents config             }-        , argsLspOptions = def {LSP.completionTriggerCharacters = Just "."}+        , argsLspOptions = def {LSP.optCompletionTriggerCharacters = Just "."}         , argsDefaultHlsConfig = def         , argsGetHieDbLoc = getHieDbLoc         , argsDebouncer = newAsyncDebouncer@@ -293,7 +293,7 @@     let hlsPlugin = asGhcIdePlugin (cmapWithPrio LogPluginHLS recorder) argsHlsPlugins         hlsCommands = allLspCmdIds' pid argsHlsPlugins         plugins = hlsPlugin <> argsGhcidePlugin-        options = argsLspOptions { LSP.executeCommandCommands = LSP.executeCommandCommands argsLspOptions <> Just hlsCommands }+        options = argsLspOptions { LSP.optExecuteCommandCommands = LSP.optExecuteCommandCommands argsLspOptions <> Just hlsCommands }         argsOnConfigChange = getConfigFromNotification argsHlsPlugins         rules = argsRules >> pluginRules plugins 
src/Development/IDE/Main/HeapStats.hs view
@@ -6,11 +6,11 @@ import           Control.Concurrent.Async import           Control.Monad import           Data.Word-import           Development.IDE.Types.Logger (Pretty (pretty), Priority (Info),-                                               Recorder, WithPriority, hsep,-                                               logWith, (<+>)) import           GHC.Stats-import           Text.Printf                  (printf)+import           Ide.Logger               (Pretty (pretty), Priority (Info),+                                           Recorder, WithPriority, hsep,+                                           logWith, (<+>))+import           Text.Printf              (printf)  data Log   = LogHeapStatsPeriod !Int
src/Development/IDE/Monitoring/EKG.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE CPP #-} module Development.IDE.Monitoring.EKG(monitoring) where -import           Development.IDE.Types.Logger     (Logger) import           Development.IDE.Types.Monitoring (Monitoring (..))+import           Ide.Logger                       (Logger) #ifdef MONITORING_EKG import           Control.Concurrent               (killThread) import           Control.Concurrent.Async         (async, waitCatch) import           Control.Monad                    (forM_) import           Data.Text                        (pack)-import           Development.IDE.Types.Logger     (logInfo)+import           Ide.Logger                       (logInfo) import qualified System.Metrics                   as Monitoring import qualified System.Remote.Monitoring.Wai     as Monitoring 
src/Development/IDE/Plugin/Completions.hs view
@@ -11,15 +11,18 @@  import           Control.Concurrent.Async                 (concurrently) import           Control.Concurrent.STM.Stats             (readTVarIO)+import           Control.Lens                             ((&), (.~)) import           Control.Monad.IO.Class-import           Control.Lens                            ((&), (.~))+import           Control.Monad.Trans.Except               (ExceptT (ExceptT),+                                                           withExceptT)+import           Data.Aeson import qualified Data.HashMap.Strict                      as Map import qualified Data.HashSet                             as Set-import           Data.Aeson import           Data.Maybe import qualified Data.Text                                as T-import           Development.IDE.Core.PositionMapping import           Development.IDE.Core.Compile+import           Development.IDE.Core.PluginUtils+import           Development.IDE.Core.PositionMapping import           Development.IDE.Core.RuleTypes import           Development.IDE.Core.Service             hiding (Log, LogShake) import           Development.IDE.Core.Shake               hiding (Log)@@ -27,24 +30,25 @@ import           Development.IDE.GHC.Compat import           Development.IDE.GHC.Util import           Development.IDE.Graph-import           Development.IDE.Spans.Common-import           Development.IDE.Spans.Documentation import           Development.IDE.Plugin.Completions.Logic import           Development.IDE.Plugin.Completions.Types+import           Development.IDE.Spans.Common+import           Development.IDE.Spans.Documentation import           Development.IDE.Types.Exports import           Development.IDE.Types.HscEnvEq           (HscEnvEq (envPackageExports, envVisibleModuleNames),                                                            hscEnv) import qualified Development.IDE.Types.KnownTargets       as KT import           Development.IDE.Types.Location-import           Development.IDE.Types.Logger             (Pretty (pretty),+import           Ide.Logger                               (Pretty (pretty),                                                            Recorder,                                                            WithPriority,                                                            cmapWithPrio)+import           Ide.Plugin.Error import           Ide.Types+import qualified Language.LSP.Protocol.Lens               as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import qualified Language.LSP.Server                      as LSP-import           Language.LSP.Types-import qualified Language.LSP.Types.Lens         as J-import qualified Language.LSP.VFS                         as VFS import           Numeric.Natural import           Text.Fuzzy.Parallel                      (Scored (..)) @@ -64,8 +68,8 @@ descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId = (defaultPluginDescriptor plId)   { pluginRules = produceCompletions recorder-  , pluginHandlers = mkPluginHandler STextDocumentCompletion getCompletionsLSP-                  <> mkPluginHandler SCompletionItemResolve resolveCompletion+  , pluginHandlers = mkPluginHandler SMethod_TextDocumentCompletion getCompletionsLSP+                     <> mkResolveHandler SMethod_CompletionItemResolve resolveCompletion   , pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}   , pluginPriority = ghcideCompletionsPluginPriority   }@@ -118,59 +122,50 @@     f x = x     in f <$> iDecl -resolveCompletion :: IdeState -> PluginId -> CompletionItem -> LSP.LspM Config (Either ResponseError CompletionItem)-resolveCompletion ide _ comp@CompletionItem{_detail,_documentation,_xdata}-  | Just resolveData <- _xdata-  , Success (CompletionResolveData uri needType (NameDetails mod occ)) <- fromJSON resolveData-  , Just file <- uriToNormalizedFilePath $ toNormalizedUri uri-  = liftIO $ runIdeAction "Completion resolve" (shakeExtras ide) $ do-    msess <- useWithStaleFast GhcSessionDeps file-    case msess of-      Nothing -> pure (Right comp) -- File doesn't compile, return original completion item-      Just (sess,_) -> do-        let nc = ideNc $ shakeExtras ide+resolveCompletion :: ResolveFunction IdeState CompletionResolveData 'Method_CompletionItemResolve+resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} uri (CompletionResolveData _ needType (NameDetails mod occ)) =+  do+    file <- getNormalizedFilePathE uri+    (sess,_) <- withExceptT (const PluginStaleResolve)+                  $ runIdeActionE "CompletionResolve.GhcSessionDeps" (shakeExtras ide)+                  $ useWithStaleFastE GhcSessionDeps file+    let nc = ideNc $ shakeExtras ide #if MIN_VERSION_ghc(9,3,0)-        name <- liftIO $ lookupNameCache nc mod occ+    name <- liftIO $ lookupNameCache nc mod occ #else-        name <- liftIO $ upNameCache nc (lookupNameCache mod occ)+    name <- liftIO $ upNameCache nc (lookupNameCache mod occ) #endif-        mdkm <- useWithStaleFast GetDocMap file-        let (dm,km) = case mdkm of-              Just (DKMap dm km, _) -> (dm,km)-              Nothing -> (mempty, mempty)-        doc <- case lookupNameEnv dm name of-          Just doc -> pure $ spanDocToMarkdown doc-          Nothing -> liftIO $ spanDocToMarkdown <$> getDocumentationTryGhc (hscEnv sess) name-        typ <- case lookupNameEnv km name of-          _ | not needType -> pure Nothing-          Just ty -> pure (safeTyThingType ty)-          Nothing -> do-            (safeTyThingType =<<) <$> liftIO (lookupName (hscEnv sess) name)-        let det1 = case typ of-              Just ty -> Just (":: " <> printOutputable (stripForall ty) <> "\n")-              Nothing -> Nothing-            doc1 = case _documentation of-              Just (CompletionDocMarkup (MarkupContent MkMarkdown old)) ->-                CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator (old:doc)-              _ -> CompletionDocMarkup $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator doc-        pure (Right $ comp & J.detail .~ (det1 <> _detail)-                           & J.documentation .~ Just doc1-                           )+    mdkm <- liftIO $ runIdeAction "CompletionResolve.GetDocMap" (shakeExtras ide) $ useWithStaleFast GetDocMap file+    let (dm,km) = case mdkm of+          Just (DKMap dm km, _) -> (dm,km)+          Nothing               -> (mempty, mempty)+    doc <- case lookupNameEnv dm name of+      Just doc -> pure $ spanDocToMarkdown doc+      Nothing -> liftIO $ spanDocToMarkdown <$> getDocumentationTryGhc (hscEnv sess) name+    typ <- case lookupNameEnv km name of+      _ | not needType -> pure Nothing+      Just ty -> pure (safeTyThingType ty)+      Nothing -> do+        (safeTyThingType =<<) <$> liftIO (lookupName (hscEnv sess) name)+    let det1 = case typ of+          Just ty -> Just (":: " <> printOutputable (stripForall ty) <> "\n")+          Nothing -> Nothing+        doc1 = case _documentation of+          Just (InR (MarkupContent MarkupKind_Markdown old)) ->+            InR $ MarkupContent MarkupKind_Markdown $ T.intercalate sectionSeparator (old:doc)+          _ -> InR $ MarkupContent MarkupKind_Markdown $ T.intercalate sectionSeparator doc+    pure  (comp & L.detail .~ (det1 <> _detail)+                & L.documentation .~ Just doc1)   where     stripForall ty = case splitForAllTyCoVars ty of       (_,res) -> res-resolveCompletion _ _ comp = pure (Right comp)  -- | Generate code actions.-getCompletionsLSP-    :: IdeState-    -> PluginId-    -> CompletionParams-    -> LSP.LspM Config (Either ResponseError (ResponseResult TextDocumentCompletion))+getCompletionsLSP :: PluginMethodHandler IdeState 'Method_TextDocumentCompletion getCompletionsLSP ide plId   CompletionParams{_textDocument=TextDocumentIdentifier uri                   ,_position=position-                  ,_context=completionContext} = do+                  ,_context=completionContext} = ExceptT $ do     contents <- LSP.getVirtualFile $ toNormalizedUri uri     fmap Right $ case (contents, uriToFilePath' uri) of       (Just cnts, Just path) -> do@@ -213,17 +208,16 @@             let pfix = getCompletionPrefix position cnts             case (pfix, completionContext) of               ((PosPrefixInfo _ "" _ _), Just CompletionContext { _triggerCharacter = Just "."})-                -> return (InL $ List [])+                -> return (InL [])               (_, _) -> do                 let clientCaps = clientCapabilities $ shakeExtras ide                     plugins = idePlugins $ shakeExtras ide                 config <- liftIO $ runAction "" ide $ getCompletionsConfig plId                  allCompletions <- liftIO $ getCompletions plugins ideOpts cci' parsedMod astres bindMap pfix clientCaps config moduleExports uri-                pure $ InL (List $ orderedCompletions allCompletions)-              _ -> return (InL $ List [])-          _ -> return (InL $ List [])-      _ -> return (InL $ List [])+                pure $ InL (orderedCompletions allCompletions)+          _ -> return (InL [])+      _ -> return (InL [])  getCompletionsConfig :: PluginId -> Action CompletionsConfig getCompletionsConfig pId =
src/Development/IDE/Plugin/Completions/Logic.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE CPP        #-}-{-# LANGUAGE GADTs      #-}-{-# LANGUAGE MultiWayIf #-}-+{-# LANGUAGE CPP              #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GADTs            #-}+{-# LANGUAGE MultiWayIf       #-}+{-# LANGUAGE OverloadedLabels #-}  -- Mostly taken from "haskell-ide-engine" module Development.IDE.Plugin.Completions.Logic (@@ -14,15 +15,19 @@ ) where  import           Control.Applicative+import           Control.Lens                             hiding (Context) import           Data.Char                                (isAlphaNum, isUpper)+import           Data.Default                             (def) import           Data.Generics import           Data.List.Extra                          as List hiding                                                                   (stripPrefix) import qualified Data.Map                                 as Map+import           Data.Row  import           Data.Maybe                               (catMaybes, fromMaybe,-                                                           isJust, listToMaybe,-                                                           mapMaybe, isNothing)+                                                           isJust, isNothing,+                                                           listToMaybe,+                                                           mapMaybe) import qualified Data.Text                                as T import qualified Text.Fuzzy.Parallel                      as Fuzzy @@ -41,7 +46,7 @@ import           Development.IDE.GHC.Compat               hiding (ppr) import qualified Development.IDE.GHC.Compat               as GHC import           Development.IDE.GHC.Compat.Util-import           Development.IDE.GHC.CoreFile            (occNamePrefixes)+import           Development.IDE.GHC.CoreFile             (occNamePrefixes) import           Development.IDE.GHC.Error import           Development.IDE.GHC.Util import           Development.IDE.Plugin.Completions.Types@@ -64,8 +69,8 @@ import           Ide.Types                                (CommandId (..),                                                            IdePlugins (..),                                                            PluginId)-import           Language.LSP.Types-import           Language.LSP.Types.Capabilities+import qualified Language.LSP.Protocol.Lens               as L+import           Language.LSP.Protocol.Types import qualified Language.LSP.VFS                         as VFS import           Text.Fuzzy.Parallel                      (Scored (score),                                                            original)@@ -76,7 +81,7 @@ import           Development.IDE.Spans.AtPoint            (pointCommand)  #if MIN_VERSION_ghc(9,5,0)-import Language.Haskell.Syntax.Basic+import           Language.Haskell.Syntax.Basic #endif  -- Chunk size used for parallelizing fuzzy matching@@ -172,11 +177,11 @@ occNameToComKind :: OccName -> CompletionItemKind occNameToComKind oc   | isVarOcc  oc = case occNameString oc of-                     i:_ | isUpper i -> CiConstructor-                     _               -> CiFunction-  | isTcOcc   oc = CiStruct-  | isDataOcc oc = CiConstructor-  | otherwise    = CiVariable+                     i:_ | isUpper i -> CompletionItemKind_Constructor+                     _               -> CompletionItemKind_Function+  | isTcOcc   oc = CompletionItemKind_Struct+  | isDataOcc oc = CompletionItemKind_Constructor+  | otherwise    = CompletionItemKind_Variable   showModName :: ModuleName -> T.Text@@ -215,13 +220,15 @@                   _sortText = Nothing,                   _filterText = Nothing,                   _insertText = Just insertText,-                  _insertTextFormat = Just Snippet,+                  _insertTextFormat = Just InsertTextFormat_Snippet,                   _insertTextMode = Nothing,                   _textEdit = Nothing,                   _additionalTextEdits = Nothing,                   _commitCharacters = Nothing,                   _command = mbCommand,-                  _xdata = toJSON <$> fmap (CompletionResolveData uri (isNothing typeText)) nameDetails}+                  _data_ = toJSON <$> fmap (CompletionResolveData uri (isNothing typeText)) nameDetails,+                  _labelDetails = Nothing,+                  _textEditText = Nothing}   removeSnippetsWhen (isJust isInfix) ci    where kind = Just compKind@@ -230,8 +237,8 @@           Local pos  -> "*Defined at " <> pprLineCol (srcSpanStart pos) <> " in this module*\n"           ImportedFrom mod -> "*Imported from '" <> mod <> "'*\n"           DefinedIn mod -> "*Defined in '" <> mod <> "'*\n"-        documentation = Just $ CompletionDocMarkup $-                        MarkupContent MkMarkdown $+        documentation = Just $ InR $+                        MarkupContent MarkupKind_Markdown $                         T.intercalate sectionSeparator docs'         pprLineCol :: SrcLoc -> T.Text         pprLineCol (UnhelpfulLoc fs) = T.pack $ unpackFS fs@@ -253,8 +260,8 @@     typeText = Nothing     label = stripPrefix $ printOutputable origName     insertText = case isInfix of-            Nothing -> label-            Just LeftSide -> label <> "`"+            Nothing         -> label+            Just LeftSide   -> label <> "`"              Just Surrounded -> label     additionalTextEdits =@@ -278,30 +285,32 @@  mkModCompl :: T.Text -> CompletionItem mkModCompl label =-  CompletionItem label (Just CiModule) Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing Nothing+    (defaultCompletionItemWithLabel label)+    { _kind = Just CompletionItemKind_Module }  mkModuleFunctionImport :: T.Text -> T.Text -> CompletionItem mkModuleFunctionImport moduleName label =-  CompletionItem label (Just CiFunction) Nothing (Just moduleName)-    Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing Nothing+    (defaultCompletionItemWithLabel label)+    { _kind = Just CompletionItemKind_Function+    , _detail = Just moduleName }  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+    (defaultCompletionItemWithLabel m)+    { _kind = Just CompletionItemKind_Module+    , _detail = Just label }   where     m = fromMaybe "" (T.stripPrefix enteredQual label)  mkExtCompl :: T.Text -> CompletionItem mkExtCompl label =-  CompletionItem label (Just CiKeyword) Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing Nothing Nothing-    Nothing Nothing Nothing Nothing Nothing Nothing+    (defaultCompletionItemWithLabel label)+    { _kind = Just CompletionItemKind_Keyword } +defaultCompletionItemWithLabel :: T.Text -> CompletionItem+defaultCompletionItemWithLabel label =+    CompletionItem label def def def def def def def def def+                         def def def def def def def def def  fromIdentInfo :: Uri -> IdentInfo -> Maybe T.Text -> CompItem fromIdentInfo doc id@IdentInfo{..} q = CI@@ -439,20 +448,20 @@     compls = concat         [ case decl of             SigD _ (TypeSig _ ids typ) ->-                [mkComp id CiFunction (Just $ showForSnippet typ) | id <- ids]+                [mkComp id CompletionItemKind_Function (Just $ showForSnippet typ) | id <- ids]             ValD _ FunBind{fun_id} ->-                [ mkComp fun_id CiFunction Nothing+                [ mkComp fun_id CompletionItemKind_Function Nothing                 | not (hasTypeSig fun_id)                 ]             ValD _ PatBind{pat_lhs} ->-                [mkComp id CiVariable Nothing+                [mkComp id CompletionItemKind_Variable Nothing                 | VarPat _ id <- listify (\(_ :: Pat GhcPs) -> True) pat_lhs]             TyClD _ ClassDecl{tcdLName, tcdSigs, tcdATs} ->-                mkComp tcdLName CiInterface (Just $ showForSnippet tcdLName) :-                [ mkComp id CiFunction (Just $ showForSnippet typ)+                mkComp tcdLName CompletionItemKind_Interface (Just $ showForSnippet tcdLName) :+                [ mkComp id CompletionItemKind_Function (Just $ showForSnippet typ)                 | L _ (ClassOpSig _ _ ids typ) <- tcdSigs                 , id <- ids] ++-                [ mkComp fdLName CiStruct (Just $ showForSnippet fdLName)+                [ mkComp fdLName CompletionItemKind_Struct (Just $ showForSnippet fdLName)                 | L _ (FamilyDecl{fdLName}) <- tcdATs]             TyClD _ x ->                 let generalCompls = [mkComp id cl (Just $ showForSnippet $ tyClDeclLName x)@@ -464,16 +473,16 @@                    -- the constructors and snippets will be duplicated here giving the user 2 choices.                    generalCompls ++ recordCompls             ForD _ ForeignImport{fd_name,fd_sig_ty} ->-                [mkComp fd_name CiVariable (Just $ showForSnippet fd_sig_ty)]+                [mkComp fd_name CompletionItemKind_Variable (Just $ showForSnippet fd_sig_ty)]             ForD _ ForeignExport{fd_name,fd_sig_ty} ->-                [mkComp fd_name CiVariable (Just $ showForSnippet fd_sig_ty)]+                [mkComp fd_name CompletionItemKind_Variable (Just $ showForSnippet fd_sig_ty)]             _ -> []             | L (locA -> pos) decl <- hsmodDecls,             let mkComp = mkLocalComp pos         ]      mkLocalComp pos n ctyp ty =-        CI ctyp pn (Local pos) pn ty Nothing (ctyp `elem` [CiStruct, CiInterface]) Nothing (Just $ NameDetails (ms_mod $ pm_mod_summary pm) occ) True+        CI ctyp pn (Local pos) pn ty Nothing (ctyp `elem` [CompletionItemKind_Struct, CompletionItemKind_Interface]) Nothing (Just $ NameDetails (ms_mod $ pm_mod_summary pm) occ) True       where         occ = rdrNameOcc $ unLoc n         pn = showForSnippet n@@ -520,7 +529,7 @@   removeSnippetsWhen (not $ enableSnippets && supported)   where     supported =-      Just True == (_textDocument >>= _completion >>= _completionItem >>= _snippetSupport)+      Just True == (_textDocument >>= _completion >>= view L.completionItem >>= (\x -> x .! #snippetSupport))  toggleAutoExtend :: CompletionsConfig -> CompItem -> CompItem toggleAutoExtend CompletionsConfig{enableAutoExtend=False} x = x {additionalTextEdits = Nothing}@@ -531,7 +540,7 @@   if condition     then       x-        { _insertTextFormat = Just PlainText,+        { _insertTextFormat = Just InsertTextFormat_PlainText,           _insertText = Nothing         }     else x@@ -613,7 +622,7 @@               -- to get the record's module, which isn't included in the type information used to get the fields.               dotFieldSelectorToCompl :: T.Text -> T.Text -> (Bool, CompItem)               dotFieldSelectorToCompl recname label = (True, CI-                { compKind = CiField+                { compKind = CompletionItemKind_Field                 , insertText = label                 , provenance = DefinedIn recname                 , label = label@@ -790,7 +799,7 @@ mkRecordSnippetCompItem uri parent ctxStr compl importedFrom imp = r   where       r  = CI {-            compKind = CiSnippet+            compKind = CompletionItemKind_Snippet           , insertText = buildSnippet           , provenance = importedFrom           , typeText = Nothing
src/Development/IDE/Plugin/Completions/Types.hs view
@@ -1,9 +1,9 @@+{-# LANGUAGE CPP                #-} {-# LANGUAGE DeriveAnyClass     #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GADTs              #-} {-# LANGUAGE OverloadedLabels   #-} {-# LANGUAGE TypeFamilies       #-}-{-# LANGUAGE CPP                #-} module Development.IDE.Plugin.Completions.Types (   module Development.IDE.Plugin.Completions.Types ) where@@ -22,12 +22,12 @@ import           Development.IDE.Spans.Common import           GHC.Generics                 (Generic) import           Ide.Plugin.Properties-import           Language.LSP.Types           (CompletionItemKind (..), Uri)-import qualified Language.LSP.Types           as J+import           Language.LSP.Protocol.Types  (CompletionItemKind (..), Uri)+import qualified Language.LSP.Protocol.Types  as J #if MIN_VERSION_ghc(9,0,0)-import qualified GHC.Types.Name.Occurrence as Occ+import qualified GHC.Types.Name.Occurrence    as Occ #else-import qualified OccName as Occ+import qualified OccName                      as Occ #endif  -- | Produce completions info for a file@@ -178,7 +178,7 @@ parseNs (String "c") = pure dataName parseNs (String "t") = pure tcClsName parseNs (String "z") = pure tvName-parseNs _ = mempty+parseNs _            = mempty  instance FromJSON NameDetails where   parseJSON v@(Array _)@@ -204,9 +204,9 @@ -- We need the URI to be able to reconstruct the GHC environment -- in the file the completion was triggered in. data CompletionResolveData = CompletionResolveData-  { itemFile :: Uri+  { itemFile      :: Uri   , itemNeedsType :: Bool -- ^ Do we need to lookup a type for this item?-  , itemName :: NameDetails+  , itemName      :: NameDetails   }   deriving stock Generic   deriving anyclass (FromJSON, ToJSON)
src/Development/IDE/Plugin/HLS.hs view
@@ -6,75 +6,92 @@ module Development.IDE.Plugin.HLS     (       asGhcIdePlugin+    , toResponseError     , Log(..)     ) where -import           Control.Exception            (SomeException)-import           Control.Lens                 ((^.))+import           Control.Exception             (SomeException)+import           Control.Lens                  ((^.)) import           Control.Monad-import qualified Data.Aeson                   as J-import           Data.Bifunctor               (first)-import           Data.Dependent.Map           (DMap)-import qualified Data.Dependent.Map           as DMap+import           Control.Monad.Trans.Except    (runExceptT)+import qualified Data.Aeson                    as A+import           Data.Bifunctor                (first)+import           Data.Dependent.Map            (DMap)+import qualified Data.Dependent.Map            as DMap import           Data.Dependent.Sum import           Data.Either-import qualified Data.List                    as List-import           Data.List.NonEmpty           (NonEmpty, nonEmpty, toList)-import qualified Data.List.NonEmpty           as NE-import qualified Data.Map                     as Map+import qualified Data.List                     as List+import           Data.List.NonEmpty            (NonEmpty, nonEmpty, toList)+import qualified Data.List.NonEmpty            as NE+import qualified Data.Map                      as Map import           Data.Some import           Data.String-import           Data.Text                    (Text)-import qualified Data.Text                    as T-import           Development.IDE.Core.Shake   hiding (Log)+import           Data.Text                     (Text)+import qualified Data.Text                     as T+import           Development.IDE.Core.Shake    hiding (Log) import           Development.IDE.Core.Tracing-import           Development.IDE.Graph        (Rules)+import           Development.IDE.Graph         (Rules) import           Development.IDE.LSP.Server import           Development.IDE.Plugin-import qualified Development.IDE.Plugin       as P-import           Development.IDE.Types.Logger+import qualified Development.IDE.Plugin        as P+import           Ide.Logger import           Ide.Plugin.Config-import           Ide.PluginUtils              (getClientConfig)-import           Ide.Types                    as HLS-import qualified Language.LSP.Server          as LSP-import           Language.LSP.Types-import qualified Language.LSP.Types           as J-import qualified Language.LSP.Types.Lens      as LSP+import           Ide.Plugin.Error+import           Ide.PluginUtils               (getClientConfig)+import           Ide.Types                     as HLS+import qualified Language.LSP.Protocol.Lens    as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types+import qualified Language.LSP.Server           as LSP import           Language.LSP.VFS-import           Prettyprinter.Render.String  (renderString)-import           Text.Regex.TDFA.Text         ()-import           UnliftIO                     (MonadUnliftIO)-import           UnliftIO.Async               (forConcurrently)-import           UnliftIO.Exception           (catchAny)+import           Prettyprinter.Render.String   (renderString)+import           Text.Regex.TDFA.Text          ()+import           UnliftIO                      (MonadUnliftIO, liftIO)+import           UnliftIO.Async                (forConcurrently)+import           UnliftIO.Exception            (catchAny)  -- --------------------------------------------------------------------- --  data Log-    = LogPluginError PluginId ResponseError+    =  LogPluginError PluginId PluginError+    | LogResponseError PluginId ResponseError     | LogNoPluginForMethod (Some SMethod)     | LogInvalidCommandIdentifier+    | ExceptionInPlugin PluginId (Some SMethod) SomeException+ instance Pretty Log where   pretty = \case-    LogPluginError (PluginId pId) err -> pretty pId <> ":" <+> prettyResponseError err+    LogPluginError (PluginId pId) err ->+      pretty pId <> ":" <+> pretty err+    LogResponseError (PluginId pId) err ->+      pretty pId <> ":" <+> prettyResponseError err     LogNoPluginForMethod (Some method) ->         "No plugin enabled for " <> pretty (show method)     LogInvalidCommandIdentifier-> "Invalid command identifier"-+    ExceptionInPlugin plId (Some method) exception ->+        "Exception in plugin " <> viaShow plId <> " while processing "+          <> viaShow method <> ": " <> viaShow exception instance Show Log where show = renderString . layoutCompact . pretty  -- various error message specific builders prettyResponseError :: ResponseError -> Doc a prettyResponseError err = errorCode <> ":" <+> errorBody     where-        errorCode = pretty $ show $ err ^. LSP.code-        errorBody = pretty $ err ^. LSP.message--pluginNotEnabled :: SMethod m -> [(PluginId, b, a)] -> Text-pluginNotEnabled method availPlugins =-    "No plugin enabled for " <> T.pack (show method) <> ", available: "-        <> (T.intercalate ", " $ map (\(PluginId plid, _, _) -> plid) availPlugins)+        errorCode = pretty $ show $ err ^. L.code+        errorBody = pretty $ err ^. L.message +noPluginEnabled :: Recorder (WithPriority Log) -> SMethod m -> [PluginId] -> IO (Either ResponseError c)+noPluginEnabled recorder m fs' = do+  logWith recorder Warning (LogNoPluginForMethod $ Some m)+  let err = ResponseError (InR ErrorCodes_MethodNotFound) msg Nothing+      msg = pluginNotEnabled m fs'+  return $ Left err+  where pluginNotEnabled :: SMethod m -> [PluginId] -> Text+        pluginNotEnabled method availPlugins =+            "No plugin enabled for " <> T.pack (show method) <> ", potentially available: "+                <> (T.intercalate ", " $ map (\(PluginId plid) -> plid) availPlugins)+   pluginDoesntExist :: PluginId -> Text pluginDoesntExist (PluginId pid) = "Plugin " <> pid <> " doesn't exist" @@ -86,17 +103,21 @@ failedToParseArgs :: CommandId  -- ^ command that failed to parse                     -> PluginId -- ^ Plugin that created the command                     -> String   -- ^ The JSON Error message-                    -> J.Value  -- ^ The Argument Values+                    -> A.Value  -- ^ The Argument Values                     -> Text failedToParseArgs (CommandId com) (PluginId pid) err arg =     "Error while parsing args for " <> com <> " in plugin " <> pid <> ": "         <> T.pack err <> ", arg = " <> T.pack (show arg) +exceptionInPlugin :: PluginId -> SMethod m -> SomeException -> Text+exceptionInPlugin plId method exception =+    "Exception in plugin " <> T.pack (show plId) <> " while processing "<> T.pack (show method) <> ": " <> T.pack (show exception)+ -- | Build a ResponseError and log it before returning to the caller-logAndReturnError :: Recorder (WithPriority Log) -> PluginId -> ErrorCode -> Text -> LSP.LspT Config IO (Either ResponseError a)+logAndReturnError :: Recorder (WithPriority Log) -> PluginId -> (LSPErrorCodes |? ErrorCodes) -> Text -> LSP.LspT Config IO (Either ResponseError a) logAndReturnError recorder p errCode msg = do     let err = ResponseError errCode msg Nothing-    logWith recorder Warning $ LogPluginError p err+    logWith recorder Warning $ LogResponseError p err     pure $ Left err  -- | Map a set of plugins to the underlying ghcide engine.@@ -146,7 +167,7 @@ executeCommandPlugins recorder ecs = mempty { P.pluginHandlers = executeCommandHandlers recorder ecs }  executeCommandHandlers :: Recorder (WithPriority Log) -> [(PluginId, [PluginCommand IdeState])] -> LSP.Handlers (ServerM Config)-executeCommandHandlers recorder ecs = requestHandler SWorkspaceExecuteCommand execCmd+executeCommandHandlers recorder ecs = requestHandler SMethod_WorkspaceExecuteCommand execCmd   where     pluginMap = Map.fromListWith (++) ecs @@ -157,29 +178,29 @@       _                    -> Nothing      -- The parameters to the HLS command are always the first element-+    execCmd :: IdeState -> ExecuteCommandParams -> LSP.LspT Config IO (Either ResponseError (A.Value |? Null))     execCmd ide (ExecuteCommandParams _ cmdId args) = do-      let cmdParams :: J.Value+      let cmdParams :: A.Value           cmdParams = case args of-            Just (J.List (x:_)) -> x-            _                   -> J.Null+            Just ((x:_)) -> x+            _            -> A.Null       case parseCmdId cmdId of         -- Shortcut for immediately applying a applyWorkspaceEdit as a fallback for v3.8 code actions         Just ("hls", "fallbackCodeAction") ->-          case J.fromJSON cmdParams of-            J.Success (FallbackCodeActionParams mEdit mCmd) -> do+          case A.fromJSON cmdParams of+            A.Success (FallbackCodeActionParams mEdit mCmd) -> do                -- Send off the workspace request if it has one               forM_ mEdit $ \edit ->-                LSP.sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing edit) (\_ -> pure ())+                LSP.sendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing edit) (\_ -> pure ())                case mCmd of                 -- If we have a command, continue to execute it-                Just (J.Command _ innerCmdId innerArgs)+                Just (Command _ innerCmdId innerArgs)                     -> execCmd ide (ExecuteCommandParams Nothing innerCmdId innerArgs)-                Nothing -> return $ Right J.Null+                Nothing -> return $ Right $ InR Null -            J.Error _str -> return $ Right J.Null+            A.Error _str -> return $ Right $ InR Null          -- Just an ordinary HIE command         Just (plugin, cmd) -> runPluginCommand ide plugin cmd cmdParams@@ -187,16 +208,26 @@         -- Couldn't parse the command identifier         _ -> do             logWith recorder Warning LogInvalidCommandIdentifier-            return $ Left $ ResponseError InvalidParams "Invalid command identifier" Nothing+            return $ Left $ ResponseError (InR ErrorCodes_InvalidParams) "Invalid command identifier" Nothing +    runPluginCommand :: IdeState -> PluginId -> CommandId -> A.Value -> LSP.LspT Config IO (Either ResponseError (A.Value |? Null))     runPluginCommand ide p com arg =       case Map.lookup p pluginMap  of-        Nothing -> logAndReturnError recorder p InvalidRequest (pluginDoesntExist p)+        Nothing -> logAndReturnError recorder p (InR ErrorCodes_InvalidRequest) (pluginDoesntExist p)         Just xs -> case List.find ((com ==) . commandId) xs of-          Nothing -> logAndReturnError recorder p InvalidRequest (commandDoesntExist com p xs)-          Just (PluginCommand _ _ f) -> case J.fromJSON arg of-            J.Error err -> logAndReturnError recorder p InvalidParams (failedToParseArgs com p err arg)-            J.Success a -> f ide a+          Nothing -> logAndReturnError recorder p (InR ErrorCodes_InvalidRequest) (commandDoesntExist com p xs)+          Just (PluginCommand _ _ f) -> case A.fromJSON arg of+            A.Error err -> logAndReturnError recorder p (InR ErrorCodes_InvalidParams) (failedToParseArgs com p err arg)+            A.Success a -> do+              res <- runExceptT (f ide a) `catchAny` -- See Note [Exception handling in plugins]+                (\e -> pure $ Left $ PluginInternalError (exceptionInPlugin p SMethod_WorkspaceExecuteCommand e))+              case res of+                (Left (PluginRequestRefused _)) ->+                  liftIO $ noPluginEnabled recorder SMethod_WorkspaceExecuteCommand (fst <$> ecs)+                (Left pluginErr) -> do+                  liftIO $ logErrors recorder [(p, pluginErr)]+                  pure $ Left $ toResponseError (p, pluginErr)+                (Right result) -> pure $ Right result  -- --------------------------------------------------------------------- @@ -218,25 +249,25 @@         let fs = filter (\(_, desc, _) -> pluginEnabled m params desc config) fs'         -- Clients generally don't display ResponseErrors so instead we log any that we come across         case nonEmpty fs of-          Nothing -> do-            logWith recorder Warning (LogNoPluginForMethod $ Some m)-            let err = ResponseError InvalidRequest msg Nothing-                msg = pluginNotEnabled m fs'-            return $ Left err+          Nothing -> liftIO $ noPluginEnabled recorder m ((\(x, _, _) -> x) <$> fs')           Just fs -> do-            let msg e pid = "Exception in plugin " <> T.pack (show pid) <> " while processing " <> T.pack (show m) <> ": " <> T.pack (show e)-                handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs-            es <- runConcurrently msg (show m) handlers ide params-+            let  handlers = fmap (\(plid,_,handler) -> (plid,handler)) fs+            es <- runConcurrently exceptionInPlugin m handlers ide params+            caps <- LSP.getClientCapabilities             let (errs,succs) = partitionEithers $ toList $ join $ NE.zipWith (\(pId,_) -> fmap (first (pId,))) handlers es-            unless (null errs) $ forM_ errs $ \(pId, err) ->-                logWith recorder Warning $ LogPluginError pId err+            liftIO $ unless (null errs) $ logErrors recorder errs             case nonEmpty succs of-              Nothing -> pure $ Left $ combineErrors $ map snd errs+              Nothing -> do+                let noRefused (_, PluginRequestRefused _) = False+                    noRefused (_, _)                      = True+                    filteredErrs = filter noRefused errs+                case nonEmpty filteredErrs of+                  Nothing -> liftIO $ noPluginEnabled recorder m ((\(x, _, _) -> x) <$> fs')+                  Just xs -> pure $ Left $ combineErrors xs               Just xs -> do-                caps <- LSP.getClientCapabilities                 pure $ Right $ combineResponses m config caps params xs + -- ---------------------------------------------------------------------  extensibleNotificationPlugins :: Recorder (WithPriority Log) -> [(PluginId, PluginDescriptor IdeState)] -> Plugin Config@@ -260,35 +291,51 @@           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 vfs params) fs+            mapM_ (\(pid,_,f) -> otTracedProvider pid (fromString $ show m) $ f ide vfs params+                                    `catchAny` -- See Note [Exception handling in plugins]+                                    (\e -> logWith recorder Warning (ExceptionInPlugin pid (Some m) e))) fs + -- ---------------------------------------------------------------------  runConcurrently   :: MonadUnliftIO m-  => (SomeException -> PluginId -> T.Text)-  -> String -- ^ label-  -> NonEmpty (PluginId, a -> b -> m (NonEmpty (Either ResponseError d)))+  => (PluginId -> SMethod method -> SomeException -> T.Text)+  -> SMethod method -- ^ Method (used for errors and tracing)+  -> NonEmpty (PluginId, a -> b -> m (NonEmpty (Either PluginError d)))   -- ^ Enabled plugin actions that we are allowed to run   -> a   -> b-  -> m (NonEmpty(NonEmpty (Either ResponseError d)))-runConcurrently msg method fs a b = forConcurrently fs $ \(pid,f) -> otTracedProvider pid (fromString method) $ do-  f a b-     `catchAny` (\e -> pure $ pure $ Left $ ResponseError InternalError (msg e pid) Nothing)+  -> m (NonEmpty(NonEmpty (Either PluginError d)))+runConcurrently msg method fs a b = forConcurrently fs $ \(pid,f) -> otTracedProvider pid (fromString (show method)) $ do+  f a b  -- See Note [Exception handling in plugins]+     `catchAny` (\e -> pure $ pure $ Left $ PluginInternalError (msg pid method e)) -combineErrors :: [ResponseError] -> ResponseError-combineErrors [x] = x-combineErrors xs  = ResponseError InternalError (T.pack (show xs)) Nothing+combineErrors :: NonEmpty (PluginId, PluginError) -> ResponseError+combineErrors (x NE.:| []) = toResponseError x+combineErrors xs = toResponseError $ NE.last $ NE.sortWith (toPriority . snd) xs +toResponseError :: (PluginId, PluginError) -> ResponseError+toResponseError (PluginId plId, err) =+        ResponseError (toErrorCode err) (plId <> ": " <> tPretty err) Nothing+    where tPretty = T.pack . show . pretty++logErrors :: Recorder (WithPriority Log) -> [(PluginId, PluginError)] -> IO ()+logErrors recorder errs = do+  forM_ errs $ \(pId, err) ->+                logIndividualErrors pId err+  where logIndividualErrors plId err =+          logWith recorder (toPriority err) $ LogPluginError plId err++ -- | Combine the 'PluginHandler' for all plugins-newtype IdeHandler (m :: J.Method FromClient Request)-  = IdeHandler [(PluginId, PluginDescriptor IdeState, IdeState -> MessageParams m -> LSP.LspM Config (NonEmpty (Either ResponseError (ResponseResult m))))]+newtype IdeHandler (m :: Method ClientToServer Request)+  = IdeHandler [(PluginId, PluginDescriptor IdeState, IdeState -> MessageParams m -> LSP.LspM Config (NonEmpty (Either PluginError (MessageResult m))))]  -- | Combine the 'PluginHandler' for all plugins-newtype IdeNotificationHandler (m :: J.Method FromClient Notification)+newtype IdeNotificationHandler (m :: Method ClientToServer Notification)   = IdeNotificationHandler [(PluginId, PluginDescriptor IdeState, IdeState -> VFS -> MessageParams m -> LSP.LspM Config ())]--- type NotificationHandler (m :: Method FromClient Notification) = MessageParams m -> IO ()`+-- type NotificationHandler (m :: Method ClientToServer Notification) = MessageParams m -> IO ()`  -- | Combine the 'PluginHandlers' for all plugins newtype IdeHandlers             = IdeHandlers             (DMap IdeMethod       IdeHandler)@@ -307,3 +354,16 @@       go _ (IdeNotificationHandler a) (IdeNotificationHandler b) = IdeNotificationHandler (a <> b) instance Monoid IdeNotificationHandlers where   mempty = IdeNotificationHandlers mempty++{- Note [Exception handling in plugins]+Plugins run in LspM, and so have access to IO. This means they are likely to+throw exceptions, even if only by accident or through calling libraries that+throw exceptions. Ultimately, we're running a bunch of less-trusted IO code,+so we should be robust to it throwing.++We don't want these to bring down HLS. So we catch and log exceptions wherever+we run a handler defined in a plugin.++The flip side of this is that it's okay for plugins to throw exceptions as a+way of signalling failure!+-}
src/Development/IDE/Plugin/HLS/GhcIde.hs view
@@ -15,8 +15,8 @@ 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           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import           Text.Regex.TDFA.Text                ()  data Log@@ -43,29 +43,23 @@  descriptor :: PluginId -> PluginDescriptor IdeState descriptor plId = (defaultPluginDescriptor plId)-  { pluginHandlers = mkPluginHandler STextDocumentHover hover'-                  <> mkPluginHandler STextDocumentDocumentSymbol symbolsProvider-                  <> mkPluginHandler STextDocumentDefinition (\ide _ DefinitionParams{..} ->+  { pluginHandlers = mkPluginHandler SMethod_TextDocumentHover hover'+                  <> mkPluginHandler SMethod_TextDocumentDocumentSymbol moduleOutline+                  <> mkPluginHandler SMethod_TextDocumentDefinition (\ide _ DefinitionParams{..} ->                       gotoDefinition ide TextDocumentPositionParams{..})-                  <> mkPluginHandler STextDocumentTypeDefinition (\ide _ TypeDefinitionParams{..} ->+                  <> mkPluginHandler SMethod_TextDocumentTypeDefinition (\ide _ TypeDefinitionParams{..} ->                       gotoTypeDefinition ide TextDocumentPositionParams{..})-                  <> mkPluginHandler STextDocumentDocumentHighlight (\ide _ DocumentHighlightParams{..} ->+                  <> mkPluginHandler SMethod_TextDocumentDocumentHighlight (\ide _ DocumentHighlightParams{..} ->                       documentHighlight ide TextDocumentPositionParams{..})-                  <> mkPluginHandler STextDocumentReferences (\ide _ params -> references ide params)-                  <> mkPluginHandler SWorkspaceSymbol (\ide _ params -> wsSymbols ide params),+                  <> mkPluginHandler SMethod_TextDocumentReferences references+                  <> mkPluginHandler SMethod_WorkspaceSymbol wsSymbols,      pluginConfigDescriptor = defaultConfigDescriptor   }  -- --------------------------------------------------------------------- -hover' :: IdeState -> PluginId -> HoverParams  -> LspM c (Either ResponseError (Maybe Hover))+hover' :: PluginMethodHandler IdeState 'Method_TextDocumentHover hover' ideState _ HoverParams{..} = do     liftIO $ logDebug (ideLogger ideState) "GhcIde.hover entered (ideLogger)" -- AZ     hover ideState TextDocumentPositionParams{..}---- ----------------------------------------------------------------------symbolsProvider :: IdeState -> PluginId -> DocumentSymbolParams -> LspM c (Either ResponseError (List DocumentSymbol |? List SymbolInformation))-symbolsProvider ide _ params = moduleOutline ide params---- ---------------------------------------------------------------------
src/Development/IDE/Plugin/Test.hs view
@@ -14,14 +14,18 @@  import           Control.Concurrent                   (threadDelay) import           Control.Monad+import           Control.Monad.Except                 (ExceptT (..), throwError) import           Control.Monad.IO.Class import           Control.Monad.STM-import           Data.Aeson-import           Data.Aeson.Types+import           Control.Monad.Trans.Class            (MonadTrans (lift))+import           Data.Aeson                           (FromJSON (parseJSON),+                                                       ToJSON (toJSON), Value)+import qualified Data.Aeson.Types                     as A import           Data.Bifunctor import           Data.CaseInsensitive                 (CI, original) import qualified Data.HashMap.Strict                  as HM import           Data.Maybe                           (isJust)+import           Data.Proxy import           Data.String import           Data.Text                            (Text, pack) import           Development.IDE.Core.OfInterest      (getFilesOfInterest)@@ -43,9 +47,11 @@ import           Development.IDE.Types.Location       (fromUri) import           GHC.Generics                         (Generic) import           Ide.Plugin.Config                    (CheckParents)+import           Ide.Plugin.Error import           Ide.Types+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import qualified Language.LSP.Server                  as LSP-import           Language.LSP.Types import qualified "list-t" ListT import qualified StmContainers.Map                    as STM import           System.Time.Extra@@ -73,26 +79,26 @@  plugin :: PluginDescriptor IdeState plugin = (defaultPluginDescriptor "test") {-    pluginHandlers = mkPluginHandler (SCustomMethod "test") $ \st _ ->+    pluginHandlers = mkPluginHandler (SMethod_CustomMethod (Proxy @"test")) $ \st _ ->         testRequestHandler' st     }   where       testRequestHandler' ide req-        | Just customReq <- parseMaybe parseJSON req-        = testRequestHandler ide customReq+        | Just customReq <- A.parseMaybe parseJSON req+        = ExceptT $ testRequestHandler ide customReq         | otherwise-        = return $ Left-        $ ResponseError InvalidRequest "Cannot parse request" Nothing+        = throwError+        $ PluginInvalidParams "Cannot parse request"   testRequestHandler ::  IdeState                 -> TestRequest-                -> LSP.LspM c (Either ResponseError Value)+                -> LSP.LspM c (Either PluginError Value) testRequestHandler _ (BlockSeconds secs) = do-    LSP.sendNotification (SCustomMethod "ghcide/blocking/request") $+    LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/blocking/request")) $       toJSON secs     liftIO $ sleep secs-    return (Right Null)+    return (Right A.Null) testRequestHandler s (GetInterfaceFilesDir file) = liftIO $ do     let nfp = fromUri $ toNormalizedUri file     sess <- runAction "Test - GhcSession" s $ use_ GhcSession nfp@@ -105,12 +111,12 @@     atomically $ do         n <- countQueue $ actionQueue $ shakeExtras s         when (n>0) retry-    return $ Right Null+    return $ Right A.Null testRequestHandler s (WaitForIdeRule k file) = liftIO $ do     let nfp = fromUri $ toNormalizedUri file     success <- runAction ("WaitForIdeRule " <> k <> " " <> show file) s $ parseAction (fromString k) nfp     let res = WaitForIdeRuleResult <$> success-    return $ bimap mkResponseError toJSON res+    return $ bimap PluginInvalidParams toJSON res testRequestHandler s GetBuildKeysBuilt = liftIO $ do     keys <- getDatabaseKeys resultBuilt $ shakeDb s     return $ Right $ toJSON $ map show keys@@ -144,9 +150,6 @@     step <- shakeGetBuildStep db     return [ k | (k, res) <- keys, field res == Step step] -mkResponseError :: Text -> ResponseError-mkResponseError msg = ResponseError InvalidRequest msg Nothing- parseAction :: CI String -> NormalizedFilePath -> Action (Either Text Bool) parseAction "typecheck" fp = Right . isJust <$> use TypeCheck fp parseAction "getLocatedImports" fp = Right . isJust <$> use GetLocatedImports fp@@ -170,6 +173,6 @@  blockCommandHandler :: CommandFunction state ExecuteCommandParams blockCommandHandler _ideState _params = do-  LSP.sendNotification (SCustomMethod "ghcide/blocking/command") Null+  lift $ LSP.sendNotification (SMethod_CustomMethod (Proxy @"ghcide/blocking/command")) A.Null   liftIO $ threadDelay maxBound-  return (Right Null)+  pure $ InR Null
src/Development/IDE/Plugin/TypeLenses.hs view
@@ -18,19 +18,20 @@ import           Control.Monad                        (mzero) import           Control.Monad.Extra                  (whenMaybe) import           Control.Monad.IO.Class               (MonadIO (liftIO))-import           Data.Aeson.Types                     (Value (..), toJSON)+import           Control.Monad.Trans.Class            (MonadTrans (lift))+import           Data.Aeson.Types                     (Value, toJSON) import qualified Data.Aeson.Types                     as A-import qualified Data.HashMap.Strict                  as Map import           Data.List                            (find)+import qualified Data.Map                             as Map import           Data.Maybe                           (catMaybes, mapMaybe) import qualified Data.Text                            as T import           Development.IDE                      (GhcSession (..),                                                        HscEnvEq (hscEnv),                                                        RuleResult, Rules,                                                        define, srcSpanToRange,-                                                       usePropertyAction,-                                                       useWithStale)+                                                       usePropertyAction) import           Development.IDE.Core.Compile         (TcModuleResult (..))+import           Development.IDE.Core.PluginUtils import           Development.IDE.Core.PositionMapping (PositionMapping,                                                        toCurrentRange) import           Development.IDE.Core.Rules           (IdeState, runAction)@@ -46,12 +47,13 @@ import           Development.IDE.Spans.LocalBindings  (Bindings, getFuzzyScope) import           Development.IDE.Types.Location       (Position (Position, _character, _line),                                                        Range (Range, _end, _start))-import           Development.IDE.Types.Logger         (Pretty (pretty),+import           GHC.Generics                         (Generic)+import           Ide.Logger                           (Pretty (pretty),                                                        Recorder, WithPriority,                                                        cmapWithPrio)-import           GHC.Generics                         (Generic)+import           Ide.Plugin.Error import           Ide.Plugin.Properties-import           Ide.PluginUtils+import           Ide.PluginUtils                      (mkLspCommand) import           Ide.Types                            (CommandFunction,                                                        CommandId (CommandId),                                                        PluginCommand (PluginCommand),@@ -63,17 +65,18 @@                                                        defaultPluginDescriptor,                                                        mkCustomConfig,                                                        mkPluginHandler)-import qualified Language.LSP.Server                  as LSP-import           Language.LSP.Types                   (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams),+import           Language.LSP.Protocol.Message        (Method (Method_TextDocumentCodeLens),+                                                       SMethod (..))+import           Language.LSP.Protocol.Types          (ApplyWorkspaceEditParams (ApplyWorkspaceEditParams),                                                        CodeLens (CodeLens),                                                        CodeLensParams (CodeLensParams, _textDocument),                                                        Diagnostic (..),-                                                       List (..),-                                                       Method (TextDocumentCodeLens),-                                                       SMethod (..),+                                                       Null (Null),                                                        TextDocumentIdentifier (TextDocumentIdentifier),                                                        TextEdit (TextEdit),-                                                       WorkspaceEdit (WorkspaceEdit))+                                                       WorkspaceEdit (WorkspaceEdit),+                                                       type (|?) (..))+import qualified Language.LSP.Server                  as LSP import           Text.Regex.TDFA                      ((=~), (=~~))  data Log = LogShake Shake.Log deriving Show@@ -88,7 +91,7 @@ descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState descriptor recorder plId =   (defaultPluginDescriptor plId)-    { pluginHandlers = mkPluginHandler STextDocumentCodeLens codeLensProvider+    { pluginHandlers = mkPluginHandler SMethod_TextDocumentCodeLens codeLensProvider     , pluginCommands = [PluginCommand (CommandId typeLensCommandId) "adds a signature" commandHandler]     , pluginRules = rules recorder     , pluginConfigDescriptor = defaultConfigDescriptor {configCustomConfig = mkCustomConfig properties}@@ -102,34 +105,28 @@     , (Diagnostics, "Follows error messages produced by GHC about missing signatures")     ] Always -codeLensProvider :: PluginMethodHandler IdeState TextDocumentCodeLens-codeLensProvider ideState pId CodeLensParams{_textDocument = TextDocumentIdentifier uri} = pluginResponse $ do+codeLensProvider :: PluginMethodHandler IdeState Method_TextDocumentCodeLens+codeLensProvider ideState pId CodeLensParams{_textDocument = TextDocumentIdentifier uri} = do     mode <- liftIO $ runAction "codeLens.config" ideState $ usePropertyAction #mode pId properties-    nfp <- getNormalizedFilePath uri-    env <- hscEnv . fst-            <$> (handleMaybeM "Unable to get GhcSession"-                $ liftIO-                $ runAction "codeLens.GhcSession" ideState (useWithStale GhcSession nfp)-                )-    tmr <- fst <$> (-                handleMaybeM "Unable to TypeCheck"-              $ liftIO-              $ runAction "codeLens.TypeCheck" ideState (useWithStale TypeCheck nfp)-              )-    bindings <- fst <$> (-                handleMaybeM "Unable to GetBindings"-                $ liftIO-                $ runAction "codeLens.GetBindings" ideState (useWithStale GetBindings nfp)-                )+    nfp <- getNormalizedFilePathE uri+    env <- hscEnv . fst <$>+      runActionE "codeLens.GhcSession" ideState+        (useWithStaleE GhcSession nfp)++    (tmr, _) <- runActionE "codeLens.TypeCheck" ideState+      (useWithStaleE TypeCheck nfp)++    (bindings, _) <- runActionE "codeLens.GetBindings" ideState+      (useWithStaleE GetBindings nfp)+     (gblSigs@(GlobalBindingTypeSigsResult gblSigs'), gblSigsMp) <--      handleMaybeM "Unable to GetGlobalBindingTypeSigs"-      $ liftIO-      $ runAction "codeLens.GetGlobalBindingTypeSigs" ideState (useWithStale GetGlobalBindingTypeSigs nfp)+      runActionE "codeLens.GetGlobalBindingTypeSigs" ideState+        (useWithStaleE GetGlobalBindingTypeSigs nfp)      diag <- liftIO $ atomically $ getDiagnostics ideState     hDiag <- liftIO $ atomically $ getHiddenDiagnostics ideState -    let toWorkSpaceEdit tedit = WorkspaceEdit (Just $ Map.singleton uri $ List tedit) Nothing Nothing+    let toWorkSpaceEdit tedit = WorkspaceEdit (Just $ Map.singleton uri $ tedit) Nothing Nothing         generateLensForGlobal mp sig@GlobalBindingTypeSig{gbRendered} = do             range <- toCurrentRange mp =<< srcSpanToRange (gbSrcSpan sig)             tedit <- gblBindingTypeSigToEdit sig (Just gblSigsMp)@@ -144,7 +141,7 @@               ]     -- `suggestLocalSignature` relies on diagnostic, if diagnostics don't have the local signature warning,     -- the `bindings` is useless, and if diagnostic has, that means we parsed success, and the `bindings` is fresh.-    pure $ List $ case mode of+    pure $ InL $ case mode of         Always ->           mapMaybe (generateLensForGlobal gblSigsMp) gblSigs'             <> generateLensFromDiags@@ -160,8 +157,8 @@  commandHandler :: CommandFunction IdeState WorkspaceEdit commandHandler _ideState wedit = do-  _ <- LSP.sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedit) (\_ -> pure ())-  return $ Right Null+  _ <- lift $ LSP.sendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedit) (\_ -> pure ())+  pure $ InR Null  -------------------------------------------------------------------------------- 
src/Development/IDE/Spans/AtPoint.hs view
@@ -1,9 +1,10 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE CPP        #-}-{-# LANGUAGE GADTs      #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE ScopedTypeVariables #-}  -- | Gives information about symbols at a given point in DAML files. -- These are all pure functions that should execute quickly.@@ -27,7 +28,8 @@ import           Development.IDE.GHC.Error import           Development.IDE.GHC.Orphans          () import           Development.IDE.Types.Location-import           Language.LSP.Types+import           Language.LSP.Protocol.Types          hiding+                                                      (SemanticTokenAbsolute (..))  -- compiler and infrastructure import           Development.IDE.Core.PositionMapping@@ -178,8 +180,8 @@       DocumentHighlight (realSrcSpanToRange sp) (Just $ highlightType $ identInfo dets)     highlightType s =       if any (isJust . getScopeFromContext) s-        then HkWrite-        else HkRead+        then DocumentHighlightKind_Write+        else DocumentHighlightKind_Read  gotoTypeDefinition   :: MonadIO m@@ -212,21 +214,33 @@   -> DocAndKindMap   -> HscEnv   -> Position-  -> Maybe (Maybe Range, [T.Text])-atPoint IdeOptions{} (HAR _ hf _ _ kind) (DKMap dm km) env pos = listToMaybe $ pointCommand hf pos hoverInfo+  -> IO (Maybe (Maybe Range, [T.Text]))+atPoint IdeOptions{} (HAR _ hf _ _ (kind :: HieKind hietype)) (DKMap dm km) env pos =+    listToMaybe <$> sequence (pointCommand hf pos hoverInfo)   where     -- Hover info for values/data-    hoverInfo ast = (Just range, prettyNames ++ pTypes)+    hoverInfo :: HieAST hietype -> IO (Maybe Range, [T.Text])+    hoverInfo ast = do+        prettyNames <- mapM prettyName filteredNames+        pure (Just range, prettyNames ++ pTypes)       where+        pTypes :: [T.Text]         pTypes           | Prelude.length names == 1 = dropEnd1 $ map wrapHaskell prettyTypes           | otherwise = map wrapHaskell prettyTypes +        range :: Range         range = realSrcSpanToRange $ nodeSpan ast +        wrapHaskell :: T.Text -> T.Text         wrapHaskell x = "\n```haskell\n"<>x<>"\n```\n"++        info :: NodeInfo hietype         info = nodeInfoH kind ast++        names :: [(Identifier, IdentifierDetails hietype)]         names = M.assocs $ nodeIdentifiers info+         -- Check for evidence bindings         isInternal :: (Identifier, IdentifierDetails a) -> Bool         isInternal (Right _, dets) =@@ -236,11 +250,12 @@           False #endif         isInternal (Left _, _) = False++        filteredNames :: [(Identifier, IdentifierDetails hietype)]         filteredNames = filter (not . isInternal) names-        types = nodeType info-        prettyNames :: [T.Text]-        prettyNames = map prettyName filteredNames-        prettyName (Right n, dets) = T.unlines $++        prettyName :: (Either ModuleName Name, IdentifierDetails hietype) -> IO T.Text+        prettyName (Right n, dets) = pure $ T.unlines $           wrapHaskell (printOutputable n <> maybe "" (" :: " <>) ((prettyType <$> identType dets) <|> maybeKind))           : maybeToList (pretty (definedAt n) (prettyPackageName n))           ++ catMaybes [ T.unlines . spanDocToMarkdown <$> lookupNameEnv dm n@@ -250,21 +265,48 @@                 pretty (Just define) Nothing = Just $ define <> "\n"                 pretty Nothing (Just pkgName) = Just $ pkgName <> "\n"                 pretty (Just define) (Just pkgName) = Just $ define <> " " <> pkgName <> "\n"-        prettyName (Left m,_) = printOutputable m+        prettyName (Left m,_) = packageNameForImportStatement m +        prettyPackageName :: Name -> Maybe T.Text         prettyPackageName n = do           m <- nameModule_maybe n+          pkgTxt <- packageNameWithVersion m env+          pure $ "*(" <> pkgTxt <> ")*"++        -- Return the module text itself and+        -- the package(with version) this `ModuleName` belongs to.+        packageNameForImportStatement :: ModuleName -> IO T.Text+        packageNameForImportStatement mod = do+          mpkg <- findImportedModule env mod :: IO (Maybe Module)+          let moduleName = printOutputable mod+          case mpkg >>= flip packageNameWithVersion env of+            Nothing             -> pure moduleName+            Just pkgWithVersion -> pure $ moduleName <> "\n\n" <> pkgWithVersion++        -- Return the package name and version of a module.+        -- For example, given module `Data.List`, it should return something like `base-4.x`.+        packageNameWithVersion :: Module -> HscEnv -> Maybe T.Text+        packageNameWithVersion m env = do           let pid = moduleUnit m           conf <- lookupUnit env pid           let pkgName = T.pack $ unitPackageNameString conf               version = T.pack $ showVersion (unitPackageVersion conf)-          pure $ "*(" <> pkgName <> "-" <> version <> ")*"+          pure $ pkgName <> "-" <> version +        -- Type info for the current node, it may contains several symbols+        -- for one range, like wildcard+        types :: [hietype]+        types = nodeType info++        prettyTypes :: [T.Text]         prettyTypes = map (("_ :: "<>) . prettyType) types++        prettyType :: hietype -> T.Text         prettyType t = case kind of           HieFresh -> printOutputable t           HieFromDisk full_file -> printOutputable $ hieTypeToIface $ recoverFullType t (hie_types full_file) +        definedAt :: Name -> Maybe T.Text         definedAt name =           -- do not show "at <no location info>" and similar messages           -- see the code of 'pprNameDefnLoc' for more information@@ -391,13 +433,15 @@  defRowToSymbolInfo :: Res DefRow -> Maybe SymbolInformation defRowToSymbolInfo (DefRow{..}:.(modInfoSrcFile -> Just srcFile))-  = Just $ SymbolInformation (printOutputable defNameOcc) kind Nothing Nothing loc Nothing+  = Just $ SymbolInformation (printOutputable defNameOcc) kind Nothing Nothing Nothing loc   where     kind-      | isVarOcc defNameOcc = SkVariable-      | isDataOcc defNameOcc = SkConstructor-      | isTcOcc defNameOcc = SkStruct-      | otherwise = SkUnknown 1+      | isVarOcc defNameOcc = SymbolKind_Variable+      | isDataOcc defNameOcc = SymbolKind_Constructor+      | isTcOcc defNameOcc = SymbolKind_Struct+        -- This used to be (SkUnknown 1), buth there is no SymbolKind_Unknown.+        -- Changing this to File, as that is enum representation of 1+      | otherwise = SymbolKind_File     loc   = Location file range     file  = fromNormalizedUri . filePathToUri' . toNormalizedFilePath' $ srcFile     range = Range start end@@ -424,6 +468,7 @@  where    sloc fs = mkRealSrcLoc fs (fromIntegral $ line+1) (fromIntegral $ cha+1)    sp fs = mkRealSrcSpan (sloc fs) (sloc fs)+   line :: UInt    line = _line pos    cha = _character pos 
src/Development/IDE/Spans/Documentation.hs view
@@ -32,7 +32,7 @@ import           System.Directory import           System.FilePath -import           Language.LSP.Types              (filePathToUri, getUri)+import           Language.LSP.Protocol.Types     (filePathToUri, getUri) #if MIN_VERSION_ghc(9,3,0) import           GHC.Types.Unique.Map #endif
src/Development/IDE/Spans/Pragmas.hs view
@@ -15,15 +15,16 @@ import qualified Data.Maybe                      as Maybe import           Data.Text                       (Text, pack) import qualified Data.Text                       as Text-import           Development.IDE                 (srcSpanToRange, IdeState, NormalizedFilePath, runAction, useWithStale, GhcSession (..), getFileContents, hscEnv)+import           Development.IDE                 (srcSpanToRange, IdeState, NormalizedFilePath, GhcSession (..), getFileContents, hscEnv, runAction) import           Development.IDE.GHC.Compat import           Development.IDE.GHC.Compat.Util-import qualified Language.LSP.Types              as LSP+import qualified Language.LSP.Protocol.Types              as LSP import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans.Except (ExceptT)+import Ide.Plugin.Error (PluginError) import Ide.Types (PluginId(..)) import qualified Data.Text as T-import Ide.PluginUtils (handleMaybeM)+import  Development.IDE.Core.PluginUtils  getNextPragmaInfo :: DynFlags -> Maybe Text -> NextPragmaInfo getNextPragmaInfo dynFlags sourceText =@@ -51,13 +52,11 @@         pragmaInsertPosition = LSP.Position (fromIntegral nextPragmaLine) 0         pragmaInsertRange = LSP.Range pragmaInsertPosition pragmaInsertPosition -getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT String m NextPragmaInfo-getFirstPragma (PluginId pId) state nfp = handleMaybeM "Could not get NextPragmaInfo" $ do-  ghcSession <- liftIO $ runAction (T.unpack pId <> ".GhcSession") state $ useWithStale GhcSession nfp+getFirstPragma :: MonadIO m => PluginId -> IdeState -> NormalizedFilePath -> ExceptT PluginError m NextPragmaInfo+getFirstPragma (PluginId pId) state nfp = do+  (hscEnv -> hsc_dflags -> sessionDynFlags, _) <- runActionE (T.unpack pId <> ".GhcSession") state $ useWithStaleE GhcSession nfp   (_, fileContents) <- liftIO $ runAction (T.unpack pId <> ".GetFileContents") state $ getFileContents nfp-  case ghcSession of-    Just (hscEnv -> hsc_dflags -> sessionDynFlags, _) -> pure $ Just $ getNextPragmaInfo sessionDynFlags fileContents-    Nothing                                           -> pure Nothing+  pure $ getNextPragmaInfo sessionDynFlags fileContents  -- Pre-declaration comments parser ----------------------------------------------------- 
src/Development/IDE/Types/Action.hs view
@@ -11,12 +11,12 @@ where  import           Control.Concurrent.STM-import           Data.Hashable                (Hashable (..))-import           Data.HashSet                 (HashSet)-import qualified Data.HashSet                 as Set-import           Data.Unique                  (Unique)-import           Development.IDE.Graph        (Action)-import           Development.IDE.Types.Logger+import           Data.Hashable          (Hashable (..))+import           Data.HashSet           (HashSet)+import qualified Data.HashSet           as Set+import           Data.Unique            (Unique)+import           Development.IDE.Graph  (Action)+import           Ide.Logger import           Numeric.Natural  data DelayedAction a = DelayedAction
src/Development/IDE/Types/Diagnostics.hs view
@@ -9,7 +9,6 @@   IdeResult,   LSP.DiagnosticSeverity(..),   DiagnosticStore,-  List(..),   ideErrorText,   ideErrorWithSource,   showDiagnostics,@@ -17,20 +16,17 @@   IdeResultNoDiagnosticsEarlyCutoff) where  import           Control.DeepSeq-import           Data.Maybe                                as Maybe-import qualified Data.Text                                 as T-import           Data.Text.Prettyprint.Doc-import           Data.Text.Prettyprint.Doc.Render.Terminal (Color (..), color)-import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Terminal-import           Data.Text.Prettyprint.Doc.Render.Text-import           Language.LSP.Diagnostics-import           Language.LSP.Types                        as LSP (Diagnostic (..),-                                                                   DiagnosticSeverity (..),-                                                                   DiagnosticSource,-                                                                   List (..))--import           Data.ByteString                           (ByteString)+import           Data.ByteString                (ByteString)+import           Data.Maybe                     as Maybe+import qualified Data.Text                      as T import           Development.IDE.Types.Location+import           Language.LSP.Diagnostics+import           Language.LSP.Protocol.Types    as LSP (Diagnostic (..),+                                                        DiagnosticSeverity (..))+import           Prettyprinter+import           Prettyprinter.Render.Terminal  (Color (..), color)+import qualified Prettyprinter.Render.Terminal  as Terminal+import           Prettyprinter.Render.Text   -- | The result of an IDE operation. Warnings and errors are in the Diagnostic,@@ -49,10 +45,10 @@ type IdeResultNoDiagnosticsEarlyCutoff  v = (Maybe ByteString, Maybe v)  ideErrorText :: NormalizedFilePath -> T.Text -> FileDiagnostic-ideErrorText = ideErrorWithSource (Just "compiler") (Just DsError)+ideErrorText = ideErrorWithSource (Just "compiler") (Just DiagnosticSeverity_Error)  ideErrorWithSource-  :: Maybe DiagnosticSource+  :: Maybe T.Text   -> Maybe DiagnosticSeverity   -> a   -> T.Text@@ -64,7 +60,9 @@     _source = source,     _message = msg,     _relatedInformation = Nothing,-    _tags = Nothing+    _tags = Nothing,+    _codeDescription = Nothing,+    _data_ = Nothing     })  -- | Defines whether a particular diagnostic should be reported@@ -117,14 +115,14 @@         , slabel_ "Severity:" $ pretty $ show sev         , slabel_ "Message: "             $ case sev of-              LSP.DsError   -> annotate $ color Red-              LSP.DsWarning -> annotate $ color Yellow-              LSP.DsInfo    -> annotate $ color Blue-              LSP.DsHint    -> annotate $ color Magenta+              LSP.DiagnosticSeverity_Error       -> annotate $ color Red+              LSP.DiagnosticSeverity_Warning     -> annotate $ color Yellow+              LSP.DiagnosticSeverity_Information -> annotate $ color Blue+              LSP.DiagnosticSeverity_Hint        -> annotate $ color Magenta             $ stringParagraphs _message         ]     where-        sev = fromMaybe LSP.DsError _severity+        sev = fromMaybe LSP.DiagnosticSeverity_Error _severity   -- | Label a document.
src/Development/IDE/Types/HscEnvEq.hs view
@@ -4,6 +4,7 @@     hscEnvWithImportPaths,     newHscEnvEqPreserveImportPaths,     newHscEnvEqWithImportPaths,+    updateHscEnvEq,     envImportPaths,     envPackageExports,     envVisibleModuleNames,@@ -32,7 +33,8 @@ import           System.FilePath  -- | An 'HscEnv' with equality. Two values are considered equal---   if they are created with the same call to 'newHscEnvEq'.+--   if they are created with the same call to 'newHscEnvEq' or+--   'updateHscEnvEq'. data HscEnvEq = HscEnvEq     { envUnique             :: !Unique     , hscEnv                :: !HscEnv@@ -50,6 +52,11 @@         -- So it's wrapped in IO here for error handling         -- If Nothing, 'listVisibleModuleNames' panic     }++updateHscEnvEq :: HscEnvEq -> HscEnv -> IO HscEnvEq+updateHscEnvEq oldHscEnvEq newHscEnv = do+  let update newUnique = oldHscEnvEq { envUnique = newUnique, hscEnv = newHscEnv }+  update <$> Unique.newUnique  -- | Wrap an 'HscEnv' into an 'HscEnvEq'. newHscEnvEq :: FilePath -> HscEnv -> [(UnitId, DynFlags)] -> IO HscEnvEq
src/Development/IDE/Types/Location.hs view
@@ -39,9 +39,9 @@ import           FastString import           SrcLoc                       as GHC #endif-import           Language.LSP.Types           (Location (..), Position (..),+import           Language.LSP.Protocol.Types  (Location (..), Position (..),                                                Range (..))-import qualified Language.LSP.Types           as LSP+import qualified Language.LSP.Protocol.Types  as LSP import           Text.ParserCombinators.ReadP as ReadP  toNormalizedFilePath' :: FilePath -> LSP.NormalizedFilePath
− src/Development/IDE/Types/Logger.hs
@@ -1,335 +0,0 @@--- Copyright (c) 2019 The DAML Authors. All rights reserved.--- SPDX-License-Identifier: Apache-2.0--{-# LANGUAGE CPP        #-}-{-# LANGUAGE RankNTypes #-}--- | This is a compatibility module that abstracts over the--- concrete choice of logging framework so users can plug in whatever--- framework they want to.-module Development.IDE.Types.Logger-  ( Priority(..)-  , Logger(..)-  , Recorder(..)-  , logError, logWarning, logInfo, logDebug-  , noLogging-  , WithPriority(..)-  , logWith-  , cmap-  , cmapIO-  , cfilter-  , withDefaultRecorder-  , makeDefaultStderrRecorder-  , makeDefaultHandleRecorder-  , LoggingColumn(..)-  , cmapWithPrio-  , withBacklog-  , lspClientMessageRecorder-  , lspClientLogRecorder-  , module PrettyPrinterModule-  , renderStrict-  , toCologActionWithPrio-  ) where--import           Control.Concurrent                    (myThreadId)-import           Control.Concurrent.Extra              (Lock, newLock, withLock)-import           Control.Concurrent.STM                (atomically,-                                                        flushTBQueue,-                                                        isFullTBQueue,-                                                        newTBQueueIO, newTVarIO,-                                                        readTVarIO,-                                                        writeTBQueue, writeTVar)-import           Control.Exception                     (IOException)-import           Control.Monad                         (unless, when, (>=>))-import           Control.Monad.IO.Class                (MonadIO (liftIO))-import           Data.Foldable                         (for_)-import           Data.Functor.Contravariant            (Contravariant (contramap))-import           Data.Maybe                            (fromMaybe)-import           Data.Text                             (Text)-import qualified Data.Text                             as T-import qualified Data.Text                             as Text-import qualified Data.Text.IO                          as Text-import           Data.Time                             (defaultTimeLocale,-                                                        formatTime,-                                                        getCurrentTime)-import           GHC.Stack                             (CallStack, HasCallStack,-                                                        SrcLoc (SrcLoc, srcLocModule, srcLocStartCol, srcLocStartLine),-                                                        callStack, getCallStack,-                                                        withFrozenCallStack)-import           Language.LSP.Server-import qualified Language.LSP.Server                   as LSP-import           Language.LSP.Types                    (LogMessageParams (..),-                                                        MessageType (..),-                                                        SMethod (SWindowLogMessage, SWindowShowMessage),-                                                        ShowMessageParams (..))-#if MIN_VERSION_prettyprinter(1,7,0)-import           Prettyprinter                         as PrettyPrinterModule-import           Prettyprinter.Render.Text             (renderStrict)-#else-import           Data.Text.Prettyprint.Doc             as PrettyPrinterModule-import           Data.Text.Prettyprint.Doc.Render.Text (renderStrict)-#endif-import           Colog.Core                            (LogAction (..),-                                                        Severity,-                                                        WithSeverity (..))-import qualified Colog.Core                            as Colog-import           System.IO                             (Handle,-                                                        IOMode (AppendMode),-                                                        hClose, hFlush,-                                                        openFile, stderr)-import           UnliftIO                              (MonadUnliftIO,-                                                        displayException,-                                                        finally, try)--data Priority--- Don't change the ordering of this type or you will mess up the Ord--- instance-    = Debug -- ^ Verbose debug logging.-    | Info  -- ^ Useful information in case an error has to be understood.-    | Warning-      -- ^ These error messages should not occur in a expected usage, and-      -- should be investigated.-    | Error -- ^ Such log messages must never occur in expected usage.-    deriving (Eq, Show, Ord, Enum, Bounded)---- | Note that this is logging actions _of the program_, not of the user.---   You shouldn't call warning/error if the user has caused an error, only---   if our code has gone wrong and is itself erroneous (e.g. we threw an exception).-newtype Logger = Logger {logPriority :: Priority -> T.Text -> IO ()}--instance Semigroup Logger where-    l1 <> l2 = Logger $ \p t -> logPriority l1 p t >> logPriority l2 p t--instance Monoid Logger where-    mempty = Logger $ \_ _ -> pure ()--logError :: Logger -> T.Text -> IO ()-logError x = logPriority x Error--logWarning :: Logger -> T.Text -> IO ()-logWarning x = logPriority x Warning--logInfo :: Logger -> T.Text -> IO ()-logInfo x = logPriority x Info--logDebug :: Logger -> T.Text -> IO ()-logDebug x = logPriority x Debug--noLogging :: Logger-noLogging = Logger $ \_ _ -> return ()--data WithPriority a = WithPriority { priority :: Priority, callStack_ :: CallStack, payload :: a } deriving Functor---- | Note that this is logging actions _of the program_, not of the user.---   You shouldn't call warning/error if the user has caused an error, only---   if our code has gone wrong and is itself erroneous (e.g. we threw an exception).-newtype Recorder msg = Recorder-  { logger_ :: forall m. (MonadIO m) => msg -> m () }--logWith :: (HasCallStack, MonadIO m) => Recorder (WithPriority msg) -> Priority -> msg -> m ()-logWith recorder priority msg = withFrozenCallStack $ logger_ recorder (WithPriority priority callStack msg)--instance Semigroup (Recorder msg) where-  (<>) Recorder{ logger_ = logger_1 } Recorder{ logger_ = logger_2 } =-    Recorder-      { logger_ = \msg -> logger_1 msg >> logger_2 msg }--instance Monoid (Recorder msg) where-  mempty =-    Recorder-      { logger_ = \_ -> pure () }--instance Contravariant Recorder where-  contramap f Recorder{ logger_ } =-    Recorder-      { logger_ = logger_ . f }--cmap :: (a -> b) -> Recorder b -> Recorder a-cmap = contramap--cmapWithPrio :: (a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)-cmapWithPrio f = cmap (fmap f)--cmapIO :: (a -> IO b) -> Recorder b -> Recorder a-cmapIO f Recorder{ logger_ } =-  Recorder-    { logger_ = (liftIO . f) >=> logger_ }--cfilter :: (a -> Bool) -> Recorder a -> Recorder a-cfilter p Recorder{ logger_ } =-  Recorder-    { logger_ = \msg -> when (p msg) (logger_ msg) }--textHandleRecorder :: Handle -> Recorder Text-textHandleRecorder handle =-  Recorder-    { logger_ = \text -> liftIO $ Text.hPutStrLn handle text *> hFlush handle }--makeDefaultStderrRecorder :: MonadIO m => Maybe [LoggingColumn] -> m (Recorder (WithPriority (Doc a)))-makeDefaultStderrRecorder columns = do-  lock <- liftIO newLock-  makeDefaultHandleRecorder columns lock stderr---- | If no path given then use stderr, otherwise use file.-withDefaultRecorder-  :: MonadUnliftIO m-  => Maybe FilePath-  -- ^ Log file path. `Nothing` uses stderr-  -> Maybe [LoggingColumn]-  -- ^ logging columns to display. `Nothing` uses `defaultLoggingColumns`-  -> (Recorder (WithPriority (Doc d)) -> m a)-  -- ^ action given a recorder-  -> m a-withDefaultRecorder path columns action = do-  lock <- liftIO newLock-  let makeHandleRecorder = makeDefaultHandleRecorder columns lock-  case path of-    Nothing -> do-      recorder <- makeHandleRecorder stderr-      let message = "No log file specified; using stderr."-      logWith recorder Info message-      action recorder-    Just path -> do-      fileHandle :: Either IOException Handle <- liftIO $ try (openFile path AppendMode)-      case fileHandle of-        Left e -> do-          recorder <- makeHandleRecorder stderr-          let exceptionMessage = pretty $ displayException e-          let message = vcat [exceptionMessage, "Couldn't open log file" <+> pretty path <> "; falling back to stderr."]-          logWith recorder Warning message-          action recorder-        Right fileHandle -> finally (makeHandleRecorder fileHandle >>= action) (liftIO $ hClose fileHandle)--makeDefaultHandleRecorder-  :: MonadIO m-  => Maybe [LoggingColumn]-  -- ^ built-in logging columns to display. Nothing uses the default-  -> Lock-  -- ^ lock to take when outputting to handle-  -> Handle-  -- ^ handle to output to-  -> m (Recorder (WithPriority (Doc a)))-makeDefaultHandleRecorder columns lock handle = do-  let Recorder{ logger_ } = textHandleRecorder handle-  let threadSafeRecorder = Recorder { logger_ = \msg -> liftIO $ withLock lock (logger_ msg) }-  let loggingColumns = fromMaybe defaultLoggingColumns columns-  let textWithPriorityRecorder = cmapIO (textWithPriorityToText loggingColumns) threadSafeRecorder-  pure (cmap docToText textWithPriorityRecorder)-  where-    docToText = fmap (renderStrict . layoutPretty defaultLayoutOptions)--data LoggingColumn-  = TimeColumn-  | ThreadIdColumn-  | PriorityColumn-  | DataColumn-  | SourceLocColumn--defaultLoggingColumns :: [LoggingColumn]-defaultLoggingColumns = [TimeColumn, PriorityColumn, DataColumn]--textWithPriorityToText :: [LoggingColumn] -> WithPriority Text -> IO Text-textWithPriorityToText columns WithPriority{ priority, callStack_, payload } = do-    textColumns <- mapM loggingColumnToText columns-    pure $ Text.intercalate " | " textColumns-    where-      showAsText :: Show a => a -> Text-      showAsText = Text.pack . show--      utcTimeToText utcTime = Text.pack $ formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%6QZ" utcTime--      priorityToText :: Priority -> Text-      priorityToText = showAsText--      threadIdToText = showAsText--      callStackToSrcLoc :: CallStack -> Maybe SrcLoc-      callStackToSrcLoc callStack =-        case getCallStack callStack of-          (_, srcLoc) : _ -> Just srcLoc-          _               -> Nothing--      srcLocToText = \case-          Nothing -> "<unknown>"-          Just SrcLoc{ srcLocModule, srcLocStartLine, srcLocStartCol } ->-            Text.pack srcLocModule <> "#" <> showAsText srcLocStartLine <> ":" <> showAsText srcLocStartCol--      loggingColumnToText :: LoggingColumn -> IO Text-      loggingColumnToText = \case-        TimeColumn -> do-          utcTime <- getCurrentTime-          pure (utcTimeToText utcTime)-        SourceLocColumn -> pure $ (srcLocToText . callStackToSrcLoc) callStack_-        ThreadIdColumn -> do-          threadId <- myThreadId-          pure (threadIdToText threadId)-        PriorityColumn -> pure (priorityToText priority)-        DataColumn -> pure payload---- | Given a 'Recorder' that requires an argument, produces a 'Recorder'--- that queues up messages until the argument is provided using the callback, at which--- point it sends the backlog and begins functioning normally.-withBacklog :: (v -> Recorder a) -> IO (Recorder a, v -> IO ())-withBacklog recFun = do-  -- Arbitrary backlog capacity-  backlog <- newTBQueueIO 100-  let backlogRecorder = Recorder $ \it -> liftIO $ atomically $ do-          -- If the queue is full just drop the message on the floor. This is most likely-          -- to happen if the callback is just never going to be called; in which case-          -- we want neither to build up an unbounded backlog in memory, nor block waiting-          -- for space!-          full <- isFullTBQueue backlog-          unless full $ writeTBQueue backlog it--  -- The variable holding the recorder starts out holding the recorder that writes-  -- to the backlog.-  recVar <- newTVarIO backlogRecorder-  -- The callback atomically swaps out the recorder for the final one, and flushes-  -- the backlog to it.-  let cb arg = do-        let recorder = recFun arg-        toRecord <- atomically $ writeTVar recVar recorder >> flushTBQueue backlog-        for_ toRecord (logger_ recorder)--  -- The recorder we actually return looks in the variable and uses whatever is there.-  let varRecorder = Recorder $ \it -> do-          r <- liftIO $ readTVarIO recVar-          logger_ r it--  pure (varRecorder, cb)---- | Creates a recorder that sends logs to the LSP client via @window/showMessage@ notifications.-lspClientMessageRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)-lspClientMessageRecorder env = Recorder $ \WithPriority {..} ->-  liftIO $ LSP.runLspT env $ LSP.sendNotification SWindowShowMessage-      ShowMessageParams-        { _xtype = priorityToLsp priority,-          _message = payload-        }---- | Creates a recorder that sends logs to the LSP client via @window/logMessage@ notifications.-lspClientLogRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)-lspClientLogRecorder env = Recorder $ \WithPriority {..} ->-  liftIO $ LSP.runLspT env $ LSP.sendNotification SWindowLogMessage-      LogMessageParams-        { _xtype = priorityToLsp priority,-          _message = payload-        }--priorityToLsp :: Priority -> MessageType-priorityToLsp =-  \case-    Debug   -> MtLog-    Info    -> MtInfo-    Warning -> MtWarning-    Error   -> MtError--toCologActionWithPrio :: (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg)-toCologActionWithPrio (Recorder _logger) = LogAction $ \WithSeverity{..} -> do-    let priority = severityToPriority getSeverity-    _logger $ WithPriority priority callStack getMsg-  where-    severityToPriority :: Severity -> Priority-    severityToPriority Colog.Debug   = Debug-    severityToPriority Colog.Info    = Info-    severityToPriority Colog.Warning = Warning-    severityToPriority Colog.Error   = Error
src/Development/IDE/Types/Options.hs view
@@ -2,7 +2,8 @@ -- SPDX-License-Identifier: Apache-2.0  -- | Options-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE RankNTypes       #-} module Development.IDE.Types.Options   ( IdeOptions(..)   , IdePreprocessedSource(..)@@ -18,7 +19,7 @@   , OptHaddockParse(..)   , ProgressReportingStyle(..)   ) where-+import           Control.Lens import qualified Data.Text                         as T import           Data.Typeable import           Development.IDE.Core.RuleTypes@@ -27,8 +28,8 @@ import           Development.IDE.Types.Diagnostics import           Ide.Plugin.Config import           Ide.Types                         (DynFlagsModifications)-import qualified Language.LSP.Types.Capabilities   as LSP-+import qualified Language.LSP.Protocol.Lens        as L+import qualified Language.LSP.Protocol.Types       as LSP data IdeOptions = IdeOptions   { optPreprocessor       :: GHC.ParsedSource -> IdePreprocessedSource     -- ^ Preprocessor to run over all parsed source trees, generating a list of warnings@@ -110,7 +111,7 @@  clientSupportsProgress :: LSP.ClientCapabilities -> IdeReportProgress clientSupportsProgress caps = IdeReportProgress $ Just True ==-    (LSP._workDoneProgress =<< LSP._window (caps :: LSP.ClientCapabilities))+    ((\x -> x ^. L.workDoneProgress) =<< LSP._window (caps :: LSP.ClientCapabilities))  defaultIdeOptions :: Action IdeGhcSession -> IdeOptions defaultIdeOptions session = IdeOptions
src/Text/Fuzzy/Parallel.hs view
@@ -1,9 +1,9 @@ -- | Parallel versions of 'filter' and 'simpleFilter'  module Text.Fuzzy.Parallel-(   filter,-    simpleFilter,-    match,+(   filter, filter',+    simpleFilter, simpleFilter',+    match, defChunkSize, defMaxResults,     Scored(..) ) where @@ -29,7 +29,6 @@ -- Just 5 -- {-# INLINABLE match #-}- match :: T.Text    -- ^ Pattern in lowercase except for first character       -> T.Text    -- ^ The text to search in.       -> Maybe Int -- ^ The score@@ -70,23 +69,14 @@      toLowerAscii w = if (w - 65) < 26 then w .|. 0x20 else w --- | The function to filter a list of values by fuzzy search on the text extracted from them.-filter :: Int           -- ^ Chunk size. 1000 works well.-       -> Int           -- ^ Max. number of results wanted-       -> T.Text        -- ^ Pattern.-       -> [t]           -- ^ The list of values containing the text to search in.-       -> (t -> T.Text) -- ^ The function to extract the text from the container.-       -> [Scored t]    -- ^ The list of results, sorted, highest score first.-filter chunkSize maxRes pattern ts extract = partialSortByAscScore maxRes perfectScore (concat vss)-  where-      -- Preserve case for the first character, make all others lowercase-      pattern' = case T.uncons pattern of-        Just (c, rest) -> T.cons c (T.toLower rest)-        _              -> pattern-      vss = map (mapMaybe (\t -> flip Scored t <$> match pattern' (extract t))) (chunkList chunkSize ts)-        `using` parList (evalList rseq)-      perfectScore = fromMaybe (error $ T.unpack pattern) $ match pattern' pattern'+-- | Sensible default value for chunk size to use when calling simple filter.+defChunkSize :: Int+defChunkSize = 1000 +-- | Sensible default value for the number of max results to use when calling simple filter.+defMaxResults :: Int+defMaxResults = 10+ -- | Return all elements of the list that have a fuzzy -- match against the pattern. Runs with default settings where -- nothing is added around the matches, as case insensitive.@@ -102,6 +92,52 @@ simpleFilter chunk maxRes pattern xs =   filter chunk maxRes pattern xs id ++-- | The function to filter a list of values by fuzzy search on the text extracted from them,+-- using a custom matching function which determines how close words are.+filter' :: Int           -- ^ Chunk size. 1000 works well.+       -> Int           -- ^ Max. number of results wanted+       -> T.Text        -- ^ Pattern.+       -> [t]           -- ^ The list of values containing the text to search in.+       -> (t -> T.Text) -- ^ The function to extract the text from the container.+       -> (T.Text -> T.Text -> Maybe Int)+       -- ^ Custom scoring function to use for calculating how close words are+       -- When the function returns Nothing, this means the values are incomparable.+       -> [Scored t]    -- ^ The list of results, sorted, highest score first.+filter' chunkSize maxRes pattern ts extract match' = partialSortByAscScore maxRes perfectScore (concat vss)+  where+      -- Preserve case for the first character, make all others lowercase+      pattern' = case T.uncons pattern of+        Just (c, rest) -> T.cons c (T.toLower rest)+        _              -> pattern+      vss = map (mapMaybe (\t -> flip Scored t <$> match' pattern' (extract t))) (chunkList chunkSize ts)+        `using` parList (evalList rseq)+      perfectScore = fromMaybe (error $ T.unpack pattern) $ match' pattern' pattern'++-- | The function to filter a list of values by fuzzy search on the text extracted from them,+-- using a custom matching function which determines how close words are.+filter :: Int           -- ^ Chunk size. 1000 works well.+       -> Int           -- ^ Max. number of results wanted+       -> T.Text        -- ^ Pattern.+       -> [t]           -- ^ The list of values containing the text to search in.+       -> (t -> T.Text) -- ^ The function to extract the text from the container.+       -> [Scored t]    -- ^ The list of results, sorted, highest score first.+filter chunkSize maxRes pattern ts extract =+  filter' chunkSize maxRes pattern ts extract match++-- | Return all elements of the list that have a fuzzy match against the pattern,+-- the closeness of the match is determined using the custom scoring match function that is passed.+-- Runs with default settings where nothing is added around the matches, as case insensitive.+{-# INLINABLE simpleFilter' #-}+simpleFilter' :: Int      -- ^ Chunk size. 1000 works well.+             -> Int      -- ^ Max. number of results wanted+             -> T.Text   -- ^ Pattern to look for.+             -> [T.Text] -- ^ List of texts to check.+             -> (T.Text -> T.Text -> Maybe Int)+             -- ^ Custom scoring function to use for calculating how close words are+             -> [Scored T.Text] -- ^ The ones that match.+simpleFilter' chunk maxRes pattern xs match' =+  filter' chunk maxRes pattern xs id match' --------------------------------------------------------------------------------  chunkList :: Int -> [a] -> [[a]]
+ test/exe/AsyncTests.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE DataKinds #-}++module AsyncTests (tests) where++import           Control.Monad+import           Control.Monad.IO.Class        (liftIO)+import           Data.Aeson                    (toJSON)+import           Data.Proxy+import qualified Data.Text                     as T+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types   hiding+                                               (SemanticTokenAbsolute (..),+                                                SemanticTokenRelative (..),+                                                SemanticTokensEdit (..),+                                                mkRange)+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Development.IDE.Plugin.Test   (TestRequest (BlockSeconds),+                                                blockCommandId)+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++-- | Test if ghcide asynchronously handles Commands and user Requests+tests :: TestTree+tests = testGroup "async"+    [+      testSession "command" $ do+            -- Execute a command that will block forever+            let req = ExecuteCommandParams Nothing blockCommandId Nothing+            void $ sendRequest SMethod_WorkspaceExecuteCommand req+            -- Load a file and check for code actions. Will only work if the command is run asynchronously+            doc <- createDoc "A.hs" "haskell" $ T.unlines+              [ "{-# OPTIONS -Wmissing-signatures #-}"+              , "foo = id"+              ]+            void waitForDiagnostics+            codeLenses <- getCodeLenses doc+            liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=?+              [ "foo :: a -> a" ]+    , testSession "request" $ do+            -- Execute a custom request that will block for 1000 seconds+            void $ sendRequest (SMethod_CustomMethod (Proxy @"test")) $ toJSON $ BlockSeconds 1000+            -- Load a file and check for code actions. Will only work if the request is run asynchronously+            doc <- createDoc "A.hs" "haskell" $ T.unlines+              [ "{-# OPTIONS -Wmissing-signatures #-}"+              , "foo = id"+              ]+            void waitForDiagnostics+            codeLenses <- getCodeLenses doc+            liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=?+              [ "foo :: a -> a" ]+    ]
+ test/exe/BootTests.hs view
@@ -0,0 +1,55 @@+module BootTests (tests) where++import           Control.Applicative.Combinators+import           Control.Monad+import           Control.Monad.IO.Class          (liftIO)+import           Development.IDE.GHC.Util+import           Development.IDE.Test            (expectNoMoreDiagnostics,+                                                  isReferenceReady)+import           Development.IDE.Types.Location+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.FilePath+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+++tests :: TestTree+tests = testGroup "boot"+  [ testCase "boot-def-test" $ runWithExtraFiles "boot" $ \dir -> do+        let cPath = dir </> "C.hs"+        cSource <- liftIO $ readFileUtf8 cPath+        -- Dirty the cache+        liftIO $ runInDir dir $ do+            cDoc <- createDoc cPath "haskell" cSource+            -- We send a hover request then wait for either the hover response or+            -- `ghcide/reference/ready` notification.+            -- Once we receive one of the above, we wait for the other that we+            -- haven't received yet.+            -- If we don't wait for the `ready` notification it is possible+            -- that the `getDefinitions` request/response in the outer ghcide+            -- session will find no definitions.+            let hoverParams = HoverParams cDoc (Position 4 3) Nothing+            hoverRequestId <- sendRequest SMethod_TextDocumentHover hoverParams+            let parseReadyMessage = isReferenceReady cPath+            let parseHoverResponse = responseForId SMethod_TextDocumentHover hoverRequestId+            hoverResponseOrReadyMessage <- skipManyTill anyMessage ((Left <$> parseHoverResponse) <|> (Right <$> parseReadyMessage))+            _ <- skipManyTill anyMessage $+              case hoverResponseOrReadyMessage of+                Left _  -> void parseReadyMessage+                Right _ -> void parseHoverResponse+            closeDoc cDoc+        cdoc <- createDoc cPath "haskell" cSource+        locs <- getDefinitions cdoc (Position 7 4)+        let floc = mkR 9 0 9 1+        checkDefs locs (pure [floc])+  , testCase "graph with boot modules" $ runWithExtraFiles "boot2" $ \dir -> do+      _ <- openDoc (dir </> "A.hs") "haskell"+      expectNoMoreDiagnostics 2+  ]
+ test/exe/CPPTests.hs view
@@ -0,0 +1,56 @@+module CPPTests (tests) where++import           Control.Exception           (catch)+import qualified Data.Text                   as T+import           Development.IDE.Test        (Cursor, expectDiagnostics,+                                              expectNoMoreDiagnostics)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests =+  testGroup "cpp"+    [ ignoreInWindowsBecause "Throw a lsp session time out in windows for ghc-8.8 and is broken for other versions" $ testCase "cpp-error" $ do+        let content =+              T.unlines+                [ "{-# LANGUAGE CPP #-}",+                  "module Testing where",+                  "#ifdef FOO",+                  "foo = 42"+                ]+        -- The error locations differ depending on which C-preprocessor is used.+        -- Some give the column number and others don't (hence maxBound == -1 unsigned). Assert either+        -- of them.+        (run $ expectError content (2, maxBound))+          `catch` ( \e -> do+                      let _ = e :: HUnitFailure+                      run $ expectError content (2, 1)+                  )+    , testSessionWait "cpp-ghcide" $ do+        _ <- createDoc "A.hs" "haskell" $ T.unlines+          ["{-# LANGUAGE CPP #-}"+          ,"main ="+          ,"#ifdef __GHCIDE__"+          ,"  worked"+          ,"#else"+          ,"  failed"+          ,"#endif"+          ]+        expectDiagnostics [("A.hs", [(DiagnosticSeverity_Error, (3, 2), "Variable not in scope: worked")])]+    ]+  where+    expectError :: T.Text -> Cursor -> Session ()+    expectError content cursor = do+      _ <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics+        [ ( "Testing.hs",+            [(DiagnosticSeverity_Error, cursor, "error: unterminated")]+          )+        ]+      expectNoMoreDiagnostics 0.5
+ test/exe/ClientSettingsTests.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE GADTs #-}+module ClientSettingsTests (tests) where++import           Control.Applicative.Combinators+import           Control.Monad+import           Data.Aeson                      (toJSON)+import qualified Data.Aeson                      as A+import qualified Data.Text                       as T+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests = testGroup "client settings handling"+    [ testSession "ghcide restarts shake session on config changes" $ do+            void $ skipManyTill anyMessage $ message SMethod_ClientRegisterCapability+            void $ createDoc "A.hs" "haskell" "module A where"+            waitForProgressDone+            sendNotification SMethod_WorkspaceDidChangeConfiguration+                (DidChangeConfigurationParams (toJSON (mempty :: A.Object)))+            skipManyTill anyMessage restartingBuildSession++    ]+  where+    restartingBuildSession :: Session ()+    restartingBuildSession = do+        FromServerMess SMethod_WindowLogMessage TNotificationMessage{_params = LogMessageParams{..}} <- loggingNotification+        guard $ "Restarting build session" `T.isInfixOf` _message
+ test/exe/CodeLensTests.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE GADTs #-}++module CodeLensTests (tests) where++import           Control.Applicative.Combinators+import           Control.Monad.IO.Class          (liftIO)+import qualified Data.Aeson                      as A+import           Data.Maybe+import qualified Data.Text                       as T+import           Development.IDE.GHC.Compat      (GhcVersion (..), ghcVersion)+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Control.Lens                    ((^.))+import           Data.Tuple.Extra+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup "code lenses"+  [ addSigLensesTests+  ]+++addSigLensesTests :: TestTree+addSigLensesTests =+  let pragmas = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}"+      moduleH exported =+        T.unlines+          [ "{-# LANGUAGE PatternSynonyms,TypeApplications,DataKinds,RankNTypes,ScopedTypeVariables,TypeOperators,GADTs,BangPatterns #-}"+          , "module Sigs(" <> exported <> ") where"+          , "import qualified Data.Complex as C"+          , "import Data.Data (Proxy (..), type (:~:) (..), mkCharType)"+          , "data T1 a where"+          , "  MkT1 :: (Show b) => a -> b -> T1 a"+          ]+      before enableGHCWarnings exported (def, _) others =+        T.unlines $ [pragmas | enableGHCWarnings] <> [moduleH exported, def] <> others+      after' enableGHCWarnings exported (def, sig) others =+        T.unlines $ [pragmas | enableGHCWarnings] <> [moduleH exported] <> maybe [] pure sig <> [def] <> others+      createConfig mode = A.object ["haskell" A..= A.object ["plugin" A..= A.object ["ghcide-type-lenses" A..= A.object ["config" A..= A.object ["mode" A..= A.String mode]]]]]+      sigSession testName enableGHCWarnings mode exported def others = testSession testName $ do+        let originalCode = before enableGHCWarnings exported def others+        let expectedCode = after' enableGHCWarnings exported def others+        sendNotification SMethod_WorkspaceDidChangeConfiguration $ DidChangeConfigurationParams $ createConfig mode+        doc <- createDoc "Sigs.hs" "haskell" originalCode+        waitForProgressDone+        codeLenses <- getCodeLenses doc+        if not $ null $ snd def+          then do+            liftIO $ length codeLenses == 1 @? "Expected 1 code lens, but got: " <> show codeLenses+            executeCommand $ fromJust $ head codeLenses ^. L.command+            modifiedCode <- skipManyTill anyMessage (getDocumentEdit doc)+            liftIO $ expectedCode @=? modifiedCode+          else liftIO $ null codeLenses @? "Expected no code lens, but got: " <> show codeLenses+      cases =+        [ ("abc = True", "abc :: Bool")+        , ("foo a b = a + b", "foo :: Num a => a -> a -> a")+        , ("bar a b = show $ a + b", "bar :: (Show a, Num a) => a -> a -> String")+        , ("(!!!) a b = a > b", "(!!!) :: Ord a => a -> a -> Bool")+        , ("a >>>> b = a + b", "(>>>>) :: Num a => a -> a -> a")+        , ("a `haha` b = a b", "haha :: (t1 -> t2) -> t1 -> t2")+        , ("pattern Some a = Just a", "pattern Some :: a -> Maybe a")+        , ("pattern Some a <- Just a", "pattern Some :: a -> Maybe a")+        , ("pattern Some a <- Just a\n  where Some a = Just a", "pattern Some :: a -> Maybe a")+        , ("pattern Some a <- Just !a\n  where Some !a = Just a", "pattern Some :: a -> Maybe a")+        , ("pattern Point{x, y} = (x, y)", "pattern Point :: a -> b -> (a, b)")+        , ("pattern Point{x, y} <- (x, y)", "pattern Point :: a -> b -> (a, b)")+        , ("pattern Point{x, y} <- (x, y)\n  where Point x y = (x, y)", "pattern Point :: a -> b -> (a, b)")+        , ("pattern MkT1' b = MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")+        , ("pattern MkT1' b <- MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")+        , ("pattern MkT1' b <- MkT1 42 b\n  where MkT1' b = MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")+        , ("qualifiedSigTest= C.realPart", "qualifiedSigTest :: C.Complex a -> a")+        , ("head = 233", "head :: Integer")+        , ("rank2Test (k :: forall a . a -> a) = (k 233 :: Int, k \"QAQ\")", "rank2Test :: (forall a. a -> a) -> (Int, " <> listOfChar <> ")")+        , ("symbolKindTest = Proxy @\"qwq\"", "symbolKindTest :: Proxy \"qwq\"")+        , ("promotedKindTest = Proxy @Nothing", if ghcVersion >= GHC96 then "promotedKindTest :: Proxy Nothing" else "promotedKindTest :: Proxy 'Nothing")+        , ("typeOperatorTest = Refl", if ghcVersion >= GHC92 then "typeOperatorTest :: forall {k} {a :: k}. a :~: a" else "typeOperatorTest :: a :~: a")+        , ("notInScopeTest = mkCharType", "notInScopeTest :: String -> Data.Data.DataType")+        ]+   in testGroup+        "add signature"+        [ testGroup "signatures are correct" [sigSession (T.unpack $ T.replace "\n" "\\n" def) False "always" "" (def, Just sig) [] | (def, sig) <- cases]+        , sigSession "exported mode works" False "exported" "xyz" ("xyz = True", Just "xyz :: Bool") (fst <$> take 3 cases)+        , testGroup+            "diagnostics mode works"+            [ sigSession "with GHC warnings" True "diagnostics" "" (second Just $ head cases) []+            , sigSession "without GHC warnings" False "diagnostics" "" (second (const Nothing) $ head cases) []+            ]+        , testSession "keep stale lens" $ do+            let content = T.unlines+                    [ "module Stale where"+                    , "f = _"+                    ]+            doc <- createDoc "Stale.hs" "haskell" content+            oldLens <- getCodeLenses doc+            liftIO $ length oldLens @?= 1+            let edit = TextEdit (mkRange 0 4 0 5) "" -- Remove the `_`+            _ <- applyEdit doc edit+            newLens <- getCodeLenses doc+            liftIO $ newLens @?= oldLens+        ]++-- | Before ghc9, lists of Char is displayed as [Char], but with ghc9 and up, it's displayed as String+listOfChar :: T.Text+listOfChar | ghcVersion >= GHC90 = "String"+           | otherwise = "[Char]"
+ test/exe/CompletionTests.hs view
@@ -0,0 +1,576 @@++{-# LANGUAGE GADTs            #-}+{-# LANGUAGE OverloadedLabels #-}++module CompletionTests (tests) where++import           Control.Lens                   ((^.))+import qualified Control.Lens                   as Lens+import           Control.Monad+import           Control.Monad.IO.Class         (liftIO)+import           Data.Default+import           Data.List.Extra+import           Data.Maybe+import           Data.Row+import qualified Data.Text                      as T+import           Development.IDE.GHC.Compat     (GhcVersion (..), ghcVersion)+import           Development.IDE.Test           (waitForTypecheck)+import           Development.IDE.Types.Location+import           Ide.Plugin.Config+import qualified Language.LSP.Protocol.Lens     as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types    hiding+                                                (SemanticTokenAbsolute (..),+                                                 SemanticTokenRelative (..),+                                                 SemanticTokensEdit (..),+                                                 mkRange)+import           Language.LSP.Test+import           System.FilePath+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+++tests :: TestTree+tests+  = testGroup "completion"+    [+    testGroup "non local" nonLocalCompletionTests+    , testGroup "topLevel" topLevelCompletionTests+    , testGroup "local" localCompletionTests+    , testGroup "package" packageCompletionTests+    , testGroup "project" projectCompletionTests+    , testGroup "other" otherCompletionTests+    , testGroup "doc" completionDocTests+    ]++completionTest :: HasCallStack => String -> [T.Text] -> Position -> [(T.Text, CompletionItemKind, T.Text, Bool, Bool, Maybe [TextEdit])] -> TestTree+completionTest name src pos expected = testSessionWait name $ do+    docId <- createDoc "A.hs" "haskell" (T.unlines src)+    _ <- waitForDiagnostics+    compls <- getAndResolveCompletions docId pos+    let compls' = [ (_label, _kind, _insertText, _additionalTextEdits) | CompletionItem{..} <- compls]+    let emptyToMaybe x = if T.null x then Nothing else Just x+    liftIO $ sortOn (Lens.view Lens._1) (take (length expected) compls') @?=+        sortOn (Lens.view Lens._1)+          [ (l, Just k, emptyToMaybe t, at) | (l,k,t,_,_,at) <- expected]+    forM_ (zip compls expected) $ \(CompletionItem{..}, (_,_,_,expectedSig, expectedDocs, _)) -> do+        when expectedSig $+            liftIO $ assertBool ("Missing type signature: " <> T.unpack _label) (isJust _detail)+        when expectedDocs $+            liftIO $ assertBool ("Missing docs: " <> T.unpack _label) (isJust _documentation)+++topLevelCompletionTests :: [TestTree]+topLevelCompletionTests = [+    completionTest+        "variable"+        ["bar = xx", "-- | haddock", "xxx :: ()", "xxx = ()", "-- | haddock", "data Xxx = XxxCon"]+        (Position 0 8)+        [("xxx", CompletionItemKind_Function, "xxx", True, True, Nothing)+        ],+    completionTest+        "constructor"+        ["bar = xx", "-- | haddock", "xxx :: ()", "xxx = ()", "-- | haddock", "data Xxx = XxxCon"]+        (Position 0 8)+        [("xxx", CompletionItemKind_Function, "xxx", True, True, Nothing)+        ],+    completionTest+        "class method"+        ["bar = xx", "class Xxx a where", "-- | haddock", "xxx :: ()", "xxx = ()"]+        (Position 0 8)+        [("xxx", CompletionItemKind_Function, "xxx", True, True, Nothing)],+    completionTest+        "type"+        ["bar :: Xz", "zzz = ()", "-- | haddock", "data Xzz = XzzCon"]+        (Position 0 9)+        [("Xzz", CompletionItemKind_Struct, "Xzz", False, True, Nothing)],+    completionTest+        "class"+        ["bar :: Xz", "zzz = ()", "-- | haddock", "class Xzz a"]+        (Position 0 9)+        [("Xzz", CompletionItemKind_Interface, "Xzz", False, True, Nothing)],+    completionTest+        "records"+        ["data Person = Person { _personName:: String, _personAge:: Int}", "bar = Person { _pers }" ]+        (Position 1 19)+        [("_personName", CompletionItemKind_Function, "_personName", False, True, Nothing),+         ("_personAge", CompletionItemKind_Function, "_personAge", False, True, Nothing)],+    completionTest+        "recordsConstructor"+        ["data XxRecord = XyRecord { x:: String, y:: Int}", "bar = Xy" ]+        (Position 1 19)+        [("XyRecord", CompletionItemKind_Constructor, "XyRecord", False, True, Nothing),+         ("XyRecord", CompletionItemKind_Snippet, "XyRecord {x=${1:_x}, y=${2:_y}}", False, True, Nothing)]+    ]++localCompletionTests :: [TestTree]+localCompletionTests = [+    completionTest+        "argument"+        ["bar (Just abcdef) abcdefg = abcd"]+        (Position 0 32)+        [("abcdef", CompletionItemKind_Function, "abcdef", True, False, Nothing),+         ("abcdefg", CompletionItemKind_Function , "abcdefg", True, False, Nothing)+        ],+    completionTest+        "let"+        ["bar = let (Just abcdef) = undefined"+        ,"          abcdefg = let abcd = undefined in undefined"+        ,"        in abcd"+        ]+        (Position 2 15)+        [("abcdef", CompletionItemKind_Function, "abcdef", True, False, Nothing),+         ("abcdefg", CompletionItemKind_Function , "abcdefg", True, False, Nothing)+        ],+    completionTest+        "where"+        ["bar = abcd"+        ,"  where (Just abcdef) = undefined"+        ,"        abcdefg = let abcd = undefined in undefined"+        ]+        (Position 0 10)+        [("abcdef", CompletionItemKind_Function, "abcdef", True, False, Nothing),+         ("abcdefg", CompletionItemKind_Function , "abcdefg", True, False, Nothing)+        ],+    completionTest+        "do/1"+        ["bar = do"+        ,"  Just abcdef <- undefined"+        ,"  abcd"+        ,"  abcdefg <- undefined"+        ,"  pure ()"+        ]+        (Position 2 6)+        [("abcdef", CompletionItemKind_Function, "abcdef", True, False, Nothing)+        ],+    completionTest+        "do/2"+        ["bar abcde = do"+        ,"    Just [(abcdef,_)] <- undefined"+        ,"    abcdefg <- undefined"+        ,"    let abcdefgh = undefined"+        ,"        (Just [abcdefghi]) = undefined"+        ,"    abcd"+        ,"  where"+        ,"    abcdefghij = undefined"+        ]+        (Position 5 8)+        [("abcde", CompletionItemKind_Function, "abcde", True, False, Nothing)+        ,("abcdefghij", CompletionItemKind_Function, "abcdefghij", True, False, Nothing)+        ,("abcdef", CompletionItemKind_Function, "abcdef", True, False, Nothing)+        ,("abcdefg", CompletionItemKind_Function, "abcdefg", True, False, Nothing)+        ,("abcdefgh", CompletionItemKind_Function, "abcdefgh", True, False, Nothing)+        ,("abcdefghi", CompletionItemKind_Function, "abcdefghi", True, False, Nothing)+        ],+    completionTest+        "type family"+        ["{-# LANGUAGE DataKinds, TypeFamilies #-}"+        ,"type family Bar a"+        ,"a :: Ba"+        ]+        (Position 2 7)+        [("Bar", CompletionItemKind_Struct, "Bar", True, False, Nothing)+        ],+    completionTest+        "class method"+        [+          "class Test a where"+        , "    abcd :: a -> ()"+        , "    abcde :: a -> Int"+        , "instance Test Int where"+        , "    abcd = abc"+        ]+        (Position 4 14)+        [("abcd", CompletionItemKind_Function, "abcd", True, False, Nothing)+        ,("abcde", CompletionItemKind_Function, "abcde", True, False, Nothing)+        ],+    testSessionWait "incomplete entries" $ do+        let src a = "data Data = " <> a+        doc <- createDoc "A.hs" "haskell" $ src "AAA"+        void $ waitForTypecheck doc+        let editA rhs =+                changeDoc doc [TextDocumentContentChangeEvent . InR . (.==) #text $ src rhs]+        editA "AAAA"+        void $ waitForTypecheck doc+        editA "AAAAA"+        void $ waitForTypecheck doc++        compls <- getCompletions doc (Position 0 15)+        liftIO $ filter ("AAA" `T.isPrefixOf`) (mapMaybe _insertText compls) @?= ["AAAAA"]+        pure ()+    ]++nonLocalCompletionTests :: [TestTree]+nonLocalCompletionTests =+  [ brokenForWinGhc $ completionTest+      "variable"+      ["module A where", "f = hea"]+      (Position 1 7)+      [("head", CompletionItemKind_Function, "head", True, True, Nothing)],+    completionTest+      "constructor"+      ["{-# OPTIONS_GHC -Wall #-}", "module A where", "f = True"]+      (Position 2 8)+      [ ("True", CompletionItemKind_Constructor, "True", True, True, Nothing)+      ],+    brokenForWinGhc $ completionTest+      "type"+      ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Boo", "f = True"]+      (Position 2 8)+      [ ("Bool", CompletionItemKind_Struct, "Bool", True, True, Nothing)+      ],+    completionTest+      "qualified"+      ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]+      (Position 2 15)+      [ ("head", CompletionItemKind_Function, "head", True, True, Nothing)+      ],+    completionTest+      "duplicate import"+      ["module A where", "import Data.List", "import Data.List", "f = permu"]+      (Position 3 9)+      [ ("permutations", CompletionItemKind_Function, "permutations", False, False, Nothing)+      ],+    completionTest+       "dont show hidden items"+       [ "{-# LANGUAGE NoImplicitPrelude #-}",+         "module A where",+         "import Control.Monad hiding (join)",+         "f = joi"+       ]+       (Position 3 6)+       [],+    testGroup "ordering"+      [completionTest "qualified has priority"+        ["module A where"+        ,"import qualified Data.ByteString as BS"+        ,"f = BS.read"+        ]+        (Position 2 10)+        [("readFile", CompletionItemKind_Function, "readFile", True, True, Nothing)]+        ],+      -- we need this test to make sure the ghcide completions module does not return completions for language pragmas. this functionality is turned on in hls+     completionTest+      "do not show pragma completions"+      [ "{-# LANGUAGE  ",+        "{module A where}",+        "main = return ()"+      ]+      (Position 0 13)+      []+  ]+  where+    brokenForWinGhc = knownBrokenFor (BrokenSpecific Windows [GHC810, GHC90, GHC92, GHC94, GHC96]) "Windows has strange things in scope for some reason"++otherCompletionTests :: [TestTree]+otherCompletionTests = [+    completionTest+      "keyword"+      ["module A where", "f = newty"]+      (Position 1 9)+      [("newtype", CompletionItemKind_Keyword, "", False, False, Nothing)],+    completionTest+      "type context"+      [ "{-# OPTIONS_GHC -Wunused-binds #-}",+        "module A () where",+        "f = f",+        "g :: Intege"+      ]+      -- At this point the module parses but does not typecheck.+      -- This should be sufficient to detect that we are in a+      -- type context and only show the completion to the type.+      (Position 3 11)+      [("Integer", CompletionItemKind_Struct, "Integer", True, True, Nothing)],++    testSession "duplicate record fields" $ do+      void $+        createDoc "B.hs" "haskell" $+          T.unlines+            [ "{-# LANGUAGE DuplicateRecordFields #-}",+              "module B where",+              "newtype Foo = Foo { member :: () }",+              "newtype Bar = Bar { member :: () }"+            ]+      docA <-+        createDoc "A.hs" "haskell" $+          T.unlines+            [ "module A where",+              "import B",+              "memb"+            ]+      _ <- waitForDiagnostics+      compls <- getCompletions docA $ Position 2 4+      let compls' = [txt | CompletionItem {_insertText = Just txt, ..} <- compls, _label == "member"]+      liftIO $ take 2 compls' @?= ["member"],++    testSessionWait "maxCompletions" $ do+        doc <- createDoc "A.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wunused-binds #-}",+                "module A () where",+                "a = Prelude."+            ]+        _ <- waitForDiagnostics+        compls <- getCompletions  doc (Position 3 13)+        liftIO $ length compls @?= maxCompletions def+  ]++packageCompletionTests :: [TestTree]+packageCompletionTests =+  [ testSession' "fromList" $ \dir -> do+        liftIO $ writeFile (dir </> "hie.yaml")+            "cradle: {direct: {arguments: [-hide-all-packages, -package, base, A]}}"+        doc <- createDoc "A.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wunused-binds #-}",+                "module A () where",+                "a = fromList"+            ]+        _ <- waitForDiagnostics+        compls <- getCompletions doc (Position 2 12)+        let compls' =+              [T.drop 1 $ T.dropEnd 3 d+              | CompletionItem {_documentation = Just (InR (MarkupContent MarkupKind_Markdown d)), _label}+                <- compls+              , _label == "fromList"+              ]+        liftIO $ take 3 (sort compls') @?=+          map ("Defined in "<>) (+              [ "'Data.List.NonEmpty"+              , "'GHC.Exts"+              ] ++ if ghcVersion >= GHC94 then [ "'GHC.IsList" ] else [])++  , testSessionWait "Map" $ do+        doc <- createDoc "A.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wunused-binds #-}",+                "module A () where",+                "a :: Map"+            ]+        _ <- waitForDiagnostics+        compls <- getCompletions doc (Position 2 7)+        let compls' =+              [T.drop 1 $ T.dropEnd 3 d+              | CompletionItem {_documentation = Just (InR (MarkupContent MarkupKind_Markdown d)), _label}+                <- compls+              , _label == "Map"+              ]+        liftIO $ take 3 (sort compls') @?=+          map ("Defined in "<>)+              [ "'Data.Map"+              , "'Data.Map.Lazy"+              , "'Data.Map.Strict"+              ]+  , testSessionWait "no duplicates" $ do+        doc <- createDoc "A.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wunused-binds #-}",+                "module A () where",+                "import GHC.Exts(fromList)",+                "a = fromList"+            ]+        _ <- waitForDiagnostics+        compls <- getCompletions doc (Position 3 13)+        let duplicate =+              filter+                (\case+                  CompletionItem+                    { _insertText = Just "fromList"+                    , _documentation =+                      Just (InR (MarkupContent MarkupKind_Markdown d))+                    } ->+                    "GHC.Exts" `T.isInfixOf` d+                  _ -> False+                ) compls+        liftIO $ length duplicate @?= 1++  , testSessionWait "non-local before global" $ do+    -- non local completions are more specific+        doc <- createDoc "A.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wunused-binds #-}",+                "module A () where",+                "import GHC.Exts(fromList)",+                "a = fromList"+            ]+        _ <- waitForDiagnostics+        compls <- getCompletions doc (Position 3 13)+        let compls' =+              [_insertText+              | CompletionItem {_label, _insertText} <- compls+              , _label == "fromList"+              ]+        liftIO $ take 3 compls' @?=+          map Just ["fromList"]+  ]++projectCompletionTests :: [TestTree]+projectCompletionTests =+    [ testSession' "from hiedb" $ \dir-> do+        liftIO $ writeFile (dir </> "hie.yaml")+            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"+        _ <- createDoc "A.hs" "haskell" $ T.unlines+            [  "module A (anidentifier) where",+               "anidentifier = ()"+            ]+        _ <- waitForDiagnostics+        -- Note that B does not import A+        doc <- createDoc "B.hs" "haskell" $ T.unlines+            [ "module B where",+              "b = anidenti"+            ]+        compls <- getCompletions doc (Position 1 10)+        let compls' =+              [T.drop 1 $ T.dropEnd 3 d+              | CompletionItem {_documentation = Just (InR (MarkupContent MarkupKind_Markdown d)), _label}+                <- compls+              , _label == "anidentifier"+              ]+        liftIO $ compls' @?= ["Defined in 'A"],+      testSession' "auto complete project imports" $ \dir-> do+        liftIO $ writeFile (dir </> "hie.yaml")+            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"ALocalModule\", \"B\"]}}"+        _ <- createDoc "ALocalModule.hs" "haskell" $ T.unlines+            [  "module ALocalModule (anidentifier) where",+               "anidentifier = ()"+            ]+        _ <- waitForDiagnostics+        -- Note that B does not import A+        doc <- createDoc "B.hs" "haskell" $ T.unlines+            [ "module B where",+              "import ALocal"+            ]+        compls <- getCompletions doc (Position 1 13)+        let item = head $ filter ((== "ALocalModule") . (^. L.label)) compls+        liftIO $ do+          item ^. L.label @?= "ALocalModule",+      testSession' "auto complete functions from qualified imports without alias" $ \dir-> do+        liftIO $ writeFile (dir </> "hie.yaml")+            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"+        _ <- createDoc "A.hs" "haskell" $ T.unlines+            [  "module A (anidentifier) where",+               "anidentifier = ()"+            ]+        _ <- waitForDiagnostics+        doc <- createDoc "B.hs" "haskell" $ T.unlines+            [ "module B where",+              "import qualified A",+              "A."+            ]+        compls <- getCompletions doc (Position 2 2)+        let item = head compls+        liftIO $ do+          item ^. L.label @?= "anidentifier",+      testSession' "auto complete functions from qualified imports with alias" $ \dir-> do+        liftIO $ writeFile (dir </> "hie.yaml")+            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"+        _ <- createDoc "A.hs" "haskell" $ T.unlines+            [  "module A (anidentifier) where",+               "anidentifier = ()"+            ]+        _ <- waitForDiagnostics+        doc <- createDoc "B.hs" "haskell" $ T.unlines+            [ "module B where",+              "import qualified A as Alias",+              "foo = Alias."+            ]+        compls <- getCompletions doc (Position 2 12)+        let item = head compls+        liftIO $ do+          item ^. L.label @?= "anidentifier"+    ]++completionDocTests :: [TestTree]+completionDocTests =+  [ testSession "local define" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = ()"+        , "bar = fo"+        ]+      let expected = "*Defined at line 2, column 1 in this module*\n"+      test doc (Position 2 8) "foo" Nothing [expected]+  , testSession "local empty doc" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = ()"+        , "bar = fo"+        ]+      test doc (Position 2 8) "foo" Nothing ["*Defined at line 2, column 1 in this module*\n"]+  , testSession "local single line doc without newline" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "-- |docdoc"+        , "foo = ()"+        , "bar = fo"+        ]+      test doc (Position 3 8) "foo" Nothing ["*Defined at line 3, column 1 in this module*\n* * *\n\n\ndocdoc\n"]+  , testSession "local multi line doc with newline" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "-- | abcabc"+        , "--"+        , "foo = ()"+        , "bar = fo"+        ]+      test doc (Position 4 8) "foo" Nothing ["*Defined at line 4, column 1 in this module*\n* * *\n\n\nabcabc\n"]+  , testSession "local multi line doc without newline" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "-- |     abcabc"+        , "--"+        , "--def"+        , "foo = ()"+        , "bar = fo"+        ]+      test doc (Position 5 8) "foo" Nothing ["*Defined at line 5, column 1 in this module*\n* * *\n\n\nabcabc \n\ndef\n"]+  , testSession "extern empty doc" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = od"+        ]+      let expected = "*Imported from 'Prelude'*\n"+      test doc (Position 1 8) "odd" (Just $ T.length expected) [expected]+  , brokenForMacGhc9 $ brokenForWinGhc90 $ testSession "extern single line doc without '\\n'" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = no"+        ]+      let expected = "*Imported from 'Prelude'*\n* * *\n\n\nBoolean \"not\"\n"+      test doc (Position 1 8) "not" (Just $ T.length expected) [expected]+  , brokenForMacGhc9 $ brokenForWinGhc90 $ testSession "extern mulit line doc" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = i"+        ]+      let expected = "*Imported from 'Prelude'*\n* * *\n\n\nIdentity function. \n```haskell\nid x = x\n```\n"+      test doc (Position 1 7) "id" (Just $ T.length expected) [expected]+  , testSession "extern defined doc" $ do+      doc <- createDoc "A.hs" "haskell" $ T.unlines+        [ "module A where"+        , "foo = i"+        ]+      let expected = "*Imported from 'Prelude'*\n"+      test doc (Position 1 7) "id" (Just $ T.length expected) [expected]+  ]+  where+    brokenForGhc9 = knownBrokenFor (BrokenForGHC [GHC90, GHC92, GHC94, GHC96]) "Completion doc doesn't support ghc9"+    brokenForWinGhc90 = knownBrokenFor (BrokenSpecific Windows [GHC90]) "Extern doc doesn't support Windows for ghc9.2"+    -- https://gitlab.haskell.org/ghc/ghc/-/issues/20903+    brokenForMacGhc9 = knownBrokenFor (BrokenSpecific MacOS [GHC90, GHC92, GHC94, GHC96]) "Extern doc doesn't support MacOS for ghc9"+    test doc pos label mn expected = do+      _ <- waitForDiagnostics+      compls <- getCompletions doc pos+      rcompls <- forM compls $ \item -> do+            if isJust (item ^. L.data_)+            then do+                rsp <- request SMethod_CompletionItemResolve item+                case rsp ^. L.result of+                    Left err -> liftIO $ assertFailure ("completionItem/resolve failed with: " <> show err)+                    Right x -> pure x+            else pure item+      let compls' = [+            -- We ignore doc uris since it points to the local path which determined by specific machines+            case mn of+                Nothing -> txt+                Just n  -> T.take n txt+            | CompletionItem {_documentation = Just (InR (MarkupContent MarkupKind_Markdown txt)), ..} <- rcompls+            , _label == label+            ]+      liftIO $ compls' @?= expected
+ test/exe/CradleTests.hs view
@@ -0,0 +1,219 @@++{-# LANGUAGE GADTs            #-}+{-# LANGUAGE OverloadedLabels #-}++module CradleTests (tests) where++import           Control.Applicative.Combinators+import           Control.Monad.IO.Class          (liftIO)+import           Data.Row+import qualified Data.Text                       as T+import           Development.IDE.GHC.Compat      (GhcVersion (..), ghcVersion)+import           Development.IDE.GHC.Util+import           Development.IDE.Test            (expectDiagnostics,+                                                  expectDiagnosticsWithTags,+                                                  expectNoMoreDiagnostics,+                                                  isReferenceReady,+                                                  waitForAction)+import           Development.IDE.Types.Location+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.FilePath+import           System.IO.Extra                 hiding (withTempDir)+-- import Test.QuickCheck.Instances ()+import           Control.Lens                    ((^.))+import           Development.IDE.Plugin.Test     (WaitForIdeRuleResult (..))+import           GHC.TypeLits                    (symbolVal)+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+++tests :: TestTree+tests = testGroup "cradle"+    [testGroup "dependencies" [sessionDepsArePickedUp]+    ,testGroup "ignore-fatal" [ignoreFatalWarning]+    ,testGroup "loading" [loadCradleOnlyonce, retryFailedCradle]+    ,testGroup "multi"   [simpleMultiTest, simpleMultiTest2, simpleMultiTest3, simpleMultiDefTest]+    ,testGroup "sub-directory"   [simpleSubDirectoryTest]+    ]++loadCradleOnlyonce :: TestTree+loadCradleOnlyonce = testGroup "load cradle only once"+    [ testSession' "implicit" implicit+    , testSession' "direct"   direct+    ]+    where+        direct dir = do+            liftIO $ writeFileUTF8 (dir </> "hie.yaml")+                "cradle: {direct: {arguments: []}}"+            test dir+        implicit dir = test dir+        test _dir = do+            doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"+            msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message SMethod_TextDocumentPublishDiagnostics))+            liftIO $ length msgs @?= 1+            changeDoc doc [TextDocumentContentChangeEvent . InR . (.==) #text $ "module B where\nimport Data.Maybe"]+            msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message SMethod_TextDocumentPublishDiagnostics))+            liftIO $ length msgs @?= 0+            _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"+            msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message SMethod_TextDocumentPublishDiagnostics))+            liftIO $ length msgs @?= 0++retryFailedCradle :: TestTree+retryFailedCradle = testSession' "retry failed" $ \dir -> do+  -- The false cradle always fails+  let hieContents = "cradle: {bios: {shell: \"false\"}}"+      hiePath = dir </> "hie.yaml"+  liftIO $ writeFile hiePath hieContents+  let aPath = dir </> "A.hs"+  doc <- createDoc aPath "haskell" "main = return ()"+  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc+  liftIO $ "Test assumption failed: cradle should error out" `assertBool` not ideResultSuccess++  -- Fix the cradle and typecheck again+  let validCradle = "cradle: {bios: {shell: \"echo A.hs\"}}"+  liftIO $ writeFileUTF8 hiePath $ T.unpack validCradle+  sendNotification SMethod_WorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $+         [FileEvent (filePathToUri $ dir </> "hie.yaml") FileChangeType_Changed ]++  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc+  liftIO $ "No joy after fixing the cradle" `assertBool` ideResultSuccess+++cradleLoadedMessage :: Session FromServerMessage+cradleLoadedMessage = satisfy $ \case+        FromServerMess (SMethod_CustomMethod p) (NotMess _) -> symbolVal p == cradleLoadedMethod+        _                                            -> False++cradleLoadedMethod :: String+cradleLoadedMethod = "ghcide/cradle/loaded"++ignoreFatalWarning :: TestTree+ignoreFatalWarning = testCase "ignore-fatal-warning" $ runWithExtraFiles "ignore-fatal" $ \dir -> do+    let srcPath = dir </> "IgnoreFatal.hs"+    src <- liftIO $ readFileUtf8 srcPath+    _ <- createDoc srcPath "haskell" src+    expectNoMoreDiagnostics 5++simpleSubDirectoryTest :: TestTree+simpleSubDirectoryTest =+  testCase "simple-subdirectory" $ runWithExtraFiles "cabal-exe" $ \dir -> do+    let mainPath = dir </> "a/src/Main.hs"+    mainSource <- liftIO $ readFileUtf8 mainPath+    _mdoc <- createDoc mainPath "haskell" mainSource+    expectDiagnosticsWithTags+      [("a/src/Main.hs", [(DiagnosticSeverity_Warning,(2,0), "Top-level binding", Nothing)]) -- So that we know P has been loaded+      ]+    expectNoMoreDiagnostics 0.5++simpleMultiTest :: TestTree+simpleMultiTest = testCase "simple-multi-test" $ withLongTimeout $ runWithExtraFiles "multi" $ \dir -> do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+    adoc <- openDoc aPath "haskell"+    bdoc <- openDoc bPath "haskell"+    WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" adoc+    liftIO $ assertBool "A should typecheck" ideResultSuccess+    WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" bdoc+    liftIO $ assertBool "B should typecheck" ideResultSuccess+    locs <- getDefinitions bdoc (Position 2 7)+    let fooL = mkL (adoc ^. L.uri) 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++-- Like simpleMultiTest but open the files in the other order+simpleMultiTest2 :: TestTree+simpleMultiTest2 = testCase "simple-multi-test2" $ runWithExtraFiles "multi" $ \dir -> do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+    bdoc <- openDoc bPath "haskell"+    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" bdoc+    TextDocumentIdentifier auri <- openDoc aPath "haskell"+    skipManyTill anyMessage $ isReferenceReady aPath+    locs <- getDefinitions bdoc (Position 2 7)+    let fooL = mkL auri 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++-- Now with 3 components+simpleMultiTest3 :: TestTree+simpleMultiTest3 =+  testCase "simple-multi-test3" $ runWithExtraFiles "multi" $ \dir -> do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+        cPath = dir </> "c/C.hs"+    bdoc <- openDoc bPath "haskell"+    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" bdoc+    TextDocumentIdentifier auri <- openDoc aPath "haskell"+    skipManyTill anyMessage $ isReferenceReady aPath+    cdoc <- openDoc cPath "haskell"+    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" cdoc+    locs <- getDefinitions cdoc (Position 2 7)+    let fooL = mkL auri 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5++-- Like simpleMultiTest but open the files in component 'a' in a separate session+simpleMultiDefTest :: TestTree+simpleMultiDefTest = testCase "simple-multi-def-test" $ runWithExtraFiles "multi" $ \dir -> do+    let aPath = dir </> "a/A.hs"+        bPath = dir </> "b/B.hs"+    adoc <- liftIO $ runInDir dir $ do+      aSource <- liftIO $ readFileUtf8 aPath+      adoc <- createDoc aPath "haskell" aSource+      skipManyTill anyMessage $ isReferenceReady aPath+      closeDoc adoc+      pure adoc+    bSource <- liftIO $ readFileUtf8 bPath+    bdoc <- createDoc bPath "haskell" bSource+    locs <- getDefinitions bdoc (Position 2 7)+    let fooL = mkL (adoc ^. L.uri) 2 0 2 3+    checkDefs locs (pure [fooL])+    expectNoMoreDiagnostics 0.5+++sessionDepsArePickedUp :: TestTree+sessionDepsArePickedUp = testSession'+  "session-deps-are-picked-up"+  $ \dir -> do+    liftIO $+      writeFileUTF8+        (dir </> "hie.yaml")+        "cradle: {direct: {arguments: []}}"+    -- Open without OverloadedStrings and expect an error.+    doc <- createDoc "Foo.hs" "haskell" fooContent+    expectDiagnostics $+        if ghcVersion >= GHC90+            -- String vs [Char] causes this change in error message+            then [("Foo.hs", [(DiagnosticSeverity_Error, (3, 6), "Couldn't match type")])]+            else [("Foo.hs", [(DiagnosticSeverity_Error, (3, 6), "Couldn't match expected type")])]+    -- Update hie.yaml to enable OverloadedStrings.+    liftIO $+      writeFileUTF8+        (dir </> "hie.yaml")+        "cradle: {direct: {arguments: [-XOverloadedStrings]}}"+    sendNotification SMethod_WorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $+        [FileEvent (filePathToUri $ dir </> "hie.yaml") FileChangeType_Changed ]+    -- Send change event.+    let change =+          TextDocumentContentChangeEvent $ InL $ #range .== Range (Position 4 0) (Position 4 0)+                                              .+ #rangeLength .== Nothing+                                              .+ #text .== "\n"+    changeDoc doc [change]+    -- Now no errors.+    expectDiagnostics [("Foo.hs", [])]+  where+    fooContent =+      T.unlines+        [ "module Foo where",+          "import Data.Text",+          "foo :: Text",+          "foo = \"hello\""+        ]
+ test/exe/DependentFileTest.hs view
@@ -0,0 +1,62 @@++{-# LANGUAGE GADTs            #-}+{-# LANGUAGE OverloadedLabels #-}++module DependentFileTest (tests) where++import           Control.Monad.IO.Class         (liftIO)+import           Data.Row+import qualified Data.Text                      as T+import           Development.IDE.GHC.Compat     (GhcVersion (..), ghcVersion)+import           Development.IDE.Test           (expectDiagnostics)+import           Development.IDE.Types.Location+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types    hiding+                                                (SemanticTokenAbsolute (..),+                                                 SemanticTokenRelative (..),+                                                 SemanticTokensEdit (..),+                                                 mkRange)+import           Language.LSP.Test+import           System.FilePath+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests = testGroup "addDependentFile"+    [testGroup "file-changed" [testSession' "test" test]+    ]+    where+      test dir = do+        -- If the file contains B then no type error+        -- otherwise type error+        let depFilePath = dir </> "dep-file.txt"+        liftIO $ writeFile depFilePath "A"+        let fooContent = T.unlines+              [ "{-# LANGUAGE TemplateHaskell #-}"+              , "module Foo where"+              , "import Language.Haskell.TH.Syntax"+              , "foo :: Int"+              , "foo = 1 + $(do"+              , "               qAddDependentFile \"dep-file.txt\""+              , "               f <- qRunIO (readFile \"dep-file.txt\")"+              , "               if f == \"B\" then [| 1 |] else lift f)"+              ]+        let bazContent = T.unlines ["module Baz where", "import Foo ()"]+        _ <- createDoc "Foo.hs" "haskell" fooContent+        doc <- createDoc "Baz.hs" "haskell" bazContent+        expectDiagnostics $+            if ghcVersion >= GHC90+                -- String vs [Char] causes this change in error message+                then [("Foo.hs", [(DiagnosticSeverity_Error, if ghcVersion >= GHC92 then (4,11) else (4, 6), "Couldn't match type")])]+                else [("Foo.hs", [(DiagnosticSeverity_Error, (4, 6), "Couldn't match expected type")])]+        -- Now modify the dependent file+        liftIO $ writeFile depFilePath "B"+        sendNotification SMethod_WorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $+            [FileEvent (filePathToUri "dep-file.txt") FileChangeType_Changed ]++        -- Modifying Baz will now trigger Foo to be rebuilt as well+        let change = TextDocumentContentChangeEvent $ InL $ #range .== Range (Position 2 0) (Position 2 6)+                                                         .+ #rangeLength .== Nothing+                                                         .+ #text .== "f = ()"+        changeDoc doc [change]+        expectDiagnostics [("Foo.hs", [])]
+ test/exe/DiagnosticTests.hs view
@@ -0,0 +1,565 @@++{-# LANGUAGE GADTs            #-}+{-# LANGUAGE OverloadedLabels #-}++module DiagnosticTests (tests) where++import           Control.Applicative.Combinators+import qualified Control.Lens                    as Lens+import           Control.Monad+import           Control.Monad.IO.Class          (liftIO)+import           Data.List.Extra+import           Data.Row+import qualified Data.Text                       as T+import           Development.IDE.GHC.Compat      (GhcVersion (..), ghcVersion)+import           Development.IDE.GHC.Util+import           Development.IDE.Test            (diagnostic,+                                                  expectCurrentDiagnostics,+                                                  expectDiagnostics,+                                                  expectDiagnosticsWithTags,+                                                  expectNoMoreDiagnostics,+                                                  flushMessages, waitForAction)+import           Development.IDE.Types.Location+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.FilePath+import           System.IO.Extra                 hiding (withTempDir)+-- import Test.QuickCheck.Instances ()+import           Control.Lens                    ((^.))+import           Control.Monad.Extra             (whenJust)+import           Development.IDE.Plugin.Test     (WaitForIdeRuleResult (..))+import           System.Time.Extra+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup "diagnostics"+  [ testSessionWait "fix syntax error" $ do+      let content = T.unlines [ "module Testing wher" ]+      doc <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics [("Testing.hs", [(DiagnosticSeverity_Error, (0, 15), "parse error")])]+      let change = TextDocumentContentChangeEvent $ InL $ #range .== Range (Position 0 15) (Position 0 19)+                                                       .+ #rangeLength .== Nothing+                                                       .+ #text .== "where"+      changeDoc doc [change]+      expectDiagnostics [("Testing.hs", [])]+  , testSessionWait "introduce syntax error" $ do+      let content = T.unlines [ "module Testing where" ]+      doc <- createDoc "Testing.hs" "haskell" content+      void $ skipManyTill anyMessage (message SMethod_WindowWorkDoneProgressCreate)+      waitForProgressBegin+      let change = TextDocumentContentChangeEvent$ InL $ #range .== Range (Position 0 15) (Position 0 18)+                                                      .+ #rangeLength .== Nothing+                                                      .+ #text .== "wher"+      changeDoc doc [change]+      expectDiagnostics [("Testing.hs", [(DiagnosticSeverity_Error, (0, 15), "parse error")])]+  , testSessionWait "update syntax error" $ do+      let content = T.unlines [ "module Testing(missing) where" ]+      doc <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics [("Testing.hs", [(DiagnosticSeverity_Error, (0, 15), "Not in scope: 'missing'")])]+      let change = TextDocumentContentChangeEvent $ InL $ #range .== Range (Position 0 15) (Position 0 16)+                                                       .+ #rangeLength .== Nothing+                                                       .+ #text .== "l"+      changeDoc doc [change]+      expectDiagnostics [("Testing.hs", [(DiagnosticSeverity_Error, (0, 15), "Not in scope: 'lissing'")])]+  , testSessionWait "variable not in scope" $ do+      let content = T.unlines+            [ "module Testing where"+            , "foo :: Int -> Int -> Int"+            , "foo a _b = a + ab"+            , "bar :: Int -> Int -> Int"+            , "bar _a b = cd + b"+            ]+      _ <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics+        [ ( "Testing.hs"+          , [ (DiagnosticSeverity_Error, (2, 15), "Variable not in scope: ab")+            , (DiagnosticSeverity_Error, (4, 11), "Variable not in scope: cd")+            ]+          )+        ]+  , testSessionWait "type error" $ do+      let content = T.unlines+            [ "module Testing where"+            , "foo :: Int -> String -> Int"+            , "foo a b = a + b"+            ]+      _ <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics+        [ ( "Testing.hs"+          , [(DiagnosticSeverity_Error, (2, 14), "Couldn't match type '[Char]' with 'Int'")]+          )+        ]+  , testSessionWait "typed hole" $ do+      let content = T.unlines+            [ "module Testing where"+            , "foo :: Int -> String"+            , "foo a = _ a"+            ]+      _ <- createDoc "Testing.hs" "haskell" content+      expectDiagnostics+        [ ( "Testing.hs"+          , [(DiagnosticSeverity_Error, (2, 8), "Found hole: _ :: Int -> String")]+          )+        ]++  , testGroup "deferral" $+    let sourceA a = T.unlines+          [ "module A where"+          , "a :: Int"+          , "a = " <> a]+        sourceB = T.unlines+          [ "module B where"+          , "import A ()"+          , "b :: Float"+          , "b = True"]+        bMessage = "Couldn't match expected type 'Float' with actual type 'Bool'"+        expectedDs aMessage =+          [ ("A.hs", [(DiagnosticSeverity_Error, (2,4), aMessage)])+          , ("B.hs", [(DiagnosticSeverity_Error, (3,4), bMessage)])]+        deferralTest title binding msg = testSessionWait title $ do+          _ <- createDoc "A.hs" "haskell" $ sourceA binding+          _ <- createDoc "B.hs" "haskell"   sourceB+          expectDiagnostics $ expectedDs msg+    in+    [ deferralTest "type error"          "True"    "Couldn't match expected type"+    , deferralTest "typed hole"          "_"       "Found hole"+    , deferralTest "out of scope var"    "unbound" "Variable not in scope"+    ]++  , testSessionWait "remove required module" $ do+      let contentA = T.unlines [ "module ModuleA where" ]+      docA <- createDoc "ModuleA.hs" "haskell" contentA+      let contentB = T.unlines+            [ "module ModuleB where"+            , "import ModuleA"+            ]+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      let change = TextDocumentContentChangeEvent $ InL $ #range .== Range (Position 0 0) (Position 0 20)+                                                       .+ #rangeLength .== Nothing+                                                       .+ #text .== ""+      changeDoc docA [change]+      expectDiagnostics [("ModuleB.hs", [(DiagnosticSeverity_Error, (1, 0), "Could not find module")])]+  , testSessionWait "add missing module" $ do+      let contentB = T.unlines+            [ "module ModuleB where"+            , "import ModuleA ()"+            ]+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      expectDiagnostics [("ModuleB.hs", [(DiagnosticSeverity_Error, (1, 7), "Could not find module")])]+      let contentA = T.unlines [ "module ModuleA where" ]+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      expectDiagnostics [("ModuleB.hs", [])]+  , testCase "add missing module (non workspace)" $+    -- By default lsp-test sends FileWatched notifications for all files, which we don't want+    -- as non workspace modules will not be watched by the LSP server.+    -- To work around this, we tell lsp-test that our client doesn't have the+    -- FileWatched capability, which is enough to disable the notifications+    withTempDir $ \tmpDir -> runInDir'' lspTestCapsNoFileWatches tmpDir "." "." [] $ do+      let contentB = T.unlines+            [ "module ModuleB where"+            , "import ModuleA ()"+            ]+      _ <- createDoc (tmpDir </> "ModuleB.hs") "haskell" contentB+      expectDiagnostics [(tmpDir </> "ModuleB.hs", [(DiagnosticSeverity_Error, (1, 7), "Could not find module")])]+      let contentA = T.unlines [ "module ModuleA where" ]+      _ <- createDoc (tmpDir </> "ModuleA.hs") "haskell" contentA+      expectDiagnostics [(tmpDir </> "ModuleB.hs", [])]+  , testSessionWait "cyclic module dependency" $ do+      let contentA = T.unlines+            [ "module ModuleA where"+            , "import ModuleB"+            ]+      let contentB = T.unlines+            [ "module ModuleB where"+            , "import ModuleA"+            ]+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      expectDiagnostics+        [ ( "ModuleA.hs"+          , [(DiagnosticSeverity_Error, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]+          )+        , ( "ModuleB.hs"+          , [(DiagnosticSeverity_Error, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]+          )+        ]+  , testSession' "deeply nested cyclic module dependency" $ \path -> do+      let contentA = unlines+            [ "module ModuleA where" , "import ModuleB" ]+      let contentB = unlines+            [ "module ModuleB where" , "import ModuleA" ]+      let contentC = unlines+            [ "module ModuleC where" , "import ModuleB" ]+      let contentD = T.unlines+            [ "module ModuleD where" , "import ModuleC" ]+          cradle =+            "cradle: {direct: {arguments: [ModuleA, ModuleB, ModuleC, ModuleD]}}"+      liftIO $ writeFile (path </> "ModuleA.hs") contentA+      liftIO $ writeFile (path </> "ModuleB.hs") contentB+      liftIO $ writeFile (path </> "ModuleC.hs") contentC+      liftIO $ writeFile (path </> "hie.yaml") cradle+      _ <- createDoc "ModuleD.hs" "haskell" contentD+      expectDiagnostics+        [ ( "ModuleB.hs"+          , [(DiagnosticSeverity_Error, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]+          )+        ]+  , testSessionWait "cyclic module dependency with hs-boot" $ do+      let contentA = T.unlines+            [ "module ModuleA where"+            , "import {-# SOURCE #-} ModuleB"+            ]+      let contentB = T.unlines+            [ "{-# OPTIONS -Wmissing-signatures#-}"+            , "module ModuleB where"+            , "import ModuleA"+            -- introduce an artificial diagnostic+            , "foo = ()"+            ]+      let contentBboot = T.unlines+            [ "module ModuleB where"+            ]+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot+      expectDiagnostics [("ModuleB.hs", [(DiagnosticSeverity_Warning, (3,0), "Top-level binding")])]+  , testSessionWait "correct reference used with hs-boot" $ do+      let contentB = T.unlines+            [ "module ModuleB where"+            , "import {-# SOURCE #-} ModuleA()"+            ]+      let contentA = T.unlines+            [ "module ModuleA where"+            , "import ModuleB()"+            , "x = 5"+            ]+      let contentAboot = T.unlines+            [ "module ModuleA where"+            ]+      let contentC = T.unlines+            [ "{-# OPTIONS -Wmissing-signatures #-}"+            , "module ModuleC where"+            , "import ModuleA"+            -- this reference will fail if it gets incorrectly+            -- resolved to the hs-boot file+            , "y = x"+            ]+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot+      _ <- createDoc "ModuleC.hs" "haskell" contentC+      expectDiagnostics [("ModuleC.hs", [(DiagnosticSeverity_Warning, (3,0), "Top-level binding")])]+  , testSessionWait "redundant import" $ do+      let contentA = T.unlines ["module ModuleA where"]+      let contentB = T.unlines+            [ "{-# OPTIONS_GHC -Wunused-imports #-}"+            , "module ModuleB where"+            , "import ModuleA"+            ]+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      expectDiagnosticsWithTags+        [ ( "ModuleB.hs"+          , [(DiagnosticSeverity_Warning, (2, 0), "The import of 'ModuleA' is redundant", Just DiagnosticTag_Unnecessary)]+          )+        ]+  , testSessionWait "redundant import even without warning" $ do+      let contentA = T.unlines ["module ModuleA where"]+      let contentB = T.unlines+            [ "{-# OPTIONS_GHC -Wno-unused-imports -Wmissing-signatures #-}"+            , "module ModuleB where"+            , "import ModuleA"+            -- introduce an artificial warning for testing purposes+            , "foo = ()"+            ]+      _ <- createDoc "ModuleA.hs" "haskell" contentA+      _ <- createDoc "ModuleB.hs" "haskell" contentB+      expectDiagnostics [("ModuleB.hs", [(DiagnosticSeverity_Warning, (3,0), "Top-level binding")])]+  , testSessionWait "package imports" $ do+      let thisDataListContent = T.unlines+            [ "module Data.List where"+            , "x :: Integer"+            , "x = 123"+            ]+      let mainContent = T.unlines+            [ "{-# LANGUAGE PackageImports #-}"+            , "module Main where"+            , "import qualified \"this\" Data.List as ThisList"+            , "import qualified \"base\" Data.List as BaseList"+            , "useThis = ThisList.x"+            , "useBase = BaseList.map"+            , "wrong1 = ThisList.map"+            , "wrong2 = BaseList.x"+            , "main = pure ()"+            ]+      _ <- createDoc "Data/List.hs" "haskell" thisDataListContent+      _ <- createDoc "Main.hs" "haskell" mainContent+      expectDiagnostics+        [ ( "Main.hs"+          , [(DiagnosticSeverity_Error, (6, 9),+                if ghcVersion >= GHC96 then+                  "Variable not in scope: ThisList.map"+                else if ghcVersion >= GHC94 then+                  "Variable not in scope: map" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130+                else+                  "Not in scope: \8216ThisList.map\8217")+            ,(DiagnosticSeverity_Error, (7, 9),+                if ghcVersion >= GHC96 then+                  "Variable not in scope: BaseList.x"+                else if ghcVersion >= GHC94 then+                  "Variable not in scope: x" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130+                else+                  "Not in scope: \8216BaseList.x\8217")+            ]+          )+        ]+  , testSessionWait "unqualified warnings" $ do+      let fooContent = T.unlines+            [ "{-# OPTIONS_GHC -Wredundant-constraints #-}"+            , "module Foo where"+            , "foo :: Ord a => a -> Int"+            , "foo _a = 1"+            ]+      _ <- createDoc "Foo.hs" "haskell" fooContent+      expectDiagnostics+        [ ( "Foo.hs"+      -- The test is to make sure that warnings contain unqualified names+      -- where appropriate. The warning should use an unqualified name 'Ord', not+      -- something like 'GHC.Classes.Ord'. The choice of redundant-constraints to+      -- test this is fairly arbitrary.+          , [(DiagnosticSeverity_Warning, (2, if ghcVersion >= GHC94 then 7 else 0), "Redundant constraint: Ord a")+            ]+          )+        ]+    , testSessionWait "lower-case drive" $ do+          let aContent = T.unlines+                [ "module A.A where"+                , "import A.B ()"+                ]+              bContent = T.unlines+                [ "{-# OPTIONS_GHC -Wall #-}"+                , "module A.B where"+                , "import Data.List"+                ]+          uriB <- getDocUri "A/B.hs"+          Just pathB <- pure $ uriToFilePath uriB+          uriB <- pure $+              let (drive, suffix) = splitDrive pathB+              in filePathToUri (joinDrive (lower drive) suffix)+          liftIO $ createDirectoryIfMissing True (takeDirectory pathB)+          liftIO $ writeFileUTF8 pathB $ T.unpack bContent+          uriA <- getDocUri "A/A.hs"+          Just pathA <- pure $ uriToFilePath uriA+          uriA <- pure $+              let (drive, suffix) = splitDrive pathA+              in filePathToUri (joinDrive (lower drive) suffix)+          let itemA = TextDocumentItem uriA "haskell" 0 aContent+          let a = TextDocumentIdentifier uriA+          sendNotification SMethod_TextDocumentDidOpen (DidOpenTextDocumentParams itemA)+          TNotificationMessage{_params = PublishDiagnosticsParams fileUri _ diags} <- skipManyTill anyMessage diagnostic+          -- Check that if we put a lower-case drive in for A.A+          -- the diagnostics for A.B will also be lower-case.+          liftIO $ fileUri @?= uriB+          let msg :: T.Text = (head diags) ^. L.message+          liftIO $ unless ("redundant" `T.isInfixOf` msg) $+              assertFailure ("Expected redundant import but got " <> T.unpack msg)+          closeDoc a+  , testSessionWait "haddock parse error" $ do+      let fooContent = T.unlines+            [ "module Foo where"+            , "foo :: Int"+            , "foo = 1 {-|-}"+            ]+      _ <- createDoc "Foo.hs" "haskell" fooContent+      if ghcVersion >= GHC90 then+          -- Haddock parse errors are ignored on ghc-9.0+            pure ()+      else+        expectDiagnostics+            [ ( "Foo.hs"+              , [(DiagnosticSeverity_Warning, (2, 8), "Haddock parse error on input")]+              )+            ]+  , testSessionWait "strip file path" $ do+      let+          name = "Testing"+          content = T.unlines+            [ "module " <> name <> " where"+            , "value :: Maybe ()"+            , "value = [()]"+            ]+      _ <- createDoc (T.unpack name <> ".hs") "haskell" content+      notification <- skipManyTill anyMessage diagnostic+      let+          offenders =+            L.params .+            L.diagnostics .+            Lens.folded .+            L.message .+            Lens.filtered (T.isInfixOf ("/" <> name <> ".hs:"))+          failure msg = liftIO $ assertFailure $ "Expected file path to be stripped but got " <> T.unpack msg+      Lens.mapMOf_ offenders failure notification+  , testSession' "-Werror in cradle is ignored" $ \sessionDir -> do+      liftIO $ writeFile (sessionDir </> "hie.yaml")+        "cradle: {direct: {arguments: [\"-Wall\", \"-Werror\"]}}"+      let fooContent = T.unlines+            [ "module Foo where"+            , "foo = ()"+            ]+      _ <- createDoc "Foo.hs" "haskell" fooContent+      expectDiagnostics+        [ ( "Foo.hs"+          , [(DiagnosticSeverity_Warning, (1, 0), "Top-level binding with no type signature:")+            ]+          )+        ]+  , testSessionWait "-Werror in pragma is ignored" $ do+      let fooContent = T.unlines+            [ "{-# OPTIONS_GHC -Wall -Werror #-}"+            , "module Foo() where"+            , "foo :: Int"+            , "foo = 1"+            ]+      _ <- createDoc "Foo.hs" "haskell" fooContent+      expectDiagnostics+        [ ( "Foo.hs"+          , [(DiagnosticSeverity_Warning, (3, 0), "Defined but not used:")+            ]+          )+        ]+  , testCase "typecheck-all-parents-of-interest" $ runWithExtraFiles "recomp" $ \dir -> do+    let bPath = dir </> "B.hs"+        pPath = dir </> "P.hs"+        aPath = dir </> "A.hs"++    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int+    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int+    aSource <- liftIO $ readFileUtf8 aPath -- x = y :: Int++    bdoc <- createDoc bPath "haskell" bSource+    _pdoc <- createDoc pPath "haskell" pSource+    expectDiagnostics+      [("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])] -- So that we know P has been loaded++    -- Change y from Int to B which introduces a type error in A (imported from P)+    changeDoc bdoc [TextDocumentContentChangeEvent . InR . (.==) #text $+                    T.unlines ["module B where", "y :: Bool", "y = undefined"]]+    expectDiagnostics+      [("A.hs", [(DiagnosticSeverity_Error, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])+      ]++    -- Open A and edit to fix the type error+    adoc <- createDoc aPath "haskell" aSource+    changeDoc adoc [TextDocumentContentChangeEvent . InR . (.==) #text $+                    T.unlines ["module A where", "import B", "x :: Bool", "x = y"]]++    expectDiagnostics+      [ ( "P.hs",+          [ (DiagnosticSeverity_Error, (4, 6), "Couldn't match expected type 'Int' with actual type 'Bool'"),+            (DiagnosticSeverity_Warning, (4, 0), "Top-level binding")+          ]+        ),+        ("A.hs", [])+      ]+    expectNoMoreDiagnostics 1++  , testSessionWait "deduplicate missing module diagnostics" $  do+      let fooContent = T.unlines [ "module Foo() where" , "import MissingModule" ]+      doc <- createDoc "Foo.hs" "haskell" fooContent+      expectDiagnostics [("Foo.hs", [(DiagnosticSeverity_Error, (1,7), "Could not find module 'MissingModule'")])]++      changeDoc doc [TextDocumentContentChangeEvent  . InR . (.==) #text $ "module Foo() where" ]+      expectDiagnostics []++      changeDoc doc [TextDocumentContentChangeEvent  . InR . (.==) #text $ T.unlines+            [ "module Foo() where" , "import MissingModule" ] ]+      expectDiagnostics [("Foo.hs", [(DiagnosticSeverity_Error, (1,7), "Could not find module 'MissingModule'")])]++  , testGroup "Cancellation"+    [ cancellationTestGroup "edit header" editHeader yesSession noParse  noTc+    , cancellationTestGroup "edit import" editImport noSession  yesParse noTc+    , cancellationTestGroup "edit body"   editBody   yesSession yesParse yesTc+    ]+  ]+  where+      editPair x y = let p = Position x y ; p' = Position x (y+2) in+        (TextDocumentContentChangeEvent $ InL $ #range .== Range p p+                                             .+ #rangeLength .== Nothing+                                             .+ #text .== "fd"+        ,TextDocumentContentChangeEvent $ InL $ #range .== Range p p'+                                             .+ #rangeLength .== Nothing+                                             .+ #text .== "")+      editHeader = editPair 0 0+      editImport = editPair 2 10+      editBody   = editPair 3 10++      noParse = False+      yesParse = True++      noSession = False+      yesSession = True++      noTc = False+      yesTc = True++cancellationTestGroup :: TestName -> (TextDocumentContentChangeEvent, TextDocumentContentChangeEvent) -> Bool -> Bool -> Bool -> TestTree+cancellationTestGroup name edits sessionDepsOutcome parseOutcome tcOutcome = testGroup name+    [ cancellationTemplate edits Nothing+    , cancellationTemplate edits $ Just ("GetFileContents", True)+    , cancellationTemplate edits $ Just ("GhcSession", True)+      -- the outcome for GetModSummary is always True because parseModuleHeader never fails (!)+    , cancellationTemplate edits $ Just ("GetModSummary", True)+    , cancellationTemplate edits $ Just ("GetModSummaryWithoutTimestamps", True)+      -- getLocatedImports never fails+    , cancellationTemplate edits $ Just ("GetLocatedImports", True)+    , cancellationTemplate edits $ Just ("GhcSessionDeps", sessionDepsOutcome)+    , cancellationTemplate edits $ Just ("GetParsedModule", parseOutcome)+    , cancellationTemplate edits $ Just ("TypeCheck", tcOutcome)+    , cancellationTemplate edits $ Just ("GetHieAst", tcOutcome)+    ]++cancellationTemplate :: (TextDocumentContentChangeEvent, TextDocumentContentChangeEvent) -> Maybe (String, Bool) -> TestTree+cancellationTemplate (edit, undoEdit) mbKey = testCase (maybe "-" fst mbKey) $ runTestNoKick $ do+      doc <- createDoc "Foo.hs" "haskell" $ T.unlines+            [ "{-# OPTIONS_GHC -Wall #-}"+            , "module Foo where"+            , "import Data.List()"+            , "f0 x = (x,x)"+            ]++      -- for the example above we expect one warning+      let missingSigDiags = [(DiagnosticSeverity_Warning, (3, 0), "Top-level binding") ]+      typeCheck doc >> expectCurrentDiagnostics doc missingSigDiags++      -- Now we edit the document and wait for the given key (if any)+      changeDoc doc [edit]+      whenJust mbKey $ \(key, expectedResult) -> do+        WaitForIdeRuleResult{ideResultSuccess} <- waitForAction key doc+        liftIO $ ideResultSuccess @?= expectedResult++      -- The 2nd edit cancels the active session and unbreaks the file+      -- wait for typecheck and check that the current diagnostics are accurate+      changeDoc doc [undoEdit]+      typeCheck doc >> expectCurrentDiagnostics doc missingSigDiags++      expectNoMoreDiagnostics 0.5+    where+        -- similar to run except it disables kick+        runTestNoKick s = withTempDir $ \dir -> runInDir' dir "." "." ["--test-no-kick"] s++        typeCheck doc = do+            WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc+            liftIO $ assertBool "The file should typecheck" ideResultSuccess+            -- wait for the debouncer to publish diagnostics if the rule runs+            liftIO $ sleep 0.2+            -- flush messages to ensure current diagnostics state is updated+            flushMessages
+ test/exe/ExceptionTests.hs view
@@ -0,0 +1,155 @@++module ExceptionTests (tests) where++import           Control.Exception                 (ArithException (DivideByZero),+                                                    throwIO)+import           Control.Lens+import           Control.Monad.Error.Class         (MonadError (throwError))+import           Control.Monad.IO.Class            (liftIO)+import qualified Data.Aeson                        as A+import           Data.Text                         as T+import           Development.IDE.Core.Shake        (IdeState (..))+import qualified Development.IDE.LSP.Notifications as Notifications+import qualified Development.IDE.Main              as IDE+import           Development.IDE.Plugin.HLS        (toResponseError)+import           Development.IDE.Plugin.Test       as Test+import           Development.IDE.Types.Options+import           GHC.Base                          (coerce)+import           Ide.Logger                        (Logger, Recorder,+                                                    WithPriority, cmapWithPrio)+import           Ide.Plugin.Error+import           Ide.PluginUtils                   (idePluginsToPluginDesc,+                                                    pluginDescToIdePlugins)+import           Ide.Types+import qualified Language.LSP.Protocol.Lens        as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types       hiding+                                                   (SemanticTokenAbsolute (..),+                                                    SemanticTokenRelative (..),+                                                    SemanticTokensEdit (..),+                                                    mkRange)+import           Language.LSP.Test+import           LogType                           (Log (..))+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: Recorder (WithPriority Log) -> Logger -> TestTree+tests recorder logger = do+  testGroup "Exceptions and PluginError" [+    testGroup "Testing that IO Exceptions are caught in..."+      [ testCase "PluginHandlers" $ do+          let pluginId = "plugin-handler-exception"+              plugins = pluginDescToIdePlugins $+                  [ (defaultPluginDescriptor pluginId)+                      { pluginHandlers = mconcat+                          [ mkPluginHandler SMethod_TextDocumentCodeLens $ \_ _ _-> do+                              _ <- liftIO $ throwIO DivideByZero+                              pure (InL [])+                          ]+                      }]+          testIde recorder (testingLite recorder logger plugins) $ do+              doc <- createDoc "A.hs" "haskell" "module A where"+              waitForProgressDone+              (view L.result -> lens) <- request SMethod_TextDocumentCodeLens (CodeLensParams Nothing Nothing doc)+              case lens of+                Left (ResponseError {_code = InR ErrorCodes_InternalError, _message}) ->+                  liftIO $ assertBool "We caught an error, but it wasn't ours!"+                          (T.isInfixOf "divide by zero" _message && T.isInfixOf (coerce pluginId) _message)+                _ -> liftIO $ assertFailure $ show lens++        , testCase "Commands" $ do+          let pluginId = "command-exception"+              commandId = CommandId "exception"+              plugins = pluginDescToIdePlugins $+                  [ (defaultPluginDescriptor pluginId)+                      { pluginCommands =+                          [ PluginCommand commandId "Causes an exception" $ \_ (_::Int) -> do+                              _ <- liftIO $ throwIO DivideByZero+                              pure (InR Null)+                          ]+                      }]+          testIde recorder (testingLite recorder logger plugins) $ do+              _ <- createDoc "A.hs" "haskell" "module A where"+              waitForProgressDone+              let cmd = mkLspCommand (coerce pluginId) commandId "" (Just [A.toJSON (1::Int)])+                  execParams = ExecuteCommandParams Nothing (cmd ^. L.command) (cmd ^. L.arguments)+              (view L.result -> res) <- request SMethod_WorkspaceExecuteCommand execParams+              case res of+                Left (ResponseError {_code = InR ErrorCodes_InternalError, _message}) ->+                  liftIO $ assertBool "We caught an error, but it wasn't ours!"+                          (T.isInfixOf "divide by zero" _message && T.isInfixOf (coerce pluginId) _message)+                _ -> liftIO $ assertFailure $ show res++        , testCase "Notification Handlers" $ do+          let pluginId = "notification-exception"+              plugins = pluginDescToIdePlugins $+                  [ (defaultPluginDescriptor pluginId)+                      { pluginNotificationHandlers = mconcat+                          [  mkPluginNotificationHandler SMethod_TextDocumentDidOpen $ \_ _ _ _ ->+                              liftIO $ throwIO DivideByZero+                          ]+                        , pluginHandlers = mconcat+                          [ mkPluginHandler SMethod_TextDocumentCodeLens $ \_ _ _-> do+                              pure (InL [])+                          ]+                      }]+          testIde recorder (testingLite recorder logger plugins) $ do+              doc <- createDoc "A.hs" "haskell" "module A where"+              waitForProgressDone+              (view L.result -> lens) <- request SMethod_TextDocumentCodeLens (CodeLensParams Nothing Nothing doc)+              case lens of+                Right (InL []) ->+                  -- We don't get error responses from notification handlers, so+                  -- we can only make sure that the server is still responding+                  pure ()+                _ -> liftIO $ assertFailure $ "We should have had an empty list" <> show lens]++   , testGroup "Testing PluginError order..."+      [ pluginOrderTestCase recorder logger  "InternalError over InvalidParams" PluginInternalError PluginInvalidParams+      , pluginOrderTestCase recorder logger  "InvalidParams over InvalidUserState" PluginInvalidParams PluginInvalidUserState+      , pluginOrderTestCase recorder logger  "InvalidUserState over RequestRefused" PluginInvalidUserState PluginRequestRefused+      ]+   ]++testingLite :: Recorder (WithPriority Log) -> Logger -> IdePlugins IdeState -> IDE.Arguments+testingLite recorder logger plugins =+  let+    arguments@IDE.Arguments{ argsIdeOptions } =+        IDE.defaultArguments (cmapWithPrio LogIDEMain recorder) logger plugins+    hlsPlugins = pluginDescToIdePlugins $+      idePluginsToPluginDesc plugins+      ++ [Notifications.descriptor (cmapWithPrio LogNotifications recorder) "ghcide-core"]+      ++ [Test.blockCommandDescriptor "block-command", Test.plugin]+    ideOptions config sessionLoader =+      let+        defOptions = argsIdeOptions config sessionLoader+      in+        defOptions{ optTesting = IdeTesting True }+  in+    arguments+      { IDE.argsHlsPlugins = hlsPlugins+      , IDE.argsIdeOptions = ideOptions+      }++pluginOrderTestCase :: Recorder (WithPriority Log) -> Logger -> TestName -> (T.Text -> PluginError) -> (T.Text -> PluginError) -> TestTree+pluginOrderTestCase recorder logger msg err1 err2 =+  testCase msg $ do+      let pluginId = "error-order-test"+          plugins = pluginDescToIdePlugins $+              [ (defaultPluginDescriptor pluginId)+                  { pluginHandlers = mconcat+                      [ mkPluginHandler SMethod_TextDocumentCodeLens $ \_ _ _-> do+                          throwError $ err1 "error test"+                        ,mkPluginHandler SMethod_TextDocumentCodeLens $ \_ _ _-> do+                          throwError $ err2 "error test"+                      ]+                  }]+      testIde recorder (testingLite recorder logger plugins) $ do+          doc <- createDoc "A.hs" "haskell" "module A where"+          waitForProgressDone+          (view L.result -> lens) <- request SMethod_TextDocumentCodeLens (CodeLensParams Nothing Nothing doc)+          case lens of+            Left re | toResponseError (pluginId, err1 "error test") == re -> pure ()+                    | otherwise -> liftIO $ assertFailure "We caught an error, but it wasn't ours!"+            _ -> liftIO $ assertFailure $ show lens
+ test/exe/FindDefinitionAndHoverTests.hs view
@@ -0,0 +1,252 @@++{-# LANGUAGE MultiWayIf #-}++module FindDefinitionAndHoverTests (tests) where++import           Control.Monad+import           Control.Monad.IO.Class         (liftIO)+import           Data.Foldable+import           Data.Maybe+import qualified Data.Text                      as T+import           Development.IDE.GHC.Compat     (GhcVersion (..), ghcVersion)+import           Development.IDE.GHC.Util+import           Development.IDE.Test           (expectDiagnostics,+                                                 standardizeQuotes)+import           Development.IDE.Types.Location+import qualified Language.LSP.Protocol.Lens     as L+import           Language.LSP.Protocol.Types    hiding+                                                (SemanticTokenAbsolute (..),+                                                 SemanticTokenRelative (..),+                                                 SemanticTokensEdit (..),+                                                 mkRange)+import           Language.LSP.Test+import           System.FilePath+import           System.Info.Extra              (isWindows)+-- import Test.QuickCheck.Instances ()+import           Control.Lens                   ((^.))+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+import           Text.Regex.TDFA                ((=~))++tests :: TestTree+tests = let++  tst :: (TextDocumentIdentifier -> Position -> Session a, a -> Session [Expect] -> Session ()) -> Position -> String -> Session [Expect] -> String -> TestTree+  tst (get, check) pos sfp targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do++    -- Dirty the cache to check that definitions work even in the presence of iface files+    liftIO $ runInDir dir $ do+      let fooPath = dir </> "Foo.hs"+      fooSource <- liftIO $ readFileUtf8 fooPath+      fooDoc <- createDoc fooPath "haskell" fooSource+      _ <- getHover fooDoc $ Position 4 3+      closeDoc fooDoc++    doc <- openTestDataDoc (dir </> sfp)+    waitForProgressDone+    found <- get doc pos+    check found targetRange++++  checkHover :: Maybe Hover -> Session [Expect] -> Session ()+  checkHover hover expectations = traverse_ check =<< expectations where++    check expected =+      case hover of+        Nothing -> unless (expected == ExpectNoHover) $ liftIO $ assertFailure "no hover found"+        Just Hover{_contents = (InL MarkupContent{_value = standardizeQuotes -> msg})+                  ,_range    = rangeInHover } ->+          case expected of+            ExpectRange  expectedRange -> checkHoverRange expectedRange rangeInHover msg+            ExpectHoverRange expectedRange -> checkHoverRange expectedRange rangeInHover msg+            ExpectHoverText snippets -> liftIO $ traverse_ (`assertFoundIn` msg) snippets+            ExpectHoverExcludeText snippets -> liftIO $ traverse_ (`assertNotFoundIn` msg) snippets+            ExpectHoverTextRegex re -> liftIO $ assertBool ("Regex not found in " <> T.unpack msg) (msg =~ re :: Bool)+            ExpectNoHover -> liftIO $ assertFailure $ "Expected no hover but got " <> show hover+            _ -> pure () -- all other expectations not relevant to hover+        _ -> liftIO $ assertFailure $ "test not expecting this kind of hover info" <> show hover++  extractLineColFromHoverMsg :: T.Text -> [T.Text]+  extractLineColFromHoverMsg = T.splitOn ":" . head . T.splitOn "*" . last . T.splitOn (sourceFileName <> ":")++  checkHoverRange :: Range -> Maybe Range -> T.Text -> Session ()+  checkHoverRange expectedRange rangeInHover msg =+    let+      lineCol = extractLineColFromHoverMsg msg+      -- looks like hovers use 1-based numbering while definitions use 0-based+      -- turns out that they are stored 1-based in RealSrcLoc by GHC itself.+      adjust Position{_line = l, _character = c} =+        Position{_line = l + 1, _character = c + 1}+    in+    case map (read . T.unpack) lineCol of+      [l,c] -> liftIO $ adjust (expectedRange ^. L.start) @=? Position l c+      _     -> liftIO $ assertFailure $+        "expected: " <> show ("[...]" <> sourceFileName <> ":<LINE>:<COL>**[...]", Just expectedRange) <>+        "\n but got: " <> show (msg, rangeInHover)++  assertFoundIn :: T.Text -> T.Text -> Assertion+  assertFoundIn part whole = assertBool+    (T.unpack $ "failed to find: `" <> part <> "` in hover message:\n" <> whole)+    (part `T.isInfixOf` whole)++  assertNotFoundIn :: T.Text -> T.Text -> Assertion+  assertNotFoundIn part whole = assertBool+    (T.unpack $ "found unexpected: `" <> part <> "` in hover message:\n" <> whole)+    (not . T.isInfixOf part $ whole)++  sourceFilePath = T.unpack sourceFileName+  sourceFileName = "GotoHover.hs"++  mkFindTests tests = testGroup "get"+    [ testGroup "definition" $ mapMaybe fst tests+    , testGroup "hover"      $ mapMaybe snd tests+    , checkFileCompiles sourceFilePath $+        expectDiagnostics+          [ ( "GotoHover.hs", [(DiagnosticSeverity_Error, (62, 7), "Found hole: _")])+          , ( "GotoHover.hs", [(DiagnosticSeverity_Error, (65, 8), "Found hole: _")])+          ]+    , testGroup "type-definition" typeDefinitionTests+    , testGroup "hover-record-dot-syntax" recordDotSyntaxTests ]++  typeDefinitionTests = [ tst (getTypeDefinitions, checkDefs) aaaL14 sourceFilePath (pure tcData) "Saturated data con"+                        , tst (getTypeDefinitions, checkDefs) aL20 sourceFilePath (pure [ExpectNoDefinitions]) "Polymorphic variable"]++  recordDotSyntaxTests+    | ghcVersion >= GHC92 =+        [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent"+        , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child"+        , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child"+        ]+    | otherwise = []++  test runDef runHover look expect = testM runDef runHover look (return expect)++  testM runDef runHover look expect title =+    ( runDef   $ tst def   look sourceFilePath expect title+    , runHover $ tst hover look sourceFilePath expect title ) where+      def   = (getDefinitions, checkDefs)+      hover = (getHover      , checkHover)++  -- search locations            expectations on results+  fffL4  = fffR  ^. L.start;  fffR = mkRange 8  4    8  7 ; fff  = [ExpectRange fffR]+  fffL8  = Position 12  4  ;+  fffL14 = Position 18  7  ;+  aL20   = Position 19 15+  aaaL14 = Position 18 20  ;  aaa    = [mkR  11  0   11  3]+  dcL7   = Position 11 11  ;  tcDC   = [mkR   7 23    9 16]+  dcL12  = Position 16 11  ;+  xtcL5  = Position  9 11  ;  xtc    = [ExpectExternFail,   ExpectHoverText ["Int", "Defined in ", "GHC.Types", "ghc-prim"]]+  tcL6   = Position 10 11  ;  tcData = [mkR   7  0    9 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:8:1"]]+  vvL16  = Position 20 12  ;  vv     = [mkR  20  4   20  6]+  opL16  = Position 20 15  ;  op     = [mkR  21  2   21  4]+  opL18  = Position 22 22  ;  opp    = [mkR  22 13   22 17]+  aL18   = Position 22 20  ;  apmp   = [mkR  22 10   22 11]+  b'L19  = Position 23 13  ;  bp     = [mkR  23  6   23  7]+  xvL20  = Position 24  8  ;  xvMsg  = [ExpectExternFail,   ExpectHoverText ["pack", ":: String -> Text", "Data.Text", "text"]]+  clL23  = Position 27 11  ;  cls    = [mkR  25  0   26 20, ExpectHoverText ["MyClass", "GotoHover.hs:26:1"]]+  clL25  = Position 29  9+  eclL15 = Position 19  8  ;  ecls   = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ", "GHC.Num", "base"]]+  dnbL29 = Position 33 18  ;  dnb    = [ExpectHoverText [":: ()"],   mkR  33 12   33 21]+  dnbL30 = Position 34 23+  lcbL33 = Position 37 26  ;  lcb    = [ExpectHoverText [":: Char"], mkR  37 26   37 27]+  lclL33 = Position 37 22+  mclL36 = Position 40  1  ;  mcl    = [mkR  40  0   40 14]+  mclL37 = Position 41  1+  spaceL37 = Position 41  24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]+  docL41 = Position 45  1  ;  doc    = [ExpectHoverText ["Recognizable docs: kpqz"]]+                           ;  constr = [ExpectHoverText ["Monad m"]]+  eitL40 = Position 44 28  ;  kindE  = [ExpectHoverText [if ghcVersion >= GHC92 then ":: Type -> Type -> Type\n" else ":: * -> * -> *\n"]]+  intL40 = Position 44 34  ;  kindI  = [ExpectHoverText [if ghcVersion >= GHC92 then ":: Type\n" else ":: *\n"]]+  tvrL40 = Position 44 37  ;  kindV  = [ExpectHoverText [":: * -> *\n"]]+  intL41 = Position 45 20  ;  litI   = [ExpectHoverText ["7518"]]+  chrL36 = Position 41 24  ;  litC   = [ExpectHoverText ["'f'"]]+  txtL8  = Position 12 14  ;  litT   = [ExpectHoverText ["\"dfgy\""]]+  lstL43 = Position 47 12  ;  litL   = [ExpectHoverText ["[8391 :: Int, 6268]"]]+  outL45 = Position 49  3  ;  outSig = [ExpectHoverText ["outer", "Bool"], mkR 50 0 50 5]+  innL48 = Position 52  5  ;  innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]+  holeL60 = Position 62 7  ;  hleInfo = [ExpectHoverText ["_ ::"]]+  holeL65 = Position 65 8  ;  hleInfo2 = [ExpectHoverText ["_ :: a -> Maybe a"]]+  cccL17 = Position 17 16  ;  docLink = [ExpectHoverTextRegex "\\*Defined in 'GHC.Types'\\* \\*\\(ghc-prim-[0-9.]+\\)\\*\n\n"]+  imported = Position 56 13 ; importedSig = getDocUri "Foo.hs" >>= \foo -> return [ExpectHoverText ["foo", "Foo", "Haddock"], mkL foo 5 0 5 3]+  reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar", "Haddock"], mkL bar 3 (if ghcVersion >= GHC94 then 5 else 0) 3 (if ghcVersion >= GHC94 then 8 else 14)]+  thLocL57 = Position 59 10 ; thLoc = [ExpectHoverText ["Identity"]]+  cmtL68 = Position 67  0  ;  lackOfdEq = [ExpectHoverExcludeText ["$dEq"]]+  import310 = Position 3 10; pkgTxt = [ExpectHoverText ["Data.Text\n\ntext-"]]+  in+  mkFindTests+  --      def    hover  look       expect+  [+    if ghcVersion >= GHC90 then+        -- It suggests either going to the constructor or to the field+        test  broken yes    fffL4      fff           "field in record definition"+    else+        test  yes    yes    fffL4      fff           "field in record definition"+  , test  yes    yes    fffL8      fff           "field in record construction    #1102"+  , test  yes    yes    fffL14     fff           "field name used as accessor"           -- https://github.com/haskell/ghcide/pull/120 in Calculate.hs+  , test  yes    yes    aaaL14     aaa           "top-level name"                        -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    dcL7       tcDC          "data constructor record         #1029"+  , test  yes    yes    dcL12      tcDC          "data constructor plain"                -- https://github.com/haskell/ghcide/pull/121+  , test  yes    yes    tcL6       tcData        "type constructor                #1028" -- https://github.com/haskell/ghcide/pull/147+  , test  broken yes    xtcL5      xtc           "type constructor external   #717,1028"+  , test  broken yes    xvL20      xvMsg         "value external package           #717" -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    vvL16      vv            "plain parameter"                       -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    aL18       apmp          "pattern match name"                    -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    opL16      op            "top-level operator               #713" -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    opL18      opp           "parameter operator"                    -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    b'L19      bp            "name in backticks"                     -- https://github.com/haskell/ghcide/pull/120+  , test  yes    yes    clL23      cls           "class in instance declaration   #1027"+  , test  yes    yes    clL25      cls           "class in signature              #1027" -- https://github.com/haskell/ghcide/pull/147+  , test  broken yes    eclL15     ecls          "external class in signature #717,1027"+  , test  yes    yes    dnbL29     dnb           "do-notation   bind              #1073"+  , test  yes    yes    dnbL30     dnb           "do-notation lookup"+  , test  yes    yes    lcbL33     lcb           "listcomp   bind                 #1073"+  , test  yes    yes    lclL33     lcb           "listcomp lookup"+  , test  yes    yes    mclL36     mcl           "top-level fn 1st clause"+  , test  yes    yes    mclL37     mcl           "top-level fn 2nd clause         #1030"+  , if ghcVersion >= GHC810 then+        test  yes    yes    spaceL37   space         "top-level fn on space           #1002"+    else+        test  yes    broken spaceL37   space         "top-level fn on space           #1002"+  , test  no     yes    docL41     doc           "documentation                   #1129"+  , test  no     yes    eitL40     kindE         "kind of Either                  #1017"+  , test  no     yes    intL40     kindI         "kind of Int                     #1017"+  , test  no     broken tvrL40     kindV         "kind of (* -> *) type variable  #1017"+  , test  no     broken intL41     litI          "literal Int  in hover info      #1016"+  , test  no     broken chrL36     litC          "literal Char in hover info      #1016"+  , test  no     broken txtL8      litT          "literal Text in hover info      #1016"+  , test  no     broken lstL43     litL          "literal List in hover info      #1016"+  , test  yes    yes    cmtL68     lackOfdEq     "no Core symbols                 #3280"+  , if ghcVersion >= GHC90 then+        test  no     yes    docL41     constr        "type constraint in hover info   #1012"+    else+        test  no     broken docL41     constr        "type constraint in hover info   #1012"+  , test  no     yes    outL45     outSig        "top-level signature              #767"+  , test  broken broken innL48     innSig        "inner     signature              #767"+  , test  no     yes    holeL60    hleInfo       "hole without internal name       #831"+  , test  no     yes    holeL65    hleInfo2      "hole with variable"+  , test  no     yes    cccL17     docLink       "Haddock html links"+  , testM yes    yes    imported   importedSig   "Imported symbol"+  , if | isWindows ->+        -- Flaky on Windows: https://github.com/haskell/haskell-language-server/issues/2997+        testM no     yes    reexported reexportedSig "Imported symbol (reexported)"+       | otherwise ->+        testM yes    yes    reexported reexportedSig "Imported symbol (reexported)"+  , if | ghcVersion == GHC90 && isWindows ->+        test  no     broken    thLocL57   thLoc         "TH Splice Hover"+       | otherwise ->+        test  no     yes       thLocL57   thLoc         "TH Splice Hover"+  , test yes yes import310 pkgTxt "show package name and its version"+  ]+  where yes, broken :: (TestTree -> Maybe TestTree)+        yes    = Just -- test should run and pass+        broken = Just . (`xfail` "known broken")+        no = const Nothing -- don't run this test at all+        skip = const Nothing -- unreliable, don't run++checkFileCompiles :: FilePath -> Session () -> TestTree+checkFileCompiles fp diag =+  testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do+    void (openTestDataDoc (dir </> fp))+    diag
test/exe/FuzzySearch.hs view
@@ -1,6 +1,5 @@ module FuzzySearch (tests) where -import           Control.Monad              (guard) import           Data.Char                  (toLower) import           Data.Maybe                 (catMaybes) import qualified Data.Monoid.Textual        as T@@ -8,12 +7,10 @@ import qualified Data.Text                  as Text import           Prelude                    hiding (filter) import           System.Directory           (doesFileExist)-import           System.Info.Extra          (isWindows) import           System.IO.Unsafe           (unsafePerformIO) import           Test.QuickCheck import           Test.Tasty import           Test.Tasty.ExpectedFailure-import           Test.Tasty.HUnit import           Test.Tasty.QuickCheck      (testProperty) import qualified Text.Fuzzy                 as Fuzzy import           Text.Fuzzy                 (Fuzzy (..))
+ test/exe/GarbageCollectionTests.hs view
@@ -0,0 +1,94 @@++{-# LANGUAGE OverloadedLabels #-}++module GarbageCollectionTests (tests) where++import           Control.Monad.IO.Class      (liftIO)+import           Data.Row+import qualified Data.Set                    as Set+import qualified Data.Text                   as T+import           Development.IDE.Test        (expectCurrentDiagnostics,+                                              getStoredKeys, waitForGC,+                                              waitForTypecheck)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+import           Text.Printf                 (printf)++tests :: TestTree+tests = testGroup "garbage collection"+  [ testGroup "dirty keys"+        [ testSession' "are collected" $ \dir -> do+            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"+            doc <- generateGarbage "A" dir+            closeDoc doc+            garbage <- waitForGC+            liftIO $ assertBool "no garbage was found" $ not $ null garbage++        , testSession' "are deleted from the state" $ \dir -> do+            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"+            docA <- generateGarbage "A" dir+            keys0 <- getStoredKeys+            closeDoc docA+            garbage <- waitForGC+            liftIO $ assertBool "something is wrong with this test - no garbage found" $ not $ null garbage+            keys1 <- getStoredKeys+            liftIO $ assertBool "keys were not deleted from the state" (length keys1 < length keys0)++        , testSession' "are not regenerated unless needed" $ \dir -> do+            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A.hs, B.hs]}}"+            docA <- generateGarbage "A" dir+            _docB <- generateGarbage "B" dir++            -- garbage collect A keys+            keysBeforeGC <- getStoredKeys+            closeDoc docA+            garbage <- waitForGC+            liftIO $ assertBool "something is wrong with this test - no garbage found" $ not $ null garbage+            keysAfterGC <- getStoredKeys+            liftIO $ assertBool "something is wrong with this test - keys were not deleted from the state"+                (length keysAfterGC < length keysBeforeGC)++            -- re-typecheck B and check that the keys for A have not materialized back+            _docB <- generateGarbage "B" dir+            keysB <- getStoredKeys+            let regeneratedKeys = Set.filter (not . isExpected) $+                    Set.intersection (Set.fromList garbage) (Set.fromList keysB)+            liftIO $ regeneratedKeys @?= mempty++        , testSession' "regenerate successfully" $ \dir -> do+            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"+            docA <- generateGarbage "A" dir+            closeDoc docA+            garbage <- waitForGC+            liftIO $ assertBool "no garbage was found" $ not $ null garbage+            let edit = T.unlines+                        [ "module A where"+                        , "a :: Bool"+                        , "a = ()"+                        ]+            doc <- generateGarbage "A" dir+            changeDoc doc [TextDocumentContentChangeEvent . InR . (.==) #text $ edit]+            builds <- waitForTypecheck doc+            liftIO $ assertBool "it still builds" builds+            expectCurrentDiagnostics doc [(DiagnosticSeverity_Error, (2,4), "Couldn't match expected type")]+        ]+  ]+  where+    isExpected k = any (`T.isPrefixOf` k) ["GhcSessionIO"]++    generateGarbage :: String -> FilePath -> Session TextDocumentIdentifier+    generateGarbage modName dir = do+        let fp = modName <> ".hs"+            body = printf "module %s where" modName+        doc <- createDoc fp "haskell" (T.pack body)+        liftIO $ writeFile (dir </> fp) body+        builds <- waitForTypecheck doc+        liftIO $ assertBool "something is wrong with this test" builds+        return doc
+ test/exe/HaddockTests.hs view
@@ -0,0 +1,90 @@++module HaddockTests (tests) where++import           Development.IDE.Spans.Common+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           Test.Tasty.HUnit++tests :: TestTree+tests+  = testGroup "haddock"+      [ testCase "Num" $ checkHaddock+          (unlines+             [ "However, '(+)' and '(*)' are"+             , "customarily expected to define a ring and have the following properties:"+             , ""+             , "[__Associativity of (+)__]: @(x + y) + z@ = @x + (y + z)@"+             , "[__Commutativity of (+)__]: @x + y@ = @y + x@"+             , "[__@fromInteger 0@ is the additive identity__]: @x + fromInteger 0@ = @x@"+             ]+          )+          (unlines+             [ ""+             , ""+             , "However,  `(+)`  and  `(*)`  are"+             , "customarily expected to define a ring and have the following properties: "+             , "+ ****Associativity of (+)****: `(x + y) + z`  =  `x + (y + z)`"+             , "+ ****Commutativity of (+)****: `x + y`  =  `y + x`"+             , "+ ****`fromInteger 0`  is the additive identity****: `x + fromInteger 0`  =  `x`"+             ]+          )+      , testCase "unsafePerformIO" $ checkHaddock+          (unlines+             [ "may require"+             , "different precautions:"+             , ""+             , "  * Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@"+             , "        that calls 'unsafePerformIO'.  If the call is inlined,"+             , "        the I\\/O may be performed more than once."+             , ""+             , "  * Use the compiler flag @-fno-cse@ to prevent common sub-expression"+             , "        elimination being performed on the module."+             , ""+             ]+          )+          (unlines+             [ ""+             , ""+             , "may require"+             , "different precautions: "+             , "+ Use  `{-# NOINLINE foo #-}`  as a pragma on any function  `foo` "+             , "  that calls  `unsafePerformIO` .  If the call is inlined,"+             , "  the I/O may be performed more than once."+             , ""+             , "+ Use the compiler flag  `-fno-cse`  to prevent common sub-expression"+             , "  elimination being performed on the module."+             , ""+             ]+          )+      , testCase "ordered list" $ checkHaddock+          (unlines+             [ "may require"+             , "different precautions:"+             , ""+             , "  1. Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@"+             , "        that calls 'unsafePerformIO'.  If the call is inlined,"+             , "        the I\\/O may be performed more than once."+             , ""+             , "  2. Use the compiler flag @-fno-cse@ to prevent common sub-expression"+             , "        elimination being performed on the module."+             , ""+             ]+          )+          (unlines+             [ ""+             , ""+             , "may require"+             , "different precautions: "+             , "1. Use  `{-# NOINLINE foo #-}`  as a pragma on any function  `foo` "+             , "  that calls  `unsafePerformIO` .  If the call is inlined,"+             , "  the I/O may be performed more than once."+             , ""+             , "2. Use the compiler flag  `-fno-cse`  to prevent common sub-expression"+             , "  elimination being performed on the module."+             , ""+             ]+          )+      ]+  where+    checkHaddock s txt = spanDocToMarkdownForTest s @?= txt
test/exe/HieDbRetry.hs view
@@ -1,22 +1,21 @@ {-# LANGUAGE MultiWayIf #-} module HieDbRetry (tests) where -import           Control.Concurrent.Extra     (Var, modifyVar, newVar, readVar,-                                               withVar)-import           Control.Exception            (ErrorCall (ErrorCall), evaluate,-                                               throwIO, tryJust)-import           Control.Monad.IO.Class       (MonadIO (liftIO))-import           Data.Tuple.Extra             (dupe)-import qualified Database.SQLite.Simple       as SQLite-import           Development.IDE.Session      (retryOnException,-                                               retryOnSqliteBusy)-import qualified Development.IDE.Session      as Session-import           Development.IDE.Types.Logger (Recorder (Recorder, logger_),-                                               WithPriority (WithPriority, payload),-                                               cmapWithPrio)-import qualified System.Random                as Random-import           Test.Tasty                   (TestTree, testGroup)-import           Test.Tasty.HUnit             (assertFailure, testCase, (@?=))+import           Control.Concurrent.Extra (Var, modifyVar, newVar, readVar,+                                           withVar)+import           Control.Exception        (ErrorCall (ErrorCall), evaluate,+                                           throwIO, tryJust)+import           Control.Monad.IO.Class   (MonadIO (liftIO))+import           Data.Tuple.Extra         (dupe)+import qualified Database.SQLite.Simple   as SQLite+import           Development.IDE.Session  (retryOnException, retryOnSqliteBusy)+import qualified Development.IDE.Session  as Session+import           Ide.Logger               (Recorder (Recorder, logger_),+                                           WithPriority (WithPriority, payload),+                                           cmapWithPrio)+import qualified System.Random            as Random+import           Test.Tasty               (TestTree, testGroup)+import           Test.Tasty.HUnit         (assertFailure, testCase, (@?=))  data Log   = LogSession Session.Log
+ test/exe/HighlightTests.hs view
@@ -0,0 +1,85 @@++module HighlightTests (tests) where++import           Control.Monad.IO.Class         (liftIO)+import qualified Data.Text                      as T+import           Development.IDE.GHC.Compat     (GhcVersion (..), ghcVersion)+import           Development.IDE.Types.Location+import           Language.LSP.Protocol.Types    hiding+                                                (SemanticTokenAbsolute (..),+                                                 SemanticTokenRelative (..),+                                                 SemanticTokensEdit (..),+                                                 mkRange)+import           Language.LSP.Test+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup "highlight"+  [ testSessionWait "value" $ do+    doc <- createDoc "A.hs" "haskell" source+    _ <- waitForDiagnostics+    highlights <- getHighlights doc (Position 3 2)+    liftIO $ highlights @?=+            [ DocumentHighlight (R 2 0 2 3) (Just DocumentHighlightKind_Read)+            , DocumentHighlight (R 3 0 3 3) (Just DocumentHighlightKind_Write)+            , DocumentHighlight (R 4 6 4 9) (Just DocumentHighlightKind_Read)+            , DocumentHighlight (R 5 22 5 25) (Just DocumentHighlightKind_Read)+            ]+  , testSessionWait "type" $ do+    doc <- createDoc "A.hs" "haskell" source+    _ <- waitForDiagnostics+    highlights <- getHighlights doc (Position 2 8)+    liftIO $ highlights @?=+            [ DocumentHighlight (R 2 7 2 10) (Just DocumentHighlightKind_Read)+            , DocumentHighlight (R 3 11 3 14) (Just DocumentHighlightKind_Read)+            ]+  , testSessionWait "local" $ do+    doc <- createDoc "A.hs" "haskell" source+    _ <- waitForDiagnostics+    highlights <- getHighlights doc (Position 6 5)+    liftIO $ highlights @?=+            [ DocumentHighlight (R 6 4 6 7) (Just DocumentHighlightKind_Write)+            , DocumentHighlight (R 6 10 6 13) (Just DocumentHighlightKind_Read)+            , DocumentHighlight (R 7 12 7 15) (Just DocumentHighlightKind_Read)+            ]+  , knownBrokenForGhcVersions [GHC90, GHC92, GHC94, GHC96] "Ghc9 highlights the constructor and not just this field" $+        testSessionWait "record" $ do+        doc <- createDoc "A.hs" "haskell" recsource+        _ <- waitForDiagnostics+        highlights <- getHighlights doc (Position 4 15)+        liftIO $ highlights @?=+          -- Span is just the .. on 8.10, but Rec{..} before+          [ if ghcVersion >= GHC810+            then DocumentHighlight (R 4 8 4 10) (Just DocumentHighlightKind_Write)+            else DocumentHighlight (R 4 4 4 11) (Just DocumentHighlightKind_Write)+          , DocumentHighlight (R 4 14 4 20) (Just DocumentHighlightKind_Read)+          ]+        highlights <- getHighlights doc (Position 3 17)+        liftIO $ highlights @?=+          [ DocumentHighlight (R 3 17 3 23) (Just DocumentHighlightKind_Write)+          -- Span is just the .. on 8.10, but Rec{..} before+          , if ghcVersion >= GHC810+              then DocumentHighlight (R 4 8 4 10) (Just DocumentHighlightKind_Read)+              else DocumentHighlight (R 4 4 4 11) (Just DocumentHighlightKind_Read)+          ]+  ]+  where+    source = T.unlines+      ["{-# OPTIONS_GHC -Wunused-binds #-}"+      ,"module Highlight () where"+      ,"foo :: Int"+      ,"foo = 3 :: Int"+      ,"bar = foo"+      ,"  where baz = let x = foo in x"+      ,"baz arg = arg + x"+      ,"  where x = arg"+      ]+    recsource = T.unlines+      ["{-# LANGUAGE RecordWildCards #-}"+      ,"{-# OPTIONS_GHC -Wunused-binds #-}"+      ,"module Highlight () where"+      ,"data Rec = Rec { field1 :: Int, field2 :: Char }"+      ,"foo Rec{..} = field2 + field1"+      ]
+ test/exe/IfaceTests.hs view
@@ -0,0 +1,163 @@++{-# LANGUAGE OverloadedLabels #-}++module IfaceTests (tests) where++import           Control.Monad.IO.Class        (liftIO)+import           Data.Row+import qualified Data.Text                     as T+import           Development.IDE.GHC.Util+import           Development.IDE.Test          (configureCheckProject,+                                                expectDiagnostics,+                                                expectNoMoreDiagnostics,+                                                getInterfaceFilesDir)+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types   hiding+                                               (SemanticTokenAbsolute (..),+                                                SemanticTokenRelative (..),+                                                SemanticTokensEdit (..),+                                                mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.FilePath+import           System.IO.Extra               hiding (withTempDir)+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup "Interface loading tests"+    [ -- https://github.com/haskell/ghcide/pull/645/+      ifaceErrorTest+    , ifaceErrorTest2+    , ifaceErrorTest3+    , ifaceTHTest+    ]+++-- | test that TH reevaluates across interfaces+ifaceTHTest :: TestTree+ifaceTHTest = testCase "iface-th-test" $ runWithExtraFiles "TH" $ \dir -> do+    let aPath = dir </> "THA.hs"+        bPath = dir </> "THB.hs"+        cPath = dir </> "THC.hs"++    aSource <- liftIO $ readFileUtf8 aPath -- [TH] a :: ()+    _bSource <- liftIO $ readFileUtf8 bPath -- a :: ()+    cSource <- liftIO $ readFileUtf8 cPath -- c = a :: ()++    cdoc <- createDoc cPath "haskell" cSource++    -- Change [TH]a from () to Bool+    liftIO $ writeFileUTF8 aPath (unlines $ init (lines $ T.unpack aSource) ++ ["th_a = [d| a = False|]"])++    -- Check that the change propagates to C+    changeDoc cdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ cSource]+    expectDiagnostics+      [("THC.hs", [(DiagnosticSeverity_Error, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'")])+      ,("THB.hs", [(DiagnosticSeverity_Warning, (4,thDollarIdx), "Top-level binding")])]+    closeDoc cdoc++ifaceErrorTest :: TestTree+ifaceErrorTest = testCase "iface-error-test-1" $ runWithExtraFiles "recomp" $ \dir -> do+    configureCheckProject True+    let bPath = dir </> "B.hs"+        pPath = dir </> "P.hs"++    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int+    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int++    bdoc <- createDoc bPath "haskell" bSource+    expectDiagnostics+      [("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])] -- So what we know P has been loaded++    -- Change y from Int to B+    changeDoc bdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]+    -- save so that we can that the error propagates to A+    sendNotification SMethod_TextDocumentDidSave (DidSaveTextDocumentParams bdoc Nothing)+++    -- Check that the error propagates to A+    expectDiagnostics+      [("A.hs", [(DiagnosticSeverity_Error, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])]++    -- Check that we wrote the interfaces for B when we saved+    hidir <- getInterfaceFilesDir bdoc+    hi_exists <- liftIO $ doesFileExist $ hidir </> "B.hi"+    liftIO $ assertBool ("Couldn't find B.hi in " ++ hidir) hi_exists++    pdoc <- openDoc pPath "haskell"+    expectDiagnostics+      [("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])+      ]+    changeDoc pdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ pSource <> "\nfoo = y :: Bool" ]+    -- Now in P we have+    -- bar = x :: Int+    -- foo = y :: Bool+    -- HOWEVER, in A...+    -- x = y  :: Int+    -- This is clearly inconsistent, and the expected outcome a bit surprising:+    --   - The diagnostic for A has already been received. Ghcide does not repeat diagnostics+    --   - P is being typechecked with the last successful artifacts for A.+    expectDiagnostics+      [("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])+      ,("P.hs", [(DiagnosticSeverity_Warning,(6,0), "Top-level binding")])+      ]+    expectNoMoreDiagnostics 2++ifaceErrorTest2 :: TestTree+ifaceErrorTest2 = testCase "iface-error-test-2" $ runWithExtraFiles "recomp" $ \dir -> do+    let bPath = dir </> "B.hs"+        pPath = dir </> "P.hs"++    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int+    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int++    bdoc <- createDoc bPath "haskell" bSource+    pdoc <- createDoc pPath "haskell" pSource+    expectDiagnostics+      [("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])] -- So that we know P has been loaded++    -- Change y from Int to B+    changeDoc bdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]++    -- Add a new definition to P+    changeDoc pdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ pSource <> "\nfoo = y :: Bool" ]+    -- Now in P we have+    -- bar = x :: Int+    -- foo = y :: Bool+    -- HOWEVER, in A...+    -- x = y  :: Int+    expectDiagnostics+    -- As in the other test, P is being typechecked with the last successful artifacts for A+    -- (ot thanks to -fdeferred-type-errors)+      [("A.hs", [(DiagnosticSeverity_Error, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])+      ,("P.hs", [(DiagnosticSeverity_Warning, (4, 0), "Top-level binding")])+      ,("P.hs", [(DiagnosticSeverity_Warning, (6, 0), "Top-level binding")])+      ]++    expectNoMoreDiagnostics 2++ifaceErrorTest3 :: TestTree+ifaceErrorTest3 = testCase "iface-error-test-3" $ runWithExtraFiles "recomp" $ \dir -> do+    let bPath = dir </> "B.hs"+        pPath = dir </> "P.hs"++    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int+    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int++    bdoc <- createDoc bPath "haskell" bSource++    -- Change y from Int to B+    changeDoc bdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]++    -- P should not typecheck, as there are no last valid artifacts for A+    _pdoc <- createDoc pPath "haskell" pSource++    -- In this example the interface file for A should not exist (modulo the cache folder)+    -- Despite that P still type checks, as we can generate an interface file for A thanks to -fdeferred-type-errors+    expectDiagnostics+      [("A.hs", [(DiagnosticSeverity_Error, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])+      ,("P.hs", [(DiagnosticSeverity_Warning,(4,0), "Top-level binding")])+      ]+    expectNoMoreDiagnostics 2
+ test/exe/InitializeResponseTests.hs view
@@ -0,0 +1,97 @@++{-# LANGUAGE DataKinds        #-}+{-# LANGUAGE OverloadedLabels #-}++module InitializeResponseTests (tests) where++import           Control.Monad+import           Data.List.Extra+import           Data.Row+import qualified Data.Text                         as T+import           Development.IDE.Plugin.TypeLenses (typeLensCommandId)+import qualified Language.LSP.Protocol.Lens        as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types       hiding+                                                   (SemanticTokenAbsolute (..),+                                                    SemanticTokenRelative (..),+                                                    SemanticTokensEdit (..),+                                                    mkRange)+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Control.Lens                      ((^.))+import           Development.IDE.Plugin.Test       (blockCommandId)+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = withResource acquire release tests where++  -- these tests document and monitor the evolution of the+  -- capabilities announced by the server in the initialize+  -- response. Currently the server advertises almost no capabilities+  -- at all, in some cases failing to announce capabilities that it+  -- actually does provide! Hopefully this will change ...+  tests :: IO (TResponseMessage Method_Initialize) -> TestTree+  tests getInitializeResponse =+    testGroup "initialize response capabilities"+    [ chk "   text doc sync"             _textDocumentSync  tds+    , chk "   hover"                         _hoverProvider (Just $ InL True)+    , chk "   completion"               _completionProvider (Just $ CompletionOptions Nothing (Just ["."]) Nothing (Just True) Nothing)+    , chk "NO signature help"        _signatureHelpProvider Nothing+    , chk "   goto definition"          _definitionProvider (Just $ InL True)+    , chk "   goto type definition" _typeDefinitionProvider (Just $ InL True)+    -- BUG in lsp-test, this test fails, just change the accepted response+    -- for now+    , chk "NO goto implementation"  _implementationProvider (Just $ InL False)+    , chk "   find references"          _referencesProvider (Just $ InL True)+    , chk "   doc highlight"     _documentHighlightProvider (Just $ InL True)+    , chk "   doc symbol"           _documentSymbolProvider (Just $ InL True)+    , chk "   workspace symbol"    _workspaceSymbolProvider (Just $ InL True)+    , chk "   code action"             _codeActionProvider  (Just $ InL False)+    , chk "   code lens"                 _codeLensProvider  (Just $ CodeLensOptions (Just False) (Just False))+    , chk "NO doc formatting"   _documentFormattingProvider (Just $ InL False)+    , chk "NO doc range formatting"+                           _documentRangeFormattingProvider (Just $ InL False)+    , chk "NO doc formatting on typing"+                          _documentOnTypeFormattingProvider Nothing+    , chk "NO renaming"                     _renameProvider (Just $ InL False)+    , chk "NO doc link"               _documentLinkProvider Nothing+    , chk "NO color"                   (^. L.colorProvider) (Just $ InL False)+    , chk "NO folding range"          _foldingRangeProvider (Just $ InL False)+    , che "   execute command"      _executeCommandProvider [typeLensCommandId, blockCommandId]+    , chk "   workspace"                   (^. L.workspace) (Just $ #workspaceFolders .== Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )}+                                                                 .+ #fileOperations   .== Nothing)+    , chk "NO experimental"             (^. L.experimental) Nothing+    ] where++      tds = Just (InL (TextDocumentSyncOptions+                              { _openClose = Just True+                              , _change    = Just TextDocumentSyncKind_Incremental+                              , _willSave  = Nothing+                              , _willSaveWaitUntil = Nothing+                              , _save = Just (InR $ SaveOptions {_includeText = Nothing})}))++      chk :: (Eq a, Show a) => TestName -> (ServerCapabilities -> a) -> a -> TestTree+      chk title getActual expected =+        testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir++      che :: TestName -> (ServerCapabilities -> Maybe ExecuteCommandOptions) -> [T.Text] -> TestTree+      che title getActual expected = testCase title doTest+        where+            doTest = do+                ir <- getInitializeResponse+                let Just ExecuteCommandOptions {_commands = commands} = getActual $ innerCaps ir+                    commandNames = (!! 2) . T.splitOn ":" <$> commands+                zipWithM_ (\e o -> T.isSuffixOf e o @? show (e,o)) (sort expected) (sort commandNames)++  innerCaps :: TResponseMessage Method_Initialize -> ServerCapabilities+  innerCaps (TResponseMessage _ _ (Right (InitializeResult c _))) = c+  innerCaps (TResponseMessage _ _ (Left _)) = error "Initialization error"++  acquire :: IO (TResponseMessage Method_Initialize)+  acquire = run initializeResponse++  release :: TResponseMessage Method_Initialize -> IO ()+  release = const $ pure ()+
+ test/exe/LogType.hs view
@@ -0,0 +1,21 @@+module LogType (Log(..)) where++import qualified Development.IDE.LSP.Notifications as Notifications+import qualified Development.IDE.Main              as IDE+import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide++import           Ide.Logger                        (Pretty (pretty))+import           Language.LSP.VFS                  (VfsLog)++data Log+  = LogGhcIde Ghcide.Log+  | LogIDEMain IDE.Log+  | LogVfs VfsLog+  | LogNotifications Notifications.Log++instance Pretty Log where+  pretty = \case+    LogGhcIde log  -> pretty log+    LogIDEMain log -> pretty log+    LogVfs log     -> pretty log+    LogNotifications log -> pretty log
test/exe/Main.hs view
@@ -26,3603 +26,102 @@     Therefore, avoid mixing both progress reports and diagnostics in the same test  -} -{-# LANGUAGE AllowAmbiguousTypes   #-}-{-# LANGUAGE CPP                   #-}-{-# LANGUAGE DataKinds             #-}-{-# LANGUAGE DuplicateRecordFields #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE ImplicitParams        #-}-{-# LANGUAGE MultiWayIf            #-}-{-# LANGUAGE PatternSynonyms       #-}-{-# LANGUAGE PolyKinds             #-}-{-# LANGUAGE TypeOperators         #-}-{-# OPTIONS_GHC -Wno-deprecations -Wno-unticked-promoted-constructors #-}--module Main (main) where--import           Control.Applicative.Combinators-import           Control.Concurrent-import           Control.Exception                        (bracket_, catch,-                                                           finally)-import qualified Control.Lens                             as Lens-import           Control.Monad-import           Control.Monad.IO.Class                   (MonadIO, liftIO)-import           Data.Aeson                               (toJSON)-import qualified Data.Aeson                               as A-import           Data.Default-import           Data.Foldable-import           Data.List.Extra-import           Data.Maybe-import qualified Data.Set                                 as Set-import qualified Data.Text                                as T-import           Data.Text.Utf16.Rope                     (Rope)-import qualified Data.Text.Utf16.Rope                     as Rope-import           Development.IDE.Core.PositionMapping     (PositionResult (..),-                                                           fromCurrent,-                                                           positionResultToMaybe,-                                                           toCurrent)-import           Development.IDE.GHC.Compat               (GhcVersion (..),-                                                           ghcVersion)-import           Development.IDE.GHC.Util-import qualified Development.IDE.Main                     as IDE-import           Development.IDE.Plugin.TypeLenses        (typeLensCommandId)-import           Development.IDE.Spans.Common-import           Development.IDE.Test                     (Cursor,-                                                           canonicalizeUri,-                                                           configureCheckProject,-                                                           diagnostic,-                                                           expectCurrentDiagnostics,-                                                           expectDiagnostics,-                                                           expectDiagnosticsWithTags,-                                                           expectNoMoreDiagnostics,-                                                           flushMessages,-                                                           getInterfaceFilesDir,-                                                           getStoredKeys,-                                                           isReferenceReady,-                                                           referenceReady,-                                                           standardizeQuotes,-                                                           waitForAction,-                                                           waitForGC,-                                                           waitForTypecheck)-import           Development.IDE.Test.Runfiles-import qualified Development.IDE.Types.Diagnostics        as Diagnostics-import           Development.IDE.Types.Location-import           Development.Shake                        (getDirectoryFilesIO)-import           Ide.Plugin.Config-import           Language.LSP.Test-import           Language.LSP.Types                       hiding-                                                          (SemanticTokenAbsolute (length, line),-                                                           SemanticTokenRelative (length),-                                                           SemanticTokensEdit (_start),-                                                           mkRange)-import           Language.LSP.Types.Capabilities-import qualified Language.LSP.Types.Lens                  as Lens (label)-import qualified Language.LSP.Types.Lens                  as Lsp (diagnostics,-                                                                  message,-                                                                  params)-import           Language.LSP.VFS                         (VfsLog, applyChange)-import           Network.URI-import           System.Directory-import           System.Environment.Blank                 (getEnv, setEnv,-                                                           unsetEnv)-import           System.Exit                              (ExitCode (ExitSuccess))-import           System.FilePath-import           System.Info.Extra                        (isMac, isWindows)-import qualified System.IO.Extra-import           System.IO.Extra                          hiding (withTempDir)-import           System.Mem                               (performGC)-import           System.Process.Extra                     (CreateProcess (cwd),-                                                           createPipe, proc,-                                                           readCreateProcessWithExitCode)-import           Test.QuickCheck--- import Test.QuickCheck.Instances ()-import           Control.Concurrent.Async-import           Control.Lens                             (to, (.~), (^.))-import           Control.Monad.Extra                      (whenJust)-import           Data.Function                            ((&))-import           Data.Functor.Identity                    (runIdentity)-import           Data.IORef-import           Data.IORef.Extra                         (atomicModifyIORef_)-import           Data.String                              (IsString (fromString))-import           Data.Tuple.Extra-import           Development.IDE.Core.FileStore           (getModTime)-import qualified Development.IDE.Plugin.HLS.GhcIde        as Ghcide-import           Development.IDE.Plugin.Test              (TestRequest (BlockSeconds),-                                                           WaitForIdeRuleResult (..),-                                                           blockCommandId)-import           Development.IDE.Types.Logger             (Logger (Logger),-                                                           LoggingColumn (DataColumn, PriorityColumn),-                                                           Pretty (pretty),-                                                           Priority (Debug),-                                                           Recorder (Recorder, logger_),-                                                           WithPriority (WithPriority, priority),-                                                           cfilter,-                                                           cmapWithPrio,-                                                           makeDefaultStderrRecorder,-                                                           toCologActionWithPrio)-import qualified FuzzySearch-import           GHC.Stack                                (emptyCallStack)-import qualified HieDbRetry-import           Ide.PluginUtils                          (pluginDescToIdePlugins)-import           Ide.Types-import qualified Language.LSP.Types                       as LSP-import           Language.LSP.Types.Lens                  (didChangeWatchedFiles,-                                                           workspace)-import qualified Language.LSP.Types.Lens                  as L-import qualified Progress-import           System.Time.Extra-import qualified Test.QuickCheck.Monadic                  as MonadicQuickCheck-import           Test.QuickCheck.Monadic                  (forAllM, monadicIO)-import           Test.Tasty-import           Test.Tasty.ExpectedFailure-import           Test.Tasty.HUnit-import           Test.Tasty.Ingredients.Rerun-import           Test.Tasty.QuickCheck-import           Text.Printf                              (printf)-import           Text.Regex.TDFA                          ((=~))--data Log-  = LogGhcIde Ghcide.Log-  | LogIDEMain IDE.Log-  | LogVfs VfsLog--instance Pretty Log where-  pretty = \case-    LogGhcIde log  -> pretty log-    LogIDEMain log -> pretty log-    LogVfs log     -> pretty log---- | Wait for the next progress begin step-waitForProgressBegin :: Session ()-waitForProgressBegin = skipManyTill anyMessage $ satisfyMaybe $ \case-  FromServerMess SProgress (NotificationMessage _ _ (ProgressParams _ (Begin _))) -> Just ()-  _ -> Nothing---- | Wait for the first progress end step--- Also implemented in hls-test-utils Test.Hls-waitForProgressDone :: Session ()-waitForProgressDone = skipManyTill anyMessage $ satisfyMaybe $ \case-  FromServerMess SProgress (NotificationMessage _ _ (ProgressParams _ (End _))) -> Just ()-  _ -> Nothing---- | Wait for all progress to be done--- Needs at least one progress done notification to return--- Also implemented in hls-test-utils Test.Hls-waitForAllProgressDone :: Session ()-waitForAllProgressDone = loop-  where-    loop = do-      ~() <- skipManyTill anyMessage $ satisfyMaybe $ \case-        FromServerMess SProgress (NotificationMessage _ _ (ProgressParams _ (End _))) -> Just ()-        _ -> Nothing-      done <- null <$> getIncompleteProgressSessions-      unless done loop--main :: IO ()-main = do-  docWithPriorityRecorder <- makeDefaultStderrRecorder (Just [PriorityColumn, DataColumn])--  let docWithFilteredPriorityRecorder@Recorder{ logger_ } =-        docWithPriorityRecorder-        & cfilter (\WithPriority{ priority } -> priority >= Debug)--  -- exists so old-style logging works. intended to be phased out-  let logger = Logger $ \p m -> logger_ (WithPriority p emptyCallStack (pretty m))--  let recorder = docWithFilteredPriorityRecorder-               & cmapWithPrio pretty--  -- We mess with env vars so run single-threaded.-  defaultMainWithRerun $ testGroup "ghcide"-    [ testSession "open close" $ do-        doc <- createDoc "Testing.hs" "haskell" ""-        void (skipManyTill anyMessage $ message SWindowWorkDoneProgressCreate)-        waitForProgressBegin-        closeDoc doc-        waitForProgressDone-    , initializeResponseTests-    , completionTests-    , cppTests-    , diagnosticTests-    , codeLensesTests-    , outlineTests-    , highlightTests-    , findDefinitionAndHoverTests-    , pluginSimpleTests-    , pluginParsedResultTests-    , preprocessorTests-    , thTests-    , symlinkTests-    , safeTests-    , unitTests recorder logger-    , haddockTests-    , positionMappingTests recorder-    , watchedFilesTests-    , cradleTests-    , dependentFileTest-    , nonLspCommandLine-    , ifaceTests-    , bootTests-    , rootUriTests-    , asyncTests-    , clientSettingsTest-    , referenceTests-    , garbageCollectionTests-    , HieDbRetry.tests-    ]--initializeResponseTests :: TestTree-initializeResponseTests = withResource acquire release tests where--  -- these tests document and monitor the evolution of the-  -- capabilities announced by the server in the initialize-  -- response. Currently the server advertises almost no capabilities-  -- at all, in some cases failing to announce capabilities that it-  -- actually does provide! Hopefully this will change ...-  tests :: IO (ResponseMessage Initialize) -> TestTree-  tests getInitializeResponse =-    testGroup "initialize response capabilities"-    [ chk "   text doc sync"             _textDocumentSync  tds-    , chk "   hover"                         _hoverProvider (Just $ InL True)-    , chk "   completion"               _completionProvider (Just $ CompletionOptions Nothing (Just ["."]) Nothing (Just True))-    , chk "NO signature help"        _signatureHelpProvider Nothing-    , chk "   goto definition"          _definitionProvider (Just $ InL True)-    , chk "   goto type definition" _typeDefinitionProvider (Just $ InL True)-    -- BUG in lsp-test, this test fails, just change the accepted response-    -- for now-    , chk "NO goto implementation"  _implementationProvider (Just $ InL False)-    , chk "   find references"          _referencesProvider (Just $ InL True)-    , chk "   doc highlight"     _documentHighlightProvider (Just $ InL True)-    , chk "   doc symbol"           _documentSymbolProvider (Just $ InL True)-    , chk "   workspace symbol"    _workspaceSymbolProvider (Just $ InL True)-    , chk "   code action"             _codeActionProvider  (Just $ InL False)-    , chk "   code lens"                 _codeLensProvider  (Just $ CodeLensOptions (Just False) (Just False))-    , chk "NO doc formatting"   _documentFormattingProvider (Just $ InL False)-    , chk "NO doc range formatting"-                           _documentRangeFormattingProvider (Just $ InL False)-    , chk "NO doc formatting on typing"-                          _documentOnTypeFormattingProvider Nothing-    , chk "NO renaming"                     _renameProvider (Just $ InL False)-    , chk "NO doc link"               _documentLinkProvider Nothing-    , chk "NO color"                   (^. L.colorProvider) (Just $ InL False)-    , chk "NO folding range"          _foldingRangeProvider (Just $ InL False)-    , che "   execute command"      _executeCommandProvider [typeLensCommandId, blockCommandId]-    , chk "   workspace"                   (^. L.workspace) (Just $ WorkspaceServerCapabilities (Just WorkspaceFoldersServerCapabilities{_supported = Just True, _changeNotifications = Just ( InR True )}))-    , chk "NO experimental"             (^. L.experimental) Nothing-    ] where--      tds = Just (InL (TextDocumentSyncOptions-                              { _openClose = Just True-                              , _change    = Just TdSyncIncremental-                              , _willSave  = Nothing-                              , _willSaveWaitUntil = Nothing-                              , _save = Just (InR $ SaveOptions {_includeText = Nothing})}))--      chk :: (Eq a, Show a) => TestName -> (ServerCapabilities -> a) -> a -> TestTree-      chk title getActual expected =-        testCase title $ getInitializeResponse >>= \ir -> expected @=? (getActual . innerCaps) ir--      che :: TestName -> (ServerCapabilities -> Maybe ExecuteCommandOptions) -> [T.Text] -> TestTree-      che title getActual expected = testCase title doTest-        where-            doTest = do-                ir <- getInitializeResponse-                let Just ExecuteCommandOptions {_commands = List commands} = getActual $ innerCaps ir-                    commandNames = (!! 2) . T.splitOn ":" <$> commands-                zipWithM_ (\e o -> T.isSuffixOf e o @? show (e,o)) (sort expected) (sort commandNames)--  innerCaps :: ResponseMessage Initialize -> ServerCapabilities-  innerCaps (ResponseMessage _ _ (Right (InitializeResult c _))) = c-  innerCaps (ResponseMessage _ _ (Left _)) = error "Initialization error"--  acquire :: IO (ResponseMessage Initialize)-  acquire = run initializeResponse--  release :: ResponseMessage Initialize -> IO ()-  release = const $ pure ()---diagnosticTests :: TestTree-diagnosticTests = testGroup "diagnostics"-  [ testSessionWait "fix syntax error" $ do-      let content = T.unlines [ "module Testing wher" ]-      doc <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]-      let change = TextDocumentContentChangeEvent-            { _range = Just (Range (Position 0 15) (Position 0 19))-            , _rangeLength = Nothing-            , _text = "where"-            }-      changeDoc doc [change]-      expectDiagnostics [("Testing.hs", [])]-  , testSessionWait "introduce syntax error" $ do-      let content = T.unlines [ "module Testing where" ]-      doc <- createDoc "Testing.hs" "haskell" content-      void $ skipManyTill anyMessage (message SWindowWorkDoneProgressCreate)-      waitForProgressBegin-      let change = TextDocumentContentChangeEvent-            { _range = Just (Range (Position 0 15) (Position 0 18))-            , _rangeLength = Nothing-            , _text = "wher"-            }-      changeDoc doc [change]-      expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "parse error")])]-  , testSessionWait "update syntax error" $ do-      let content = T.unlines [ "module Testing(missing) where" ]-      doc <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "Not in scope: 'missing'")])]-      let change = TextDocumentContentChangeEvent-            { _range = Just (Range (Position 0 15) (Position 0 16))-            , _rangeLength = Nothing-            , _text = "l"-            }-      changeDoc doc [change]-      expectDiagnostics [("Testing.hs", [(DsError, (0, 15), "Not in scope: 'lissing'")])]-  , testSessionWait "variable not in scope" $ do-      let content = T.unlines-            [ "module Testing where"-            , "foo :: Int -> Int -> Int"-            , "foo a _b = a + ab"-            , "bar :: Int -> Int -> Int"-            , "bar _a b = cd + b"-            ]-      _ <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics-        [ ( "Testing.hs"-          , [ (DsError, (2, 15), "Variable not in scope: ab")-            , (DsError, (4, 11), "Variable not in scope: cd")-            ]-          )-        ]-  , testSessionWait "type error" $ do-      let content = T.unlines-            [ "module Testing where"-            , "foo :: Int -> String -> Int"-            , "foo a b = a + b"-            ]-      _ <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics-        [ ( "Testing.hs"-          , [(DsError, (2, 14), "Couldn't match type '[Char]' with 'Int'")]-          )-        ]-  , testSessionWait "typed hole" $ do-      let content = T.unlines-            [ "module Testing where"-            , "foo :: Int -> String"-            , "foo a = _ a"-            ]-      _ <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics-        [ ( "Testing.hs"-          , [(DsError, (2, 8), "Found hole: _ :: Int -> String")]-          )-        ]--  , testGroup "deferral" $-    let sourceA a = T.unlines-          [ "module A where"-          , "a :: Int"-          , "a = " <> a]-        sourceB = T.unlines-          [ "module B where"-          , "import A ()"-          , "b :: Float"-          , "b = True"]-        bMessage = "Couldn't match expected type 'Float' with actual type 'Bool'"-        expectedDs aMessage =-          [ ("A.hs", [(DsError, (2,4), aMessage)])-          , ("B.hs", [(DsError, (3,4), bMessage)])]-        deferralTest title binding msg = testSessionWait title $ do-          _ <- createDoc "A.hs" "haskell" $ sourceA binding-          _ <- createDoc "B.hs" "haskell"   sourceB-          expectDiagnostics $ expectedDs msg-    in-    [ deferralTest "type error"          "True"    "Couldn't match expected type"-    , deferralTest "typed hole"          "_"       "Found hole"-    , deferralTest "out of scope var"    "unbound" "Variable not in scope"-    ]--  , testSessionWait "remove required module" $ do-      let contentA = T.unlines [ "module ModuleA where" ]-      docA <- createDoc "ModuleA.hs" "haskell" contentA-      let contentB = T.unlines-            [ "module ModuleB where"-            , "import ModuleA"-            ]-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      let change = TextDocumentContentChangeEvent-            { _range = Just (Range (Position 0 0) (Position 0 20))-            , _rangeLength = Nothing-            , _text = ""-            }-      changeDoc docA [change]-      expectDiagnostics [("ModuleB.hs", [(DsError, (1, 0), "Could not find module")])]-  , testSessionWait "add missing module" $ do-      let contentB = T.unlines-            [ "module ModuleB where"-            , "import ModuleA ()"-            ]-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      expectDiagnostics [("ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]-      let contentA = T.unlines [ "module ModuleA where" ]-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      expectDiagnostics [("ModuleB.hs", [])]-  , testCase "add missing module (non workspace)" $-    -- By default lsp-test sends FileWatched notifications for all files, which we don't want-    -- as non workspace modules will not be watched by the LSP server.-    -- To work around this, we tell lsp-test that our client doesn't have the-    -- FileWatched capability, which is enough to disable the notifications-    withTempDir $ \tmpDir -> runInDir'' lspTestCapsNoFileWatches tmpDir "." "." [] $ do-      let contentB = T.unlines-            [ "module ModuleB where"-            , "import ModuleA ()"-            ]-      _ <- createDoc (tmpDir </> "ModuleB.hs") "haskell" contentB-      expectDiagnostics [(tmpDir </> "ModuleB.hs", [(DsError, (1, 7), "Could not find module")])]-      let contentA = T.unlines [ "module ModuleA where" ]-      _ <- createDoc (tmpDir </> "ModuleA.hs") "haskell" contentA-      expectDiagnostics [(tmpDir </> "ModuleB.hs", [])]-  , testSessionWait "cyclic module dependency" $ do-      let contentA = T.unlines-            [ "module ModuleA where"-            , "import ModuleB"-            ]-      let contentB = T.unlines-            [ "module ModuleB where"-            , "import ModuleA"-            ]-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      expectDiagnostics-        [ ( "ModuleA.hs"-          , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]-          )-        , ( "ModuleB.hs"-          , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]-          )-        ]-  , testSession' "deeply nested cyclic module dependency" $ \path -> do-      let contentA = unlines-            [ "module ModuleA where" , "import ModuleB" ]-      let contentB = unlines-            [ "module ModuleB where" , "import ModuleA" ]-      let contentC = unlines-            [ "module ModuleC where" , "import ModuleB" ]-      let contentD = T.unlines-            [ "module ModuleD where" , "import ModuleC" ]-          cradle =-            "cradle: {direct: {arguments: [ModuleA, ModuleB, ModuleC, ModuleD]}}"-      liftIO $ writeFile (path </> "ModuleA.hs") contentA-      liftIO $ writeFile (path </> "ModuleB.hs") contentB-      liftIO $ writeFile (path </> "ModuleC.hs") contentC-      liftIO $ writeFile (path </> "hie.yaml") cradle-      _ <- createDoc "ModuleD.hs" "haskell" contentD-      expectDiagnostics-        [ ( "ModuleB.hs"-          , [(DsError, (1, 7), "Cyclic module dependency between ModuleA, ModuleB")]-          )-        ]-  , testSessionWait "cyclic module dependency with hs-boot" $ do-      let contentA = T.unlines-            [ "module ModuleA where"-            , "import {-# SOURCE #-} ModuleB"-            ]-      let contentB = T.unlines-            [ "{-# OPTIONS -Wmissing-signatures#-}"-            , "module ModuleB where"-            , "import ModuleA"-            -- introduce an artificial diagnostic-            , "foo = ()"-            ]-      let contentBboot = T.unlines-            [ "module ModuleB where"-            ]-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      _ <- createDoc "ModuleB.hs-boot" "haskell" contentBboot-      expectDiagnostics [("ModuleB.hs", [(DsWarning, (3,0), "Top-level binding")])]-  , testSessionWait "correct reference used with hs-boot" $ do-      let contentB = T.unlines-            [ "module ModuleB where"-            , "import {-# SOURCE #-} ModuleA()"-            ]-      let contentA = T.unlines-            [ "module ModuleA where"-            , "import ModuleB()"-            , "x = 5"-            ]-      let contentAboot = T.unlines-            [ "module ModuleA where"-            ]-      let contentC = T.unlines-            [ "{-# OPTIONS -Wmissing-signatures #-}"-            , "module ModuleC where"-            , "import ModuleA"-            -- this reference will fail if it gets incorrectly-            -- resolved to the hs-boot file-            , "y = x"-            ]-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      _ <- createDoc "ModuleA.hs-boot" "haskell" contentAboot-      _ <- createDoc "ModuleC.hs" "haskell" contentC-      expectDiagnostics [("ModuleC.hs", [(DsWarning, (3,0), "Top-level binding")])]-  , testSessionWait "redundant import" $ do-      let contentA = T.unlines ["module ModuleA where"]-      let contentB = T.unlines-            [ "{-# OPTIONS_GHC -Wunused-imports #-}"-            , "module ModuleB where"-            , "import ModuleA"-            ]-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      expectDiagnosticsWithTags-        [ ( "ModuleB.hs"-          , [(DsWarning, (2, 0), "The import of 'ModuleA' is redundant", Just DtUnnecessary)]-          )-        ]-  , testSessionWait "redundant import even without warning" $ do-      let contentA = T.unlines ["module ModuleA where"]-      let contentB = T.unlines-            [ "{-# OPTIONS_GHC -Wno-unused-imports -Wmissing-signatures #-}"-            , "module ModuleB where"-            , "import ModuleA"-            -- introduce an artificial warning for testing purposes-            , "foo = ()"-            ]-      _ <- createDoc "ModuleA.hs" "haskell" contentA-      _ <- createDoc "ModuleB.hs" "haskell" contentB-      expectDiagnostics [("ModuleB.hs", [(DsWarning, (3,0), "Top-level binding")])]-  , testSessionWait "package imports" $ do-      let thisDataListContent = T.unlines-            [ "module Data.List where"-            , "x :: Integer"-            , "x = 123"-            ]-      let mainContent = T.unlines-            [ "{-# LANGUAGE PackageImports #-}"-            , "module Main where"-            , "import qualified \"this\" Data.List as ThisList"-            , "import qualified \"base\" Data.List as BaseList"-            , "useThis = ThisList.x"-            , "useBase = BaseList.map"-            , "wrong1 = ThisList.map"-            , "wrong2 = BaseList.x"-            , "main = pure ()"-            ]-      _ <- createDoc "Data/List.hs" "haskell" thisDataListContent-      _ <- createDoc "Main.hs" "haskell" mainContent-      expectDiagnostics-        [ ( "Main.hs"-          , [(DsError, (6, 9),-                if ghcVersion >= GHC96 then-                  "Variable not in scope: ThisList.map"-                else if ghcVersion >= GHC94 then-                  "Variable not in scope: map" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130-                else-                  "Not in scope: \8216ThisList.map\8217")-            ,(DsError, (7, 9),-                if ghcVersion >= GHC96 then-                  "Variable not in scope: BaseList.x"-                else if ghcVersion >= GHC94 then-                  "Variable not in scope: x" -- See https://gitlab.haskell.org/ghc/ghc/-/issues/22130-                else-                  "Not in scope: \8216BaseList.x\8217")-            ]-          )-        ]-  , testSessionWait "unqualified warnings" $ do-      let fooContent = T.unlines-            [ "{-# OPTIONS_GHC -Wredundant-constraints #-}"-            , "module Foo where"-            , "foo :: Ord a => a -> Int"-            , "foo _a = 1"-            ]-      _ <- createDoc "Foo.hs" "haskell" fooContent-      expectDiagnostics-        [ ( "Foo.hs"-      -- The test is to make sure that warnings contain unqualified names-      -- where appropriate. The warning should use an unqualified name 'Ord', not-      -- something like 'GHC.Classes.Ord'. The choice of redundant-constraints to-      -- test this is fairly arbitrary.-          , [(DsWarning, (2, if ghcVersion >= GHC94 then 7 else 0), "Redundant constraint: Ord a")-            ]-          )-        ]-    , testSessionWait "lower-case drive" $ do-          let aContent = T.unlines-                [ "module A.A where"-                , "import A.B ()"-                ]-              bContent = T.unlines-                [ "{-# OPTIONS_GHC -Wall #-}"-                , "module A.B where"-                , "import Data.List"-                ]-          uriB <- getDocUri "A/B.hs"-          Just pathB <- pure $ uriToFilePath uriB-          uriB <- pure $-              let (drive, suffix) = splitDrive pathB-              in filePathToUri (joinDrive (lower drive) suffix)-          liftIO $ createDirectoryIfMissing True (takeDirectory pathB)-          liftIO $ writeFileUTF8 pathB $ T.unpack bContent-          uriA <- getDocUri "A/A.hs"-          Just pathA <- pure $ uriToFilePath uriA-          uriA <- pure $-              let (drive, suffix) = splitDrive pathA-              in filePathToUri (joinDrive (lower drive) suffix)-          let itemA = TextDocumentItem uriA "haskell" 0 aContent-          let a = TextDocumentIdentifier uriA-          sendNotification STextDocumentDidOpen (DidOpenTextDocumentParams itemA)-          NotificationMessage{_params = PublishDiagnosticsParams fileUri _ diags} <- skipManyTill anyMessage diagnostic-          -- Check that if we put a lower-case drive in for A.A-          -- the diagnostics for A.B will also be lower-case.-          liftIO $ fileUri @?= uriB-          let msg = head (toList diags) ^. L.message-          liftIO $ unless ("redundant" `T.isInfixOf` msg) $-              assertFailure ("Expected redundant import but got " <> T.unpack msg)-          closeDoc a-  , testSessionWait "haddock parse error" $ do-      let fooContent = T.unlines-            [ "module Foo where"-            , "foo :: Int"-            , "foo = 1 {-|-}"-            ]-      _ <- createDoc "Foo.hs" "haskell" fooContent-      if ghcVersion >= GHC90 then-          -- Haddock parse errors are ignored on ghc-9.0-            pure ()-      else-        expectDiagnostics-            [ ( "Foo.hs"-              , [(DsWarning, (2, 8), "Haddock parse error on input")]-              )-            ]-  , testSessionWait "strip file path" $ do-      let-          name = "Testing"-          content = T.unlines-            [ "module " <> name <> " where"-            , "value :: Maybe ()"-            , "value = [()]"-            ]-      _ <- createDoc (T.unpack name <> ".hs") "haskell" content-      notification <- skipManyTill anyMessage diagnostic-      let-          offenders =-            Lsp.params .-            Lsp.diagnostics .-            Lens.folded .-            Lsp.message .-            Lens.filtered (T.isInfixOf ("/" <> name <> ".hs:"))-          failure msg = liftIO $ assertFailure $ "Expected file path to be stripped but got " <> T.unpack msg-      Lens.mapMOf_ offenders failure notification-  , testSession' "-Werror in cradle is ignored" $ \sessionDir -> do-      liftIO $ writeFile (sessionDir </> "hie.yaml")-        "cradle: {direct: {arguments: [\"-Wall\", \"-Werror\"]}}"-      let fooContent = T.unlines-            [ "module Foo where"-            , "foo = ()"-            ]-      _ <- createDoc "Foo.hs" "haskell" fooContent-      expectDiagnostics-        [ ( "Foo.hs"-          , [(DsWarning, (1, 0), "Top-level binding with no type signature:")-            ]-          )-        ]-  , testSessionWait "-Werror in pragma is ignored" $ do-      let fooContent = T.unlines-            [ "{-# OPTIONS_GHC -Wall -Werror #-}"-            , "module Foo() where"-            , "foo :: Int"-            , "foo = 1"-            ]-      _ <- createDoc "Foo.hs" "haskell" fooContent-      expectDiagnostics-        [ ( "Foo.hs"-          , [(DsWarning, (3, 0), "Defined but not used:")-            ]-          )-        ]-  , testCase "typecheck-all-parents-of-interest" $ runWithExtraFiles "recomp" $ \dir -> do-    let bPath = dir </> "B.hs"-        pPath = dir </> "P.hs"-        aPath = dir </> "A.hs"--    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int-    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int-    aSource <- liftIO $ readFileUtf8 aPath -- x = y :: Int--    bdoc <- createDoc bPath "haskell" bSource-    _pdoc <- createDoc pPath "haskell" pSource-    expectDiagnostics-      [("P.hs", [(DsWarning,(4,0), "Top-level binding")])] -- So that we know P has been loaded--    -- Change y from Int to B which introduces a type error in A (imported from P)-    changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $-                    T.unlines ["module B where", "y :: Bool", "y = undefined"]]-    expectDiagnostics-      [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])-      ]--    -- Open A and edit to fix the type error-    adoc <- createDoc aPath "haskell" aSource-    changeDoc adoc [TextDocumentContentChangeEvent Nothing Nothing $-                    T.unlines ["module A where", "import B", "x :: Bool", "x = y"]]--    expectDiagnostics-      [ ( "P.hs",-          [ (DsError, (4, 6), "Couldn't match expected type 'Int' with actual type 'Bool'"),-            (DsWarning, (4, 0), "Top-level binding")-          ]-        ),-        ("A.hs", [])-      ]-    expectNoMoreDiagnostics 1--  , testSessionWait "deduplicate missing module diagnostics" $  do-      let fooContent = T.unlines [ "module Foo() where" , "import MissingModule" ]-      doc <- createDoc "Foo.hs" "haskell" fooContent-      expectDiagnostics [("Foo.hs", [(DsError, (1,7), "Could not find module 'MissingModule'")])]--      changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module Foo() where" ]-      expectDiagnostics []--      changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines-            [ "module Foo() where" , "import MissingModule" ] ]-      expectDiagnostics [("Foo.hs", [(DsError, (1,7), "Could not find module 'MissingModule'")])]--  , testGroup "Cancellation"-    [ cancellationTestGroup "edit header" editHeader yesSession noParse  noTc-    , cancellationTestGroup "edit import" editImport noSession  yesParse noTc-    , cancellationTestGroup "edit body"   editBody   yesSession yesParse yesTc-    ]-  ]-  where-      editPair x y = let p = Position x y ; p' = Position x (y+2) in-        (TextDocumentContentChangeEvent {_range=Just (Range p p), _rangeLength=Nothing, _text="fd"}-        ,TextDocumentContentChangeEvent {_range=Just (Range p p'), _rangeLength=Nothing, _text=""})-      editHeader = editPair 0 0-      editImport = editPair 2 10-      editBody   = editPair 3 10--      noParse = False-      yesParse = True--      noSession = False-      yesSession = True--      noTc = False-      yesTc = True--cancellationTestGroup :: TestName -> (TextDocumentContentChangeEvent, TextDocumentContentChangeEvent) -> Bool -> Bool -> Bool -> TestTree-cancellationTestGroup name edits sessionDepsOutcome parseOutcome tcOutcome = testGroup name-    [ cancellationTemplate edits Nothing-    , cancellationTemplate edits $ Just ("GetFileContents", True)-    , cancellationTemplate edits $ Just ("GhcSession", True)-      -- the outcome for GetModSummary is always True because parseModuleHeader never fails (!)-    , cancellationTemplate edits $ Just ("GetModSummary", True)-    , cancellationTemplate edits $ Just ("GetModSummaryWithoutTimestamps", True)-      -- getLocatedImports never fails-    , cancellationTemplate edits $ Just ("GetLocatedImports", True)-    , cancellationTemplate edits $ Just ("GhcSessionDeps", sessionDepsOutcome)-    , cancellationTemplate edits $ Just ("GetParsedModule", parseOutcome)-    , cancellationTemplate edits $ Just ("TypeCheck", tcOutcome)-    , cancellationTemplate edits $ Just ("GetHieAst", tcOutcome)-    ]--cancellationTemplate :: (TextDocumentContentChangeEvent, TextDocumentContentChangeEvent) -> Maybe (String, Bool) -> TestTree-cancellationTemplate (edit, undoEdit) mbKey = testCase (maybe "-" fst mbKey) $ runTestNoKick $ do-      doc <- createDoc "Foo.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wall #-}"-            , "module Foo where"-            , "import Data.List()"-            , "f0 x = (x,x)"-            ]--      -- for the example above we expect one warning-      let missingSigDiags = [(DsWarning, (3, 0), "Top-level binding") ]-      typeCheck doc >> expectCurrentDiagnostics doc missingSigDiags--      -- Now we edit the document and wait for the given key (if any)-      changeDoc doc [edit]-      whenJust mbKey $ \(key, expectedResult) -> do-        WaitForIdeRuleResult{ideResultSuccess} <- waitForAction key doc-        liftIO $ ideResultSuccess @?= expectedResult--      -- The 2nd edit cancels the active session and unbreaks the file-      -- wait for typecheck and check that the current diagnostics are accurate-      changeDoc doc [undoEdit]-      typeCheck doc >> expectCurrentDiagnostics doc missingSigDiags--      expectNoMoreDiagnostics 0.5-    where-        -- similar to run except it disables kick-        runTestNoKick s = withTempDir $ \dir -> runInDir' dir "." "." ["--test-no-kick"] s--        typeCheck doc = do-            WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc-            liftIO $ assertBool "The file should typecheck" ideResultSuccess-            -- wait for the debouncer to publish diagnostics if the rule runs-            liftIO $ sleep 0.2-            -- flush messages to ensure current diagnostics state is updated-            flushMessages--codeLensesTests :: TestTree-codeLensesTests = testGroup "code lenses"-  [ addSigLensesTests-  ]--watchedFilesTests :: TestTree-watchedFilesTests = testGroup "watched files"-  [ testGroup "Subscriptions"-    [ testSession' "workspace files" $ \sessionDir -> do-        liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\", \"A\", \"WatchedFilesMissingModule\"]}}"-        _doc <- createDoc "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"-        watchedFileRegs <- getWatchedFilesSubscriptionsUntil STextDocumentPublishDiagnostics--        -- Expect 2 subscriptions: one for all .hs files and one for the hie.yaml cradle-        liftIO $ length watchedFileRegs @?= 2--    , testSession' "non workspace file" $ \sessionDir -> do-        tmpDir <- liftIO getTemporaryDirectory-        let yaml = "cradle: {direct: {arguments: [\"-i" <> tail(init(show tmpDir)) <> "\", \"A\", \"WatchedFilesMissingModule\"]}}"-        liftIO $ writeFile (sessionDir </> "hie.yaml") yaml-        _doc <- createDoc "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"-        watchedFileRegs <- getWatchedFilesSubscriptionsUntil STextDocumentPublishDiagnostics--        -- Expect 2 subscriptions: one for all .hs files and one for the hie.yaml cradle-        liftIO $ length watchedFileRegs @?= 2--    -- TODO add a test for didChangeWorkspaceFolder-    ]-  , testGroup "Changes"-    [-      testSession' "workspace files" $ \sessionDir -> do-        liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\", \"A\", \"B\"]}}"-        liftIO $ writeFile (sessionDir </> "B.hs") $ unlines-          ["module B where"-          ,"b :: Bool"-          ,"b = False"]-        _doc <- createDoc "A.hs" "haskell" $ T.unlines-          ["module A where"-          ,"import B"-          ,"a :: ()"-          ,"a = b"-          ]-        expectDiagnostics [("A.hs", [(DsError, (3, 4), "Couldn't match expected type '()' with actual type 'Bool'")])]-        -- modify B off editor-        liftIO $ writeFile (sessionDir </> "B.hs") $ unlines-          ["module B where"-          ,"b :: Int"-          ,"b = 0"]-        sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $-                List [FileEvent (filePathToUri $ sessionDir </> "B.hs") FcChanged ]-        expectDiagnostics [("A.hs", [(DsError, (3, 4), "Couldn't match expected type '()' with actual type 'Int'")])]-    ]-  ]--addSigLensesTests :: TestTree-addSigLensesTests =-  let pragmas = "{-# OPTIONS_GHC -Wmissing-signatures -Wmissing-pattern-synonym-signatures #-}"-      moduleH exported =-        T.unlines-          [ "{-# LANGUAGE PatternSynonyms,TypeApplications,DataKinds,RankNTypes,ScopedTypeVariables,TypeOperators,GADTs,BangPatterns #-}"-          , "module Sigs(" <> exported <> ") where"-          , "import qualified Data.Complex as C"-          , "import Data.Data (Proxy (..), type (:~:) (..), mkCharType)"-          , "data T1 a where"-          , "  MkT1 :: (Show b) => a -> b -> T1 a"-          ]-      before enableGHCWarnings exported (def, _) others =-        T.unlines $ [pragmas | enableGHCWarnings] <> [moduleH exported, def] <> others-      after' enableGHCWarnings exported (def, sig) others =-        T.unlines $ [pragmas | enableGHCWarnings] <> [moduleH exported] <> maybe [] pure sig <> [def] <> others-      createConfig mode = A.object ["haskell" A..= A.object ["plugin" A..= A.object ["ghcide-type-lenses" A..= A.object ["config" A..= A.object ["mode" A..= A.String mode]]]]]-      sigSession testName enableGHCWarnings mode exported def others = testSession testName $ do-        let originalCode = before enableGHCWarnings exported def others-        let expectedCode = after' enableGHCWarnings exported def others-        sendNotification SWorkspaceDidChangeConfiguration $ DidChangeConfigurationParams $ createConfig mode-        doc <- createDoc "Sigs.hs" "haskell" originalCode-        waitForProgressDone-        codeLenses <- getCodeLenses doc-        if not $ null $ snd def-          then do-            liftIO $ length codeLenses == 1 @? "Expected 1 code lens, but got: " <> show codeLenses-            executeCommand $ fromJust $ head codeLenses ^. L.command-            modifiedCode <- skipManyTill anyMessage (getDocumentEdit doc)-            liftIO $ expectedCode @=? modifiedCode-          else liftIO $ null codeLenses @? "Expected no code lens, but got: " <> show codeLenses-      cases =-        [ ("abc = True", "abc :: Bool")-        , ("foo a b = a + b", "foo :: Num a => a -> a -> a")-        , ("bar a b = show $ a + b", "bar :: (Show a, Num a) => a -> a -> String")-        , ("(!!!) a b = a > b", "(!!!) :: Ord a => a -> a -> Bool")-        , ("a >>>> b = a + b", "(>>>>) :: Num a => a -> a -> a")-        , ("a `haha` b = a b", "haha :: (t1 -> t2) -> t1 -> t2")-        , ("pattern Some a = Just a", "pattern Some :: a -> Maybe a")-        , ("pattern Some a <- Just a", "pattern Some :: a -> Maybe a")-        , ("pattern Some a <- Just a\n  where Some a = Just a", "pattern Some :: a -> Maybe a")-        , ("pattern Some a <- Just !a\n  where Some !a = Just a", "pattern Some :: a -> Maybe a")-        , ("pattern Point{x, y} = (x, y)", "pattern Point :: a -> b -> (a, b)")-        , ("pattern Point{x, y} <- (x, y)", "pattern Point :: a -> b -> (a, b)")-        , ("pattern Point{x, y} <- (x, y)\n  where Point x y = (x, y)", "pattern Point :: a -> b -> (a, b)")-        , ("pattern MkT1' b = MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")-        , ("pattern MkT1' b <- MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")-        , ("pattern MkT1' b <- MkT1 42 b\n  where MkT1' b = MkT1 42 b", "pattern MkT1' :: (Eq a, Num a) => Show b => b -> T1 a")-        , ("qualifiedSigTest= C.realPart", "qualifiedSigTest :: C.Complex a -> a")-        , ("head = 233", "head :: Integer")-        , ("rank2Test (k :: forall a . a -> a) = (k 233 :: Int, k \"QAQ\")", "rank2Test :: (forall a. a -> a) -> (Int, " <> listOfChar <> ")")-        , ("symbolKindTest = Proxy @\"qwq\"", "symbolKindTest :: Proxy \"qwq\"")-        , ("promotedKindTest = Proxy @Nothing", if ghcVersion >= GHC96 then "promotedKindTest :: Proxy Nothing" else "promotedKindTest :: Proxy 'Nothing")-        , ("typeOperatorTest = Refl", if ghcVersion >= GHC92 then "typeOperatorTest :: forall {k} {a :: k}. a :~: a" else "typeOperatorTest :: a :~: a")-        , ("notInScopeTest = mkCharType", "notInScopeTest :: String -> Data.Data.DataType")-        ]-   in testGroup-        "add signature"-        [ testGroup "signatures are correct" [sigSession (T.unpack $ T.replace "\n" "\\n" def) False "always" "" (def, Just sig) [] | (def, sig) <- cases]-        , sigSession "exported mode works" False "exported" "xyz" ("xyz = True", Just "xyz :: Bool") (fst <$> take 3 cases)-        , testGroup-            "diagnostics mode works"-            [ sigSession "with GHC warnings" True "diagnostics" "" (second Just $ head cases) []-            , sigSession "without GHC warnings" False "diagnostics" "" (second (const Nothing) $ head cases) []-            ]-        , testSession "keep stale lens" $ do-            let content = T.unlines-                    [ "module Stale where"-                    , "f = _"-                    ]-            doc <- createDoc "Stale.hs" "haskell" content-            oldLens <- getCodeLenses doc-            liftIO $ length oldLens @?= 1-            let edit = TextEdit (mkRange 0 4 0 5) "" -- Remove the `_`-            _ <- applyEdit doc edit-            newLens <- getCodeLenses doc-            liftIO $ newLens @?= oldLens-        ]--linkToLocation :: [LocationLink] -> [Location]-linkToLocation = map (\LocationLink{_targetUri,_targetRange} -> Location _targetUri _targetRange)--checkDefs :: [Location] |? [LocationLink] -> Session [Expect] -> Session ()-checkDefs (either id linkToLocation . toEither -> defs) mkExpectations = traverse_ check =<< mkExpectations where-  check (ExpectRange expectedRange) = do-    assertNDefinitionsFound 1 defs-    assertRangeCorrect (head defs) expectedRange-  check (ExpectLocation expectedLocation) = do-    assertNDefinitionsFound 1 defs-    liftIO $ do-      canonActualLoc <- canonicalizeLocation (head defs)-      canonExpectedLoc <- canonicalizeLocation expectedLocation-      canonActualLoc @?= canonExpectedLoc-  check ExpectNoDefinitions = do-    assertNDefinitionsFound 0 defs-  check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"-  check _ = pure () -- all other expectations not relevant to getDefinition--  assertNDefinitionsFound :: Int -> [a] -> Session ()-  assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)--  assertRangeCorrect Location{_range = foundRange} expectedRange =-    liftIO $ expectedRange @=? foundRange--canonicalizeLocation :: Location -> IO Location-canonicalizeLocation (Location uri range) = Location <$> canonicalizeUri uri <*> pure range--findDefinitionAndHoverTests :: TestTree-findDefinitionAndHoverTests = let--  tst :: (TextDocumentIdentifier -> Position -> Session a, a -> Session [Expect] -> Session ()) -> Position -> String -> Session [Expect] -> String -> TestTree-  tst (get, check) pos sfp targetRange title = testSessionWithExtraFiles "hover" title $ \dir -> do--    -- Dirty the cache to check that definitions work even in the presence of iface files-    liftIO $ runInDir dir $ do-      let fooPath = dir </> "Foo.hs"-      fooSource <- liftIO $ readFileUtf8 fooPath-      fooDoc <- createDoc fooPath "haskell" fooSource-      _ <- getHover fooDoc $ Position 4 3-      closeDoc fooDoc--    doc <- openTestDataDoc (dir </> sfp)-    waitForProgressDone-    found <- get doc pos-    check found targetRange----  checkHover :: Maybe Hover -> Session [Expect] -> Session ()-  checkHover hover expectations = traverse_ check =<< expectations where--    check expected =-      case hover of-        Nothing -> unless (expected == ExpectNoHover) $ liftIO $ assertFailure "no hover found"-        Just Hover{_contents = (HoverContents MarkupContent{_value = standardizeQuotes -> msg})-                  ,_range    = rangeInHover } ->-          case expected of-            ExpectRange  expectedRange -> checkHoverRange expectedRange rangeInHover msg-            ExpectHoverRange expectedRange -> checkHoverRange expectedRange rangeInHover msg-            ExpectHoverText snippets -> liftIO $ traverse_ (`assertFoundIn` msg) snippets-            ExpectHoverExcludeText snippets -> liftIO $ traverse_ (`assertNotFoundIn` msg) snippets-            ExpectHoverTextRegex re -> liftIO $ assertBool ("Regex not found in " <> T.unpack msg) (msg =~ re :: Bool)-            ExpectNoHover -> liftIO $ assertFailure $ "Expected no hover but got " <> show hover-            _ -> pure () -- all other expectations not relevant to hover-        _ -> liftIO $ assertFailure $ "test not expecting this kind of hover info" <> show hover--  extractLineColFromHoverMsg :: T.Text -> [T.Text]-  extractLineColFromHoverMsg = T.splitOn ":" . head . T.splitOn "*" . last . T.splitOn (sourceFileName <> ":")--  checkHoverRange :: Range -> Maybe Range -> T.Text -> Session ()-  checkHoverRange expectedRange rangeInHover msg =-    let-      lineCol = extractLineColFromHoverMsg msg-      -- looks like hovers use 1-based numbering while definitions use 0-based-      -- turns out that they are stored 1-based in RealSrcLoc by GHC itself.-      adjust Position{_line = l, _character = c} =-        Position{_line = l + 1, _character = c + 1}-    in-    case map (read . T.unpack) lineCol of-      [l,c] -> liftIO $ adjust (_start expectedRange) @=? Position l c-      _     -> liftIO $ assertFailure $-        "expected: " <> show ("[...]" <> sourceFileName <> ":<LINE>:<COL>**[...]", Just expectedRange) <>-        "\n but got: " <> show (msg, rangeInHover)--  assertFoundIn :: T.Text -> T.Text -> Assertion-  assertFoundIn part whole = assertBool-    (T.unpack $ "failed to find: `" <> part <> "` in hover message:\n" <> whole)-    (part `T.isInfixOf` whole)--  assertNotFoundIn :: T.Text -> T.Text -> Assertion-  assertNotFoundIn part whole = assertBool-    (T.unpack $ "found unexpected: `" <> part <> "` in hover message:\n" <> whole)-    (not . T.isInfixOf part $ whole)--  sourceFilePath = T.unpack sourceFileName-  sourceFileName = "GotoHover.hs"--  mkFindTests tests = testGroup "get"-    [ testGroup "definition" $ mapMaybe fst tests-    , testGroup "hover"      $ mapMaybe snd tests-    , checkFileCompiles sourceFilePath $-        expectDiagnostics-          [ ( "GotoHover.hs", [(DsError, (62, 7), "Found hole: _")])-          , ( "GotoHover.hs", [(DsError, (65, 8), "Found hole: _")])-          ]-    , testGroup "type-definition" typeDefinitionTests-    , testGroup "hover-record-dot-syntax" recordDotSyntaxTests ]--  typeDefinitionTests = [ tst (getTypeDefinitions, checkDefs) aaaL14 sourceFilePath (pure tcData) "Saturated data con"-                        , tst (getTypeDefinitions, checkDefs) aL20 sourceFilePath (pure [ExpectNoDefinitions]) "Polymorphic variable"]--  recordDotSyntaxTests-    | ghcVersion >= GHC92 =-        [ tst (getHover, checkHover) (Position 19 24) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["x :: MyRecord"]]) "hover over parent"-        , tst (getHover, checkHover) (Position 19 25) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over dot shows child"-        , tst (getHover, checkHover) (Position 19 26) (T.unpack "RecordDotSyntax.hs") (pure [ExpectHoverText ["_ :: MyChild"]]) "hover over child"-        ]-    | otherwise = []--  test runDef runHover look expect = testM runDef runHover look (return expect)--  testM runDef runHover look expect title =-    ( runDef   $ tst def   look sourceFilePath expect title-    , runHover $ tst hover look sourceFilePath expect title ) where-      def   = (getDefinitions, checkDefs)-      hover = (getHover      , checkHover)--  -- search locations            expectations on results-  fffL4  = _start fffR     ;  fffR = mkRange 8  4    8  7 ; fff  = [ExpectRange fffR]-  fffL8  = Position 12  4  ;-  fffL14 = Position 18  7  ;-  aL20   = Position 19 15-  aaaL14 = Position 18 20  ;  aaa    = [mkR  11  0   11  3]-  dcL7   = Position 11 11  ;  tcDC   = [mkR   7 23    9 16]-  dcL12  = Position 16 11  ;-  xtcL5  = Position  9 11  ;  xtc    = [ExpectExternFail,   ExpectHoverText ["Int", "Defined in ", "GHC.Types", "ghc-prim"]]-  tcL6   = Position 10 11  ;  tcData = [mkR   7  0    9 16, ExpectHoverText ["TypeConstructor", "GotoHover.hs:8:1"]]-  vvL16  = Position 20 12  ;  vv     = [mkR  20  4   20  6]-  opL16  = Position 20 15  ;  op     = [mkR  21  2   21  4]-  opL18  = Position 22 22  ;  opp    = [mkR  22 13   22 17]-  aL18   = Position 22 20  ;  apmp   = [mkR  22 10   22 11]-  b'L19  = Position 23 13  ;  bp     = [mkR  23  6   23  7]-  xvL20  = Position 24  8  ;  xvMsg  = [ExpectExternFail,   ExpectHoverText ["pack", ":: String -> Text", "Data.Text", "text"]]-  clL23  = Position 27 11  ;  cls    = [mkR  25  0   26 20, ExpectHoverText ["MyClass", "GotoHover.hs:26:1"]]-  clL25  = Position 29  9-  eclL15 = Position 19  8  ;  ecls   = [ExpectExternFail, ExpectHoverText ["Num", "Defined in ", "GHC.Num", "base"]]-  dnbL29 = Position 33 18  ;  dnb    = [ExpectHoverText [":: ()"],   mkR  33 12   33 21]-  dnbL30 = Position 34 23-  lcbL33 = Position 37 26  ;  lcb    = [ExpectHoverText [":: Char"], mkR  37 26   37 27]-  lclL33 = Position 37 22-  mclL36 = Position 40  1  ;  mcl    = [mkR  40  0   40 14]-  mclL37 = Position 41  1-  spaceL37 = Position 41  24 ; space = [ExpectNoDefinitions, ExpectHoverText [":: Char"]]-  docL41 = Position 45  1  ;  doc    = [ExpectHoverText ["Recognizable docs: kpqz"]]-                           ;  constr = [ExpectHoverText ["Monad m"]]-  eitL40 = Position 44 28  ;  kindE  = [ExpectHoverText [if ghcVersion >= GHC92 then ":: Type -> Type -> Type\n" else ":: * -> * -> *\n"]]-  intL40 = Position 44 34  ;  kindI  = [ExpectHoverText [if ghcVersion >= GHC92 then ":: Type\n" else ":: *\n"]]-  tvrL40 = Position 44 37  ;  kindV  = [ExpectHoverText [":: * -> *\n"]]-  intL41 = Position 45 20  ;  litI   = [ExpectHoverText ["7518"]]-  chrL36 = Position 41 24  ;  litC   = [ExpectHoverText ["'f'"]]-  txtL8  = Position 12 14  ;  litT   = [ExpectHoverText ["\"dfgy\""]]-  lstL43 = Position 47 12  ;  litL   = [ExpectHoverText ["[8391 :: Int, 6268]"]]-  outL45 = Position 49  3  ;  outSig = [ExpectHoverText ["outer", "Bool"], mkR 50 0 50 5]-  innL48 = Position 52  5  ;  innSig = [ExpectHoverText ["inner", "Char"], mkR 49 2 49 7]-  holeL60 = Position 62 7  ;  hleInfo = [ExpectHoverText ["_ ::"]]-  holeL65 = Position 65 8  ;  hleInfo2 = [ExpectHoverText ["_ :: a -> Maybe a"]]-  cccL17 = Position 17 16  ;  docLink = [ExpectHoverTextRegex "\\*Defined in 'GHC.Types'\\* \\*\\(ghc-prim-[0-9.]+\\)\\*\n\n"]-  imported = Position 56 13 ; importedSig = getDocUri "Foo.hs" >>= \foo -> return [ExpectHoverText ["foo", "Foo", "Haddock"], mkL foo 5 0 5 3]-  reexported = Position 55 14 ; reexportedSig = getDocUri "Bar.hs" >>= \bar -> return [ExpectHoverText ["Bar", "Bar", "Haddock"], mkL bar 3 (if ghcVersion >= GHC94 then 5 else 0) 3 (if ghcVersion >= GHC94 then 8 else 14)]-  thLocL57 = Position 59 10 ; thLoc = [ExpectHoverText ["Identity"]]-  cmtL68 = Position 67  0  ;  lackOfdEq = [ExpectHoverExcludeText ["$dEq"]]-  in-  mkFindTests-  --      def    hover  look       expect-  [-    if ghcVersion >= GHC90 then-        -- It suggests either going to the constructor or to the field-        test  broken yes    fffL4      fff           "field in record definition"-    else-        test  yes    yes    fffL4      fff           "field in record definition"-  , test  yes    yes    fffL8      fff           "field in record construction    #1102"-  , test  yes    yes    fffL14     fff           "field name used as accessor"           -- https://github.com/haskell/ghcide/pull/120 in Calculate.hs-  , test  yes    yes    aaaL14     aaa           "top-level name"                        -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    dcL7       tcDC          "data constructor record         #1029"-  , test  yes    yes    dcL12      tcDC          "data constructor plain"                -- https://github.com/haskell/ghcide/pull/121-  , test  yes    yes    tcL6       tcData        "type constructor                #1028" -- https://github.com/haskell/ghcide/pull/147-  , test  broken yes    xtcL5      xtc           "type constructor external   #717,1028"-  , test  broken yes    xvL20      xvMsg         "value external package           #717" -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    vvL16      vv            "plain parameter"                       -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    aL18       apmp          "pattern match name"                    -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    opL16      op            "top-level operator               #713" -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    opL18      opp           "parameter operator"                    -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    b'L19      bp            "name in backticks"                     -- https://github.com/haskell/ghcide/pull/120-  , test  yes    yes    clL23      cls           "class in instance declaration   #1027"-  , test  yes    yes    clL25      cls           "class in signature              #1027" -- https://github.com/haskell/ghcide/pull/147-  , test  broken yes    eclL15     ecls          "external class in signature #717,1027"-  , test  yes    yes    dnbL29     dnb           "do-notation   bind              #1073"-  , test  yes    yes    dnbL30     dnb           "do-notation lookup"-  , test  yes    yes    lcbL33     lcb           "listcomp   bind                 #1073"-  , test  yes    yes    lclL33     lcb           "listcomp lookup"-  , test  yes    yes    mclL36     mcl           "top-level fn 1st clause"-  , test  yes    yes    mclL37     mcl           "top-level fn 2nd clause         #1030"-  , if ghcVersion >= GHC810 then-        test  yes    yes    spaceL37   space         "top-level fn on space           #1002"-    else-        test  yes    broken spaceL37   space         "top-level fn on space           #1002"-  , test  no     yes    docL41     doc           "documentation                   #1129"-  , test  no     yes    eitL40     kindE         "kind of Either                  #1017"-  , test  no     yes    intL40     kindI         "kind of Int                     #1017"-  , test  no     broken tvrL40     kindV         "kind of (* -> *) type variable  #1017"-  , test  no     broken intL41     litI          "literal Int  in hover info      #1016"-  , test  no     broken chrL36     litC          "literal Char in hover info      #1016"-  , test  no     broken txtL8      litT          "literal Text in hover info      #1016"-  , test  no     broken lstL43     litL          "literal List in hover info      #1016"-  , test  yes    yes    cmtL68     lackOfdEq     "no Core symbols                 #3280"-  , if ghcVersion >= GHC90 then-        test  no     yes    docL41     constr        "type constraint in hover info   #1012"-    else-        test  no     broken docL41     constr        "type constraint in hover info   #1012"-  , test  no     yes    outL45     outSig        "top-level signature              #767"-  , test  broken broken innL48     innSig        "inner     signature              #767"-  , test  no     yes    holeL60    hleInfo       "hole without internal name       #831"-  , test  no     yes    holeL65    hleInfo2      "hole with variable"-  , test  no     yes    cccL17     docLink       "Haddock html links"-  , testM yes    yes    imported   importedSig   "Imported symbol"-  , if | isWindows ->-        -- Flaky on Windows: https://github.com/haskell/haskell-language-server/issues/2997-        testM no     yes    reexported reexportedSig "Imported symbol (reexported)"-       | otherwise ->-        testM yes    yes    reexported reexportedSig "Imported symbol (reexported)"-  , if | ghcVersion == GHC90 && isWindows ->-        test  no     broken    thLocL57   thLoc         "TH Splice Hover"-       | otherwise ->-        test  no     yes       thLocL57   thLoc         "TH Splice Hover"-  ]-  where yes, broken :: (TestTree -> Maybe TestTree)-        yes    = Just -- test should run and pass-        broken = Just . (`xfail` "known broken")-        no = const Nothing -- don't run this test at all-        skip = const Nothing -- unreliable, don't run--checkFileCompiles :: FilePath -> Session () -> TestTree-checkFileCompiles fp diag =-  testSessionWithExtraFiles "hover" ("Does " ++ fp ++ " compile") $ \dir -> do-    void (openTestDataDoc (dir </> fp))-    diag---pluginSimpleTests :: TestTree-pluginSimpleTests =-  ignoreInWindowsForGHC810 $-  -- Build profile: -w ghc-9.4.2 -O1-  -- In order, the following will be built (use -v for more details):-  -- - ghc-typelits-natnormalise-0.7.7 (lib) (requires build)-  -- - ghc-typelits-knownnat-0.7.7 (lib) (requires build)-  -- - plugin-1.0.0 (lib) (first run)-  -- Starting     ghc-typelits-natnormalise-0.7.7 (lib)-  -- Building     ghc-typelits-natnormalise-0.7.7 (lib)--  -- Failed to build ghc-typelits-natnormalise-0.7.7.-  -- Build log (-  -- C:\cabal\logs\ghc-9.4.2\ghc-typelits-_-0.7.7-3f036a52a0d9bfc3389d1852a87da2e87c6de2e4.log-  -- ):-  -- Preprocessing library for ghc-typelits-natnormalise-0.7.7..-  -- Building library for ghc-typelits-natnormalise-0.7.7..-  -- [1 of 3] Compiling GHC.TypeLits.Normalise.SOP ( src\GHC\TypeLits\Normalise\SOP.hs, dist\build\GHC\TypeLits\Normalise\SOP.o )-  -- [2 of 3] Compiling GHC.TypeLits.Normalise.Unify ( src\GHC\TypeLits\Normalise\Unify.hs, dist\build\GHC\TypeLits\Normalise\Unify.o )-  -- [3 of 3] Compiling GHC.TypeLits.Normalise ( src-ghc-9.4\GHC\TypeLits\Normalise.hs, dist\build\GHC\TypeLits\Normalise.o )-  -- C:\tools\ghc-9.4.2\lib\../mingw/bin/llvm-ar.exe: error: dist\build\objs-5156\libHSghc-typelits-_-0.7.7-3f036a52a0d9bfc3389d1852a87da2e87c6de2e4.a: No such file or directory--  -- Error: cabal: Failed to build ghc-typelits-natnormalise-0.7.7 (which is-  -- required by plugin-1.0.0). See the build log above for details.-  ignoreFor (BrokenForGHC [GHC96]) "fragile, frequently times out" $-  ignoreFor (BrokenSpecific Windows [GHC94]) "ghc-typelist-natnormalise fails to build on GHC 9.4.2 for windows only" $-  testSessionWithExtraFiles "plugin-knownnat" "simple plugin" $ \dir -> do-    _ <- openDoc (dir </> "KnownNat.hs") "haskell"-    liftIO $ writeFile (dir</>"hie.yaml")-      "cradle: {cabal: [{path: '.', component: 'lib:plugin'}]}"--    expectDiagnostics-      [ ( "KnownNat.hs",-          [(DsError, (9, 15), "Variable not in scope: c")]-          )-      ]--pluginParsedResultTests :: TestTree-pluginParsedResultTests =-  ignoreInWindowsForGHC810 $-  ignoreForGHC92Plus "No need for this plugin anymore!" $-  testSessionWithExtraFiles "plugin-recorddot" "parsedResultAction plugin" $ \dir -> do-    _ <- openDoc (dir</> "RecordDot.hs") "haskell"-    expectNoMoreDiagnostics 2--cppTests :: TestTree-cppTests =-  testGroup "cpp"-    [ ignoreInWindowsBecause "Throw a lsp session time out in windows for ghc-8.8 and is broken for other versions" $ testCase "cpp-error" $ do-        let content =-              T.unlines-                [ "{-# LANGUAGE CPP #-}",-                  "module Testing where",-                  "#ifdef FOO",-                  "foo = 42"-                ]-        -- The error locations differ depending on which C-preprocessor is used.-        -- Some give the column number and others don't (hence maxBound == -1 unsigned). Assert either-        -- of them.-        (run $ expectError content (2, maxBound))-          `catch` ( \e -> do-                      let _ = e :: HUnitFailure-                      run $ expectError content (2, 1)-                  )-    , testSessionWait "cpp-ghcide" $ do-        _ <- createDoc "A.hs" "haskell" $ T.unlines-          ["{-# LANGUAGE CPP #-}"-          ,"main ="-          ,"#ifdef __GHCIDE__"-          ,"  worked"-          ,"#else"-          ,"  failed"-          ,"#endif"-          ]-        expectDiagnostics [("A.hs", [(DsError, (3, 2), "Variable not in scope: worked")])]-    ]-  where-    expectError :: T.Text -> Cursor -> Session ()-    expectError content cursor = do-      _ <- createDoc "Testing.hs" "haskell" content-      expectDiagnostics-        [ ( "Testing.hs",-            [(DsError, cursor, "error: unterminated")]-          )-        ]-      expectNoMoreDiagnostics 0.5--preprocessorTests :: TestTree-preprocessorTests = testSessionWait "preprocessor" $ do-  let content =-        T.unlines-          [ "{-# OPTIONS_GHC -F -pgmF=ghcide-test-preprocessor #-}"-          , "module Testing where"-          , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic-          ]-  _ <- createDoc "Testing.hs" "haskell" content-  expectDiagnostics-    [ ( "Testing.hs",-        [(DsError, (2, 8), "Variable not in scope: z")]-      )-    ]---safeTests :: TestTree-safeTests =-  testGroup-    "SafeHaskell"-    [ -- Test for https://github.com/haskell/ghcide/issues/424-      testSessionWait "load" $ do-        let sourceA =-              T.unlines-                ["{-# LANGUAGE Trustworthy #-}"-                ,"module A where"-                ,"import System.IO.Unsafe"-                ,"import System.IO ()"-                ,"trustWorthyId :: a -> a"-                ,"trustWorthyId i = unsafePerformIO $ do"-                ,"  putStrLn \"I'm safe\""-                ,"  return i"]-            sourceB =-              T.unlines-                ["{-# LANGUAGE Safe #-}"-                ,"module B where"-                ,"import A"-                ,"safeId :: a -> a"-                ,"safeId = trustWorthyId"-                ]--        _ <- createDoc "A.hs" "haskell" sourceA-        _ <- createDoc "B.hs" "haskell" sourceB-        expectNoMoreDiagnostics 1 ]--thTests :: TestTree-thTests =-  testGroup-    "TemplateHaskell"-    [ -- Test for https://github.com/haskell/ghcide/pull/212-      testSessionWait "load" $ do-        let sourceA =-              T.unlines-                [ "{-# LANGUAGE PackageImports #-}",-                  "{-# LANGUAGE TemplateHaskell #-}",-                  "module A where",-                  "import \"template-haskell\" Language.Haskell.TH",-                  "a :: Integer",-                  "a = $(litE $ IntegerL 3)"-                ]-            sourceB =-              T.unlines-                [ "{-# LANGUAGE PackageImports #-}",-                  "{-# LANGUAGE TemplateHaskell #-}",-                  "module B where",-                  "import A",-                  "import \"template-haskell\" Language.Haskell.TH",-                  "b :: Integer",-                  "b = $(litE $ IntegerL $ a) + n"-                ]-        _ <- createDoc "A.hs" "haskell" sourceA-        _ <- createDoc "B.hs" "haskell" sourceB-        expectDiagnostics [ ( "B.hs", [(DsError, (6, 29), "Variable not in scope: n")] ) ]-    , testSessionWait "newtype-closure" $ do-        let sourceA =-              T.unlines-                [ "{-# LANGUAGE DeriveDataTypeable #-}"-                  ,"{-# LANGUAGE TemplateHaskell #-}"-                  ,"module A (a) where"-                  ,"import Data.Data"-                  ,"import Language.Haskell.TH"-                  ,"newtype A = A () deriving (Data)"-                  ,"a :: ExpQ"-                  ,"a = [| 0 |]"]-        let sourceB =-              T.unlines-                [ "{-# LANGUAGE TemplateHaskell #-}"-                ,"module B where"-                ,"import A"-                ,"b :: Int"-                ,"b = $( a )" ]-        _ <- createDoc "A.hs" "haskell" sourceA-        _ <- createDoc "B.hs" "haskell" sourceB-        return ()-    , thReloadingTest False-    , thLoadingTest-    , thCoreTest-    , ignoreInWindowsBecause "Broken in windows" $ thReloadingTest True-    -- Regression test for https://github.com/haskell/haskell-language-server/issues/891-    , thLinkingTest False-    , ignoreInWindowsBecause "Broken in windows" $ thLinkingTest True-    , testSessionWait "findsTHIdentifiers" $ do-        let sourceA =-              T.unlines-                [ "{-# LANGUAGE TemplateHaskell #-}"-                , "module A (a) where"-                , "import Language.Haskell.TH (ExpQ)"-                , "a :: ExpQ" -- TH 2.17 requires an explicit type signature since splices are polymorphic-                , "a = [| glorifiedID |]"-                , "glorifiedID :: a -> a"-                , "glorifiedID = id" ]-        let sourceB =-              T.unlines-                [ "{-# OPTIONS_GHC -Wall #-}"-                , "{-# LANGUAGE TemplateHaskell #-}"-                , "module B where"-                , "import A"-                , "main = $a (putStrLn \"success!\")"]-        _ <- createDoc "A.hs" "haskell" sourceA-        _ <- createDoc "B.hs" "haskell" sourceB-        expectDiagnostics [ ( "B.hs", [(DsWarning, (4, 0), "Top-level binding with no type signature: main :: IO ()")] ) ]-    , testCase "findsTHnewNameConstructor" $ runWithExtraFiles "THNewName" $ \dir -> do--    -- This test defines a TH value with the meaning "data A = A" in A.hs-    -- Loads and export the template in B.hs-    -- And checks wether the constructor A can be loaded in C.hs-    -- This test does not fail when either A and B get manually loaded before C.hs-    -- or when we remove the seemingly unnecessary TH pragma from C.hs--    let cPath = dir </> "C.hs"-    _ <- openDoc cPath "haskell"-    expectDiagnostics [ ( cPath, [(DsWarning, (3, 0), "Top-level binding with no type signature: a :: A")] ) ]-    ]---- | Tests for projects that use symbolic links one way or another-symlinkTests :: TestTree-symlinkTests =-  testGroup "Projects using Symlinks"-    [ testCase "Module is symlinked" $ runWithExtraFiles "symlink" $ \dir -> do-        liftIO $ createFileLink (dir </> "some_loc" </> "Sym.hs") (dir </> "other_loc" </> "Sym.hs")-        let fooPath = dir </> "src" </> "Foo.hs"-        _ <- openDoc fooPath "haskell"-        expectDiagnosticsWithTags  [("src" </> "Foo.hs", [(DsWarning, (2, 0), "The import of 'Sym' is redundant", Just DtUnnecessary)])]-        pure ()-    ]---- | Test that all modules have linkables-thLoadingTest :: TestTree-thLoadingTest = testCase "Loading linkables" $ runWithExtraFiles "THLoading" $ \dir -> do-    let thb = dir </> "THB.hs"-    _ <- openDoc thb "haskell"-    expectNoMoreDiagnostics 1--thCoreTest :: TestTree-thCoreTest = testCase "Verifying TH core files" $ runWithExtraFiles "THCoreFile" $ \dir -> do-    let thc = dir </> "THC.hs"-    _ <- openDoc thc "haskell"-    expectNoMoreDiagnostics 1---- | test that TH is reevaluated on typecheck-thReloadingTest :: Bool -> TestTree-thReloadingTest unboxed = testCase name $ runWithExtraFiles dir $ \dir -> do--    let aPath = dir </> "THA.hs"-        bPath = dir </> "THB.hs"-        cPath = dir </> "THC.hs"--    aSource <- liftIO $ readFileUtf8 aPath --  th = [d|a = ()|]-    bSource <- liftIO $ readFileUtf8 bPath --  $th-    cSource <- liftIO $ readFileUtf8 cPath --  c = a :: ()--    adoc <- createDoc aPath "haskell" aSource-    bdoc <- createDoc bPath "haskell" bSource-    cdoc <- createDoc cPath "haskell" cSource--    expectDiagnostics [("THB.hs", [(DsWarning, (4,thDollarIdx), "Top-level binding")])]--    -- Change th from () to Bool-    let aSource' = T.unlines $ init (T.lines aSource) ++ ["th_a = [d| a = False|]"]-    changeDoc adoc [TextDocumentContentChangeEvent Nothing Nothing aSource']-    -- generate an artificial warning to avoid timing out if the TH change does not propagate-    changeDoc cdoc [TextDocumentContentChangeEvent Nothing Nothing $ cSource <> "\nfoo=()"]--    -- Check that the change propagates to C-    expectDiagnostics-        [("THC.hs", [(DsError, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'")])-        ,("THC.hs", [(DsWarning, (6,0), "Top-level binding")])-        ,("THB.hs", [(DsWarning, (4,thDollarIdx), "Top-level bindin")])-        ]--    closeDoc adoc-    closeDoc bdoc-    closeDoc cdoc-  where-    name = "reloading-th-test" <> if unboxed then "-unboxed" else ""-    dir | unboxed = "THUnboxed"-        | otherwise = "TH"--thLinkingTest :: Bool -> TestTree-thLinkingTest unboxed = testCase name $ runWithExtraFiles dir $ \dir -> do--    let aPath = dir </> "THA.hs"-        bPath = dir </> "THB.hs"--    aSource <- liftIO $ readFileUtf8 aPath --  th_a = [d|a :: ()|]-    bSource <- liftIO $ readFileUtf8 bPath --  $th_a--    adoc <- createDoc aPath "haskell" aSource-    bdoc <- createDoc bPath "haskell" bSource--    expectDiagnostics [("THB.hs", [(DsWarning, (4,thDollarIdx), "Top-level binding")])]--    let aSource' = T.unlines $ init (init (T.lines aSource)) ++ ["th :: DecsQ", "th = [d| a = False|]"]-    changeDoc adoc [TextDocumentContentChangeEvent Nothing Nothing aSource']--    -- modify b too-    let bSource' = T.unlines $ init (T.lines bSource) ++ ["$th"]-    changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing bSource']-    waitForProgressBegin-    waitForAllProgressDone--    expectCurrentDiagnostics bdoc [(DsWarning, (4,thDollarIdx), "Top-level binding")]--    closeDoc adoc-    closeDoc bdoc-  where-    name = "th-linking-test" <> if unboxed then "-unboxed" else ""-    dir | unboxed = "THUnboxed"-        | otherwise = "TH"--completionTests :: TestTree-completionTests-  = testGroup "completion"-    [-    testGroup "non local" nonLocalCompletionTests-    , testGroup "topLevel" topLevelCompletionTests-    , testGroup "local" localCompletionTests-    , testGroup "package" packageCompletionTests-    , testGroup "project" projectCompletionTests-    , testGroup "other" otherCompletionTests-    , testGroup "doc" completionDocTests-    ]--completionTest :: HasCallStack => String -> [T.Text] -> Position -> [(T.Text, CompletionItemKind, T.Text, Bool, Bool, Maybe (List TextEdit))] -> TestTree-completionTest name src pos expected = testSessionWait name $ do-    docId <- createDoc "A.hs" "haskell" (T.unlines src)-    _ <- waitForDiagnostics-    compls <- getCompletions docId pos-    let compls' = [ (_label, _kind, _insertText, _additionalTextEdits) | CompletionItem{..} <- compls]-    let emptyToMaybe x = if T.null x then Nothing else Just x-    liftIO $ sortOn (Lens.view Lens._1) (take (length expected) compls') @?=-        sortOn (Lens.view Lens._1)-          [ (l, Just k, emptyToMaybe t, at) | (l,k,t,_,_,at) <- expected]-    forM_ (zip compls expected) $ \(item, (_,_,_,expectedSig, expectedDocs, _)) -> do-        CompletionItem{..} <--          if expectedSig || expectedDocs-          then do-            rsp <- request SCompletionItemResolve item-            case rsp ^. L.result of-              Left err -> liftIO $ assertFailure ("completionItem/resolve failed with: " <> show err)-              Right x -> pure x-          else pure item-        when expectedSig $-            liftIO $ assertBool ("Missing type signature: " <> T.unpack _label) (isJust _detail)-        when expectedDocs $-            liftIO $ assertBool ("Missing docs: " <> T.unpack _label) (isJust _documentation)---topLevelCompletionTests :: [TestTree]-topLevelCompletionTests = [-    completionTest-        "variable"-        ["bar = xx", "-- | haddock", "xxx :: ()", "xxx = ()", "-- | haddock", "data Xxx = XxxCon"]-        (Position 0 8)-        [("xxx", CiFunction, "xxx", True, True, Nothing)-        ],-    completionTest-        "constructor"-        ["bar = xx", "-- | haddock", "xxx :: ()", "xxx = ()", "-- | haddock", "data Xxx = XxxCon"]-        (Position 0 8)-        [("xxx", CiFunction, "xxx", True, True, Nothing)-        ],-    completionTest-        "class method"-        ["bar = xx", "class Xxx a where", "-- | haddock", "xxx :: ()", "xxx = ()"]-        (Position 0 8)-        [("xxx", CiFunction, "xxx", True, True, Nothing)],-    completionTest-        "type"-        ["bar :: Xz", "zzz = ()", "-- | haddock", "data Xzz = XzzCon"]-        (Position 0 9)-        [("Xzz", CiStruct, "Xzz", False, True, Nothing)],-    completionTest-        "class"-        ["bar :: Xz", "zzz = ()", "-- | haddock", "class Xzz a"]-        (Position 0 9)-        [("Xzz", CiInterface, "Xzz", False, True, Nothing)],-    completionTest-        "records"-        ["data Person = Person { _personName:: String, _personAge:: Int}", "bar = Person { _pers }" ]-        (Position 1 19)-        [("_personName", CiFunction, "_personName", False, True, Nothing),-         ("_personAge", CiFunction, "_personAge", False, True, Nothing)],-    completionTest-        "recordsConstructor"-        ["data XxRecord = XyRecord { x:: String, y:: Int}", "bar = Xy" ]-        (Position 1 19)-        [("XyRecord", CiConstructor, "XyRecord", False, True, Nothing),-         ("XyRecord", CiSnippet, "XyRecord {x=${1:_x}, y=${2:_y}}", False, True, Nothing)]-    ]--localCompletionTests :: [TestTree]-localCompletionTests = [-    completionTest-        "argument"-        ["bar (Just abcdef) abcdefg = abcd"]-        (Position 0 32)-        [("abcdef", CiFunction, "abcdef", True, False, Nothing),-         ("abcdefg", CiFunction , "abcdefg", True, False, Nothing)-        ],-    completionTest-        "let"-        ["bar = let (Just abcdef) = undefined"-        ,"          abcdefg = let abcd = undefined in undefined"-        ,"        in abcd"-        ]-        (Position 2 15)-        [("abcdef", CiFunction, "abcdef", True, False, Nothing),-         ("abcdefg", CiFunction , "abcdefg", True, False, Nothing)-        ],-    completionTest-        "where"-        ["bar = abcd"-        ,"  where (Just abcdef) = undefined"-        ,"        abcdefg = let abcd = undefined in undefined"-        ]-        (Position 0 10)-        [("abcdef", CiFunction, "abcdef", True, False, Nothing),-         ("abcdefg", CiFunction , "abcdefg", True, False, Nothing)-        ],-    completionTest-        "do/1"-        ["bar = do"-        ,"  Just abcdef <- undefined"-        ,"  abcd"-        ,"  abcdefg <- undefined"-        ,"  pure ()"-        ]-        (Position 2 6)-        [("abcdef", CiFunction, "abcdef", True, False, Nothing)-        ],-    completionTest-        "do/2"-        ["bar abcde = do"-        ,"    Just [(abcdef,_)] <- undefined"-        ,"    abcdefg <- undefined"-        ,"    let abcdefgh = undefined"-        ,"        (Just [abcdefghi]) = undefined"-        ,"    abcd"-        ,"  where"-        ,"    abcdefghij = undefined"-        ]-        (Position 5 8)-        [("abcde", CiFunction, "abcde", True, False, Nothing)-        ,("abcdefghij", CiFunction, "abcdefghij", True, False, Nothing)-        ,("abcdef", CiFunction, "abcdef", True, False, Nothing)-        ,("abcdefg", CiFunction, "abcdefg", True, False, Nothing)-        ,("abcdefgh", CiFunction, "abcdefgh", True, False, Nothing)-        ,("abcdefghi", CiFunction, "abcdefghi", True, False, Nothing)-        ],-    completionTest-        "type family"-        ["{-# LANGUAGE DataKinds, TypeFamilies #-}"-        ,"type family Bar a"-        ,"a :: Ba"-        ]-        (Position 2 7)-        [("Bar", CiStruct, "Bar", True, False, Nothing)-        ],-    completionTest-        "class method"-        [-          "class Test a where"-        , "    abcd :: a -> ()"-        , "    abcde :: a -> Int"-        , "instance Test Int where"-        , "    abcd = abc"-        ]-        (Position 4 14)-        [("abcd", CiFunction, "abcd", True, False, Nothing)-        ,("abcde", CiFunction, "abcde", True, False, Nothing)-        ],-    testSessionWait "incomplete entries" $ do-        let src a = "data Data = " <> a-        doc <- createDoc "A.hs" "haskell" $ src "AAA"-        void $ waitForTypecheck doc-        let editA rhs =-                changeDoc doc [TextDocumentContentChangeEvent-                    { _range=Nothing-                    , _rangeLength=Nothing-                    , _text=src rhs}]--        editA "AAAA"-        void $ waitForTypecheck doc-        editA "AAAAA"-        void $ waitForTypecheck doc--        compls <- getCompletions doc (Position 0 15)-        liftIO $ filter ("AAA" `T.isPrefixOf`) (mapMaybe _insertText compls) @?= ["AAAAA"]-        pure ()-    ]--nonLocalCompletionTests :: [TestTree]-nonLocalCompletionTests =-  [ brokenForWinGhc $ completionTest-      "variable"-      ["module A where", "f = hea"]-      (Position 1 7)-      [("head", CiFunction, "head", True, True, Nothing)],-    completionTest-      "constructor"-      ["{-# OPTIONS_GHC -Wall #-}", "module A where", "f = True"]-      (Position 2 8)-      [ ("True", CiConstructor, "True", True, True, Nothing)-      ],-    brokenForWinGhc $ completionTest-      "type"-      ["{-# OPTIONS_GHC -Wall #-}", "module A () where", "f :: Boo", "f = True"]-      (Position 2 8)-      [ ("Bool", CiStruct, "Bool", True, True, Nothing)-      ],-    completionTest-      "qualified"-      ["{-# OPTIONS_GHC -Wunused-binds #-}", "module A () where", "f = Prelude.hea"]-      (Position 2 15)-      [ ("head", CiFunction, "head", True, True, Nothing)-      ],-    completionTest-      "duplicate import"-      ["module A where", "import Data.List", "import Data.List", "f = permu"]-      (Position 3 9)-      [ ("permutations", CiFunction, "permutations", False, False, Nothing)-      ],-    completionTest-       "dont show hidden items"-       [ "{-# LANGUAGE NoImplicitPrelude #-}",-         "module A where",-         "import Control.Monad hiding (join)",-         "f = joi"-       ]-       (Position 3 6)-       [],-    testGroup "ordering"-      [completionTest "qualified has priority"-        ["module A where"-        ,"import qualified Data.ByteString as BS"-        ,"f = BS.read"-        ]-        (Position 2 10)-        [("readFile", CiFunction, "readFile", True, True, Nothing)]-        ],-      -- we need this test to make sure the ghcide completions module does not return completions for language pragmas. this functionality is turned on in hls-     completionTest-      "do not show pragma completions"-      [ "{-# LANGUAGE  ",-        "{module A where}",-        "main = return ()"-      ]-      (Position 0 13)-      []-  ]-  where-    brokenForWinGhc = knownBrokenFor (BrokenSpecific Windows [GHC810, GHC90, GHC92, GHC94, GHC96]) "Windows has strange things in scope for some reason"--otherCompletionTests :: [TestTree]-otherCompletionTests = [-    completionTest-      "keyword"-      ["module A where", "f = newty"]-      (Position 1 9)-      [("newtype", CiKeyword, "", False, False, Nothing)],-    completionTest-      "type context"-      [ "{-# OPTIONS_GHC -Wunused-binds #-}",-        "module A () where",-        "f = f",-        "g :: Intege"-      ]-      -- At this point the module parses but does not typecheck.-      -- This should be sufficient to detect that we are in a-      -- type context and only show the completion to the type.-      (Position 3 11)-      [("Integer", CiStruct, "Integer", True, True, Nothing)],--    testSession "duplicate record fields" $ do-      void $-        createDoc "B.hs" "haskell" $-          T.unlines-            [ "{-# LANGUAGE DuplicateRecordFields #-}",-              "module B where",-              "newtype Foo = Foo { member :: () }",-              "newtype Bar = Bar { member :: () }"-            ]-      docA <--        createDoc "A.hs" "haskell" $-          T.unlines-            [ "module A where",-              "import B",-              "memb"-            ]-      _ <- waitForDiagnostics-      compls <- getCompletions docA $ Position 2 4-      let compls' = [txt | CompletionItem {_insertText = Just txt, ..} <- compls, _label == "member"]-      liftIO $ take 2 compls' @?= ["member"],--    testSessionWait "maxCompletions" $ do-        doc <- createDoc "A.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wunused-binds #-}",-                "module A () where",-                "a = Prelude."-            ]-        _ <- waitForDiagnostics-        compls <- getCompletions  doc (Position 3 13)-        liftIO $ length compls @?= maxCompletions def-  ]--packageCompletionTests :: [TestTree]-packageCompletionTests =-  [ testSession' "fromList" $ \dir -> do-        liftIO $ writeFile (dir </> "hie.yaml")-            "cradle: {direct: {arguments: [-hide-all-packages, -package, base, A]}}"-        doc <- createDoc "A.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wunused-binds #-}",-                "module A () where",-                "a = fromList"-            ]-        _ <- waitForDiagnostics-        compls <- getCompletions doc (Position 2 12)-        let compls' =-              [T.drop 1 $ T.dropEnd 3 d-              | CompletionItem {_documentation = Just (CompletionDocMarkup (MarkupContent MkMarkdown d)), _label}-                <- compls-              , _label == "fromList"-              ]-        liftIO $ take 3 (sort compls') @?=-          map ("Defined in "<>) (-              [ "'Data.List.NonEmpty"-              , "'GHC.Exts"-              ] ++ if ghcVersion >= GHC94 then [ "'GHC.IsList" ] else [])--  , testSessionWait "Map" $ do-        doc <- createDoc "A.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wunused-binds #-}",-                "module A () where",-                "a :: Map"-            ]-        _ <- waitForDiagnostics-        compls <- getCompletions doc (Position 2 7)-        let compls' =-              [T.drop 1 $ T.dropEnd 3 d-              | CompletionItem {_documentation = Just (CompletionDocMarkup (MarkupContent MkMarkdown d)), _label}-                <- compls-              , _label == "Map"-              ]-        liftIO $ take 3 (sort compls') @?=-          map ("Defined in "<>)-              [ "'Data.Map"-              , "'Data.Map.Lazy"-              , "'Data.Map.Strict"-              ]-  , testSessionWait "no duplicates" $ do-        doc <- createDoc "A.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wunused-binds #-}",-                "module A () where",-                "import GHC.Exts(fromList)",-                "a = fromList"-            ]-        _ <- waitForDiagnostics-        compls <- getCompletions doc (Position 3 13)-        let duplicate =-              filter-                (\case-                  CompletionItem-                    { _insertText = Just "fromList"-                    , _documentation =-                      Just (CompletionDocMarkup (MarkupContent MkMarkdown d))-                    } ->-                    "GHC.Exts" `T.isInfixOf` d-                  _ -> False-                ) compls-        liftIO $ length duplicate @?= 1--  , testSessionWait "non-local before global" $ do-    -- non local completions are more specific-        doc <- createDoc "A.hs" "haskell" $ T.unlines-            [ "{-# OPTIONS_GHC -Wunused-binds #-}",-                "module A () where",-                "import GHC.Exts(fromList)",-                "a = fromList"-            ]-        _ <- waitForDiagnostics-        compls <- getCompletions doc (Position 3 13)-        let compls' =-              [_insertText-              | CompletionItem {_label, _insertText} <- compls-              , _label == "fromList"-              ]-        liftIO $ take 3 compls' @?=-          map Just ["fromList"]-  ]--projectCompletionTests :: [TestTree]-projectCompletionTests =-    [ testSession' "from hiedb" $ \dir-> do-        liftIO $ writeFile (dir </> "hie.yaml")-            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"-        _ <- createDoc "A.hs" "haskell" $ T.unlines-            [  "module A (anidentifier) where",-               "anidentifier = ()"-            ]-        _ <- waitForDiagnostics-        -- Note that B does not import A-        doc <- createDoc "B.hs" "haskell" $ T.unlines-            [ "module B where",-              "b = anidenti"-            ]-        compls <- getCompletions doc (Position 1 10)-        let compls' =-              [T.drop 1 $ T.dropEnd 3 d-              | CompletionItem {_documentation = Just (CompletionDocMarkup (MarkupContent MkMarkdown d)), _label}-                <- compls-              , _label == "anidentifier"-              ]-        liftIO $ compls' @?= ["Defined in 'A"],-      testSession' "auto complete project imports" $ \dir-> do-        liftIO $ writeFile (dir </> "hie.yaml")-            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"ALocalModule\", \"B\"]}}"-        _ <- createDoc "ALocalModule.hs" "haskell" $ T.unlines-            [  "module ALocalModule (anidentifier) where",-               "anidentifier = ()"-            ]-        _ <- waitForDiagnostics-        -- Note that B does not import A-        doc <- createDoc "B.hs" "haskell" $ T.unlines-            [ "module B where",-              "import ALocal"-            ]-        compls <- getCompletions doc (Position 1 13)-        let item = head $ filter ((== "ALocalModule") . (^. Lens.label)) compls-        liftIO $ do-          item ^. Lens.label @?= "ALocalModule",-      testSession' "auto complete functions from qualified imports without alias" $ \dir-> do-        liftIO $ writeFile (dir </> "hie.yaml")-            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"-        _ <- createDoc "A.hs" "haskell" $ T.unlines-            [  "module A (anidentifier) where",-               "anidentifier = ()"-            ]-        _ <- waitForDiagnostics-        doc <- createDoc "B.hs" "haskell" $ T.unlines-            [ "module B where",-              "import qualified A",-              "A."-            ]-        compls <- getCompletions doc (Position 2 2)-        let item = head compls-        liftIO $ do-          item ^. L.label @?= "anidentifier",-      testSession' "auto complete functions from qualified imports with alias" $ \dir-> do-        liftIO $ writeFile (dir </> "hie.yaml")-            "cradle: {direct: {arguments: [\"-Wmissing-signatures\", \"A\", \"B\"]}}"-        _ <- createDoc "A.hs" "haskell" $ T.unlines-            [  "module A (anidentifier) where",-               "anidentifier = ()"-            ]-        _ <- waitForDiagnostics-        doc <- createDoc "B.hs" "haskell" $ T.unlines-            [ "module B where",-              "import qualified A as Alias",-              "foo = Alias."-            ]-        compls <- getCompletions doc (Position 2 12)-        let item = head compls-        liftIO $ do-          item ^. L.label @?= "anidentifier"-    ]--completionDocTests :: [TestTree]-completionDocTests =-  [ testSession "local define" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = ()"-        , "bar = fo"-        ]-      let expected = "*Defined at line 2, column 1 in this module*\n"-      test doc (Position 2 8) "foo" Nothing [expected]-  , testSession "local empty doc" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = ()"-        , "bar = fo"-        ]-      test doc (Position 2 8) "foo" Nothing ["*Defined at line 2, column 1 in this module*\n"]-  , testSession "local single line doc without newline" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "-- |docdoc"-        , "foo = ()"-        , "bar = fo"-        ]-      test doc (Position 3 8) "foo" Nothing ["*Defined at line 3, column 1 in this module*\n* * *\n\n\ndocdoc\n"]-  , testSession "local multi line doc with newline" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "-- | abcabc"-        , "--"-        , "foo = ()"-        , "bar = fo"-        ]-      test doc (Position 4 8) "foo" Nothing ["*Defined at line 4, column 1 in this module*\n* * *\n\n\nabcabc\n"]-  , testSession "local multi line doc without newline" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "-- |     abcabc"-        , "--"-        , "--def"-        , "foo = ()"-        , "bar = fo"-        ]-      test doc (Position 5 8) "foo" Nothing ["*Defined at line 5, column 1 in this module*\n* * *\n\n\nabcabc \n\ndef\n"]-  , testSession "extern empty doc" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = od"-        ]-      let expected = "*Imported from 'Prelude'*\n"-      test doc (Position 1 8) "odd" (Just $ T.length expected) [expected]-  , brokenForMacGhc9 $ brokenForWinGhc90 $ testSession "extern single line doc without '\\n'" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = no"-        ]-      let expected = "*Imported from 'Prelude'*\n* * *\n\n\nBoolean \"not\"\n"-      test doc (Position 1 8) "not" (Just $ T.length expected) [expected]-  , brokenForMacGhc9 $ brokenForWinGhc90 $ testSession "extern mulit line doc" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = i"-        ]-      let expected = "*Imported from 'Prelude'*\n* * *\n\n\nIdentity function. \n```haskell\nid x = x\n```\n"-      test doc (Position 1 7) "id" (Just $ T.length expected) [expected]-  , testSession "extern defined doc" $ do-      doc <- createDoc "A.hs" "haskell" $ T.unlines-        [ "module A where"-        , "foo = i"-        ]-      let expected = "*Imported from 'Prelude'*\n"-      test doc (Position 1 7) "id" (Just $ T.length expected) [expected]-  ]-  where-    brokenForGhc9 = knownBrokenFor (BrokenForGHC [GHC90, GHC92, GHC94, GHC96]) "Completion doc doesn't support ghc9"-    brokenForWinGhc90 = knownBrokenFor (BrokenSpecific Windows [GHC90]) "Extern doc doesn't support Windows for ghc9.2"-    -- https://gitlab.haskell.org/ghc/ghc/-/issues/20903-    brokenForMacGhc9 = knownBrokenFor (BrokenSpecific MacOS [GHC90, GHC92, GHC94, GHC96]) "Extern doc doesn't support MacOS for ghc9"-    test doc pos label mn expected = do-      _ <- waitForDiagnostics-      compls <- getCompletions doc pos-      rcompls <- forM compls $ \item -> do-        rsp <- request SCompletionItemResolve item-        case rsp ^. L.result of-          Left err -> liftIO $ assertFailure ("completionItem/resolve failed with: " <> show err)-          Right x -> pure x-      let compls' = [-            -- We ignore doc uris since it points to the local path which determined by specific machines-            case mn of-                Nothing -> txt-                Just n  -> T.take n txt-            | CompletionItem {_documentation = Just (CompletionDocMarkup (MarkupContent MkMarkdown txt)), ..} <- rcompls-            , _label == label-            ]-      liftIO $ compls' @?= expected--highlightTests :: TestTree-highlightTests = testGroup "highlight"-  [ testSessionWait "value" $ do-    doc <- createDoc "A.hs" "haskell" source-    _ <- waitForDiagnostics-    highlights <- getHighlights doc (Position 3 2)-    liftIO $ highlights @?= List-            [ DocumentHighlight (R 2 0 2 3) (Just HkRead)-            , DocumentHighlight (R 3 0 3 3) (Just HkWrite)-            , DocumentHighlight (R 4 6 4 9) (Just HkRead)-            , DocumentHighlight (R 5 22 5 25) (Just HkRead)-            ]-  , testSessionWait "type" $ do-    doc <- createDoc "A.hs" "haskell" source-    _ <- waitForDiagnostics-    highlights <- getHighlights doc (Position 2 8)-    liftIO $ highlights @?= List-            [ DocumentHighlight (R 2 7 2 10) (Just HkRead)-            , DocumentHighlight (R 3 11 3 14) (Just HkRead)-            ]-  , testSessionWait "local" $ do-    doc <- createDoc "A.hs" "haskell" source-    _ <- waitForDiagnostics-    highlights <- getHighlights doc (Position 6 5)-    liftIO $ highlights @?= List-            [ DocumentHighlight (R 6 4 6 7) (Just HkWrite)-            , DocumentHighlight (R 6 10 6 13) (Just HkRead)-            , DocumentHighlight (R 7 12 7 15) (Just HkRead)-            ]-  , knownBrokenForGhcVersions [GHC90, GHC92, GHC94, GHC96] "Ghc9 highlights the constructor and not just this field" $-        testSessionWait "record" $ do-        doc <- createDoc "A.hs" "haskell" recsource-        _ <- waitForDiagnostics-        highlights <- getHighlights doc (Position 4 15)-        liftIO $ highlights @?= List-          -- Span is just the .. on 8.10, but Rec{..} before-          [ if ghcVersion >= GHC810-            then DocumentHighlight (R 4 8 4 10) (Just HkWrite)-            else DocumentHighlight (R 4 4 4 11) (Just HkWrite)-          , DocumentHighlight (R 4 14 4 20) (Just HkRead)-          ]-        highlights <- getHighlights doc (Position 3 17)-        liftIO $ highlights @?= List-          [ DocumentHighlight (R 3 17 3 23) (Just HkWrite)-          -- Span is just the .. on 8.10, but Rec{..} before-          , if ghcVersion >= GHC810-              then DocumentHighlight (R 4 8 4 10) (Just HkRead)-              else DocumentHighlight (R 4 4 4 11) (Just HkRead)-          ]-  ]-  where-    source = T.unlines-      ["{-# OPTIONS_GHC -Wunused-binds #-}"-      ,"module Highlight () where"-      ,"foo :: Int"-      ,"foo = 3 :: Int"-      ,"bar = foo"-      ,"  where baz = let x = foo in x"-      ,"baz arg = arg + x"-      ,"  where x = arg"-      ]-    recsource = T.unlines-      ["{-# LANGUAGE RecordWildCards #-}"-      ,"{-# OPTIONS_GHC -Wunused-binds #-}"-      ,"module Highlight () where"-      ,"data Rec = Rec { field1 :: Int, field2 :: Char }"-      ,"foo Rec{..} = field2 + field1"-      ]--outlineTests :: TestTree-outlineTests = testGroup-  "outline"-  [ testSessionWait "type class" $ do-    let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ moduleSymbol-          "A"-          (R 0 7 0 8)-          [ classSymbol "A a"-                        (R 1 0 1 30)-                        [docSymbol' "a" SkMethod (R 1 16 1 30) (R 1 16 1 17)]-          ]-      ]-  , testSessionWait "type class instance " $ do-    let source = T.unlines ["class A a where", "instance A () where"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ classSymbol "A a" (R 0 0 0 15) []-      , docSymbol "A ()" SkInterface (R 1 0 1 19)-      ]-  , testSessionWait "type family" $ do-    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" SkFunction (R 1 0 1 13)]-  , testSessionWait "type family instance " $ do-    let source = T.unlines-          [ "{-# language TypeFamilies #-}"-          , "type family A a"-          , "type instance A () = ()"-          ]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ 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" SkFunction (R 1 0 1 11)]-  , testSessionWait "data family instance " $ do-    let source = T.unlines-          [ "{-# language TypeFamilies #-}"-          , "data family A a"-          , "data instance A () = A ()"-          ]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ docSymbolD "A a"   "data family" SkFunction     (R 1 0 1 11)-      , docSymbol "A ()" SkInterface (R 2 0 2 25)-      ]-  , testSessionWait "constant" $ do-    let source = T.unlines ["a = ()"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbol "a" SkFunction (R 0 0 0 6)]-  , testSessionWait "pattern" $ do-    let source = T.unlines ["Just foo = Just 21"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbol "Just foo" SkFunction (R 0 0 0 18)]-  , testSessionWait "pattern with type signature" $ do-    let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbol "a :: ()" SkFunction (R 1 0 1 12)]-  , testSessionWait "function" $ do-    let source = T.unlines ["a _x = ()"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left [docSymbol "a" SkFunction (R 0 0 0 9)]-  , testSessionWait "type synonym" $ do-    let source = T.unlines ["type A = Bool"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbol' "A" SkTypeParameter (R 0 0 0 13) (R 0 5 0 6)]-  , testSessionWait "datatype" $ do-    let source = T.unlines ["data A = C"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ docSymbolWithChildren "A"-                              SkStruct-                              (R 0 0 0 10)-                              [docSymbol "C" SkConstructor (R 0 9 0 10)]-      ]-  , testSessionWait "record fields" $ do-    let source = T.unlines ["data A = B {", "  x :: Int", "  , y :: Int}"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [ docSymbolWithChildren "A" SkStruct (R 0 0 2 13)-          [ docSymbolWithChildren' "B" SkConstructor (R 0 9 2 13) (R 0 9 0 10)-            [ docSymbol "x" SkField (R 1 2 1 3)-            , docSymbol "y" SkField (R 2 4 2 5)-            ]-          ]-      ]-  , testSessionWait "import" $ do-    let source = T.unlines ["import Data.Maybe ()"]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbolWithChildren "imports"-                             SkModule-                             (R 0 0 0 20)-                             [ docSymbol "import Data.Maybe" SkModule (R 0 0 0 20)-                             ]-      ]-  , testSessionWait "multiple import" $ do-    let source = T.unlines ["", "import Data.Maybe ()", "", "import Control.Exception ()", ""]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left-      [docSymbolWithChildren "imports"-                             SkModule-                             (R 1 0 3 27)-                             [ docSymbol "import Data.Maybe" SkModule (R 1 0 1 20)-                             , docSymbol "import Control.Exception" SkModule (R 3 0 3 27)-                             ]-      ]-  , testSessionWait "foreign import" $ do-    let source = T.unlines-          [ "{-# language ForeignFunctionInterface #-}"-          , "foreign import ccall \"a\" a :: Int"-          ]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left [docSymbolD "a" "import" SkObject (R 1 0 1 33)]-  , testSessionWait "foreign export" $ do-    let source = T.unlines-          [ "{-# language ForeignFunctionInterface #-}"-          , "foreign export ccall odd :: Int -> Bool"-          ]-    docId   <- createDoc "A.hs" "haskell" source-    symbols <- getDocumentSymbols docId-    liftIO $ symbols @?= Left [docSymbolD "odd" "export" SkObject (R 1 0 1 39)]-  ]- where-  docSymbol name kind loc =-    DocumentSymbol name Nothing kind Nothing Nothing loc loc Nothing-  docSymbol' name kind loc selectionLoc =-    DocumentSymbol name Nothing kind Nothing Nothing loc selectionLoc Nothing-  docSymbolD name detail kind loc =-    DocumentSymbol name (Just detail) kind Nothing Nothing loc loc Nothing-  docSymbolWithChildren name kind loc cc =-    DocumentSymbol name Nothing kind Nothing Nothing loc loc (Just $ List cc)-  docSymbolWithChildren' name kind loc selectionLoc 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")-                                           SkInterface-                                           Nothing-                                           Nothing-                                           loc-                                           loc-                                           (Just $ List cc)--pattern R :: UInt -> UInt -> UInt -> UInt -> Range-pattern R x y x' y' = Range (Position x y) (Position x' y')--xfail :: TestTree -> String -> TestTree-xfail = flip expectFailBecause--ignoreInWindowsBecause :: String -> TestTree -> TestTree-ignoreInWindowsBecause = ignoreFor (BrokenForOS Windows)--ignoreInWindowsForGHC810 :: TestTree -> TestTree-ignoreInWindowsForGHC810 =-    ignoreFor (BrokenSpecific Windows [GHC810]) "tests are unreliable in windows for ghc 8.10"--ignoreForGHC92Plus :: String -> TestTree -> TestTree-ignoreForGHC92Plus = ignoreFor (BrokenForGHC [GHC92, GHC94, GHC96])--knownBrokenForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree-knownBrokenForGhcVersions ghcVers = knownBrokenFor (BrokenForGHC ghcVers)--data BrokenOS = Linux | MacOS | Windows deriving (Show)--data IssueSolution = Broken | Ignore deriving (Show)--data BrokenTarget =-    BrokenSpecific BrokenOS [GhcVersion]-    -- ^Broken for `BrokenOS` with `GhcVersion`-    | BrokenForOS BrokenOS-    -- ^Broken for `BrokenOS`-    | BrokenForGHC [GhcVersion]-    -- ^Broken for `GhcVersion`-    deriving (Show)---- | Ignore test for specific os and ghc with reason.-ignoreFor :: BrokenTarget -> String -> TestTree -> TestTree-ignoreFor = knownIssueFor Ignore---- | Known broken for specific os and ghc with reason.-knownBrokenFor :: BrokenTarget -> String -> TestTree -> TestTree-knownBrokenFor = knownIssueFor Broken---- | Deal with `IssueSolution` for specific OS and GHC.-knownIssueFor :: IssueSolution -> BrokenTarget -> String -> TestTree -> TestTree-knownIssueFor solution = go . \case-    BrokenSpecific bos vers -> isTargetOS bos && isTargetGhc vers-    BrokenForOS bos         -> isTargetOS bos-    BrokenForGHC vers       -> isTargetGhc vers-    where-        isTargetOS = \case-            Windows -> isWindows-            MacOS   -> isMac-            Linux   -> not isWindows && not isMac--        isTargetGhc = elem ghcVersion--        go True = case solution of-            Broken -> expectFailBecause-            Ignore -> ignoreTestBecause-        go False = \_ -> id--data Expect-  = ExpectRange Range -- Both gotoDef and hover should report this range-  | ExpectLocation Location---  | ExpectDefRange Range -- Only gotoDef should report this range-  | ExpectHoverRange Range -- Only hover should report this range-  | ExpectHoverText [T.Text] -- the hover message must contain these snippets-  | ExpectHoverExcludeText [T.Text] -- the hover message must _not_ contain these snippets-  | ExpectHoverTextRegex T.Text -- the hover message must match this pattern-  | ExpectExternFail -- definition lookup in other file expected to fail-  | ExpectNoDefinitions-  | ExpectNoHover---  | ExpectExtern -- TODO: as above, but expected to succeed: need some more info in here, once we have some working examples-  deriving Eq--mkR :: UInt -> UInt -> UInt -> UInt -> Expect-mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn--mkL :: Uri -> UInt -> UInt -> UInt -> UInt -> Expect-mkL uri startLine startColumn endLine endColumn = ExpectLocation $ Location uri $ mkRange startLine startColumn endLine endColumn--haddockTests :: TestTree-haddockTests-  = testGroup "haddock"-      [ testCase "Num" $ checkHaddock-          (unlines-             [ "However, '(+)' and '(*)' are"-             , "customarily expected to define a ring and have the following properties:"-             , ""-             , "[__Associativity of (+)__]: @(x + y) + z@ = @x + (y + z)@"-             , "[__Commutativity of (+)__]: @x + y@ = @y + x@"-             , "[__@fromInteger 0@ is the additive identity__]: @x + fromInteger 0@ = @x@"-             ]-          )-          (unlines-             [ ""-             , ""-             , "However,  `(+)`  and  `(*)`  are"-             , "customarily expected to define a ring and have the following properties: "-             , "+ ****Associativity of (+)****: `(x + y) + z`  =  `x + (y + z)`"-             , "+ ****Commutativity of (+)****: `x + y`  =  `y + x`"-             , "+ ****`fromInteger 0`  is the additive identity****: `x + fromInteger 0`  =  `x`"-             ]-          )-      , testCase "unsafePerformIO" $ checkHaddock-          (unlines-             [ "may require"-             , "different precautions:"-             , ""-             , "  * Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@"-             , "        that calls 'unsafePerformIO'.  If the call is inlined,"-             , "        the I\\/O may be performed more than once."-             , ""-             , "  * Use the compiler flag @-fno-cse@ to prevent common sub-expression"-             , "        elimination being performed on the module."-             , ""-             ]-          )-          (unlines-             [ ""-             , ""-             , "may require"-             , "different precautions: "-             , "+ Use  `{-# NOINLINE foo #-}`  as a pragma on any function  `foo` "-             , "  that calls  `unsafePerformIO` .  If the call is inlined,"-             , "  the I/O may be performed more than once."-             , ""-             , "+ Use the compiler flag  `-fno-cse`  to prevent common sub-expression"-             , "  elimination being performed on the module."-             , ""-             ]-          )-      , testCase "ordered list" $ checkHaddock-          (unlines-             [ "may require"-             , "different precautions:"-             , ""-             , "  1. Use @{\\-\\# NOINLINE foo \\#-\\}@ as a pragma on any function @foo@"-             , "        that calls 'unsafePerformIO'.  If the call is inlined,"-             , "        the I\\/O may be performed more than once."-             , ""-             , "  2. Use the compiler flag @-fno-cse@ to prevent common sub-expression"-             , "        elimination being performed on the module."-             , ""-             ]-          )-          (unlines-             [ ""-             , ""-             , "may require"-             , "different precautions: "-             , "1. Use  `{-# NOINLINE foo #-}`  as a pragma on any function  `foo` "-             , "  that calls  `unsafePerformIO` .  If the call is inlined,"-             , "  the I/O may be performed more than once."-             , ""-             , "2. Use the compiler flag  `-fno-cse`  to prevent common sub-expression"-             , "  elimination being performed on the module."-             , ""-             ]-          )-      ]-  where-    checkHaddock s txt = spanDocToMarkdownForTest s @?= txt--cradleTests :: TestTree-cradleTests = testGroup "cradle"-    [testGroup "dependencies" [sessionDepsArePickedUp]-    ,testGroup "ignore-fatal" [ignoreFatalWarning]-    ,testGroup "loading" [loadCradleOnlyonce, retryFailedCradle]-    ,testGroup "multi"   [simpleMultiTest, simpleMultiTest2, simpleMultiTest3, simpleMultiDefTest]-    ,testGroup "sub-directory"   [simpleSubDirectoryTest]-    ]--loadCradleOnlyonce :: TestTree-loadCradleOnlyonce = testGroup "load cradle only once"-    [ testSession' "implicit" implicit-    , testSession' "direct"   direct-    ]-    where-        direct dir = do-            liftIO $ writeFileUTF8 (dir </> "hie.yaml")-                "cradle: {direct: {arguments: []}}"-            test dir-        implicit dir = test dir-        test _dir = do-            doc <- createDoc "B.hs" "haskell" "module B where\nimport Data.Foo"-            msgs <- someTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message STextDocumentPublishDiagnostics))-            liftIO $ length msgs @?= 1-            changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing "module B where\nimport Data.Maybe"]-            msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message STextDocumentPublishDiagnostics))-            liftIO $ length msgs @?= 0-            _ <- createDoc "A.hs" "haskell" "module A where\nimport LoadCradleBar"-            msgs <- manyTill (skipManyTill anyMessage cradleLoadedMessage) (skipManyTill anyMessage (message STextDocumentPublishDiagnostics))-            liftIO $ length msgs @?= 0--retryFailedCradle :: TestTree-retryFailedCradle = testSession' "retry failed" $ \dir -> do-  -- The false cradle always fails-  let hieContents = "cradle: {bios: {shell: \"false\"}}"-      hiePath = dir </> "hie.yaml"-  liftIO $ writeFile hiePath hieContents-  let aPath = dir </> "A.hs"-  doc <- createDoc aPath "haskell" "main = return ()"-  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc-  liftIO $ "Test assumption failed: cradle should error out" `assertBool` not ideResultSuccess--  -- Fix the cradle and typecheck again-  let validCradle = "cradle: {bios: {shell: \"echo A.hs\"}}"-  liftIO $ writeFileUTF8 hiePath $ T.unpack validCradle-  sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $-          List [FileEvent (filePathToUri $ dir </> "hie.yaml") FcChanged ]--  WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" doc-  liftIO $ "No joy after fixing the cradle" `assertBool` ideResultSuccess---dependentFileTest :: TestTree-dependentFileTest = testGroup "addDependentFile"-    [testGroup "file-changed" [testSession' "test" test]-    ]-    where-      test dir = do-        -- If the file contains B then no type error-        -- otherwise type error-        let depFilePath = dir </> "dep-file.txt"-        liftIO $ writeFile depFilePath "A"-        let fooContent = T.unlines-              [ "{-# LANGUAGE TemplateHaskell #-}"-              , "module Foo where"-              , "import Language.Haskell.TH.Syntax"-              , "foo :: Int"-              , "foo = 1 + $(do"-              , "               qAddDependentFile \"dep-file.txt\""-              , "               f <- qRunIO (readFile \"dep-file.txt\")"-              , "               if f == \"B\" then [| 1 |] else lift f)"-              ]-        let bazContent = T.unlines ["module Baz where", "import Foo ()"]-        _ <- createDoc "Foo.hs" "haskell" fooContent-        doc <- createDoc "Baz.hs" "haskell" bazContent-        expectDiagnostics $-            if ghcVersion >= GHC90-                -- String vs [Char] causes this change in error message-                then [("Foo.hs", [(DsError, if ghcVersion >= GHC92 then (4,11) else (4, 6), "Couldn't match type")])]-                else [("Foo.hs", [(DsError, (4, 6), "Couldn't match expected type")])]-        -- Now modify the dependent file-        liftIO $ writeFile depFilePath "B"-        sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $-          List [FileEvent (filePathToUri "dep-file.txt") FcChanged ]--        -- Modifying Baz will now trigger Foo to be rebuilt as well-        let change = TextDocumentContentChangeEvent-              { _range = Just (Range (Position 2 0) (Position 2 6))-              , _rangeLength = Nothing-              , _text = "f = ()"-              }-        changeDoc doc [change]-        expectDiagnostics [("Foo.hs", [])]---cradleLoadedMessage :: Session FromServerMessage-cradleLoadedMessage = satisfy $ \case-        FromServerMess (SCustomMethod m) (NotMess _) -> m == cradleLoadedMethod-        _                                            -> False--cradleLoadedMethod :: T.Text-cradleLoadedMethod = "ghcide/cradle/loaded"--ignoreFatalWarning :: TestTree-ignoreFatalWarning = testCase "ignore-fatal-warning" $ runWithExtraFiles "ignore-fatal" $ \dir -> do-    let srcPath = dir </> "IgnoreFatal.hs"-    src <- liftIO $ readFileUtf8 srcPath-    _ <- createDoc srcPath "haskell" src-    expectNoMoreDiagnostics 5--simpleSubDirectoryTest :: TestTree-simpleSubDirectoryTest =-  testCase "simple-subdirectory" $ runWithExtraFiles "cabal-exe" $ \dir -> do-    let mainPath = dir </> "a/src/Main.hs"-    mainSource <- liftIO $ readFileUtf8 mainPath-    _mdoc <- createDoc mainPath "haskell" mainSource-    expectDiagnosticsWithTags-      [("a/src/Main.hs", [(DsWarning,(2,0), "Top-level binding", Nothing)]) -- So that we know P has been loaded-      ]-    expectNoMoreDiagnostics 0.5--simpleMultiTest :: TestTree-simpleMultiTest = testCase "simple-multi-test" $ withLongTimeout $ runWithExtraFiles "multi" $ \dir -> do-    let aPath = dir </> "a/A.hs"-        bPath = dir </> "b/B.hs"-    adoc <- openDoc aPath "haskell"-    bdoc <- openDoc bPath "haskell"-    WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" adoc-    liftIO $ assertBool "A should typecheck" ideResultSuccess-    WaitForIdeRuleResult {..} <- waitForAction "TypeCheck" bdoc-    liftIO $ assertBool "B should typecheck" ideResultSuccess-    locs <- getDefinitions bdoc (Position 2 7)-    let fooL = mkL (adoc ^. L.uri) 2 0 2 3-    checkDefs locs (pure [fooL])-    expectNoMoreDiagnostics 0.5---- Like simpleMultiTest but open the files in the other order-simpleMultiTest2 :: TestTree-simpleMultiTest2 = testCase "simple-multi-test2" $ runWithExtraFiles "multi" $ \dir -> do-    let aPath = dir </> "a/A.hs"-        bPath = dir </> "b/B.hs"-    bdoc <- openDoc bPath "haskell"-    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" bdoc-    TextDocumentIdentifier auri <- openDoc aPath "haskell"-    skipManyTill anyMessage $ isReferenceReady aPath-    locs <- getDefinitions bdoc (Position 2 7)-    let fooL = mkL auri 2 0 2 3-    checkDefs locs (pure [fooL])-    expectNoMoreDiagnostics 0.5---- Now with 3 components-simpleMultiTest3 :: TestTree-simpleMultiTest3 =-  testCase "simple-multi-test3" $ runWithExtraFiles "multi" $ \dir -> do-    let aPath = dir </> "a/A.hs"-        bPath = dir </> "b/B.hs"-        cPath = dir </> "c/C.hs"-    bdoc <- openDoc bPath "haskell"-    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" bdoc-    TextDocumentIdentifier auri <- openDoc aPath "haskell"-    skipManyTill anyMessage $ isReferenceReady aPath-    cdoc <- openDoc cPath "haskell"-    WaitForIdeRuleResult {} <- waitForAction "TypeCheck" cdoc-    locs <- getDefinitions cdoc (Position 2 7)-    let fooL = mkL auri 2 0 2 3-    checkDefs locs (pure [fooL])-    expectNoMoreDiagnostics 0.5---- Like simpleMultiTest but open the files in component 'a' in a separate session-simpleMultiDefTest :: TestTree-simpleMultiDefTest = testCase "simple-multi-def-test" $ runWithExtraFiles "multi" $ \dir -> do-    let aPath = dir </> "a/A.hs"-        bPath = dir </> "b/B.hs"-    adoc <- liftIO $ runInDir dir $ do-      aSource <- liftIO $ readFileUtf8 aPath-      adoc <- createDoc aPath "haskell" aSource-      skipManyTill anyMessage $ isReferenceReady aPath-      closeDoc adoc-      pure adoc-    bSource <- liftIO $ readFileUtf8 bPath-    bdoc <- createDoc bPath "haskell" bSource-    locs <- getDefinitions bdoc (Position 2 7)-    let fooL = mkL (adoc ^. L.uri) 2 0 2 3-    checkDefs locs (pure [fooL])-    expectNoMoreDiagnostics 0.5--ifaceTests :: TestTree-ifaceTests = testGroup "Interface loading tests"-    [ -- https://github.com/haskell/ghcide/pull/645/-      ifaceErrorTest-    , ifaceErrorTest2-    , ifaceErrorTest3-    , ifaceTHTest-    ]--bootTests :: TestTree-bootTests = testGroup "boot"-  [ testCase "boot-def-test" $ runWithExtraFiles "boot" $ \dir -> do-        let cPath = dir </> "C.hs"-        cSource <- liftIO $ readFileUtf8 cPath-        -- Dirty the cache-        liftIO $ runInDir dir $ do-            cDoc <- createDoc cPath "haskell" cSource-            -- We send a hover request then wait for either the hover response or-            -- `ghcide/reference/ready` notification.-            -- Once we receive one of the above, we wait for the other that we-            -- haven't received yet.-            -- If we don't wait for the `ready` notification it is possible-            -- that the `getDefinitions` request/response in the outer ghcide-            -- session will find no definitions.-            let hoverParams = HoverParams cDoc (Position 4 3) Nothing-            hoverRequestId <- sendRequest STextDocumentHover hoverParams-            let parseReadyMessage = isReferenceReady cPath-            let parseHoverResponse = responseForId STextDocumentHover hoverRequestId-            hoverResponseOrReadyMessage <- skipManyTill anyMessage ((Left <$> parseHoverResponse) <|> (Right <$> parseReadyMessage))-            _ <- skipManyTill anyMessage $-              case hoverResponseOrReadyMessage of-                Left _  -> void parseReadyMessage-                Right _ -> void parseHoverResponse-            closeDoc cDoc-        cdoc <- createDoc cPath "haskell" cSource-        locs <- getDefinitions cdoc (Position 7 4)-        let floc = mkR 9 0 9 1-        checkDefs locs (pure [floc])-  , testCase "graph with boot modules" $ runWithExtraFiles "boot2" $ \dir -> do-      _ <- openDoc (dir </> "A.hs") "haskell"-      expectNoMoreDiagnostics 2-  ]---- | test that TH reevaluates across interfaces-ifaceTHTest :: TestTree-ifaceTHTest = testCase "iface-th-test" $ runWithExtraFiles "TH" $ \dir -> do-    let aPath = dir </> "THA.hs"-        bPath = dir </> "THB.hs"-        cPath = dir </> "THC.hs"--    aSource <- liftIO $ readFileUtf8 aPath -- [TH] a :: ()-    _bSource <- liftIO $ readFileUtf8 bPath -- a :: ()-    cSource <- liftIO $ readFileUtf8 cPath -- c = a :: ()--    cdoc <- createDoc cPath "haskell" cSource--    -- Change [TH]a from () to Bool-    liftIO $ writeFileUTF8 aPath (unlines $ init (lines $ T.unpack aSource) ++ ["th_a = [d| a = False|]"])--    -- Check that the change propagates to C-    changeDoc cdoc [TextDocumentContentChangeEvent Nothing Nothing cSource]-    expectDiagnostics-      [("THC.hs", [(DsError, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'")])-      ,("THB.hs", [(DsWarning, (4,thDollarIdx), "Top-level binding")])]-    closeDoc cdoc--ifaceErrorTest :: TestTree-ifaceErrorTest = testCase "iface-error-test-1" $ runWithExtraFiles "recomp" $ \dir -> do-    configureCheckProject True-    let bPath = dir </> "B.hs"-        pPath = dir </> "P.hs"--    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int-    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int--    bdoc <- createDoc bPath "haskell" bSource-    expectDiagnostics-      [("P.hs", [(DsWarning,(4,0), "Top-level binding")])] -- So what we know P has been loaded--    -- Change y from Int to B-    changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]-    -- save so that we can that the error propagates to A-    sendNotification STextDocumentDidSave (DidSaveTextDocumentParams bdoc Nothing)---    -- Check that the error propagates to A-    expectDiagnostics-      [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])]--    -- Check that we wrote the interfaces for B when we saved-    hidir <- getInterfaceFilesDir bdoc-    hi_exists <- liftIO $ doesFileExist $ hidir </> "B.hi"-    liftIO $ assertBool ("Couldn't find B.hi in " ++ hidir) hi_exists--    pdoc <- openDoc pPath "haskell"-    expectDiagnostics-      [("P.hs", [(DsWarning,(4,0), "Top-level binding")])-      ]-    changeDoc pdoc [TextDocumentContentChangeEvent Nothing Nothing $ pSource <> "\nfoo = y :: Bool" ]-    -- Now in P we have-    -- bar = x :: Int-    -- foo = y :: Bool-    -- HOWEVER, in A...-    -- x = y  :: Int-    -- This is clearly inconsistent, and the expected outcome a bit surprising:-    --   - The diagnostic for A has already been received. Ghcide does not repeat diagnostics-    --   - P is being typechecked with the last successful artifacts for A.-    expectDiagnostics-      [("P.hs", [(DsWarning,(4,0), "Top-level binding")])-      ,("P.hs", [(DsWarning,(6,0), "Top-level binding")])-      ]-    expectNoMoreDiagnostics 2--ifaceErrorTest2 :: TestTree-ifaceErrorTest2 = testCase "iface-error-test-2" $ runWithExtraFiles "recomp" $ \dir -> do-    let bPath = dir </> "B.hs"-        pPath = dir </> "P.hs"--    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int-    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int--    bdoc <- createDoc bPath "haskell" bSource-    pdoc <- createDoc pPath "haskell" pSource-    expectDiagnostics-      [("P.hs", [(DsWarning,(4,0), "Top-level binding")])] -- So that we know P has been loaded--    -- Change y from Int to B-    changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]--    -- Add a new definition to P-    changeDoc pdoc [TextDocumentContentChangeEvent Nothing Nothing $ pSource <> "\nfoo = y :: Bool" ]-    -- Now in P we have-    -- bar = x :: Int-    -- foo = y :: Bool-    -- HOWEVER, in A...-    -- x = y  :: Int-    expectDiagnostics-    -- As in the other test, P is being typechecked with the last successful artifacts for A-    -- (ot thanks to -fdeferred-type-errors)-      [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])-      ,("P.hs", [(DsWarning, (4, 0), "Top-level binding")])-      ,("P.hs", [(DsWarning, (6, 0), "Top-level binding")])-      ]--    expectNoMoreDiagnostics 2--ifaceErrorTest3 :: TestTree-ifaceErrorTest3 = testCase "iface-error-test-3" $ runWithExtraFiles "recomp" $ \dir -> do-    let bPath = dir </> "B.hs"-        pPath = dir </> "P.hs"--    bSource <- liftIO $ readFileUtf8 bPath -- y :: Int-    pSource <- liftIO $ readFileUtf8 pPath -- bar = x :: Int--    bdoc <- createDoc bPath "haskell" bSource--    -- Change y from Int to B-    changeDoc bdoc [TextDocumentContentChangeEvent Nothing Nothing $ T.unlines ["module B where", "y :: Bool", "y = undefined"]]--    -- P should not typecheck, as there are no last valid artifacts for A-    _pdoc <- createDoc pPath "haskell" pSource--    -- In this example the interface file for A should not exist (modulo the cache folder)-    -- Despite that P still type checks, as we can generate an interface file for A thanks to -fdeferred-type-errors-    expectDiagnostics-      [("A.hs", [(DsError, (5, 4), "Couldn't match expected type 'Int' with actual type 'Bool'")])-      ,("P.hs", [(DsWarning,(4,0), "Top-level binding")])-      ]-    expectNoMoreDiagnostics 2--sessionDepsArePickedUp :: TestTree-sessionDepsArePickedUp = testSession'-  "session-deps-are-picked-up"-  $ \dir -> do-    liftIO $-      writeFileUTF8-        (dir </> "hie.yaml")-        "cradle: {direct: {arguments: []}}"-    -- Open without OverloadedStrings and expect an error.-    doc <- createDoc "Foo.hs" "haskell" fooContent-    expectDiagnostics $-        if ghcVersion >= GHC90-            -- String vs [Char] causes this change in error message-            then [("Foo.hs", [(DsError, (3, 6), "Couldn't match type")])]-            else [("Foo.hs", [(DsError, (3, 6), "Couldn't match expected type")])]-    -- Update hie.yaml to enable OverloadedStrings.-    liftIO $-      writeFileUTF8-        (dir </> "hie.yaml")-        "cradle: {direct: {arguments: [-XOverloadedStrings]}}"-    sendNotification SWorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $-          List [FileEvent (filePathToUri $ dir </> "hie.yaml") FcChanged ]-    -- Send change event.-    let change =-          TextDocumentContentChangeEvent-            { _range = Just (Range (Position 4 0) (Position 4 0)),-              _rangeLength = Nothing,-              _text = "\n"-            }-    changeDoc doc [change]-    -- Now no errors.-    expectDiagnostics [("Foo.hs", [])]-  where-    fooContent =-      T.unlines-        [ "module Foo where",-          "import Data.Text",-          "foo :: Text",-          "foo = \"hello\""-        ]---- A test to ensure that the command line ghcide workflow stays working-nonLspCommandLine :: TestTree-nonLspCommandLine = testGroup "ghcide command line"-  [ testCase "works" $ withTempDir $ \dir -> do-        ghcide <- locateGhcideExecutable-        copyTestDataFiles dir "multi"-        let cmd = (proc ghcide ["a/A.hs"]){cwd = Just dir}--        setEnv "HOME" "/homeless-shelter" False--        (ec, _, _) <- readCreateProcessWithExitCode cmd ""--        ec @?= ExitSuccess-  ]---- | checks if we use InitializeParams.rootUri for loading session-rootUriTests :: TestTree-rootUriTests = testCase "use rootUri" . runTest "dirA" "dirB" $ \dir -> do-  let bPath = dir </> "dirB/Foo.hs"-  liftIO $ copyTestDataFiles dir "rootUri"-  bSource <- liftIO $ readFileUtf8 bPath-  _ <- createDoc "Foo.hs" "haskell" bSource-  expectNoMoreDiagnostics 0.5-  where-    -- similar to run' except we can configure where to start ghcide and session-    runTest :: FilePath -> FilePath -> (FilePath -> Session ()) -> IO ()-    runTest dir1 dir2 s = withTempDir $ \dir -> runInDir' dir dir1 dir2 [] (s dir)---- | Test if ghcide asynchronously handles Commands and user Requests-asyncTests :: TestTree-asyncTests = testGroup "async"-    [-      testSession "command" $ do-            -- Execute a command that will block forever-            let req = ExecuteCommandParams Nothing blockCommandId Nothing-            void $ sendRequest SWorkspaceExecuteCommand req-            -- Load a file and check for code actions. Will only work if the command is run asynchronously-            doc <- createDoc "A.hs" "haskell" $ T.unlines-              [ "{-# OPTIONS -Wmissing-signatures #-}"-              , "foo = id"-              ]-            void waitForDiagnostics-            codeLenses <- getCodeLenses doc-            liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=?-              [ "foo :: a -> a" ]-    , testSession "request" $ do-            -- Execute a custom request that will block for 1000 seconds-            void $ sendRequest (SCustomMethod "test") $ toJSON $ BlockSeconds 1000-            -- Load a file and check for code actions. Will only work if the request is run asynchronously-            doc <- createDoc "A.hs" "haskell" $ T.unlines-              [ "{-# OPTIONS -Wmissing-signatures #-}"-              , "foo = id"-              ]-            void waitForDiagnostics-            codeLenses <- getCodeLenses doc-            liftIO $ [ _title | CodeLens{_command = Just Command{_title}} <- codeLenses] @=?-              [ "foo :: a -> a" ]-    ]---clientSettingsTest :: TestTree-clientSettingsTest = testGroup "client settings handling"-    [ testSession "ghcide restarts shake session on config changes" $ do-            void $ skipManyTill anyMessage $ message SClientRegisterCapability-            void $ createDoc "A.hs" "haskell" "module A where"-            waitForProgressDone-            sendNotification SWorkspaceDidChangeConfiguration-                (DidChangeConfigurationParams (toJSON (mempty :: A.Object)))-            skipManyTill anyMessage restartingBuildSession--    ]-  where-    restartingBuildSession :: Session ()-    restartingBuildSession = do-        FromServerMess SWindowLogMessage NotificationMessage{_params = LogMessageParams{..}} <- loggingNotification-        guard $ "Restarting build session" `T.isInfixOf` _message--referenceTests :: TestTree-referenceTests = testGroup "references"-    [ testGroup "can get references to FOIs"-          [ referenceTest "can get references to symbols"-                          ("References.hs", 4, 7)-                          YesIncludeDeclaration-                          [ ("References.hs", 4, 6)-                          , ("References.hs", 6, 0)-                          , ("References.hs", 6, 14)-                          , ("References.hs", 9, 7)-                          , ("References.hs", 10, 11)-                          ]--          , referenceTest "can get references to data constructor"-                          ("References.hs", 13, 2)-                          YesIncludeDeclaration-                          [ ("References.hs", 13, 2)-                          , ("References.hs", 16, 14)-                          , ("References.hs", 19, 21)-                          ]--          , referenceTest "getting references works in the other module"-                          ("OtherModule.hs", 6, 0)-                          YesIncludeDeclaration-                          [ ("OtherModule.hs", 6, 0)-                          , ("OtherModule.hs", 8, 16)-                          ]--          , referenceTest "getting references works in the Main module"-                          ("Main.hs", 9, 0)-                          YesIncludeDeclaration-                          [ ("Main.hs", 9, 0)-                          , ("Main.hs", 10, 4)-                          ]--          , referenceTest "getting references to main works"-                          ("Main.hs", 5, 0)-                          YesIncludeDeclaration-                          [ ("Main.hs", 4, 0)-                          , ("Main.hs", 5, 0)-                          ]--          , referenceTest "can get type references"-                          ("Main.hs", 9, 9)-                          YesIncludeDeclaration-                          [ ("Main.hs", 9, 0)-                          , ("Main.hs", 9, 9)-                          , ("Main.hs", 10, 0)-                          ]--          , expectFailBecause "references provider does not respect includeDeclaration parameter" $- referenceTest "works when we ask to exclude declarations"-                          ("References.hs", 4, 7)-                          NoExcludeDeclaration-                          [ ("References.hs", 6, 0)-                          , ("References.hs", 6, 14)-                          , ("References.hs", 9, 7)-                          , ("References.hs", 10, 11)-                          ]--          , referenceTest "INCORRECTLY returns declarations when we ask to exclude them"-                          ("References.hs", 4, 7)-                          NoExcludeDeclaration-                          [ ("References.hs", 4, 6)-                          , ("References.hs", 6, 0)-                          , ("References.hs", 6, 14)-                          , ("References.hs", 9, 7)-                          , ("References.hs", 10, 11)-                          ]-          ]--    , testGroup "can get references to non FOIs"-          [ referenceTest "can get references to symbol defined in a module we import"-                          ("References.hs", 22, 4)-                          YesIncludeDeclaration-                          [ ("References.hs", 22, 4)-                          , ("OtherModule.hs", 0, 20)-                          , ("OtherModule.hs", 4, 0)-                          ]--          , referenceTest "can get references in modules that import us to symbols we define"-                          ("OtherModule.hs", 4, 0)-                          YesIncludeDeclaration-                          [ ("References.hs", 22, 4)-                          , ("OtherModule.hs", 0, 20)-                          , ("OtherModule.hs", 4, 0)-                          ]--          , referenceTest "can get references to symbol defined in a module we import transitively"-                          ("References.hs", 24, 4)-                          YesIncludeDeclaration-                          [ ("References.hs", 24, 4)-                          , ("OtherModule.hs", 0, 48)-                          , ("OtherOtherModule.hs", 2, 0)-                          ]--          , referenceTest "can get references in modules that import us transitively to symbols we define"-                          ("OtherOtherModule.hs", 2, 0)-                          YesIncludeDeclaration-                          [ ("References.hs", 24, 4)-                          , ("OtherModule.hs", 0, 48)-                          , ("OtherOtherModule.hs", 2, 0)-                          ]--          , referenceTest "can get type references to other modules"-                          ("Main.hs", 12, 10)-                          YesIncludeDeclaration-                          [ ("Main.hs", 12, 7)-                          , ("Main.hs", 13, 0)-                          , ("References.hs", 12, 5)-                          , ("References.hs", 16, 0)-                          ]-          ]-    ]---- | When we ask for all references to symbol "foo", should the declaration "foo--- = 2" be among the references returned?-data IncludeDeclaration =-    YesIncludeDeclaration-    | NoExcludeDeclaration--getReferences' :: SymbolLocation -> IncludeDeclaration -> Session (List Location)-getReferences' (file, l, c) includeDeclaration = do-    doc <- openDoc file "haskell"-    getReferences doc (Position l c) $ toBool includeDeclaration-    where toBool YesIncludeDeclaration = True-          toBool NoExcludeDeclaration  = False--referenceTestSession :: String -> FilePath -> [FilePath] -> (FilePath -> Session ()) -> TestTree-referenceTestSession name thisDoc docs' f = testSessionWithExtraFiles "references" name $ \dir -> do-  -- needed to build whole project indexing-  configureCheckProject True-  let docs = map (dir </>) $ delete thisDoc $ nubOrd docs'-  -- Initial Index-  docid <- openDoc thisDoc "haskell"-  let-    loop :: [FilePath] -> Session ()-    loop [] = pure ()-    loop docs = do-      doc <- skipManyTill anyMessage $ referenceReady (`elem` docs)-      loop (delete doc docs)-  loop docs-  f dir-  closeDoc docid---- | Given a location, lookup the symbol and all references to it. Make sure--- they are the ones we expect.-referenceTest :: String -> SymbolLocation -> IncludeDeclaration -> [SymbolLocation] -> TestTree-referenceTest name loc includeDeclaration expected =-    referenceTestSession name (fst3 loc) docs $ \dir -> do-        List actual <- getReferences' loc includeDeclaration-        liftIO $ actual `expectSameLocations` map (first3 (dir </>)) expected-  where-    docs = map fst3 expected--type SymbolLocation = (FilePath, UInt, UInt)--expectSameLocations :: [Location] -> [SymbolLocation] -> Assertion-expectSameLocations actual expected = do-    let actual' =-            Set.map (\location -> (location ^. L.uri-                                   , location ^. L.range . L.start . L.line . to fromIntegral-                                   , location ^. L.range . L.start . L.character . to fromIntegral))-            $ Set.fromList actual-    expected' <- Set.fromList <$>-        (forM expected $ \(file, l, c) -> do-                              fp <- canonicalizePath file-                              return (filePathToUri fp, l, c))-    actual' @?= expected'--------------------------------------------------------------------------- Utils-------------------------------------------------------------------------testSession :: String -> Session () -> TestTree-testSession name = testCase name . run--testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree-testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix--testSession' :: String -> (FilePath -> Session ()) -> TestTree-testSession' name = testCase name . run'--testSessionWait :: HasCallStack => String -> Session () -> TestTree-testSessionWait name = testSession name .-      -- Check that any diagnostics produced were already consumed by the test case.-      ---      -- If in future we add test cases where we don't care about checking the diagnostics,-      -- this could move elsewhere.-      ---      -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.-      ( >> expectNoMoreDiagnostics 0.5)--mkRange :: UInt -> UInt -> UInt -> UInt -> Range-mkRange a b c d = Range (Position a b) (Position c d)--run :: Session a -> IO a-run s = run' (const s)--runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a-runWithExtraFiles prefix s = withTempDir $ \dir -> do-  copyTestDataFiles dir prefix-  runInDir dir (s dir)--copyTestDataFiles :: FilePath -> FilePath -> IO ()-copyTestDataFiles dir prefix = do-  -- Copy all the test data files to the temporary workspace-  testDataFiles <- getDirectoryFilesIO ("test/data" </> prefix) ["//*"]-  for_ testDataFiles $ \f -> do-    createDirectoryIfMissing True $ dir </> takeDirectory f-    copyFile ("test/data" </> prefix </> f) (dir </> f)--run' :: (FilePath -> Session a) -> IO a-run' s = withTempDir $ \dir -> runInDir dir (s dir)--runInDir :: FilePath -> Session a -> IO a-runInDir dir = runInDir' dir "." "." []--withLongTimeout :: IO a -> IO a-withLongTimeout = bracket_ (setEnv "LSP_TIMEOUT" "120" True) (unsetEnv "LSP_TIMEOUT")---- | Takes a directory as well as relative paths to where we should launch the executable as well as the session root.-runInDir' :: FilePath -> FilePath -> FilePath -> [String] -> Session a -> IO a-runInDir' = runInDir'' lspTestCaps--runInDir''-    :: ClientCapabilities-    -> FilePath-    -> FilePath-    -> FilePath-    -> [String]-    -> Session b-    -> IO b-runInDir'' lspCaps dir startExeIn startSessionIn extraOptions s = do--  ghcideExe <- locateGhcideExecutable-  let startDir = dir </> startExeIn-  let projDir = dir </> startSessionIn--  createDirectoryIfMissing True startDir-  createDirectoryIfMissing True projDir-  -- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56-  -- since the package import test creates "Data/List.hs", which otherwise has no physical home-  createDirectoryIfMissing True $ projDir ++ "/Data"--  shakeProfiling <- getEnv "SHAKE_PROFILING"-  let cmd = unwords $-       [ghcideExe, "--lsp", "--test", "--verify-core-file", "--verbose", "-j2", "--cwd", startDir-       ] ++ ["--shake-profiling=" <> dir | Just dir <- [shakeProfiling]-       ] ++ extraOptions-  -- HIE calls getXgdDirectory which assumes that HOME is set.-  -- Only sets HOME if it wasn't already set.-  setEnv "HOME" "/homeless-shelter" False-  conf <- getConfigFromEnv-  runSessionWithConfig conf cmd lspCaps projDir $ do-      configureCheckProject False-      s---getConfigFromEnv :: IO SessionConfig-getConfigFromEnv = do-  logColor <- fromMaybe True <$> checkEnv "LSP_TEST_LOG_COLOR"-  timeoutOverride <- fmap read <$> getEnv "LSP_TIMEOUT"-  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) Nothing Nothing }--lspTestCapsNoFileWatches :: ClientCapabilities-lspTestCapsNoFileWatches = lspTestCaps & workspace . Lens._Just . didChangeWatchedFiles .~ Nothing--openTestDataDoc :: FilePath -> Session TextDocumentIdentifier-openTestDataDoc path = do-  source <- liftIO $ readFileUtf8 $ "test/data" </> path-  createDoc path "haskell" source--unitTests :: Recorder (WithPriority Log) -> Logger -> TestTree-unitTests recorder logger = do-  testGroup "Unit"-     [ testCase "empty file path does NOT work with the empty String literal" $-         uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just "."-     , testCase "empty file path works using toNormalizedFilePath'" $-         uriToFilePath' (fromNormalizedUri $ filePathToUri' (toNormalizedFilePath' "")) @?= Just ""-     , testCase "empty path URI" $ do-         Just URI{..} <- pure $ parseURI (T.unpack $ getUri $ fromNormalizedUri emptyPathUri)-         uriScheme @?= "file:"-         uriPath @?= ""-     , testCase "from empty path URI" $ do-         let uri = Uri "file://"-         uriToFilePath' uri @?= Just ""-     , testCase "showDiagnostics prints ranges 1-based (like vscode)" $ do-         let diag = ("", Diagnostics.ShowDiag, Diagnostic-               { _range = Range-                   { _start = Position{_line = 0, _character = 1}-                   , _end = Position{_line = 2, _character = 3}-                   }-               , _severity = Nothing-               , _code = Nothing-               , _source = Nothing-               , _message = ""-               , _relatedInformation = Nothing-               , _tags = Nothing-               })-         let shown = T.unpack (Diagnostics.showDiagnostics [diag])-         let expected = "1:2-3:4"-         assertBool (unwords ["expected to find range", expected, "in diagnostic", shown]) $-             expected `isInfixOf` shown-     , testCase "notification handlers run in priority order" $ do-        orderRef <- newIORef []-        let plugins = pluginDescToIdePlugins $-                [ (priorityPluginDescriptor i)-                    { pluginNotificationHandlers = mconcat-                        [ mkPluginNotificationHandler LSP.STextDocumentDidOpen $ \_ _ _ _ ->-                            liftIO $ atomicModifyIORef_ orderRef (i:)-                        ]-                    }-                    | i <- [1..20]-                ] ++ Ghcide.descriptors (cmapWithPrio LogGhcIde recorder)-            priorityPluginDescriptor i = (defaultPluginDescriptor $ fromString $ show i){pluginPriority = i}--        testIde recorder (IDE.testing (cmapWithPrio LogIDEMain recorder) logger plugins) $ do-            _ <- createDoc "A.hs" "haskell" "module A where"-            waitForProgressDone-            actualOrder <- liftIO $ reverse <$> readIORef orderRef--            -- Handlers are run in priority descending order-            liftIO $ actualOrder @?= [20, 19 .. 1]-     , ignoreTestBecause "The test fails sometimes showing 10000us" $-         testCase "timestamps have millisecond resolution" $ do-           resolution_us <- findResolution_us 1-           let msg = printf "Timestamps do not have millisecond resolution: %dus" resolution_us-           assertBool msg (resolution_us <= 1000)-     , Progress.tests-     , FuzzySearch.tests-     ]--garbageCollectionTests :: TestTree-garbageCollectionTests = testGroup "garbage collection"-  [ testGroup "dirty keys"-        [ testSession' "are collected" $ \dir -> do-            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"-            doc <- generateGarbage "A" dir-            closeDoc doc-            garbage <- waitForGC-            liftIO $ assertBool "no garbage was found" $ not $ null garbage--        , testSession' "are deleted from the state" $ \dir -> do-            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"-            docA <- generateGarbage "A" dir-            keys0 <- getStoredKeys-            closeDoc docA-            garbage <- waitForGC-            liftIO $ assertBool "something is wrong with this test - no garbage found" $ not $ null garbage-            keys1 <- getStoredKeys-            liftIO $ assertBool "keys were not deleted from the state" (length keys1 < length keys0)--        , testSession' "are not regenerated unless needed" $ \dir -> do-            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A.hs, B.hs]}}"-            docA <- generateGarbage "A" dir-            _docB <- generateGarbage "B" dir--            -- garbage collect A keys-            keysBeforeGC <- getStoredKeys-            closeDoc docA-            garbage <- waitForGC-            liftIO $ assertBool "something is wrong with this test - no garbage found" $ not $ null garbage-            keysAfterGC <- getStoredKeys-            liftIO $ assertBool "something is wrong with this test - keys were not deleted from the state"-                (length keysAfterGC < length keysBeforeGC)--            -- re-typecheck B and check that the keys for A have not materialized back-            _docB <- generateGarbage "B" dir-            keysB <- getStoredKeys-            let regeneratedKeys = Set.filter (not . isExpected) $-                    Set.intersection (Set.fromList garbage) (Set.fromList keysB)-            liftIO $ regeneratedKeys @?= mempty--        , testSession' "regenerate successfully" $ \dir -> do-            liftIO $ writeFile (dir </> "hie.yaml") "cradle: {direct: {arguments: [A]}}"-            docA <- generateGarbage "A" dir-            closeDoc docA-            garbage <- waitForGC-            liftIO $ assertBool "no garbage was found" $ not $ null garbage-            let edit = T.unlines-                        [ "module A where"-                        , "a :: Bool"-                        , "a = ()"-                        ]-            doc <- generateGarbage "A" dir-            changeDoc doc [TextDocumentContentChangeEvent Nothing Nothing edit]-            builds <- waitForTypecheck doc-            liftIO $ assertBool "it still builds" builds-            expectCurrentDiagnostics doc [(DsError, (2,4), "Couldn't match expected type")]-        ]-  ]-  where-    isExpected k = any (`T.isPrefixOf` k) ["GhcSessionIO"]--    generateGarbage :: String -> FilePath -> Session TextDocumentIdentifier-    generateGarbage modName dir = do-        let fp = modName <> ".hs"-            body = printf "module %s where" modName-        doc <- createDoc fp "haskell" (T.pack body)-        liftIO $ writeFile (dir </> fp) body-        builds <- waitForTypecheck doc-        liftIO $ assertBool "something is wrong with this test" builds-        return doc--findResolution_us :: Int -> IO Int-findResolution_us delay_us | delay_us >= 1000000 = error "Unable to compute timestamp resolution"-findResolution_us delay_us = withTempFile $ \f -> withTempFile $ \f' -> do-    performGC-    writeFile f ""-    threadDelay delay_us-    writeFile f' ""-    t <- getModTime f-    t' <- getModTime f'-    if t /= t' then return delay_us else findResolution_us (delay_us * 10)---testIde :: Recorder (WithPriority Log) -> IDE.Arguments -> Session () -> IO ()-testIde recorder arguments session = do-    config <- getConfigFromEnv-    cwd <- getCurrentDirectory-    (hInRead, hInWrite) <- createPipe-    (hOutRead, hOutWrite) <- createPipe-    let projDir = "."-    let server = IDE.defaultMain (cmapWithPrio LogIDEMain recorder) arguments-            { IDE.argsHandleIn = pure hInRead-            , IDE.argsHandleOut = pure hOutWrite-            }--    flip finally (setCurrentDirectory cwd) $ withAsync server $ \_ ->-        runSessionWithHandles hInWrite hOutRead config lspTestCaps projDir session--positionMappingTests :: Recorder (WithPriority Log) -> TestTree-positionMappingTests recorder =-    testGroup "position mapping"-        [ testGroup "toCurrent"-              [ testCase "before" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "ab"-                    (Position 0 0) @?= PositionExact (Position 0 0)-              , testCase "after, same line, same length" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "ab"-                    (Position 0 3) @?= PositionExact (Position 0 3)-              , testCase "after, same line, increased length" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc"-                    (Position 0 3) @?= PositionExact (Position 0 4)-              , testCase "after, same line, decreased length" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "a"-                    (Position 0 3) @?= PositionExact (Position 0 2)-              , testCase "after, next line, no newline" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc"-                    (Position 1 3) @?= PositionExact (Position 1 3)-              , testCase "after, next line, newline" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\ndef"-                    (Position 1 0) @?= PositionExact (Position 2 0)-              , testCase "after, same line, newline" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\nd"-                    (Position 0 4) @?= PositionExact (Position 1 2)-              , testCase "after, same line, newline + newline at end" $-                toCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\nd\n"-                    (Position 0 4) @?= PositionExact (Position 2 1)-              , testCase "after, same line, newline + newline at end" $-                toCurrent-                    (Range (Position 0 1) (Position 0 1))-                    "abc"-                    (Position 0 1) @?= PositionExact (Position 0 4)-              ]-        , testGroup "fromCurrent"-              [ testCase "before" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "ab"-                    (Position 0 0) @?= PositionExact (Position 0 0)-              , testCase "after, same line, same length" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "ab"-                    (Position 0 3) @?= PositionExact (Position 0 3)-              , testCase "after, same line, increased length" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc"-                    (Position 0 4) @?= PositionExact (Position 0 3)-              , testCase "after, same line, decreased length" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "a"-                    (Position 0 2) @?= PositionExact (Position 0 3)-              , testCase "after, next line, no newline" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc"-                    (Position 1 3) @?= PositionExact (Position 1 3)-              , testCase "after, next line, newline" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\ndef"-                    (Position 2 0) @?= PositionExact (Position 1 0)-              , testCase "after, same line, newline" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\nd"-                    (Position 1 2) @?= PositionExact (Position 0 4)-              , testCase "after, same line, newline + newline at end" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 3))-                    "abc\nd\n"-                    (Position 2 1) @?= PositionExact (Position 0 4)-              , testCase "after, same line, newline + newline at end" $-                fromCurrent-                    (Range (Position 0 1) (Position 0 1))-                    "abc"-                    (Position 0 4) @?= PositionExact (Position 0 1)-              ]-        , adjustOption (\(QuickCheckTests i) -> QuickCheckTests (max 1000 i)) $ testGroup "properties"-              [ testProperty "fromCurrent r t <=< toCurrent r t" $ do-                -- Note that it is important to use suchThatMap on all values at once-                -- instead of only using it on the position. Otherwise you can get-                -- into situations where there is no position that can be mapped back-                -- for the edit which will result in QuickCheck looping forever.-                let gen = do-                        rope <- genRope-                        range <- genRange rope-                        PrintableText replacement <- arbitrary-                        oldPos <- genPosition rope-                        pure (range, replacement, oldPos)-                forAll-                    (suchThatMap gen-                        (\(range, replacement, oldPos) -> positionResultToMaybe $ (range, replacement, oldPos,) <$> toCurrent range replacement oldPos)) $-                    \(range, replacement, oldPos, newPos) ->-                    fromCurrent range replacement newPos === PositionExact oldPos-              , testProperty "toCurrent r t <=< fromCurrent r t" $ do-                let gen = do-                        rope <- genRope-                        range <- genRange rope-                        PrintableText replacement <- arbitrary-                        let newRope = runIdentity $ applyChange mempty rope-                                (TextDocumentContentChangeEvent (Just range) Nothing replacement)-                        newPos <- genPosition newRope-                        pure (range, replacement, newPos)-                forAll-                    (suchThatMap gen-                        (\(range, replacement, newPos) -> positionResultToMaybe $ (range, replacement, newPos,) <$> fromCurrent range replacement newPos)) $-                    \(range, replacement, newPos, oldPos) ->-                    toCurrent range replacement oldPos === PositionExact newPos-              ]-        ]--newtype PrintableText = PrintableText { getPrintableText :: T.Text }-    deriving Show--instance Arbitrary PrintableText where-    arbitrary = PrintableText . T.pack . getPrintableString <$> arbitrary---genRope :: Gen Rope-genRope = Rope.fromText . getPrintableText <$> arbitrary--genPosition :: Rope -> Gen Position-genPosition r = do-    let rows :: Int = fromIntegral $ Rope.lengthInLines r-    row <- choose (0, max 0 $ rows - 1) `suchThat` inBounds @UInt-    let columns = T.length (nthLine (fromIntegral row) r)-    column <- choose (0, max 0 $ columns - 1) `suchThat` inBounds @UInt-    pure $ Position (fromIntegral row) (fromIntegral column)--genRange :: Rope -> Gen Range-genRange r = do-    let rows :: Int = fromIntegral $ Rope.lengthInLines r-    startPos@(Position startLine startColumn) <- genPosition r-    let maxLineDiff = max 0 $ rows - 1 - fromIntegral startLine-    endLine <- choose (fromIntegral startLine, fromIntegral startLine + maxLineDiff) `suchThat` inBounds @UInt-    let columns = T.length (nthLine (fromIntegral endLine) r)-    endColumn <--        if fromIntegral startLine == endLine-            then choose (fromIntegral startColumn, columns)-            else choose (0, max 0 $ columns - 1)-        `suchThat` inBounds @UInt-    pure $ Range startPos (Position (fromIntegral endLine) (fromIntegral endColumn))--inBounds :: forall b a . (Integral a, Integral b, Bounded b) => a -> Bool-inBounds a = let i = toInteger a in i <= toInteger (maxBound @b) && i >= toInteger (minBound @b)---- | Get the ith line of a rope, starting from 0. Trailing newline not included.-nthLine :: Int -> Rope -> T.Text-nthLine i r-    | Rope.null r = ""-    | otherwise = Rope.lines r !! i--getWatchedFilesSubscriptionsUntil :: forall m. SServerMethod m -> Session [DidChangeWatchedFilesRegistrationOptions]-getWatchedFilesSubscriptionsUntil m = do-      msgs <- manyTill (Just <$> message SClientRegisterCapability <|> Nothing <$ anyMessage) (message m)-      return-            [ args-            | Just RequestMessage{_params = RegistrationParams (List regs)} <- msgs-            , SomeRegistration (Registration _id SWorkspaceDidChangeWatchedFiles args) <- regs-            ]---- | Version of 'System.IO.Extra.withTempDir' that canonicalizes the path--- Which we need to do on macOS since the $TMPDIR can be in @/private/var@ or--- @/var@-withTempDir :: (FilePath -> IO a) -> IO a-withTempDir f = System.IO.Extra.withTempDir $ \dir -> do-  dir' <- canonicalizePath dir-  f dir'----- | Before ghc9, lists of Char is displayed as [Char], but with ghc9 and up, it's displayed as String-listOfChar :: T.Text-listOfChar | ghcVersion >= GHC90 = "String"-           | otherwise = "[Char]"---- | Ghc 9 doesn't include the $-sign in TH warnings like earlier versions did-thDollarIdx :: UInt-thDollarIdx | ghcVersion >= GHC90 = 1-            | otherwise = 0+++module Main (main) where+-- import Test.QuickCheck.Instances ()+import           Data.Function                            ((&))+import           Ide.Logger             (Logger (Logger),+                                                           LoggingColumn (DataColumn, PriorityColumn),+                                                           Pretty (pretty),+                                                           Priority (Debug),+                                                           Recorder (Recorder, logger_),+                                                           WithPriority (WithPriority, priority),+                                                           cfilter,+                                                           cmapWithPrio,+                                                           makeDefaultStderrRecorder)+import           GHC.Stack                                (emptyCallStack)+import qualified HieDbRetry+import           Test.Tasty+import           Test.Tasty.Ingredients.Rerun++import LogType ()+import OpenCloseTest+import InitializeResponseTests+import CompletionTests+import CPPTests+import DiagnosticTests+import CodeLensTests+import OutlineTests+import HighlightTests+import FindDefinitionAndHoverTests+import PluginSimpleTests+import PluginParsedResultTests+import PreprocessorTests+import THTests+import SymlinkTests+import SafeTests+import UnitTests+import HaddockTests+import PositionMappingTests+import WatchedFileTests+import CradleTests+import DependentFileTest+import NonLspCommandLine+import IfaceTests+import BootTests+import RootUriTests+import AsyncTests+import ClientSettingsTests+import ReferenceTests+import GarbageCollectionTests+import ExceptionTests++main :: IO ()+main = do+  docWithPriorityRecorder <- makeDefaultStderrRecorder (Just [PriorityColumn, DataColumn])++  let docWithFilteredPriorityRecorder@Recorder{ logger_ } =+        docWithPriorityRecorder+        & cfilter (\WithPriority{ priority } -> priority >= Debug)++  -- exists so old-style logging works. intended to be phased out+  let logger = Logger $ \p m -> logger_ (WithPriority p emptyCallStack (pretty m))++  let recorder = docWithFilteredPriorityRecorder+               & cmapWithPrio pretty++  -- We mess with env vars so run single-threaded.+  defaultMainWithRerun $ testGroup "ghcide"+    [ OpenCloseTest.tests+    , InitializeResponseTests.tests+    , CompletionTests.tests+    , CPPTests.tests+    , DiagnosticTests.tests+    , CodeLensTests.tests+    , OutlineTests.tests+    , HighlightTests.tests+    , FindDefinitionAndHoverTests.tests+    , PluginSimpleTests.tests+    , PluginParsedResultTests.tests+    , PreprocessorTests.tests+    , THTests.tests+    , SymlinkTests.tests+    , SafeTests.tests+    , UnitTests.tests recorder logger+    , HaddockTests.tests+    , PositionMappingTests.tests+    , WatchedFileTests.tests+    , CradleTests.tests+    , DependentFileTest.tests+    , NonLspCommandLine.tests+    , IfaceTests.tests+    , BootTests.tests+    , RootUriTests.tests+    , AsyncTests.tests+    , ClientSettingsTests.tests+    , ReferenceTests.tests+    , GarbageCollectionTests.tests+    , HieDbRetry.tests+    , ExceptionTests.tests recorder logger+    ]
+ test/exe/NonLspCommandLine.hs view
@@ -0,0 +1,27 @@++module NonLspCommandLine (tests) where++import           Development.IDE.Test.Runfiles+import           System.Environment.Blank      (setEnv)+import           System.Exit                   (ExitCode (ExitSuccess))+import           System.Process.Extra          (CreateProcess (cwd), proc,+                                                readCreateProcessWithExitCode)+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+++-- A test to ensure that the command line ghcide workflow stays working+tests :: TestTree+tests = testGroup "ghcide command line"+  [ testCase "works" $ withTempDir $ \dir -> do+        ghcide <- locateGhcideExecutable+        copyTestDataFiles dir "multi"+        let cmd = (proc ghcide ["a/A.hs"]){cwd = Just dir}++        setEnv "HOME" "/homeless-shelter" False++        (ec, _, _) <- readCreateProcessWithExitCode cmd ""++        ec @?= ExitSuccess+  ]
+ test/exe/OpenCloseTest.hs view
@@ -0,0 +1,18 @@++module OpenCloseTest (tests) where++import           Control.Applicative.Combinators+import           Control.Monad+import           Language.LSP.Protocol.Message+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests = testSession "open close" $ do+    doc <- createDoc "Testing.hs" "haskell" ""+    void (skipManyTill anyMessage $ message SMethod_WindowWorkDoneProgressCreate)+    waitForProgressBegin+    closeDoc doc+    waitForProgressDone
+ test/exe/OutlineTests.hs view
@@ -0,0 +1,189 @@++module OutlineTests (tests) where++import           Control.Monad.IO.Class      (liftIO)+import qualified Data.Text                   as T+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup+  "outline"+  [ testSessionWait "type class" $ do+    let source = T.unlines ["module A where", "class A a where a :: a -> Bool"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ moduleSymbol+          "A"+          (R 0 7 0 8)+          [ classSymbol "A a"+                        (R 1 0 1 30)+                        [docSymbol' "a" SymbolKind_Method (R 1 16 1 30) (R 1 16 1 17)]+          ]+      ]+  , testSessionWait "type class instance " $ do+    let source = T.unlines ["class A a where", "instance A () where"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ classSymbol "A a" (R 0 0 0 15) []+      , docSymbol "A ()" SymbolKind_Interface (R 1 0 1 19)+      ]+  , testSessionWait "type family" $ do+    let source = T.unlines ["{-# language TypeFamilies #-}", "type family A"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right [docSymbolD "A" "type family" SymbolKind_Function (R 1 0 1 13)]+  , testSessionWait "type family instance " $ do+    let source = T.unlines+          [ "{-# language TypeFamilies #-}"+          , "type family A a"+          , "type instance A () = ()"+          ]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ docSymbolD "A a"   "type family" SymbolKind_Function     (R 1 0 1 15)+      , docSymbol "A ()" SymbolKind_Interface (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 @?= Right [docSymbolD "A" "data family" SymbolKind_Function (R 1 0 1 11)]+  , testSessionWait "data family instance " $ do+    let source = T.unlines+          [ "{-# language TypeFamilies #-}"+          , "data family A a"+          , "data instance A () = A ()"+          ]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ docSymbolD "A a"   "data family" SymbolKind_Function     (R 1 0 1 11)+      , docSymbol "A ()" SymbolKind_Interface (R 2 0 2 25)+      ]+  , testSessionWait "constant" $ do+    let source = T.unlines ["a = ()"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbol "a" SymbolKind_Function (R 0 0 0 6)]+  , testSessionWait "pattern" $ do+    let source = T.unlines ["Just foo = Just 21"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbol "Just foo" SymbolKind_Function (R 0 0 0 18)]+  , testSessionWait "pattern with type signature" $ do+    let source = T.unlines ["{-# language ScopedTypeVariables #-}", "a :: () = ()"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbol "a :: ()" SymbolKind_Function (R 1 0 1 12)]+  , testSessionWait "function" $ do+    let source = T.unlines ["a _x = ()"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right [docSymbol "a" SymbolKind_Function (R 0 0 0 9)]+  , testSessionWait "type synonym" $ do+    let source = T.unlines ["type A = Bool"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbol' "A" SymbolKind_TypeParameter (R 0 0 0 13) (R 0 5 0 6)]+  , testSessionWait "datatype" $ do+    let source = T.unlines ["data A = C"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ docSymbolWithChildren "A"+                              SymbolKind_Struct+                              (R 0 0 0 10)+                              [docSymbol "C" SymbolKind_Constructor (R 0 9 0 10)]+      ]+  , testSessionWait "record fields" $ do+    let source = T.unlines ["data A = B {", "  x :: Int", "  , y :: Int}"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [ docSymbolWithChildren "A" SymbolKind_Struct (R 0 0 2 13)+          [ docSymbolWithChildren' "B" SymbolKind_Constructor (R 0 9 2 13) (R 0 9 0 10)+            [ docSymbol "x" SymbolKind_Field (R 1 2 1 3)+            , docSymbol "y" SymbolKind_Field (R 2 4 2 5)+            ]+          ]+      ]+  , testSessionWait "import" $ do+    let source = T.unlines ["import Data.Maybe ()"]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbolWithChildren "imports"+                             SymbolKind_Module+                             (R 0 0 0 20)+                             [ docSymbol "import Data.Maybe" SymbolKind_Module (R 0 0 0 20)+                             ]+      ]+  , testSessionWait "multiple import" $ do+    let source = T.unlines ["", "import Data.Maybe ()", "", "import Control.Exception ()", ""]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right+      [docSymbolWithChildren "imports"+                             SymbolKind_Module+                             (R 1 0 3 27)+                             [ docSymbol "import Data.Maybe" SymbolKind_Module (R 1 0 1 20)+                             , docSymbol "import Control.Exception" SymbolKind_Module (R 3 0 3 27)+                             ]+      ]+  , testSessionWait "foreign import" $ do+    let source = T.unlines+          [ "{-# language ForeignFunctionInterface #-}"+          , "foreign import ccall \"a\" a :: Int"+          ]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right [docSymbolD "a" "import" SymbolKind_Object (R 1 0 1 33)]+  , testSessionWait "foreign export" $ do+    let source = T.unlines+          [ "{-# language ForeignFunctionInterface #-}"+          , "foreign export ccall odd :: Int -> Bool"+          ]+    docId   <- createDoc "A.hs" "haskell" source+    symbols <- getDocumentSymbols docId+    liftIO $ symbols @?= Right [docSymbolD "odd" "export" SymbolKind_Object (R 1 0 1 39)]+  ]+ where+  docSymbol name kind loc =+    DocumentSymbol name Nothing kind Nothing Nothing loc loc Nothing+  docSymbol' name kind loc selectionLoc =+    DocumentSymbol name Nothing kind Nothing Nothing loc selectionLoc Nothing+  docSymbolD name detail kind loc =+    DocumentSymbol name (Just detail) kind Nothing Nothing loc loc Nothing+  docSymbolWithChildren name kind loc cc =+    DocumentSymbol name Nothing kind Nothing Nothing loc loc (Just cc)+  docSymbolWithChildren' name kind loc selectionLoc cc =+    DocumentSymbol name Nothing kind Nothing Nothing loc selectionLoc (Just cc)+  moduleSymbol name loc cc = DocumentSymbol name+                                            Nothing+                                            SymbolKind_File+                                            Nothing+                                            Nothing+                                            (R 0 0 maxBound 0)+                                            loc+                                            (Just cc)+  classSymbol name loc cc = DocumentSymbol name+                                           (Just "class")+                                           SymbolKind_Interface+                                           Nothing+                                           Nothing+                                           loc+                                           loc+                                           (Just cc)
+ test/exe/PluginParsedResultTests.hs view
@@ -0,0 +1,17 @@++module PluginParsedResultTests (tests) where++import           Development.IDE.Test (expectNoMoreDiagnostics)+import           Language.LSP.Test+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests =+  ignoreInWindowsForGHC810 $+  ignoreForGHC92Plus "No need for this plugin anymore!" $+  testSessionWithExtraFiles "plugin-recorddot" "parsedResultAction plugin" $ \dir -> do+    _ <- openDoc (dir</> "RecordDot.hs") "haskell"+    expectNoMoreDiagnostics 2
+ test/exe/PluginSimpleTests.hs view
@@ -0,0 +1,51 @@++module PluginSimpleTests (tests) where++import           Control.Monad.IO.Class      (liftIO)+import           Development.IDE.GHC.Compat  (GhcVersion (..))+import           Development.IDE.Test        (expectDiagnostics)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests =+  ignoreInWindowsForGHC810 $+  -- Build profile: -w ghc-9.4.2 -O1+  -- In order, the following will be built (use -v for more details):+  -- - ghc-typelits-natnormalise-0.7.7 (lib) (requires build)+  -- - ghc-typelits-knownnat-0.7.7 (lib) (requires build)+  -- - plugin-1.0.0 (lib) (first run)+  -- Starting     ghc-typelits-natnormalise-0.7.7 (lib)+  -- Building     ghc-typelits-natnormalise-0.7.7 (lib)++  -- Failed to build ghc-typelits-natnormalise-0.7.7.+  -- Build log (+  -- C:\cabal\logs\ghc-9.4.2\ghc-typelits-_-0.7.7-3f036a52a0d9bfc3389d1852a87da2e87c6de2e4.log+  -- ):+  -- Preprocessing library for ghc-typelits-natnormalise-0.7.7..+  -- Building library for ghc-typelits-natnormalise-0.7.7..+  -- [1 of 3] Compiling GHC.TypeLits.Normalise.SOP ( src\GHC\TypeLits\Normalise\SOP.hs, dist\build\GHC\TypeLits\Normalise\SOP.o )+  -- [2 of 3] Compiling GHC.TypeLits.Normalise.Unify ( src\GHC\TypeLits\Normalise\Unify.hs, dist\build\GHC\TypeLits\Normalise\Unify.o )+  -- [3 of 3] Compiling GHC.TypeLits.Normalise ( src-ghc-9.4\GHC\TypeLits\Normalise.hs, dist\build\GHC\TypeLits\Normalise.o )+  -- C:\tools\ghc-9.4.2\lib\../mingw/bin/llvm-ar.exe: error: dist\build\objs-5156\libHSghc-typelits-_-0.7.7-3f036a52a0d9bfc3389d1852a87da2e87c6de2e4.a: No such file or directory++  -- Error: cabal: Failed to build ghc-typelits-natnormalise-0.7.7 (which is+  -- required by plugin-1.0.0). See the build log above for details.+  ignoreFor (BrokenForGHC [GHC96]) "fragile, frequently times out" $+  ignoreFor (BrokenSpecific Windows [GHC94]) "ghc-typelist-natnormalise fails to build on GHC 9.4.2 for windows only" $+  testSessionWithExtraFiles "plugin-knownnat" "simple plugin" $ \dir -> do+    _ <- openDoc (dir </> "KnownNat.hs") "haskell"+    liftIO $ writeFile (dir</>"hie.yaml")+      "cradle: {cabal: [{path: '.', component: 'lib:plugin'}]}"++    expectDiagnostics+      [ ( "KnownNat.hs",+          [(DiagnosticSeverity_Error, (9, 15), "Variable not in scope: c")]+          )+      ]
+ test/exe/PositionMappingTests.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE OverloadedLabels    #-}++module PositionMappingTests (tests) where++import           Data.Row+import qualified Data.Text                            as T+import           Data.Text.Utf16.Rope                 (Rope)+import qualified Data.Text.Utf16.Rope                 as Rope+import           Development.IDE.Core.PositionMapping (PositionResult (..),+                                                       fromCurrent,+                                                       positionResultToMaybe,+                                                       toCurrent)+import           Development.IDE.Types.Location+import           Language.LSP.Protocol.Types          hiding+                                                      (SemanticTokenAbsolute (..),+                                                       SemanticTokenRelative (..),+                                                       SemanticTokensEdit (..),+                                                       mkRange)+import           Language.LSP.VFS                     (applyChange)+import           Test.QuickCheck+-- import Test.QuickCheck.Instances ()+import           Data.Functor.Identity                (runIdentity)+import           Test.Tasty+import           Test.Tasty.HUnit+import           Test.Tasty.QuickCheck++tests ::  TestTree+tests =+    testGroup "position mapping"+        [ testGroup "toCurrent"+              [ testCase "before" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "ab"+                    (Position 0 0) @?= PositionExact (Position 0 0)+              , testCase "after, same line, same length" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "ab"+                    (Position 0 3) @?= PositionExact (Position 0 3)+              , testCase "after, same line, increased length" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc"+                    (Position 0 3) @?= PositionExact (Position 0 4)+              , testCase "after, same line, decreased length" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "a"+                    (Position 0 3) @?= PositionExact (Position 0 2)+              , testCase "after, next line, no newline" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc"+                    (Position 1 3) @?= PositionExact (Position 1 3)+              , testCase "after, next line, newline" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\ndef"+                    (Position 1 0) @?= PositionExact (Position 2 0)+              , testCase "after, same line, newline" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\nd"+                    (Position 0 4) @?= PositionExact (Position 1 2)+              , testCase "after, same line, newline + newline at end" $+                toCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\nd\n"+                    (Position 0 4) @?= PositionExact (Position 2 1)+              , testCase "after, same line, newline + newline at end" $+                toCurrent+                    (Range (Position 0 1) (Position 0 1))+                    "abc"+                    (Position 0 1) @?= PositionExact (Position 0 4)+              ]+        , testGroup "fromCurrent"+              [ testCase "before" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "ab"+                    (Position 0 0) @?= PositionExact (Position 0 0)+              , testCase "after, same line, same length" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "ab"+                    (Position 0 3) @?= PositionExact (Position 0 3)+              , testCase "after, same line, increased length" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc"+                    (Position 0 4) @?= PositionExact (Position 0 3)+              , testCase "after, same line, decreased length" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "a"+                    (Position 0 2) @?= PositionExact (Position 0 3)+              , testCase "after, next line, no newline" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc"+                    (Position 1 3) @?= PositionExact (Position 1 3)+              , testCase "after, next line, newline" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\ndef"+                    (Position 2 0) @?= PositionExact (Position 1 0)+              , testCase "after, same line, newline" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\nd"+                    (Position 1 2) @?= PositionExact (Position 0 4)+              , testCase "after, same line, newline + newline at end" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 3))+                    "abc\nd\n"+                    (Position 2 1) @?= PositionExact (Position 0 4)+              , testCase "after, same line, newline + newline at end" $+                fromCurrent+                    (Range (Position 0 1) (Position 0 1))+                    "abc"+                    (Position 0 4) @?= PositionExact (Position 0 1)+              ]+        , adjustOption (\(QuickCheckTests i) -> QuickCheckTests (max 1000 i)) $ testGroup "properties"+              [ testProperty "fromCurrent r t <=< toCurrent r t" $ do+                -- Note that it is important to use suchThatMap on all values at once+                -- instead of only using it on the position. Otherwise you can get+                -- into situations where there is no position that can be mapped back+                -- for the edit which will result in QuickCheck looping forever.+                let gen = do+                        rope <- genRope+                        range <- genRange rope+                        PrintableText replacement <- arbitrary+                        oldPos <- genPosition rope+                        pure (range, replacement, oldPos)+                forAll+                    (suchThatMap gen+                        (\(range, replacement, oldPos) -> positionResultToMaybe $ (range, replacement, oldPos,) <$> toCurrent range replacement oldPos)) $+                    \(range, replacement, oldPos, newPos) ->+                    fromCurrent range replacement newPos === PositionExact oldPos+              , testProperty "toCurrent r t <=< fromCurrent r t" $ do+                let gen = do+                        rope <- genRope+                        range <- genRange rope+                        PrintableText replacement <- arbitrary+                        let newRope = runIdentity $ applyChange mempty rope+                                (TextDocumentContentChangeEvent $ InL $ #range .== range+                                                                     .+ #rangeLength .== Nothing+                                                                     .+ #text .== replacement)+                        newPos <- genPosition newRope+                        pure (range, replacement, newPos)+                forAll+                    (suchThatMap gen+                        (\(range, replacement, newPos) -> positionResultToMaybe $ (range, replacement, newPos,) <$> fromCurrent range replacement newPos)) $+                    \(range, replacement, newPos, oldPos) ->+                    toCurrent range replacement oldPos === PositionExact newPos+              ]+        ]++newtype PrintableText = PrintableText { getPrintableText :: T.Text }+    deriving Show++instance Arbitrary PrintableText where+    arbitrary = PrintableText . T.pack . getPrintableString <$> arbitrary++genRope :: Gen Rope+genRope = Rope.fromText . getPrintableText <$> arbitrary++genPosition :: Rope -> Gen Position+genPosition r = do+    let rows :: Int = fromIntegral $ Rope.lengthInLines r+    row <- choose (0, max 0 $ rows - 1) `suchThat` inBounds @UInt+    let columns = T.length (nthLine (fromIntegral row) r)+    column <- choose (0, max 0 $ columns - 1) `suchThat` inBounds @UInt+    pure $ Position (fromIntegral row) (fromIntegral column)++genRange :: Rope -> Gen Range+genRange r = do+    let rows :: Int = fromIntegral $ Rope.lengthInLines r+    startPos@(Position startLine startColumn) <- genPosition r+    let maxLineDiff = max 0 $ rows - 1 - fromIntegral startLine+    endLine <- choose (fromIntegral startLine, fromIntegral startLine + maxLineDiff) `suchThat` inBounds @UInt+    let columns = T.length (nthLine (fromIntegral endLine) r)+    endColumn <-+        if fromIntegral startLine == endLine+            then choose (fromIntegral startColumn, columns)+            else choose (0, max 0 $ columns - 1)+        `suchThat` inBounds @UInt+    pure $ Range startPos (Position (fromIntegral endLine) (fromIntegral endColumn))++inBounds :: forall b a . (Integral a, Integral b, Bounded b) => a -> Bool+inBounds a = let i = toInteger a in i <= toInteger (maxBound @b) && i >= toInteger (minBound @b)++-- | Get the ith line of a rope, starting from 0. Trailing newline not included.+nthLine :: Int -> Rope -> T.Text+nthLine i r+    | Rope.null r = ""+    | otherwise = Rope.lines r !! i
+ test/exe/PreprocessorTests.hs view
@@ -0,0 +1,27 @@++module PreprocessorTests (tests) where++import qualified Data.Text                   as T+import           Development.IDE.Test        (expectDiagnostics)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           TestUtils++tests :: TestTree+tests = testSessionWait "preprocessor" $ do+  let content =+        T.unlines+          [ "{-# OPTIONS_GHC -F -pgmF=ghcide-test-preprocessor #-}"+          , "module Testing where"+          , "y = x + z" -- plugin replaces x with y, making this have only one diagnostic+          ]+  _ <- createDoc "Testing.hs" "haskell" content+  expectDiagnostics+    [ ( "Testing.hs",+        [(DiagnosticSeverity_Error, (2, 8), "Variable not in scope: z")]+      )+    ]
+ test/exe/ReferenceTests.hs view
@@ -0,0 +1,199 @@++module ReferenceTests (tests) where++import           Control.Applicative.Combinators+import qualified Control.Lens                    as Lens+import           Control.Monad+import           Control.Monad.IO.Class          (liftIO)+import           Data.List.Extra+import qualified Data.Set                        as Set+import           Development.IDE.Test            (configureCheckProject,+                                                  referenceReady)+import           Development.IDE.Types.Location+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Control.Lens                    ((^.))+import           Data.Tuple.Extra+import           Test.Tasty+import           Test.Tasty.ExpectedFailure+import           Test.Tasty.HUnit+import           TestUtils+++tests :: TestTree+tests = testGroup "references"+    [ testGroup "can get references to FOIs"+          [ referenceTest "can get references to symbols"+                          ("References.hs", 4, 7)+                          YesIncludeDeclaration+                          [ ("References.hs", 4, 6)+                          , ("References.hs", 6, 0)+                          , ("References.hs", 6, 14)+                          , ("References.hs", 9, 7)+                          , ("References.hs", 10, 11)+                          ]++          , referenceTest "can get references to data constructor"+                          ("References.hs", 13, 2)+                          YesIncludeDeclaration+                          [ ("References.hs", 13, 2)+                          , ("References.hs", 16, 14)+                          , ("References.hs", 19, 21)+                          ]++          , referenceTest "getting references works in the other module"+                          ("OtherModule.hs", 6, 0)+                          YesIncludeDeclaration+                          [ ("OtherModule.hs", 6, 0)+                          , ("OtherModule.hs", 8, 16)+                          ]++          , referenceTest "getting references works in the Main module"+                          ("Main.hs", 9, 0)+                          YesIncludeDeclaration+                          [ ("Main.hs", 9, 0)+                          , ("Main.hs", 10, 4)+                          ]++          , referenceTest "getting references to main works"+                          ("Main.hs", 5, 0)+                          YesIncludeDeclaration+                          [ ("Main.hs", 4, 0)+                          , ("Main.hs", 5, 0)+                          ]++          , referenceTest "can get type references"+                          ("Main.hs", 9, 9)+                          YesIncludeDeclaration+                          [ ("Main.hs", 9, 0)+                          , ("Main.hs", 9, 9)+                          , ("Main.hs", 10, 0)+                          ]++          , expectFailBecause "references provider does not respect includeDeclaration parameter" $+ referenceTest "works when we ask to exclude declarations"+                          ("References.hs", 4, 7)+                          NoExcludeDeclaration+                          [ ("References.hs", 6, 0)+                          , ("References.hs", 6, 14)+                          , ("References.hs", 9, 7)+                          , ("References.hs", 10, 11)+                          ]++          , referenceTest "INCORRECTLY returns declarations when we ask to exclude them"+                          ("References.hs", 4, 7)+                          NoExcludeDeclaration+                          [ ("References.hs", 4, 6)+                          , ("References.hs", 6, 0)+                          , ("References.hs", 6, 14)+                          , ("References.hs", 9, 7)+                          , ("References.hs", 10, 11)+                          ]+          ]++    , testGroup "can get references to non FOIs"+          [ referenceTest "can get references to symbol defined in a module we import"+                          ("References.hs", 22, 4)+                          YesIncludeDeclaration+                          [ ("References.hs", 22, 4)+                          , ("OtherModule.hs", 0, 20)+                          , ("OtherModule.hs", 4, 0)+                          ]++          , referenceTest "can get references in modules that import us to symbols we define"+                          ("OtherModule.hs", 4, 0)+                          YesIncludeDeclaration+                          [ ("References.hs", 22, 4)+                          , ("OtherModule.hs", 0, 20)+                          , ("OtherModule.hs", 4, 0)+                          ]++          , referenceTest "can get references to symbol defined in a module we import transitively"+                          ("References.hs", 24, 4)+                          YesIncludeDeclaration+                          [ ("References.hs", 24, 4)+                          , ("OtherModule.hs", 0, 48)+                          , ("OtherOtherModule.hs", 2, 0)+                          ]++          , referenceTest "can get references in modules that import us transitively to symbols we define"+                          ("OtherOtherModule.hs", 2, 0)+                          YesIncludeDeclaration+                          [ ("References.hs", 24, 4)+                          , ("OtherModule.hs", 0, 48)+                          , ("OtherOtherModule.hs", 2, 0)+                          ]++          , referenceTest "can get type references to other modules"+                          ("Main.hs", 12, 10)+                          YesIncludeDeclaration+                          [ ("Main.hs", 12, 7)+                          , ("Main.hs", 13, 0)+                          , ("References.hs", 12, 5)+                          , ("References.hs", 16, 0)+                          ]+          ]+    ]++-- | When we ask for all references to symbol "foo", should the declaration "foo+-- = 2" be among the references returned?+data IncludeDeclaration =+    YesIncludeDeclaration+    | NoExcludeDeclaration++getReferences' :: SymbolLocation -> IncludeDeclaration -> Session ([Location])+getReferences' (file, l, c) includeDeclaration = do+    doc <- openDoc file "haskell"+    getReferences doc (Position l c) $ toBool includeDeclaration+    where toBool YesIncludeDeclaration = True+          toBool NoExcludeDeclaration  = False++referenceTestSession :: String -> FilePath -> [FilePath] -> (FilePath -> Session ()) -> TestTree+referenceTestSession name thisDoc docs' f = testSessionWithExtraFiles "references" name $ \dir -> do+  -- needed to build whole project indexing+  configureCheckProject True+  let docs = map (dir </>) $ delete thisDoc $ nubOrd docs'+  -- Initial Index+  docid <- openDoc thisDoc "haskell"+  let+    loop :: [FilePath] -> Session ()+    loop [] = pure ()+    loop docs = do+      doc <- skipManyTill anyMessage $ referenceReady (`elem` docs)+      loop (delete doc docs)+  loop docs+  f dir+  closeDoc docid++-- | Given a location, lookup the symbol and all references to it. Make sure+-- they are the ones we expect.+referenceTest :: String -> SymbolLocation -> IncludeDeclaration -> [SymbolLocation] -> TestTree+referenceTest name loc includeDeclaration expected =+    referenceTestSession name (fst3 loc) docs $ \dir -> do+        actual <- getReferences' loc includeDeclaration+        liftIO $ actual `expectSameLocations` map (first3 (dir </>)) expected+  where+    docs = map fst3 expected++type SymbolLocation = (FilePath, UInt, UInt)++expectSameLocations :: [Location] -> [SymbolLocation] -> Assertion+expectSameLocations actual expected = do+    let actual' =+            Set.map (\location -> (location ^. L.uri+                                   , location ^. L.range . L.start . L.line . Lens.to fromIntegral+                                   , location ^. L.range . L.start . L.character . Lens.to fromIntegral))+            $ Set.fromList actual+    expected' <- Set.fromList <$>+        (forM expected $ \(file, l, c) -> do+                              fp <- canonicalizePath file+                              return (filePathToUri fp, l, c))+    actual' @?= expected'
+ test/exe/RootUriTests.hs view
@@ -0,0 +1,26 @@++module RootUriTests (tests) where++import           Control.Monad.IO.Class   (liftIO)+import           Development.IDE.GHC.Util+import           Development.IDE.Test     (expectNoMoreDiagnostics)+import           Language.LSP.Test+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils+++-- | checks if we use InitializeParams.rootUri for loading session+tests :: TestTree+tests = testCase "use rootUri" . runTest "dirA" "dirB" $ \dir -> do+  let bPath = dir </> "dirB/Foo.hs"+  liftIO $ copyTestDataFiles dir "rootUri"+  bSource <- liftIO $ readFileUtf8 bPath+  _ <- createDoc "Foo.hs" "haskell" bSource+  expectNoMoreDiagnostics 0.5+  where+    -- similar to run' except we can configure where to start ghcide and session+    runTest :: FilePath -> FilePath -> (FilePath -> Session ()) -> IO ()+    runTest dir1 dir2 s = withTempDir $ \dir -> runInDir' dir dir1 dir2 [] (s dir)
+ test/exe/SafeTests.hs view
@@ -0,0 +1,38 @@++module SafeTests (tests) where++import qualified Data.Text            as T+import           Development.IDE.Test (expectNoMoreDiagnostics)+import           Language.LSP.Test++import           Test.Tasty+import           TestUtils++tests :: TestTree+tests =+  testGroup+    "SafeHaskell"+    [ -- Test for https://github.com/haskell/ghcide/issues/424+      testSessionWait "load" $ do+        let sourceA =+              T.unlines+                ["{-# LANGUAGE Trustworthy #-}"+                ,"module A where"+                ,"import System.IO.Unsafe"+                ,"import System.IO ()"+                ,"trustWorthyId :: a -> a"+                ,"trustWorthyId i = unsafePerformIO $ do"+                ,"  putStrLn \"I'm safe\""+                ,"  return i"]+            sourceB =+              T.unlines+                ["{-# LANGUAGE Safe #-}"+                ,"module B where"+                ,"import A"+                ,"safeId :: a -> a"+                ,"safeId = trustWorthyId"+                ]++        _ <- createDoc "A.hs" "haskell" sourceA+        _ <- createDoc "B.hs" "haskell" sourceB+        expectNoMoreDiagnostics 1 ]
+ test/exe/SymlinkTests.hs view
@@ -0,0 +1,27 @@++module SymlinkTests (tests) where++import           Control.Monad.IO.Class      (liftIO)+import           Development.IDE.Test        (expectDiagnosticsWithTags)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.FilePath++import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++-- | Tests for projects that use symbolic links one way or another+tests :: TestTree+tests =+  testGroup "Projects using Symlinks"+    [ testCase "Module is symlinked" $ runWithExtraFiles "symlink" $ \dir -> do+        liftIO $ createFileLink (dir </> "some_loc" </> "Sym.hs") (dir </> "other_loc" </> "Sym.hs")+        let fooPath = dir </> "src" </> "Foo.hs"+        _ <- openDoc fooPath "haskell"+        expectDiagnosticsWithTags  [("src" </> "Foo.hs", [(DiagnosticSeverity_Warning, (2, 0), "The import of 'Sym' is redundant", Just DiagnosticTag_Unnecessary)])]+        pure ()+    ]
+ test/exe/THTests.hs view
@@ -0,0 +1,194 @@++{-# LANGUAGE OverloadedLabels #-}++module THTests (tests) where++import           Control.Monad.IO.Class      (liftIO)+import           Data.Row+import qualified Data.Text                   as T+import           Development.IDE.GHC.Util+import           Development.IDE.Test        (expectCurrentDiagnostics,+                                              expectDiagnostics,+                                              expectNoMoreDiagnostics)+import           Language.LSP.Protocol.Types hiding (SemanticTokenAbsolute (..),+                                              SemanticTokenRelative (..),+                                              SemanticTokensEdit (..), mkRange)+import           Language.LSP.Test+import           System.FilePath+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests =+  testGroup+    "TemplateHaskell"+    [ -- Test for https://github.com/haskell/ghcide/pull/212+      testSessionWait "load" $ do+        let sourceA =+              T.unlines+                [ "{-# LANGUAGE PackageImports #-}",+                  "{-# LANGUAGE TemplateHaskell #-}",+                  "module A where",+                  "import \"template-haskell\" Language.Haskell.TH",+                  "a :: Integer",+                  "a = $(litE $ IntegerL 3)"+                ]+            sourceB =+              T.unlines+                [ "{-# LANGUAGE PackageImports #-}",+                  "{-# LANGUAGE TemplateHaskell #-}",+                  "module B where",+                  "import A",+                  "import \"template-haskell\" Language.Haskell.TH",+                  "b :: Integer",+                  "b = $(litE $ IntegerL $ a) + n"+                ]+        _ <- createDoc "A.hs" "haskell" sourceA+        _ <- createDoc "B.hs" "haskell" sourceB+        expectDiagnostics [ ( "B.hs", [(DiagnosticSeverity_Error, (6, 29), "Variable not in scope: n")] ) ]+    , testSessionWait "newtype-closure" $ do+        let sourceA =+              T.unlines+                [ "{-# LANGUAGE DeriveDataTypeable #-}"+                  ,"{-# LANGUAGE TemplateHaskell #-}"+                  ,"module A (a) where"+                  ,"import Data.Data"+                  ,"import Language.Haskell.TH"+                  ,"newtype A = A () deriving (Data)"+                  ,"a :: ExpQ"+                  ,"a = [| 0 |]"]+        let sourceB =+              T.unlines+                [ "{-# LANGUAGE TemplateHaskell #-}"+                ,"module B where"+                ,"import A"+                ,"b :: Int"+                ,"b = $( a )" ]+        _ <- createDoc "A.hs" "haskell" sourceA+        _ <- createDoc "B.hs" "haskell" sourceB+        return ()+    , thReloadingTest False+    , thLoadingTest+    , thCoreTest+    , ignoreInWindowsBecause "Broken in windows" $ thReloadingTest True+    -- Regression test for https://github.com/haskell/haskell-language-server/issues/891+    , thLinkingTest False+    , ignoreInWindowsBecause "Broken in windows" $ thLinkingTest True+    , testSessionWait "findsTHIdentifiers" $ do+        let sourceA =+              T.unlines+                [ "{-# LANGUAGE TemplateHaskell #-}"+                , "module A (a) where"+                , "import Language.Haskell.TH (ExpQ)"+                , "a :: ExpQ" -- TH 2.17 requires an explicit type signature since splices are polymorphic+                , "a = [| glorifiedID |]"+                , "glorifiedID :: a -> a"+                , "glorifiedID = id" ]+        let sourceB =+              T.unlines+                [ "{-# OPTIONS_GHC -Wall #-}"+                , "{-# LANGUAGE TemplateHaskell #-}"+                , "module B where"+                , "import A"+                , "main = $a (putStrLn \"success!\")"]+        _ <- createDoc "A.hs" "haskell" sourceA+        _ <- createDoc "B.hs" "haskell" sourceB+        expectDiagnostics [ ( "B.hs", [(DiagnosticSeverity_Warning, (4, 0), "Top-level binding with no type signature: main :: IO ()")] ) ]+    , testCase "findsTHnewNameConstructor" $ runWithExtraFiles "THNewName" $ \dir -> do++    -- This test defines a TH value with the meaning "data A = A" in A.hs+    -- Loads and export the template in B.hs+    -- And checks wether the constructor A can be loaded in C.hs+    -- This test does not fail when either A and B get manually loaded before C.hs+    -- or when we remove the seemingly unnecessary TH pragma from C.hs++    let cPath = dir </> "C.hs"+    _ <- openDoc cPath "haskell"+    expectDiagnostics [ ( cPath, [(DiagnosticSeverity_Warning, (3, 0), "Top-level binding with no type signature: a :: A")] ) ]+    ]+++-- | Test that all modules have linkables+thLoadingTest :: TestTree+thLoadingTest = testCase "Loading linkables" $ runWithExtraFiles "THLoading" $ \dir -> do+    let thb = dir </> "THB.hs"+    _ <- openDoc thb "haskell"+    expectNoMoreDiagnostics 1++thCoreTest :: TestTree+thCoreTest = testCase "Verifying TH core files" $ runWithExtraFiles "THCoreFile" $ \dir -> do+    let thc = dir </> "THC.hs"+    _ <- openDoc thc "haskell"+    expectNoMoreDiagnostics 1++-- | test that TH is reevaluated on typecheck+thReloadingTest :: Bool -> TestTree+thReloadingTest unboxed = testCase name $ runWithExtraFiles dir $ \dir -> do++    let aPath = dir </> "THA.hs"+        bPath = dir </> "THB.hs"+        cPath = dir </> "THC.hs"++    aSource <- liftIO $ readFileUtf8 aPath --  th = [d|a = ()|]+    bSource <- liftIO $ readFileUtf8 bPath --  $th+    cSource <- liftIO $ readFileUtf8 cPath --  c = a :: ()++    adoc <- createDoc aPath "haskell" aSource+    bdoc <- createDoc bPath "haskell" bSource+    cdoc <- createDoc cPath "haskell" cSource++    expectDiagnostics [("THB.hs", [(DiagnosticSeverity_Warning, (4,thDollarIdx), "Top-level binding")])]++    -- Change th from () to Bool+    let aSource' = T.unlines $ init (T.lines aSource) ++ ["th_a = [d| a = False|]"]+    changeDoc adoc [TextDocumentContentChangeEvent . InR . (.==) #text $ aSource']+    -- generate an artificial warning to avoid timing out if the TH change does not propagate+    changeDoc cdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ cSource <> "\nfoo=()"]++    -- Check that the change propagates to C+    expectDiagnostics+        [("THC.hs", [(DiagnosticSeverity_Error, (4, 4), "Couldn't match expected type '()' with actual type 'Bool'")])+        ,("THC.hs", [(DiagnosticSeverity_Warning, (6,0), "Top-level binding")])+        ,("THB.hs", [(DiagnosticSeverity_Warning, (4,thDollarIdx), "Top-level bindin")])+        ]++    closeDoc adoc+    closeDoc bdoc+    closeDoc cdoc+  where+    name = "reloading-th-test" <> if unboxed then "-unboxed" else ""+    dir | unboxed = "THUnboxed"+        | otherwise = "TH"++thLinkingTest :: Bool -> TestTree+thLinkingTest unboxed = testCase name $ runWithExtraFiles dir $ \dir -> do++    let aPath = dir </> "THA.hs"+        bPath = dir </> "THB.hs"++    aSource <- liftIO $ readFileUtf8 aPath --  th_a = [d|a :: ()|]+    bSource <- liftIO $ readFileUtf8 bPath --  $th_a++    adoc <- createDoc aPath "haskell" aSource+    bdoc <- createDoc bPath "haskell" bSource++    expectDiagnostics [("THB.hs", [(DiagnosticSeverity_Warning, (4,thDollarIdx), "Top-level binding")])]++    let aSource' = T.unlines $ init (init (T.lines aSource)) ++ ["th :: DecsQ", "th = [d| a = False|]"]+    changeDoc adoc [TextDocumentContentChangeEvent . InR . (.==) #text $ aSource']++    -- modify b too+    let bSource' = T.unlines $ init (T.lines bSource) ++ ["$th"]+    changeDoc bdoc [TextDocumentContentChangeEvent . InR . (.==) #text $ bSource']+    waitForProgressBegin+    waitForAllProgressDone++    expectCurrentDiagnostics bdoc [(DiagnosticSeverity_Warning, (4,thDollarIdx), "Top-level binding")]++    closeDoc adoc+    closeDoc bdoc+  where+    name = "th-linking-test" <> if unboxed then "-unboxed" else ""+    dir | unboxed = "THUnboxed"+        | otherwise = "TH"
+ test/exe/TestUtils.hs view
@@ -0,0 +1,329 @@++{-# LANGUAGE GADTs           #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TypeOperators   #-}++module TestUtils where++import           Control.Applicative.Combinators+import           Control.Concurrent.Async+import           Control.Exception               (bracket_, finally)+import           Control.Lens                    ((.~))+import qualified Control.Lens                    as Lens+import qualified Control.Lens.Extras             as Lens+import           Control.Monad+import           Control.Monad.IO.Class          (liftIO)+import           Data.Foldable+import           Data.Function                   ((&))+import           Data.Maybe+import qualified Data.Text                       as T+import           Development.IDE.GHC.Compat      (GhcVersion (..), ghcVersion)+import           Development.IDE.GHC.Util+import qualified Development.IDE.Main            as IDE+import           Development.IDE.Test            (canonicalizeUri,+                                                  configureCheckProject,+                                                  expectNoMoreDiagnostics)+import           Development.IDE.Test.Runfiles+import           Development.IDE.Types.Location+import           Development.Shake               (getDirectoryFilesIO)+import           Ide.Logger                      (Recorder, WithPriority,+                                                  cmapWithPrio)+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.Environment.Blank        (getEnv, setEnv, unsetEnv)+import           System.FilePath+import           System.Info.Extra               (isMac, isWindows)+import qualified System.IO.Extra+import           System.Process.Extra            (createPipe)+import           Test.Tasty+import           Test.Tasty.ExpectedFailure+import           Test.Tasty.HUnit++import           LogType++-- | Wait for the next progress begin step+waitForProgressBegin :: Session ()+waitForProgressBegin = skipManyTill anyMessage $ satisfyMaybe $ \case+  FromServerMess  SMethod_Progress  (TNotificationMessage _ _ (ProgressParams _ v)) | Lens.is _workDoneProgressBegin v-> Just ()+  _ -> Nothing++-- | Wait for the first progress end step+-- Also implemented in hls-test-utils Test.Hls+waitForProgressDone :: Session ()+waitForProgressDone = skipManyTill anyMessage $ satisfyMaybe $ \case+  FromServerMess  SMethod_Progress  (TNotificationMessage _ _ (ProgressParams _ v)) | Lens.is _workDoneProgressEnd v -> Just ()+  _ -> Nothing++-- | Wait for all progress to be done+-- Needs at least one progress done notification to return+-- Also implemented in hls-test-utils Test.Hls+waitForAllProgressDone :: Session ()+waitForAllProgressDone = loop+  where+    loop = do+      ~() <- skipManyTill anyMessage $ satisfyMaybe $ \case+        FromServerMess  SMethod_Progress  (TNotificationMessage _ _ (ProgressParams _ v)) |Lens.is _workDoneProgressEnd v-> Just ()+        _ -> Nothing+      done <- null <$> getIncompleteProgressSessions+      unless done loop++run :: Session a -> IO a+run s = run' (const s)++run' :: (FilePath -> Session a) -> IO a+run' s = withTempDir $ \dir -> runInDir dir (s dir)++runInDir :: FilePath -> Session a -> IO a+runInDir dir = runInDir' dir "." "." []++-- | Takes a directory as well as relative paths to where we should launch the executable as well as the session root.+runInDir' :: FilePath -> FilePath -> FilePath -> [String] -> Session a -> IO a+runInDir' = runInDir'' lspTestCaps++runInDir''+    :: ClientCapabilities+    -> FilePath+    -> FilePath+    -> FilePath+    -> [String]+    -> Session b+    -> IO b+runInDir'' lspCaps dir startExeIn startSessionIn extraOptions s = do++  ghcideExe <- locateGhcideExecutable+  let startDir = dir </> startExeIn+  let projDir = dir </> startSessionIn++  createDirectoryIfMissing True startDir+  createDirectoryIfMissing True projDir+  -- Temporarily hack around https://github.com/mpickering/hie-bios/pull/56+  -- since the package import test creates "Data/List.hs", which otherwise has no physical home+  createDirectoryIfMissing True $ projDir ++ "/Data"++  shakeProfiling <- getEnv "SHAKE_PROFILING"+  let cmd = unwords $+       [ghcideExe, "--lsp", "--test", "--verify-core-file", "--verbose", "-j2", "--cwd", startDir+       ] ++ ["--shake-profiling=" <> dir | Just dir <- [shakeProfiling]+       ] ++ extraOptions+  -- HIE calls getXgdDirectory which assumes that HOME is set.+  -- Only sets HOME if it wasn't already set.+  setEnv "HOME" "/homeless-shelter" False+  conf <- getConfigFromEnv+  runSessionWithConfig conf cmd lspCaps projDir $ do+      configureCheckProject False+      s++-- | Version of 'System.IO.Extra.withTempDir' that canonicalizes the path+-- Which we need to do on macOS since the $TMPDIR can be in @/private/var@ or+-- @/var@+withTempDir :: (FilePath -> IO a) -> IO a+withTempDir f = System.IO.Extra.withTempDir $ \dir -> do+  dir' <- canonicalizePath dir+  f dir'++lspTestCaps :: ClientCapabilities+lspTestCaps = fullCaps { _window = Just $ WindowClientCapabilities (Just True) Nothing Nothing }++getConfigFromEnv :: IO SessionConfig+getConfigFromEnv = do+  logColor <- fromMaybe True <$> checkEnv "LSP_TEST_LOG_COLOR"+  timeoutOverride <- fmap read <$> getEnv "LSP_TIMEOUT"+  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++testSessionWait :: HasCallStack => String -> Session () -> TestTree+testSessionWait name = testSession name .+      -- Check that any diagnostics produced were already consumed by the test case.+      --+      -- If in future we add test cases where we don't care about checking the diagnostics,+      -- this could move elsewhere.+      --+      -- Experimentally, 0.5s seems to be long enough to wait for any final diagnostics to appear.+      ( >> expectNoMoreDiagnostics 0.5)++testSession :: String -> Session () -> TestTree+testSession name = testCase name . run++xfail :: TestTree -> String -> TestTree+xfail = flip expectFailBecause++ignoreInWindowsBecause :: String -> TestTree -> TestTree+ignoreInWindowsBecause = ignoreFor (BrokenForOS Windows)++ignoreInWindowsForGHC810 :: TestTree -> TestTree+ignoreInWindowsForGHC810 =+    ignoreFor (BrokenSpecific Windows [GHC810]) "tests are unreliable in windows for ghc 8.10"++ignoreForGHC92Plus :: String -> TestTree -> TestTree+ignoreForGHC92Plus = ignoreFor (BrokenForGHC [GHC92, GHC94, GHC96])++knownBrokenForGhcVersions :: [GhcVersion] -> String -> TestTree -> TestTree+knownBrokenForGhcVersions ghcVers = knownBrokenFor (BrokenForGHC ghcVers)++data BrokenOS = Linux | MacOS | Windows deriving (Show)++data IssueSolution = Broken | Ignore deriving (Show)++data BrokenTarget =+    BrokenSpecific BrokenOS [GhcVersion]+    -- ^Broken for `BrokenOS` with `GhcVersion`+    | BrokenForOS BrokenOS+    -- ^Broken for `BrokenOS`+    | BrokenForGHC [GhcVersion]+    -- ^Broken for `GhcVersion`+    deriving (Show)++-- | Ignore test for specific os and ghc with reason.+ignoreFor :: BrokenTarget -> String -> TestTree -> TestTree+ignoreFor = knownIssueFor Ignore++-- | Known broken for specific os and ghc with reason.+knownBrokenFor :: BrokenTarget -> String -> TestTree -> TestTree+knownBrokenFor = knownIssueFor Broken++-- | Deal with `IssueSolution` for specific OS and GHC.+knownIssueFor :: IssueSolution -> BrokenTarget -> String -> TestTree -> TestTree+knownIssueFor solution = go . \case+    BrokenSpecific bos vers -> isTargetOS bos && isTargetGhc vers+    BrokenForOS bos         -> isTargetOS bos+    BrokenForGHC vers       -> isTargetGhc vers+    where+        isTargetOS = \case+            Windows -> isWindows+            MacOS   -> isMac+            Linux   -> not isWindows && not isMac++        isTargetGhc = elem ghcVersion++        go True = case solution of+            Broken -> expectFailBecause+            Ignore -> ignoreTestBecause+        go False = \_ -> id++data Expect+  = ExpectRange Range -- Both gotoDef and hover should report this range+  | ExpectLocation Location+--  | ExpectDefRange Range -- Only gotoDef should report this range+  | ExpectHoverRange Range -- Only hover should report this range+  | ExpectHoverText [T.Text] -- the hover message must contain these snippets+  | ExpectHoverExcludeText [T.Text] -- the hover message must _not_ contain these snippets+  | ExpectHoverTextRegex T.Text -- the hover message must match this pattern+  | ExpectExternFail -- definition lookup in other file expected to fail+  | ExpectNoDefinitions+  | ExpectNoHover+--  | ExpectExtern -- TODO: as above, but expected to succeed: need some more info in here, once we have some working examples+  deriving Eq++mkR :: UInt -> UInt -> UInt -> UInt -> Expect+mkR startLine startColumn endLine endColumn = ExpectRange $ mkRange startLine startColumn endLine endColumn++mkL :: Uri -> UInt -> UInt -> UInt -> UInt -> Expect+mkL uri startLine startColumn endLine endColumn = ExpectLocation $ Location uri $ mkRange startLine startColumn endLine endColumn++++testSessionWithExtraFiles :: FilePath -> String -> (FilePath -> Session ()) -> TestTree+testSessionWithExtraFiles prefix name = testCase name . runWithExtraFiles prefix++testSession' :: String -> (FilePath -> Session ()) -> TestTree+testSession' name = testCase name . run'++++mkRange :: UInt -> UInt -> UInt -> UInt -> Range+mkRange a b c d = Range (Position a b) (Position c d)+++runWithExtraFiles :: FilePath -> (FilePath -> Session a) -> IO a+runWithExtraFiles prefix s = withTempDir $ \dir -> do+  copyTestDataFiles dir prefix+  runInDir dir (s dir)++copyTestDataFiles :: FilePath -> FilePath -> IO ()+copyTestDataFiles dir prefix = do+  -- Copy all the test data files to the temporary workspace+  testDataFiles <- getDirectoryFilesIO ("test/data" </> prefix) ["//*"]+  for_ testDataFiles $ \f -> do+    createDirectoryIfMissing True $ dir </> takeDirectory f+    copyFile ("test/data" </> prefix </> f) (dir </> f)++withLongTimeout :: IO a -> IO a+withLongTimeout = bracket_ (setEnv "LSP_TIMEOUT" "120" True) (unsetEnv "LSP_TIMEOUT")++++lspTestCapsNoFileWatches :: ClientCapabilities+lspTestCapsNoFileWatches = lspTestCaps & L.workspace . Lens._Just . L.didChangeWatchedFiles .~ Nothing++openTestDataDoc :: FilePath -> Session TextDocumentIdentifier+openTestDataDoc path = do+  source <- liftIO $ readFileUtf8 $ "test/data" </> path+  createDoc path "haskell" source++pattern R :: UInt -> UInt -> UInt -> UInt -> Range+pattern R x y x' y' = Range (Position x y) (Position x' y')++checkDefs :: Definition |? ([DefinitionLink] |? Null) -> Session [Expect] -> Session ()+checkDefs (defToLocation -> defs) mkExpectations = traverse_ check =<< mkExpectations where+  check (ExpectRange expectedRange) = do+    assertNDefinitionsFound 1 defs+    assertRangeCorrect (head defs) expectedRange+  check (ExpectLocation expectedLocation) = do+    assertNDefinitionsFound 1 defs+    liftIO $ do+      canonActualLoc <- canonicalizeLocation (head defs)+      canonExpectedLoc <- canonicalizeLocation expectedLocation+      canonActualLoc @?= canonExpectedLoc+  check ExpectNoDefinitions = do+    assertNDefinitionsFound 0 defs+  check ExpectExternFail = liftIO $ assertFailure "Expecting to fail to find in external file"+  check _ = pure () -- all other expectations not relevant to getDefinition++  assertNDefinitionsFound :: Int -> [a] -> Session ()+  assertNDefinitionsFound n defs = liftIO $ assertEqual "number of definitions" n (length defs)++  assertRangeCorrect Location{_range = foundRange} expectedRange =+    liftIO $ expectedRange @=? foundRange++canonicalizeLocation :: Location -> IO Location+canonicalizeLocation (Location uri range) = Location <$> canonicalizeUri uri <*> pure range++defToLocation :: Definition |? ([DefinitionLink] |? Null) -> [Location]+defToLocation (InL (Definition (InL l))) = [l]+defToLocation (InL (Definition (InR ls))) = ls+defToLocation (InR (InL defLink)) = (\(DefinitionLink LocationLink{_targetUri,_targetRange}) -> Location _targetUri _targetRange) <$> defLink+defToLocation (InR (InR Null)) = []++-- | Ghc 9 doesn't include the $-sign in TH warnings like earlier versions did+thDollarIdx :: UInt+thDollarIdx | ghcVersion >= GHC90 = 1+            | otherwise = 0++testIde :: Recorder (WithPriority Log) -> IDE.Arguments -> Session () -> IO ()+testIde recorder arguments session = do+    config <- getConfigFromEnv+    cwd <- getCurrentDirectory+    (hInRead, hInWrite) <- createPipe+    (hOutRead, hOutWrite) <- createPipe+    let projDir = "."+    let server = IDE.defaultMain (cmapWithPrio LogIDEMain recorder) arguments+            { IDE.argsHandleIn = pure hInRead+            , IDE.argsHandleOut = pure hOutWrite+            }++    flip finally (setCurrentDirectory cwd) $ withAsync server $ \_ ->+        runSessionWithHandles hInWrite hOutRead config lspTestCaps projDir session
+ test/exe/UnitTests.hs view
@@ -0,0 +1,110 @@++module UnitTests (tests) where++import           Control.Concurrent+import           Control.Monad.IO.Class            (liftIO)+import           Data.IORef+import           Data.IORef.Extra                  (atomicModifyIORef_)+import           Data.List.Extra+import           Data.String                       (IsString (fromString))+import qualified Data.Text                         as T+import           Development.IDE.Core.FileStore    (getModTime)+import qualified Development.IDE.Main              as IDE+import qualified Development.IDE.Plugin.HLS.GhcIde as Ghcide+import qualified Development.IDE.Types.Diagnostics as Diagnostics+import           Development.IDE.Types.Location+import qualified FuzzySearch+import           Ide.Logger                        (Logger, Recorder,+                                                    WithPriority, cmapWithPrio)+import           Ide.PluginUtils                   (pluginDescToIdePlugins)+import           Ide.Types+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types       hiding+                                                   (SemanticTokenAbsolute (..),+                                                    SemanticTokenRelative (..),+                                                    SemanticTokensEdit (..),+                                                    mkRange)+import           Language.LSP.Test+import           LogType                           (Log (..))+import           Network.URI+import qualified Progress+import           System.IO.Extra                   hiding (withTempDir)+import           System.Mem                        (performGC)+import           Test.Tasty+import           Test.Tasty.ExpectedFailure+import           Test.Tasty.HUnit+import           TestUtils+import           Text.Printf                       (printf)++tests :: Recorder (WithPriority Log) -> Logger -> TestTree+tests recorder logger = do+  testGroup "Unit"+     [ testCase "empty file path does NOT work with the empty String literal" $+         uriToFilePath' (fromNormalizedUri $ filePathToUri' "") @?= Just "."+     , testCase "empty file path works using toNormalizedFilePath'" $+         uriToFilePath' (fromNormalizedUri $ filePathToUri' (toNormalizedFilePath' "")) @?= Just ""+     , testCase "empty path URI" $ do+         Just URI{..} <- pure $ parseURI (T.unpack $ getUri $ fromNormalizedUri emptyPathUri)+         uriScheme @?= "file:"+         uriPath @?= ""+     , testCase "from empty path URI" $ do+         let uri = Uri "file://"+         uriToFilePath' uri @?= Just ""+     , testCase "showDiagnostics prints ranges 1-based (like vscode)" $ do+         let diag = ("", Diagnostics.ShowDiag, Diagnostic+               {  _codeDescription = Nothing+                , _data_ = Nothing+                , _range = Range+                   { _start = Position{_line = 0, _character = 1}+                   , _end = Position{_line = 2, _character = 3}+                   }+               , _severity = Nothing+               , _code = Nothing+               , _source = Nothing+               , _message = ""+               , _relatedInformation = Nothing+               , _tags = Nothing+               })+         let shown = T.unpack (Diagnostics.showDiagnostics [diag])+         let expected = "1:2-3:4"+         assertBool (unwords ["expected to find range", expected, "in diagnostic", shown]) $+             expected `isInfixOf` shown+     , testCase "notification handlers run in priority order" $ do+        orderRef <- newIORef []+        let plugins = pluginDescToIdePlugins $+                [ (priorityPluginDescriptor i)+                    { pluginNotificationHandlers = mconcat+                        [ mkPluginNotificationHandler SMethod_TextDocumentDidOpen $ \_ _ _ _ ->+                            liftIO $ atomicModifyIORef_ orderRef (i:)+                        ]+                    }+                    | i <- [1..20]+                ] ++ Ghcide.descriptors (cmapWithPrio LogGhcIde recorder)+            priorityPluginDescriptor i = (defaultPluginDescriptor $ fromString $ show i){pluginPriority = i}++        testIde recorder (IDE.testing (cmapWithPrio LogIDEMain recorder) logger plugins) $ do+            _ <- createDoc "A.hs" "haskell" "module A where"+            waitForProgressDone+            actualOrder <- liftIO $ reverse <$> readIORef orderRef++            -- Handlers are run in priority descending order+            liftIO $ actualOrder @?= [20, 19 .. 1]+     , ignoreTestBecause "The test fails sometimes showing 10000us" $+         testCase "timestamps have millisecond resolution" $ do+           resolution_us <- findResolution_us 1+           let msg = printf "Timestamps do not have millisecond resolution: %dus" resolution_us+           assertBool msg (resolution_us <= 1000)+     , Progress.tests+     , FuzzySearch.tests+     ]++findResolution_us :: Int -> IO Int+findResolution_us delay_us | delay_us >= 1000000 = error "Unable to compute timestamp resolution"+findResolution_us delay_us = withTempFile $ \f -> withTempFile $ \f' -> do+    performGC+    writeFile f ""+    threadDelay delay_us+    writeFile f' ""+    t <- getModTime f+    t' <- getModTime f'+    if t /= t' then return delay_us else findResolution_us (delay_us * 10)
+ test/exe/WatchedFileTests.hs view
@@ -0,0 +1,83 @@++{-# LANGUAGE GADTs     #-}+{-# LANGUAGE PolyKinds #-}++module WatchedFileTests (tests) where++import           Control.Applicative.Combinators+import           Control.Monad.IO.Class          (liftIO)+import qualified Data.Aeson                      as A+import qualified Data.Text                       as T+import           Development.IDE.Test            (expectDiagnostics)+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types     hiding+                                                 (SemanticTokenAbsolute (..),+                                                  SemanticTokenRelative (..),+                                                  SemanticTokensEdit (..),+                                                  mkRange)+import           Language.LSP.Test+import           System.Directory+import           System.FilePath+-- import Test.QuickCheck.Instances ()+import           Test.Tasty+import           Test.Tasty.HUnit+import           TestUtils++tests :: TestTree+tests = testGroup "watched files"+  [ testGroup "Subscriptions"+    [ testSession' "workspace files" $ \sessionDir -> do+        liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\", \"A\", \"WatchedFilesMissingModule\"]}}"+        _doc <- createDoc "A.hs" "haskell" "{-#LANGUAGE NoImplicitPrelude #-}\nmodule A where\nimport WatchedFilesMissingModule"+        watchedFileRegs <- getWatchedFilesSubscriptionsUntil SMethod_TextDocumentPublishDiagnostics++        -- Expect 2 subscriptions: one for all .hs files and one for the hie.yaml cradle+        liftIO $ length watchedFileRegs @?= 2++    , testSession' "non workspace file" $ \sessionDir -> do+        tmpDir <- liftIO getTemporaryDirectory+        let yaml = "cradle: {direct: {arguments: [\"-i" <> tail(init(show tmpDir)) <> "\", \"A\", \"WatchedFilesMissingModule\"]}}"+        liftIO $ writeFile (sessionDir </> "hie.yaml") yaml+        _doc <- createDoc "A.hs" "haskell" "{-# LANGUAGE NoImplicitPrelude#-}\nmodule A where\nimport WatchedFilesMissingModule"+        watchedFileRegs <- getWatchedFilesSubscriptionsUntil SMethod_TextDocumentPublishDiagnostics++        -- Expect 2 subscriptions: one for all .hs files and one for the hie.yaml cradle+        liftIO $ length watchedFileRegs @?= 2++    -- TODO add a test for didChangeWorkspaceFolder+    ]+  , testGroup "Changes"+    [+      testSession' "workspace files" $ \sessionDir -> do+        liftIO $ writeFile (sessionDir </> "hie.yaml") "cradle: {direct: {arguments: [\"-isrc\", \"A\", \"B\"]}}"+        liftIO $ writeFile (sessionDir </> "B.hs") $ unlines+          ["module B where"+          ,"b :: Bool"+          ,"b = False"]+        _doc <- createDoc "A.hs" "haskell" $ T.unlines+          ["module A where"+          ,"import B"+          ,"a :: ()"+          ,"a = b"+          ]+        expectDiagnostics [("A.hs", [(DiagnosticSeverity_Error, (3, 4), "Couldn't match expected type '()' with actual type 'Bool'")])]+        -- modify B off editor+        liftIO $ writeFile (sessionDir </> "B.hs") $ unlines+          ["module B where"+          ,"b :: Int"+          ,"b = 0"]+        sendNotification SMethod_WorkspaceDidChangeWatchedFiles $ DidChangeWatchedFilesParams $+               [FileEvent (filePathToUri $ sessionDir </> "B.hs") FileChangeType_Changed ]+        expectDiagnostics [("A.hs", [(DiagnosticSeverity_Error, (3, 4), "Couldn't match expected type '()' with actual type 'Int'")])]+    ]+  ]++getWatchedFilesSubscriptionsUntil :: forall m. SServerMethod m -> Session [DidChangeWatchedFilesRegistrationOptions]+getWatchedFilesSubscriptionsUntil m = do+      msgs <- manyTill (Just <$> message SMethod_ClientRegisterCapability <|> Nothing <$ anyMessage) (message m)+      return+            [ x+            | Just TRequestMessage{_params = RegistrationParams regs} <- msgs+            , Registration _id "workspace/didChangeWatchedFiles" (Just args) <- regs+            , Just x@(DidChangeWatchedFilesRegistrationOptions _) <- [A.decode . A.encode $ args]+            ]
test/src/Development/IDE/Test.hs view
@@ -43,20 +43,20 @@ import           Data.Default import qualified Data.Map.Strict                 as Map import           Data.Maybe                      (fromJust)+import           Data.Proxy import           Data.Text                       (Text) import qualified Data.Text                       as T import           Development.IDE.Plugin.Test     (TestRequest (..),                                                   WaitForIdeRuleResult,                                                   ideResultSuccess) import           Development.IDE.Test.Diagnostic+import           GHC.TypeLits                    ( symbolVal ) import           Ide.Plugin.Config               (CheckParents, checkProject)+import qualified Language.LSP.Protocol.Lens      as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types import           Language.LSP.Test               hiding (message) import qualified Language.LSP.Test               as LspTest-import           Language.LSP.Types              hiding-                                                 (SemanticTokenAbsolute (length, line),-                                                  SemanticTokenRelative (length),-                                                  SemanticTokensEdit (_start))-import           Language.LSP.Types.Lens         as Lsp import           System.Directory                (canonicalizePath) import           System.FilePath                 (equalFilePath) import           System.Time.Extra@@ -75,23 +75,23 @@ -- if any diagnostic messages arrive in that period expectNoMoreDiagnostics :: HasCallStack => Seconds -> Session () expectNoMoreDiagnostics timeout =-  expectMessages STextDocumentPublishDiagnostics timeout $ \diagsNot -> do-    let fileUri = diagsNot ^. params . uri-        actual = diagsNot ^. params . diagnostics-    unless (actual == List []) $ liftIO $+  expectMessages SMethod_TextDocumentPublishDiagnostics timeout $ \diagsNot -> do+    let fileUri = diagsNot ^. L.params . L.uri+        actual = diagsNot ^. L.params . L.diagnostics+    unless (actual == []) $ liftIO $       assertFailure $         "Got unexpected diagnostics for " <> show fileUri           <> " got "           <> show actual -expectMessages :: SMethod m -> Seconds -> (ServerMessage m -> Session ()) -> Session ()+expectMessages :: SMethod m -> Seconds -> (TServerMessage m -> Session ()) -> Session () expectMessages m timeout handle = do     -- Give any further diagnostic messages time to arrive.     liftIO $ sleep timeout     -- Send a dummy message to provoke a response from the server.     -- This guarantees that we have at least one message to     -- process, so message won't block or timeout.-    let cm = SCustomMethod "test"+    let cm = SMethod_CustomMethod (Proxy @"test")     i <- sendRequest cm $ A.toJSON GetShakeSessionQueueCount     go cm i   where@@ -102,7 +102,7 @@  flushMessages :: Session () flushMessages = do-    let cm = SCustomMethod "non-existent-method"+    let cm = SMethod_CustomMethod (Proxy @"non-existent-method")     i <- sendRequest cm A.Null     void (responseForId cm i) <|> ignoreOthers cm i     where@@ -118,8 +118,8 @@   = expectDiagnosticsWithTags   . map (second (map (\(ds, c, t) -> (ds, c, t, Nothing)))) -unwrapDiagnostic :: NotificationMessage TextDocumentPublishDiagnostics  -> (Uri, List Diagnostic)-unwrapDiagnostic diagsNot = (diagsNot^.params.uri, diagsNot^.params.diagnostics)+unwrapDiagnostic :: TServerMessage Method_TextDocumentPublishDiagnostics  -> (Uri, [Diagnostic])+unwrapDiagnostic diagsNot = (diagsNot^. L.params . L.uri, diagsNot^. L.params . L.diagnostics)  expectDiagnosticsWithTags :: HasCallStack => [(String, [(DiagnosticSeverity, Cursor, T.Text, Maybe DiagnosticTag)])] -> Session () expectDiagnosticsWithTags expected = do@@ -130,13 +130,13 @@  expectDiagnosticsWithTags' ::   (HasCallStack, MonadIO m) =>-  m (Uri, List Diagnostic) ->+  m (Uri, [Diagnostic]) ->   Map.Map NormalizedUri [(DiagnosticSeverity, Cursor, T.Text, Maybe DiagnosticTag)] ->   m () expectDiagnosticsWithTags' next m | null m = do     (_,actual) <- next     case actual of-        List [] ->+        [] ->             return ()         _ ->             liftIO $ assertFailure $ "Got unexpected diagnostics:" <> show actual@@ -178,19 +178,19 @@ checkDiagnosticsForDoc TextDocumentIdentifier {_uri} expected obtained = do     let expected' = Map.fromList [(nuri, map (\(ds, c, t) -> (ds, c, t, Nothing)) expected)]         nuri = toNormalizedUri _uri-    expectDiagnosticsWithTags' (return (_uri, List obtained)) expected'+    expectDiagnosticsWithTags' (return (_uri, obtained)) expected'  canonicalizeUri :: Uri -> IO Uri canonicalizeUri uri = filePathToUri <$> canonicalizePath (fromJust (uriToFilePath uri)) -diagnostic :: Session (NotificationMessage TextDocumentPublishDiagnostics)-diagnostic = LspTest.message STextDocumentPublishDiagnostics+diagnostic :: Session (TNotificationMessage Method_TextDocumentPublishDiagnostics)+diagnostic = LspTest.message SMethod_TextDocumentPublishDiagnostics  tryCallTestPlugin :: (A.FromJSON b) => TestRequest -> Session (Either ResponseError b) tryCallTestPlugin cmd = do-    let cm = SCustomMethod "test"+    let cm = SMethod_CustomMethod (Proxy @"test")     waitId <- sendRequest cm (A.toJSON cmd)-    ResponseMessage{_result} <- skipManyTill anyMessage $ responseForId cm waitId+    TResponseMessage{_result} <- skipManyTill anyMessage $ responseForId cm waitId     return $ case _result of          Left e -> Left e          Right json -> case A.fromJSON json of@@ -230,8 +230,8 @@ waitForCustomMessage :: T.Text -> (A.Value -> Maybe res) -> Session res waitForCustomMessage msg pred =     skipManyTill anyMessage $ satisfyMaybe $ \case-        FromServerMess (SCustomMethod lbl) (NotMess NotificationMessage{_params = value})-            | lbl == msg -> pred value+        FromServerMess (SMethod_CustomMethod p) (NotMess TNotificationMessage{_params = value})+            | symbolVal p == T.unpack msg -> pred value         _ -> Nothing  waitForGC :: Session [T.Text]@@ -242,7 +242,7 @@  configureCheckProject :: Bool -> Session () configureCheckProject overrideCheckProject =-    sendNotification SWorkspaceDidChangeConfiguration+    sendNotification SMethod_WorkspaceDidChangeConfiguration         (DidChangeConfigurationParams $ toJSON             def{checkProject = overrideCheckProject}) @@ -252,9 +252,10 @@  referenceReady :: (FilePath -> Bool) -> Session FilePath referenceReady pred = satisfyMaybe $ \case-  FromServerMess (SCustomMethod "ghcide/reference/ready") (NotMess NotificationMessage{_params})+  FromServerMess (SMethod_CustomMethod p) (NotMess TNotificationMessage{_params})     | A.Success fp <- A.fromJSON _params     , pred fp+    , symbolVal p == "ghcide/reference/ready"     -> Just fp   _ -> Nothing 
test/src/Development/IDE/Test/Diagnostic.hs view
@@ -1,10 +1,10 @@ module Development.IDE.Test.Diagnostic where -import           Control.Lens            ((^.))-import qualified Data.Text               as T-import           GHC.Stack               (HasCallStack)-import           Language.LSP.Types-import           Language.LSP.Types.Lens as Lsp+import           Control.Lens                ((^.))+import qualified Data.Text                   as T+import           GHC.Stack                   (HasCallStack)+import           Language.LSP.Protocol.Lens+import           Language.LSP.Protocol.Types  -- | (0-based line number, 0-based column number) type Cursor = (UInt, UInt)@@ -33,10 +33,10 @@            standardizeQuotes (T.toLower $ d ^. message)         && hasTag expectedTag (d ^. tags) -    hasTag :: Maybe DiagnosticTag -> Maybe (List DiagnosticTag) -> Bool-    hasTag Nothing  _                          = True-    hasTag (Just _) Nothing                    = False-    hasTag (Just actualTag) (Just (List tags)) = actualTag `elem` tags+    hasTag :: Maybe DiagnosticTag -> Maybe [DiagnosticTag] -> Bool+    hasTag Nothing  _                   = True+    hasTag (Just _) Nothing             = False+    hasTag (Just actualTag) (Just tags) = actualTag `elem` tags  standardizeQuotes :: T.Text -> T.Text standardizeQuotes msg = let