packages feed

hls-plugin-api 2.0.0.1 → 2.1.0.0

raw patch · 9 files changed

+1237/−406 lines, 9 filesdep +co-log-coredep +mtldep +prettyprinterdep ~hls-graphdep ~lspPVP ok

version bump matches the API change (PVP)

Dependencies added: co-log-core, mtl, prettyprinter, row-types, stm, time, unliftio

Dependency ranges changed: hls-graph, lsp

API changes (from Hackage documentation)

- Ide.PluginUtils: extractRange :: Range -> Text -> Text
- Ide.PluginUtils: getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath
- Ide.PluginUtils: handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b
- Ide.PluginUtils: handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b
- Ide.PluginUtils: pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a)
- Ide.PluginUtils: responseError :: Text -> ResponseError
- Ide.PluginUtils: throwPluginError :: Monad m => String -> ExceptT String m b
- Ide.Types: instance (Language.LSP.Types.Lens.HasTextDocument a doc, Language.LSP.Types.Lens.HasUri doc Language.LSP.Types.Uri.Uri) => Ide.Types.HasTracing a
- Ide.Types: instance Ide.Types.HasTracing (GHC.Maybe.Maybe Language.LSP.Types.Initialize.InitializedParams)
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.CallHierarchy.CallHierarchyIncomingCallsParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.CallHierarchy.CallHierarchyOutgoingCallsParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.Command.ExecuteCommandParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.Completion.CompletionItem
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.Configuration.DidChangeConfigurationParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.Initialize.InitializeParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.WatchedFiles.DidChangeWatchedFilesParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.WorkspaceFolders.DidChangeWorkspaceFoldersParams
- Ide.Types: instance Ide.Types.HasTracing Language.LSP.Types.WorkspaceSymbol.WorkspaceSymbolParams
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.Initialized
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.TextDocumentDidChange
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.TextDocumentDidClose
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.TextDocumentDidOpen
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.TextDocumentDidSave
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.WorkspaceDidChangeConfiguration
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.WorkspaceDidChangeWatchedFiles
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Notification 'Language.LSP.Types.Method.WorkspaceDidChangeWorkspaceFolders
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.CallHierarchyIncomingCalls
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.CallHierarchyOutgoingCalls
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.CompletionItemResolve
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.CustomMethod
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentCodeAction
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentCodeLens
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentCompletion
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentDefinition
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentDocumentHighlight
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentDocumentSymbol
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentFoldingRange
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentFormatting
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentHover
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentPrepareCallHierarchy
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentRangeFormatting
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentReferences
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentRename
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentSelectionRange
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.TextDocumentTypeDefinition
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.Request 'Language.LSP.Types.Method.WorkspaceSymbol
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.Initialized
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.TextDocumentDidChange
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.TextDocumentDidClose
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.TextDocumentDidOpen
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.TextDocumentDidSave
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.WorkspaceDidChangeConfiguration
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.WorkspaceDidChangeWatchedFiles
- Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Types.Method.WorkspaceDidChangeWorkspaceFolders
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.CallHierarchyIncomingCalls
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.CallHierarchyOutgoingCalls
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.CompletionItemResolve
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.CustomMethod
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentCodeAction
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentCodeLens
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentCompletion
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentDefinition
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentDocumentHighlight
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentDocumentSymbol
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentFoldingRange
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentFormatting
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentHover
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentPrepareCallHierarchy
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentRangeFormatting
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentReferences
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentRename
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentSelectionRange
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.TextDocumentTypeDefinition
- Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Types.Method.WorkspaceSymbol
- Ide.Types: responseError :: Text -> ResponseError
+ Ide.Logger: DataColumn :: LoggingColumn
+ Ide.Logger: Debug :: Priority
+ Ide.Logger: Error :: Priority
+ Ide.Logger: Info :: Priority
+ Ide.Logger: Logger :: (Priority -> Text -> IO ()) -> Logger
+ Ide.Logger: PriorityColumn :: LoggingColumn
+ Ide.Logger: Recorder :: (forall m. MonadIO m => msg -> m ()) -> Recorder msg
+ Ide.Logger: SourceLocColumn :: LoggingColumn
+ Ide.Logger: ThreadIdColumn :: LoggingColumn
+ Ide.Logger: TimeColumn :: LoggingColumn
+ Ide.Logger: Warning :: Priority
+ Ide.Logger: WithPriority :: Priority -> CallStack -> a -> WithPriority a
+ Ide.Logger: [callStack_] :: WithPriority a -> CallStack
+ Ide.Logger: [logPriority] :: Logger -> Priority -> Text -> IO ()
+ Ide.Logger: [logger_] :: Recorder msg -> forall m. MonadIO m => msg -> m ()
+ Ide.Logger: [payload] :: WithPriority a -> a
+ Ide.Logger: [priority] :: WithPriority a -> Priority
+ Ide.Logger: cfilter :: (a -> Bool) -> Recorder a -> Recorder a
+ Ide.Logger: cmap :: (a -> b) -> Recorder b -> Recorder a
+ Ide.Logger: cmapIO :: (a -> IO b) -> Recorder b -> Recorder a
+ Ide.Logger: cmapWithPrio :: (a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
+ Ide.Logger: data LoggingColumn
+ Ide.Logger: data Priority
+ Ide.Logger: data WithPriority a
+ Ide.Logger: instance Data.Functor.Contravariant.Contravariant Ide.Logger.Recorder
+ Ide.Logger: instance GHC.Base.Functor Ide.Logger.WithPriority
+ Ide.Logger: instance GHC.Base.Monoid (Ide.Logger.Recorder msg)
+ Ide.Logger: instance GHC.Base.Monoid Ide.Logger.Logger
+ Ide.Logger: instance GHC.Base.Semigroup (Ide.Logger.Recorder msg)
+ Ide.Logger: instance GHC.Base.Semigroup Ide.Logger.Logger
+ Ide.Logger: instance GHC.Classes.Eq Ide.Logger.Priority
+ Ide.Logger: instance GHC.Classes.Ord Ide.Logger.Priority
+ Ide.Logger: instance GHC.Enum.Bounded Ide.Logger.Priority
+ Ide.Logger: instance GHC.Enum.Enum Ide.Logger.Priority
+ Ide.Logger: instance GHC.Read.Read Ide.Logger.Priority
+ Ide.Logger: instance GHC.Show.Show Ide.Logger.Priority
+ Ide.Logger: logDebug :: Logger -> Text -> IO ()
+ Ide.Logger: logError :: Logger -> Text -> IO ()
+ Ide.Logger: logInfo :: Logger -> Text -> IO ()
+ Ide.Logger: logWarning :: Logger -> Text -> IO ()
+ Ide.Logger: logWith :: (HasCallStack, MonadIO m) => Recorder (WithPriority msg) -> Priority -> msg -> m ()
+ Ide.Logger: lspClientLogRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)
+ Ide.Logger: lspClientMessageRecorder :: LanguageContextEnv config -> Recorder (WithPriority Text)
+ Ide.Logger: makeDefaultHandleRecorder :: MonadIO m => Maybe [LoggingColumn] -> Lock -> Handle -> m (Recorder (WithPriority (Doc a)))
+ Ide.Logger: makeDefaultStderrRecorder :: MonadIO m => Maybe [LoggingColumn] -> m (Recorder (WithPriority (Doc a)))
+ Ide.Logger: newtype Logger
+ Ide.Logger: newtype Recorder msg
+ Ide.Logger: noLogging :: Logger
+ Ide.Logger: renderStrict :: SimpleDocStream ann -> Text
+ Ide.Logger: toCologActionWithPrio :: (MonadIO m, HasCallStack) => Recorder (WithPriority msg) -> LogAction m (WithSeverity msg)
+ Ide.Logger: withBacklog :: (v -> Recorder a) -> IO (Recorder a, v -> IO ())
+ Ide.Logger: withFileRecorder :: MonadUnliftIO m => FilePath -> Maybe [LoggingColumn] -> (Either IOException (Recorder (WithPriority (Doc d))) -> m a) -> m a
+ Ide.Plugin.Error: PluginInternalError :: Text -> PluginError
+ Ide.Plugin.Error: PluginInvalidParams :: Text -> PluginError
+ Ide.Plugin.Error: PluginInvalidUserState :: Text -> PluginError
+ Ide.Plugin.Error: PluginRequestRefused :: Text -> PluginError
+ Ide.Plugin.Error: PluginRuleFailed :: Text -> PluginError
+ Ide.Plugin.Error: PluginStaleResolve :: PluginError
+ Ide.Plugin.Error: data PluginError
+ Ide.Plugin.Error: getNormalizedFilePathE :: Monad m => Uri -> ExceptT PluginError m NormalizedFilePath
+ Ide.Plugin.Error: handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b
+ Ide.Plugin.Error: handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b
+ Ide.Plugin.Error: instance Prettyprinter.Internal.Pretty Ide.Plugin.Error.PluginError
+ Ide.Plugin.Error: toErrorCode :: PluginError -> LSPErrorCodes |? ErrorCodes
+ Ide.Plugin.Error: toPriority :: PluginError -> Priority
+ Ide.Plugin.Resolve: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Plugin.Resolve.WithURI
+ Ide.Plugin.Resolve: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Plugin.Resolve.WithURI
+ Ide.Plugin.Resolve: instance GHC.Generics.Generic Ide.Plugin.Resolve.WithURI
+ Ide.Plugin.Resolve: instance GHC.Show.Show Ide.Plugin.Resolve.WithURI
+ Ide.Plugin.Resolve: instance Prettyprinter.Internal.Pretty Ide.Plugin.Resolve.Log
+ Ide.Plugin.Resolve: mkCodeActionHandlerWithResolve :: forall ideState a. FromJSON a => Recorder (WithPriority Log) -> PluginMethodHandler ideState 'Method_TextDocumentCodeAction -> ResolveFunction ideState a 'Method_CodeActionResolve -> PluginHandlers ideState
+ Ide.Plugin.Resolve: mkCodeActionWithResolveAndCommand :: forall ideState a. FromJSON a => Recorder (WithPriority Log) -> PluginId -> PluginMethodHandler ideState 'Method_TextDocumentCodeAction -> ResolveFunction ideState a 'Method_CodeActionResolve -> ([PluginCommand ideState], PluginHandlers ideState)
+ Ide.PluginUtils: extendToFullLines :: Range -> Range
+ Ide.PluginUtils: extractTextInRange :: Range -> Text -> Text
+ Ide.Types: instance (Language.LSP.Protocol.Internal.Lens.HasTextDocument a doc, Language.LSP.Protocol.Internal.Lens.HasUri doc Language.LSP.Protocol.Types.Uri.Uri) => Ide.Types.HasTracing a
+ Ide.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Types.PluginResolveData
+ Ide.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Types.PluginId
+ Ide.Types: instance Data.Aeson.Types.ToJSON.ToJSON Ide.Types.PluginResolveData
+ Ide.Types: instance GHC.Generics.Generic Ide.Types.PluginResolveData
+ Ide.Types: instance GHC.Show.Show Ide.Types.PluginResolveData
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.CallHierarchyIncomingCallsParams.CallHierarchyIncomingCallsParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.CallHierarchyOutgoingCallsParams.CallHierarchyOutgoingCallsParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.CodeAction.CodeAction
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.CodeLens.CodeLens
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.CompletionItem.CompletionItem
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.DidChangeConfigurationParams.DidChangeConfigurationParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.DidChangeWatchedFilesParams.DidChangeWatchedFilesParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.DidChangeWorkspaceFoldersParams.DidChangeWorkspaceFoldersParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.DocumentLink.DocumentLink
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.ExecuteCommandParams.ExecuteCommandParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.InitializeParams.InitializeParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.InitializedParams.InitializedParams
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.InlayHint.InlayHint
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.WorkspaceSymbol.WorkspaceSymbol
+ Ide.Types: instance Ide.Types.HasTracing Language.LSP.Protocol.Internal.Types.WorkspaceSymbolParams.WorkspaceSymbolParams
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_Initialized
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidChange
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidClose
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidOpen
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidSave
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeConfiguration
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeWatchedFiles
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Notification 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeWorkspaceFolders
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_CallHierarchyIncomingCalls
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_CallHierarchyOutgoingCalls
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_CodeActionResolve
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_CodeLensResolve
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_CompletionItemResolve
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCodeAction
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCodeLens
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCompletion
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDefinition
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDocumentHighlight
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDocumentSymbol
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentFoldingRange
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentFormatting
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentHover
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentPrepareCallHierarchy
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentRangeFormatting
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentReferences
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentRename
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentSelectionRange
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentTypeDefinition
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceSymbol
+ Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Protocol.Message.Meta.Request ('Language.LSP.Protocol.Internal.Method.Method_CustomMethod m)
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_Initialized
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidChange
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidClose
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidOpen
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDidSave
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeConfiguration
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeWatchedFiles
+ Ide.Types: instance Ide.Types.PluginNotificationMethod 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceDidChangeWorkspaceFolders
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_CallHierarchyIncomingCalls
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_CallHierarchyOutgoingCalls
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_CodeActionResolve
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_CodeLensResolve
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_CompletionItemResolve
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCodeAction
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCodeLens
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentCompletion
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDefinition
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDocumentHighlight
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentDocumentSymbol
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentFoldingRange
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentFormatting
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentHover
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentPrepareCallHierarchy
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentRangeFormatting
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentReferences
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentRename
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentSelectionRange
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_TextDocumentTypeDefinition
+ Ide.Types: instance Ide.Types.PluginRequestMethod 'Language.LSP.Protocol.Internal.Method.Method_WorkspaceSymbol
+ Ide.Types: instance Ide.Types.PluginRequestMethod ('Language.LSP.Protocol.Internal.Method.Method_CustomMethod m)
+ Ide.Types: mkResolveHandler :: forall ideState a m. (FromJSON a, PluginRequestMethod m, HasData_ (MessageParams m) (Maybe Value)) => SClientMethod m -> ResolveFunction ideState a m -> PluginHandlers ideState
+ Ide.Types: type ResolveFunction ideState a (m :: Method ClientToServer Request) = ideState -> PluginId -> MessageParams m -> Uri -> a -> ExceptT PluginError (LspM Config) (MessageResult m)
- Ide.PluginUtils: diffText :: ClientCapabilities -> (Uri, Text) -> Text -> WithDeletions -> WorkspaceEdit
+ Ide.PluginUtils: diffText :: ClientCapabilities -> (VersionedTextDocumentIdentifier, Text) -> Text -> WithDeletions -> WorkspaceEdit
- Ide.PluginUtils: diffText' :: Bool -> (Uri, Text) -> Text -> WithDeletions -> WorkspaceEdit
+ Ide.PluginUtils: diffText' :: Bool -> (VersionedTextDocumentIdentifier, Text) -> Text -> WithDeletions -> WorkspaceEdit
- Ide.PluginUtils: makeDiffTextEdit :: Text -> Text -> List TextEdit
+ Ide.PluginUtils: makeDiffTextEdit :: Text -> Text -> [TextEdit]
- Ide.PluginUtils: makeDiffTextEditAdditive :: Text -> Text -> List TextEdit
+ Ide.PluginUtils: makeDiffTextEditAdditive :: Text -> Text -> [TextEdit]
- Ide.Types: IdeMethod :: SMethod m -> IdeMethod (m :: Method FromClient Request)
+ Ide.Types: IdeMethod :: SMethod m -> IdeMethod (m :: Method ClientToServer Request)
- Ide.Types: IdeNotification :: SMethod m -> IdeNotification (m :: Method FromClient Notification)
+ Ide.Types: IdeNotification :: SMethod m -> IdeNotification (m :: Method ClientToServer Notification)
- Ide.Types: PluginDescriptor :: !PluginId -> Natural -> !Rules () -> ![PluginCommand ideState] -> PluginHandlers ideState -> ConfigDescriptor -> PluginNotificationHandlers ideState -> DynFlagsModifications -> Maybe (ParserInfo (IdeCommand ideState)) -> [Text] -> PluginDescriptor (ideState :: *)
+ Ide.Types: PluginDescriptor :: !PluginId -> Natural -> !Rules () -> ![PluginCommand ideState] -> PluginHandlers ideState -> ConfigDescriptor -> PluginNotificationHandlers ideState -> DynFlagsModifications -> Maybe (ParserInfo (IdeCommand ideState)) -> [Text] -> PluginDescriptor (ideState :: Type)
- Ide.Types: PluginHandler :: (PluginId -> a -> MessageParams m -> LspM Config (NonEmpty (Either ResponseError (ResponseResult m)))) -> PluginHandler a (m :: Method FromClient Request)
+ Ide.Types: PluginHandler :: (PluginId -> a -> MessageParams m -> LspM Config (NonEmpty (Either PluginError (MessageResult m)))) -> PluginHandler a (m :: Method ClientToServer Request)
- Ide.Types: PluginNotificationHandler :: (PluginId -> a -> VFS -> MessageParams m -> LspM Config ()) -> PluginNotificationHandler a (m :: Method FromClient Notification)
+ Ide.Types: PluginNotificationHandler :: (PluginId -> a -> VFS -> MessageParams m -> LspM Config ()) -> PluginNotificationHandler a (m :: Method ClientToServer Notification)
- Ide.Types: [pluginCli] :: PluginDescriptor (ideState :: *) -> Maybe (ParserInfo (IdeCommand ideState))
+ Ide.Types: [pluginCli] :: PluginDescriptor (ideState :: Type) -> Maybe (ParserInfo (IdeCommand ideState))
- Ide.Types: [pluginCommands] :: PluginDescriptor (ideState :: *) -> ![PluginCommand ideState]
+ Ide.Types: [pluginCommands] :: PluginDescriptor (ideState :: Type) -> ![PluginCommand ideState]
- Ide.Types: [pluginConfigDescriptor] :: PluginDescriptor (ideState :: *) -> ConfigDescriptor
+ Ide.Types: [pluginConfigDescriptor] :: PluginDescriptor (ideState :: Type) -> ConfigDescriptor
- Ide.Types: [pluginFileType] :: PluginDescriptor (ideState :: *) -> [Text]
+ Ide.Types: [pluginFileType] :: PluginDescriptor (ideState :: Type) -> [Text]
- Ide.Types: [pluginHandlers] :: PluginDescriptor (ideState :: *) -> PluginHandlers ideState
+ Ide.Types: [pluginHandlers] :: PluginDescriptor (ideState :: Type) -> PluginHandlers ideState
- Ide.Types: [pluginId] :: PluginDescriptor (ideState :: *) -> !PluginId
+ Ide.Types: [pluginId] :: PluginDescriptor (ideState :: Type) -> !PluginId
- Ide.Types: [pluginModifyDynflags] :: PluginDescriptor (ideState :: *) -> DynFlagsModifications
+ Ide.Types: [pluginModifyDynflags] :: PluginDescriptor (ideState :: Type) -> DynFlagsModifications
- Ide.Types: [pluginNotificationHandlers] :: PluginDescriptor (ideState :: *) -> PluginNotificationHandlers ideState
+ Ide.Types: [pluginNotificationHandlers] :: PluginDescriptor (ideState :: Type) -> PluginNotificationHandlers ideState
- Ide.Types: [pluginPriority] :: PluginDescriptor (ideState :: *) -> Natural
+ Ide.Types: [pluginPriority] :: PluginDescriptor (ideState :: Type) -> Natural
- Ide.Types: [pluginRules] :: PluginDescriptor (ideState :: *) -> !Rules ()
+ Ide.Types: [pluginRules] :: PluginDescriptor (ideState :: Type) -> !Rules ()
- Ide.Types: class HasTracing (MessageParams m) => PluginMethod (k :: MethodType) (m :: Method FromClient k)
+ Ide.Types: class HasTracing (MessageParams m) => PluginMethod (k :: MessageKind) (m :: Method ClientToServer k)
- Ide.Types: class PluginMethod Request m => PluginRequestMethod (m :: Method FromClient Request)
+ Ide.Types: class PluginMethod Request m => PluginRequestMethod (m :: Method ClientToServer Request)
- Ide.Types: combineResponses :: (PluginRequestMethod m, Semigroup (ResponseResult m)) => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (ResponseResult m) -> ResponseResult m
+ Ide.Types: combineResponses :: (PluginRequestMethod m, Semigroup (MessageResult m)) => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (MessageResult m) -> MessageResult m
- Ide.Types: data IdeMethod (m :: Method FromClient Request)
+ Ide.Types: data IdeMethod (m :: Method ClientToServer Request)
- Ide.Types: data IdeNotification (m :: Method FromClient Notification)
+ Ide.Types: data IdeNotification (m :: Method ClientToServer Notification)
- Ide.Types: data PluginDescriptor (ideState :: *)
+ Ide.Types: data PluginDescriptor (ideState :: Type)
- Ide.Types: mkPluginHandler :: PluginRequestMethod m => SClientMethod m -> PluginMethodHandler ideState m -> PluginHandlers ideState
+ Ide.Types: mkPluginHandler :: forall ideState m. PluginRequestMethod m => SClientMethod m -> PluginMethodHandler ideState m -> PluginHandlers ideState
- Ide.Types: mkPluginNotificationHandler :: PluginNotificationMethod m => SClientMethod (m :: Method FromClient Notification) -> PluginNotificationMethodHandler ideState m -> PluginNotificationHandlers ideState
+ Ide.Types: mkPluginNotificationHandler :: PluginNotificationMethod m => SClientMethod (m :: Method ClientToServer Notification) -> PluginNotificationMethodHandler ideState m -> PluginNotificationHandlers ideState
- Ide.Types: newtype PluginHandler a (m :: Method FromClient Request)
+ Ide.Types: newtype PluginHandler a (m :: Method ClientToServer Request)
- Ide.Types: newtype PluginNotificationHandler a (m :: Method FromClient Notification)
+ Ide.Types: newtype PluginNotificationHandler a (m :: Method ClientToServer Notification)
- Ide.Types: type CommandFunction ideState a = ideState -> a -> LspM Config (Either ResponseError Value)
+ Ide.Types: type CommandFunction ideState a = ideState -> a -> ExceptT PluginError (LspM Config) (Value |? Null)
- Ide.Types: type FormattingHandler a = a -> FormattingType -> Text -> NormalizedFilePath -> FormattingOptions -> LspM Config (Either ResponseError (List TextEdit))
+ Ide.Types: type FormattingHandler a = a -> FormattingType -> Text -> NormalizedFilePath -> FormattingOptions -> ExceptT PluginError (LspM Config) ([TextEdit] |? Null)
- Ide.Types: type FormattingMethod m = (HasOptions (MessageParams m) FormattingOptions, HasTextDocument (MessageParams m) TextDocumentIdentifier, ResponseResult m ~ List TextEdit)
+ Ide.Types: type FormattingMethod m = (HasOptions (MessageParams m) FormattingOptions, HasTextDocument (MessageParams m) TextDocumentIdentifier, MessageResult m ~ ([TextEdit] |? Null))
- Ide.Types: type PluginMethodHandler a m = a -> PluginId -> MessageParams m -> LspM Config (Either ResponseError (ResponseResult m))
+ Ide.Types: type PluginMethodHandler a m = a -> PluginId -> MessageParams m -> ExceptT PluginError (LspM Config) (MessageResult m)

Files

hls-plugin-api.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:          hls-plugin-api-version:       2.0.0.1+version:       2.1.0.0 synopsis:      Haskell Language Server API for plugin communication description:   Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>@@ -34,17 +34,21 @@  library   exposed-modules:+    Ide.Plugin.Error     Ide.Plugin.Config     Ide.Plugin.ConfigUtils     Ide.Plugin.Properties     Ide.Plugin.RangeMap+    Ide.Plugin.Resolve     Ide.PluginUtils     Ide.Types+    Ide.Logger    hs-source-dirs:     src   build-depends:     , aeson     , base                  >=4.12    && <5+    , co-log-core     , containers     , data-default     , dependent-map@@ -55,15 +59,21 @@     , filepath     , ghc     , hashable-    , hls-graph             == 2.0.0.1+    , hls-graph             == 2.1.0.0     , lens     , lens-aeson-    , lsp                   ^>=1.6.0.0+    , lsp                   ^>=2.1.0.0+    , mtl     , opentelemetry         >=0.4     , optparse-applicative+    , prettyprinter     , regex-tdfa            >=1.3.1.0+    , row-types+    , stm     , text+    , time     , transformers+    , unliftio     , unordered-containers     , megaparsec > 9 
+ src/Ide/Logger.hs view
@@ -0,0 +1,316 @@+-- Copyright (c) 2019 The DAML Authors. All rights reserved.+-- SPDX-License-Identifier: Apache-2.0++{-# LANGUAGE DeriveFunctor       #-}+{-# LANGUAGE LambdaCase          #-}+{-# LANGUAGE NamedFieldPuns      #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE RecordWildCards     #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- | 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 Ide.Logger+  ( Priority(..)+  , Logger(..)+  , Recorder(..)+  , logError, logWarning, logInfo, logDebug+  , noLogging+  , WithPriority(..)+  , logWith+  , cmap+  , cmapIO+  , cfilter+  , withFileRecorder+  , makeDefaultStderrRecorder+  , makeDefaultHandleRecorder+  , LoggingColumn(..)+  , cmapWithPrio+  , withBacklog+  , lspClientMessageRecorder+  , lspClientLogRecorder+  , module PrettyPrinterModule+  , renderStrict+  , toCologActionWithPrio+  ) where++import           Colog.Core                    (LogAction (..), Severity,+                                                WithSeverity (..))+import qualified Colog.Core                    as Colog+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.Protocol.Message (SMethod (SMethod_WindowLogMessage, SMethod_WindowShowMessage))+import           Language.LSP.Protocol.Types   (LogMessageParams (..),+                                                MessageType (..),+                                                ShowMessageParams (..))+import           Language.LSP.Server+import qualified Language.LSP.Server           as LSP+import           Prettyprinter                 as PrettyPrinterModule+import           Prettyprinter.Render.Text     (renderStrict)+import           System.IO                     (Handle, IOMode (AppendMode),+                                                hClose, hFlush, openFile,+                                                stderr)+import           UnliftIO                      (MonadUnliftIO, 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, Read, 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++withFileRecorder+  :: MonadUnliftIO m+  => FilePath+  -- ^ Log file path.+  -> Maybe [LoggingColumn]+  -- ^ logging columns to display. `Nothing` uses `defaultLoggingColumns`+  -> (Either IOException (Recorder (WithPriority (Doc d))) -> m a)+  -- ^ action given a recorder, or the exception if we failed to open the file+  -> m a+withFileRecorder path columns action = do+  lock <- liftIO newLock+  let makeHandleRecorder = makeDefaultHandleRecorder columns lock+  fileHandle :: Either IOException Handle <- liftIO $ try (openFile path AppendMode)+  case fileHandle of+    Left e -> action $ Left e+    Right fileHandle -> finally ((Right <$> 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 SMethod_WindowShowMessage+      ShowMessageParams+        { _type_ = 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 SMethod_WindowLogMessage+      LogMessageParams+        { _type_ = priorityToLsp priority,+          _message = payload+        }++priorityToLsp :: Priority -> MessageType+priorityToLsp =+  \case+    Debug   -> MessageType_Log+    Info    -> MessageType_Info+    Warning -> MessageType_Warning+    Error   -> MessageType_Error++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/Ide/Plugin/ConfigUtils.hs view
@@ -5,20 +5,21 @@  module Ide.Plugin.ConfigUtils where -import           Control.Lens          (at, ix, (&), (?~))-import qualified Data.Aeson            as A-import           Data.Aeson.Lens       (_Object)-import qualified Data.Aeson.Types      as A+import           Control.Lens                  (at, ix, (&), (?~))+import qualified Data.Aeson                    as A+import           Data.Aeson.Lens               (_Object)+import qualified Data.Aeson.Types              as A import           Data.Default-import qualified Data.Dependent.Map    as DMap-import qualified Data.Dependent.Sum    as DSum-import           Data.List.Extra       (nubOrd)-import           Data.String           (IsString (fromString))-import qualified Data.Text             as T+import qualified Data.Dependent.Map            as DMap+import qualified Data.Dependent.Sum            as DSum+import           Data.List.Extra               (nubOrd)+import           Data.String                   (IsString (fromString))+import qualified Data.Text                     as T import           Ide.Plugin.Config-import           Ide.Plugin.Properties (toDefaultJSON, toVSCodeExtensionSchema)+import           Ide.Plugin.Properties         (toDefaultJSON,+                                                toVSCodeExtensionSchema) import           Ide.Types-import           Language.LSP.Types+import           Language.LSP.Protocol.Message  -- Attention: -- 'diagnosticsOn' will never be added into the default config or the schema,@@ -86,13 +87,13 @@         -- This function captures ide methods registered by the plugin, and then converts it to kv pairs         handlersToGenericDefaultConfig :: PluginConfig -> DSum.DSum IdeMethod f -> [A.Pair]         handlersToGenericDefaultConfig PluginConfig{..} (IdeMethod m DSum.:=> _) = case m of-          STextDocumentCodeAction           -> ["codeActionsOn" A..= plcCodeActionsOn]-          STextDocumentCodeLens             -> ["codeLensOn" A..= plcCodeLensOn]-          STextDocumentRename               -> ["renameOn" A..= plcRenameOn]-          STextDocumentHover                -> ["hoverOn" A..= plcHoverOn]-          STextDocumentDocumentSymbol       -> ["symbolsOn" A..= plcSymbolsOn]-          STextDocumentCompletion           -> ["completionOn" A..= plcCompletionOn]-          STextDocumentPrepareCallHierarchy -> ["callHierarchyOn" A..= plcCallHierarchyOn]+          SMethod_TextDocumentCodeAction           -> ["codeActionsOn" A..= plcCodeActionsOn]+          SMethod_TextDocumentCodeLens             -> ["codeLensOn" A..= plcCodeLensOn]+          SMethod_TextDocumentRename               -> ["renameOn" A..= plcRenameOn]+          SMethod_TextDocumentHover                -> ["hoverOn" A..= plcHoverOn]+          SMethod_TextDocumentDocumentSymbol       -> ["symbolsOn" A..= plcSymbolsOn]+          SMethod_TextDocumentCompletion           -> ["completionOn" A..= plcCompletionOn]+          SMethod_TextDocumentPrepareCallHierarchy -> ["callHierarchyOn" A..= plcCallHierarchyOn]           _                                 -> []  -- | Generates json schema used in haskell vscode extension@@ -116,13 +117,13 @@                 _   -> x         dedicatedSchema = customConfigToDedicatedSchema configCustomConfig         handlersToGenericSchema (IdeMethod m DSum.:=> _) = case m of-          STextDocumentCodeAction -> [toKey' "codeActionsOn" A..= schemaEntry "code actions"]-          STextDocumentCodeLens -> [toKey' "codeLensOn" A..= schemaEntry "code lenses"]-          STextDocumentRename -> [toKey' "renameOn" A..= schemaEntry "rename"]-          STextDocumentHover -> [toKey' "hoverOn" A..= schemaEntry "hover"]-          STextDocumentDocumentSymbol -> [toKey' "symbolsOn" A..= schemaEntry "symbols"]-          STextDocumentCompletion -> [toKey' "completionOn" A..= schemaEntry "completions"]-          STextDocumentPrepareCallHierarchy -> [toKey' "callHierarchyOn" A..= schemaEntry "call hierarchy"]+          SMethod_TextDocumentCodeAction -> [toKey' "codeActionsOn" A..= schemaEntry "code actions"]+          SMethod_TextDocumentCodeLens -> [toKey' "codeLensOn" A..= schemaEntry "code lenses"]+          SMethod_TextDocumentRename -> [toKey' "renameOn" A..= schemaEntry "rename"]+          SMethod_TextDocumentHover -> [toKey' "hoverOn" A..= schemaEntry "hover"]+          SMethod_TextDocumentDocumentSymbol -> [toKey' "symbolsOn" A..= schemaEntry "symbols"]+          SMethod_TextDocumentCompletion -> [toKey' "completionOn" A..= schemaEntry "completions"]+          SMethod_TextDocumentPrepareCallHierarchy -> [toKey' "callHierarchyOn" A..= schemaEntry "call hierarchy"]           _ -> []         schemaEntry desc =           A.object
+ src/Ide/Plugin/Error.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE LambdaCase                #-}+{-# LANGUAGE OverloadedStrings         #-}+module Ide.Plugin.Error (+      -- * Plugin Error Handling API+    PluginError(..),+    toErrorCode,+    toPriority,+    handleMaybe,+    handleMaybeM,+    getNormalizedFilePathE,+) where++import           Control.Monad.Extra         (maybeM)+import           Control.Monad.Trans.Class   (lift)+import           Control.Monad.Trans.Except  (ExceptT (..), throwE)+import qualified Data.Text                   as T+import           Ide.Logger+import           Language.LSP.Protocol.Types++-- ----------------------------------------------------------------------------+-- Plugin Error wrapping+-- ----------------------------------------------------------------------------++-- |Each PluginError corresponds to either a specific ResponseError we want to+-- return or a specific way we want to log the error. If the currently present+-- ones are insufficient for the needs of your plugin, please feel free to add+-- a new one.+--+-- Currently the PluginErrors we provide can be broken up into several groups.+-- First is PluginInternalError, which is the most serious of the errors, and+-- also the "default" error that is used for things such as uncaught exceptions.+-- Then we have PluginInvalidParams, which along with PluginInternalError map+-- to a corresponding ResponseError.+--+-- Next we have PluginRuleFailed and PluginInvalidUserState, with the only+-- difference being PluginRuleFailed is specific to Shake rules and+-- PluginInvalidUserState can be used for everything else. Both of these are+-- "non-errors", and happen whenever the user's code is in a state where the+-- plugin is unable to provide a answer to the users request. PluginStaleResolve+-- is similar to the above two Error types, but is specific to resolve plugins,+-- and is used only when the data provided by the resolve request is stale,+-- preventing the proper resolution of it.+--+-- Finally we have the outlier, PluginRequestRefused, where we allow a handler+-- to preform "pluginEnabled" checks inside the handler, and reject the request+-- after viewing it. The behavior of only one handler passing `pluginEnabled`+-- and then returning PluginRequestRefused should be the same as if no plugins+-- passed the `pluginEnabled` stage.+data PluginError+  = -- |PluginInternalError should be used if an error has occurred. This+    -- should only rarely be returned. As it's logged with Error, it will be+    -- shown by the client to the user via `showWindow`. All uncaught exceptions+    -- will be caught and converted to this error.+    --+    -- This error will be be converted into an InternalError response code. It+    -- will be logged with Error and takes the highest precedence (1) in being+    -- returned as a response to the client.+    PluginInternalError T.Text+    -- |PluginInvalidParams should be used if the parameters of the request are+    -- invalid. This error means that there is a bug in the client's code+    -- (otherwise they wouldn't be sending you requests with invalid+    -- parameters).+    --+    -- This error will be will be converted into a InvalidParams response code.+    -- It will be logged with Warning and takes medium precedence (2) in being+    -- returned as a response to the client.+  | PluginInvalidParams T.Text+    -- |PluginInvalidUserState should be thrown when a function that your plugin+    -- depends on fails. This should only be used when the function fails+    -- because the user's code is in an invalid state.+    --+    -- This error takes the name of the function that failed. Prefer to catch+    -- this error as close to the source as possible.+    --+    -- This error will be logged with Debug, and will be converted into a+    -- RequestFailed response. It takes a low precedence (3) in being returned+    -- as a response to the client.+  | PluginInvalidUserState T.Text+    -- |PluginRequestRefused allows your handler to inspect a request before+    -- rejecting it. In effect it allows your plugin to act make a secondary+    -- `pluginEnabled` decision after receiving the request. This should only be+    -- used if the decision to accept the request can not be made in+    -- `pluginEnabled`.+    --+    -- This error will be with Debug. If it's the only response to a request,+    -- HLS will respond as if no plugins passed the `pluginEnabled` stage.+  | PluginRequestRefused T.Text+    -- |PluginRuleFailed should be thrown when a Rule your response depends on+    -- fails.+    --+    -- This error takes the name of the Rule that failed.+    --+    -- This error will be logged with Debug, and will be converted into a+    -- RequestFailed response code. It takes a low precedence (3) in being+    -- returned as a response to the client.+  | PluginRuleFailed T.Text+    -- |PluginStaleResolve should be thrown when your resolve request is+    -- provided with data it can no longer resolve.+    --+    -- This error will be logged with Debug, and will be converted into a+    -- ContentModified response. It takes a low precedence (3) in being returned+    -- as a response to the client.+  | PluginStaleResolve++instance Pretty PluginError where+    pretty = \case+      PluginInternalError msg     -> "Internal Error:"     <+> pretty msg+      PluginStaleResolve          -> "Stale Resolve"+      PluginRuleFailed rule       -> "Rule Failed:"        <+> pretty rule+      PluginInvalidParams text    -> "Invalid Params:"     <+> pretty text+      PluginInvalidUserState text -> "Invalid User State:" <+> pretty text+      PluginRequestRefused msg    -> "Request Refused: "   <+> pretty msg++-- |Converts to ErrorCode used in LSP ResponseErrors+toErrorCode :: PluginError -> (LSPErrorCodes |? ErrorCodes)+toErrorCode (PluginInternalError _)    = InR ErrorCodes_InternalError+toErrorCode (PluginInvalidParams _)    = InR ErrorCodes_InvalidParams+toErrorCode (PluginInvalidUserState _) = InL LSPErrorCodes_RequestFailed+-- PluginRequestRefused should never be a argument to `toResponseError`, as+-- it should be dealt with in `extensiblePlugins`, but this is here to make+-- this function complete+toErrorCode (PluginRequestRefused _)   = InR ErrorCodes_MethodNotFound+toErrorCode (PluginRuleFailed _)       = InL LSPErrorCodes_RequestFailed+toErrorCode PluginStaleResolve         = InL LSPErrorCodes_ContentModified++-- |Converts to a logging priority. In addition to being used by the logger,+-- `combineResponses` currently uses this to  choose which response to return,+-- so care should be taken in changing it.+toPriority :: PluginError -> Priority+toPriority (PluginInternalError _)    = Error+toPriority (PluginInvalidParams _)    = Warning+toPriority (PluginInvalidUserState _) = Debug+toPriority (PluginRequestRefused _)   = Debug+toPriority (PluginRuleFailed _)       = Debug+toPriority PluginStaleResolve         = Debug++handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b+handleMaybe msg = maybe (throwE msg) return++handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b+handleMaybeM msg act = maybeM (throwE msg) return $ lift act++getNormalizedFilePathE :: Monad m => Uri -> ExceptT PluginError m NormalizedFilePath+getNormalizedFilePathE uri = handleMaybe (PluginInvalidParams (T.pack $ "uriToNormalizedFile failed. Uri:" <>  show uri))+        $ uriToNormalizedFilePath+        $ toNormalizedUri uri
src/Ide/Plugin/RangeMap.hs view
@@ -22,9 +22,8 @@ import           Data.Bifunctor                           (first) import           Data.Foldable                            (foldl') import           Development.IDE.Graph.Classes            (NFData)-import           Language.LSP.Types                       (Position,-                                                           Range (Range),-                                                           isSubrangeOf)+import           Language.LSP.Protocol.Types              (Position,+                                                           Range (Range)) #ifdef USE_FINGERTREE import qualified HaskellWorks.Data.IntervalMap.FingerTree as IM #endif
+ src/Ide/Plugin/Resolve.hs view
@@ -0,0 +1,210 @@+{-# LANGUAGE DeriveGeneric            #-}+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE LambdaCase               #-}+{-# LANGUAGE NamedFieldPuns           #-}+{-# LANGUAGE OverloadedLabels         #-}+{-# LANGUAGE OverloadedStrings        #-}+{-# LANGUAGE RankNTypes               #-}+{-# LANGUAGE ScopedTypeVariables      #-}+module Ide.Plugin.Resolve+(mkCodeActionHandlerWithResolve,+mkCodeActionWithResolveAndCommand) where++import           Control.Lens                  (_Just, (&), (.~), (?~), (^.),+                                                (^?))+import           Control.Monad.Error.Class     (MonadError (throwError))+import           Control.Monad.Trans.Class     (lift)+import           Control.Monad.Trans.Except    (ExceptT (..), runExceptT)++import qualified Data.Aeson                    as A+import           Data.Maybe                    (catMaybes)+import           Data.Row                      ((.!))+import qualified Data.Text                     as T+import           GHC.Generics                  (Generic)+import           Ide.Logger+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           Language.LSP.Server           (LspT,+                                                ProgressCancellable (Cancellable),+                                                getClientCapabilities,+                                                sendRequest,+                                                withIndefiniteProgress)++data Log+    = DoesNotSupportResolve T.Text+    | ApplyWorkspaceEditFailed ResponseError+instance Pretty Log where+    pretty = \case+        DoesNotSupportResolve fallback->+            "Client does not support resolve," <+> pretty fallback+        ApplyWorkspaceEditFailed err ->+            "ApplyWorkspaceEditFailed:" <+> viaShow err++-- |When provided with both a codeAction provider and an affiliated codeAction+-- resolve provider, this function creates a handler that automatically uses+-- your resolve provider to fill out you original codeAction if the client doesn't+-- have codeAction resolve support. This means you don't have to check whether+-- the client supports resolve and act accordingly in your own providers.+mkCodeActionHandlerWithResolve+  :: forall ideState a. (A.FromJSON a) =>+  Recorder (WithPriority Log)+  -> PluginMethodHandler ideState 'Method_TextDocumentCodeAction+  -> ResolveFunction ideState a 'Method_CodeActionResolve+  -> PluginHandlers ideState+mkCodeActionHandlerWithResolve recorder codeActionMethod codeResolveMethod =+  let newCodeActionMethod ideState pid params =+        do codeActionReturn <- codeActionMethod ideState pid params+           caps <- lift getClientCapabilities+           case codeActionReturn of+             r@(InR Null) -> pure r+             (InL ls) | -- We don't need to do anything if the client supports+                        -- resolve+                        supportsCodeActionResolve caps -> pure $ InL ls+                        --This is the actual part where we call resolveCodeAction which fills in the edit data for the client+                      | otherwise -> do+                        logWith recorder Debug (DoesNotSupportResolve "filling in the code action")+                        InL <$> traverse (resolveCodeAction (params ^. L.textDocument . L.uri) ideState pid) ls+  in (mkPluginHandler SMethod_TextDocumentCodeAction newCodeActionMethod+  <> mkResolveHandler SMethod_CodeActionResolve codeResolveMethod)+  where dropData :: CodeAction -> CodeAction+        dropData ca = ca & L.data_ .~ Nothing+        resolveCodeAction :: Uri -> ideState -> PluginId -> (Command |? CodeAction) -> ExceptT PluginError (LspT Config IO) (Command |? CodeAction)+        resolveCodeAction _uri _ideState _plId c@(InL _) = pure c+        resolveCodeAction uri ideState pid (InR codeAction@CodeAction{_data_=Just value}) = do+          case A.fromJSON value of+            A.Error err -> throwError $ parseError (Just value) (T.pack err)+            A.Success innerValueDecoded -> do+              resolveResult <- codeResolveMethod ideState pid codeAction uri innerValueDecoded+              case resolveResult of+                CodeAction {_edit = Just _ } -> do+                  pure $ InR $ dropData resolveResult+                _ -> throwError $ invalidParamsError "Returned CodeAction has no data field"+        resolveCodeAction _ _ _ (InR CodeAction{_data_=Nothing}) = throwError $ invalidParamsError "CodeAction has no data field"+++-- |When provided with both a codeAction provider with a data field and a resolve+--  provider, this function creates a handler that creates a command that uses+-- your resolve if the client doesn't have code action resolve support. This means+-- you don't have to check whether the client supports resolve and act+-- accordingly in your own providers. see Note [Code action resolve fallback to commands]+-- Also: This helper only works with workspace edits, not commands. Any command set+-- either in the original code action or in the resolve will be ignored.+mkCodeActionWithResolveAndCommand+  :: forall ideState a. (A.FromJSON a) =>+  Recorder (WithPriority Log)+  -> PluginId+  -> PluginMethodHandler ideState 'Method_TextDocumentCodeAction+  -> ResolveFunction ideState a 'Method_CodeActionResolve+  -> ([PluginCommand ideState], PluginHandlers ideState)+mkCodeActionWithResolveAndCommand recorder plId codeActionMethod codeResolveMethod =+  let newCodeActionMethod ideState pid params =+        do codeActionReturn <- codeActionMethod ideState pid params+           caps <- lift getClientCapabilities+           case codeActionReturn of+             r@(InR Null) -> pure r+             (InL ls) | -- We don't need to do anything if the client supports+                        -- resolve+                        supportsCodeActionResolve caps -> pure $ InL ls+                        -- If they do not we will drop the data field, in addition we will populate the command+                        -- field with our command to execute the resolve, with the whole code action as it's argument.+                      | otherwise -> do+                        logWith recorder Debug (DoesNotSupportResolve "rewriting the code action to use commands")+                        pure $ InL $ moveDataToCommand (params ^. L.textDocument . L.uri) <$> ls+  in ([PluginCommand "codeActionResolve" "Executes resolve for code action" (executeResolveCmd codeResolveMethod)],+  mkPluginHandler SMethod_TextDocumentCodeAction newCodeActionMethod+  <> mkResolveHandler SMethod_CodeActionResolve codeResolveMethod)+  where moveDataToCommand :: Uri -> Command |? CodeAction -> Command |? CodeAction+        moveDataToCommand uri ca =+          let dat = A.toJSON . wrapWithURI uri <$> ca ^? _R -- We need to take the whole codeAction+              -- And put it in the argument for the Command, that way we can later+              -- pass it to the resolve handler (which expects a whole code action)+              -- It should be noted that mkLspCommand already specifies the command+              -- to the plugin, so we don't need to do that here.+              cmd = mkLspCommand plId (CommandId "codeActionResolve") "Execute Code Action" (pure <$> dat)+          in ca+              & _R . L.data_ .~ Nothing -- Set the data field to nothing+              & _R . L.command ?~ cmd -- And set the command to our previously created command+        wrapWithURI ::  Uri -> CodeAction -> CodeAction+        wrapWithURI  uri codeAction =+          codeAction & L.data_ .~  (A.toJSON .WithURI uri <$> data_)+          where data_ = codeAction ^? L.data_ . _Just+        executeResolveCmd :: ResolveFunction ideState a 'Method_CodeActionResolve -> CommandFunction ideState CodeAction+        executeResolveCmd resolveProvider ideState ca@CodeAction{_data_=Just value} = do+          ExceptT $ withIndefiniteProgress "Applying edits for code action..." Cancellable $ runExceptT $ do+            case A.fromJSON value of+              A.Error err -> throwError $ parseError (Just value) (T.pack err)+              A.Success (WithURI uri innerValue) -> do+                case A.fromJSON innerValue of+                  A.Error err -> throwError $ parseError (Just value) (T.pack err)+                  A.Success innerValueDecoded -> do+                    resolveResult <- resolveProvider ideState plId ca uri innerValueDecoded+                    case resolveResult of+                      ca2@CodeAction {_edit = Just wedits } | diffCodeActions ca ca2 == ["edit"] -> do+                          _ <- ExceptT $ Right <$> sendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing wedits) handleWEditCallback+                          pure $ InR Null+                      ca2@CodeAction {_edit = Just _ }  ->+                        throwError $ internalError $+                            "The resolve provider unexpectedly returned a code action with the following differing fields: "+                            <> (T.pack $ show $  diffCodeActions ca ca2)+                      _ -> throwError $ internalError "The resolve provider unexpectedly returned a result with no data field"+        executeResolveCmd _ _ CodeAction{_data_= value} = throwError $ invalidParamsError ("The code action to resolve has an illegal data field: " <> (T.pack $ show value))+        handleWEditCallback (Left err ) = do+            logWith recorder Warning (ApplyWorkspaceEditFailed err)+            pure ()+        handleWEditCallback _ = pure ()++-- TODO: Remove once provided by lsp-types+-- |Compares two CodeActions and returns a list of fields that are not equal+diffCodeActions :: CodeAction -> CodeAction -> [T.Text]+diffCodeActions ca ca2 =+  let titleDiff = if ca ^. L.title == ca2 ^. L.title then Nothing else Just "title"+      kindDiff = if ca ^. L.kind == ca2 ^. L.kind then Nothing else Just "kind"+      diagnosticsDiff = if ca ^. L.diagnostics == ca2 ^. L.diagnostics then Nothing else Just "diagnostics"+      commandDiff = if ca ^. L.command == ca2 ^. L.command then Nothing else Just "diagnostics"+      isPreferredDiff = if ca ^. L.isPreferred == ca2 ^. L.isPreferred then Nothing else Just "isPreferred"+      dataDiff = if ca ^. L.data_ == ca2 ^. L.data_ then Nothing else Just "data"+      disabledDiff = if ca ^. L.disabled == ca2 ^. L.disabled then Nothing else Just "disabled"+      editDiff = if ca ^. L.edit == ca2 ^. L.edit then Nothing else Just "edit"+  in catMaybes [titleDiff, kindDiff, diagnosticsDiff, commandDiff, isPreferredDiff, dataDiff, disabledDiff, editDiff]++-- |To execute the resolve provider as a command, we need to additionally store+-- the URI that was provided to the original code action.+data WithURI = WithURI {+ _uri    :: Uri+, _value :: A.Value+} deriving (Generic, Show)+instance A.ToJSON WithURI+instance A.FromJSON WithURI++-- |Checks if the the client supports resolve for code action. We currently only check+--  whether resolve for the edit field is supported, because that's the only one we care+-- about at the moment.+supportsCodeActionResolve :: ClientCapabilities -> Bool+supportsCodeActionResolve caps =+    caps ^? L.textDocument . _Just . L.codeAction . _Just . L.dataSupport . _Just == Just True+    && case caps ^? L.textDocument . _Just . L.codeAction . _Just . L.resolveSupport . _Just of+        Just row -> "edit" `elem` row .! #properties+        _        -> False++internalError :: T.Text -> PluginError+internalError msg = PluginInternalError ("Ide.Plugin.Resolve: " <> msg)++invalidParamsError :: T.Text -> PluginError+invalidParamsError msg = PluginInvalidParams ("Ide.Plugin.Resolve: : " <> msg)++parseError :: Maybe A.Value -> T.Text -> PluginError+parseError value errMsg = PluginInternalError ("Ide.Plugin.Resolve: Error parsing value:"<> (T.pack $ show value) <> " Error: "<> errMsg)++{- Note [Code action resolve fallback to commands]+  To make supporting code action resolve easy for plugins, we want to let them+  provide one implementation that can be used both when clients support+  resolve, and when they don't.+  The way we do this is to have them always implement a resolve handler.+  Then, if the client doesn't support resolve, we instead install the resolve+  handler as a _command_ handler, passing the code action literal itself+  as the command argument. This allows the command handler to have+  the same interface as the resolve handler!+  -}
src/Ide/PluginUtils.hs view
@@ -1,11 +1,14 @@ {-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE LambdaCase        #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies      #-}+ module Ide.PluginUtils   ( -- * LSP Range manipulation functions     normalize,     extendNextLine,     extendLineStart,+    extendToFullLines,     WithDeletions(..),     getProcessID,     makeDiffTextEdit,@@ -14,12 +17,11 @@     diffText',     pluginDescToIdePlugins,     idePluginsToPluginDesc,-    responseError,     getClientConfig,     getPluginConfig,     configForPlugin,     pluginEnabled,-    extractRange,+    extractTextInRange,     fullRange,     mkLspCommand,     mkLspCmdId,@@ -30,42 +32,29 @@     subRange,     positionInRange,     usePropertyLsp,-    getNormalizedFilePath,-    pluginResponse,-    handleMaybe,-    handleMaybeM,-    throwPluginError,+    -- * Escape     unescape,-    )+  ) where --import           Control.Arrow                   ((&&&))-import           Control.Monad.Extra             (maybeM)-import           Control.Monad.Trans.Class       (lift)-import           Control.Monad.Trans.Except      (ExceptT, runExceptT, throwE)+import           Control.Arrow               ((&&&))+import           Control.Lens                (_head, _last, re, (%~), (^.)) import           Data.Algorithm.Diff import           Data.Algorithm.DiffOutput-import           Data.Bifunctor                  (Bifunctor (first))-import           Data.Char                       (isPrint, showLitChar)-import           Data.Functor                    (void)-import qualified Data.HashMap.Strict             as H-import           Data.String                     (IsString (fromString))-import qualified Data.Text                       as T-import           Data.Void                       (Void)+import           Data.Char                   (isPrint, showLitChar)+import           Data.Functor                (void)+import qualified Data.Map                    as M+import qualified Data.Text                   as T+import           Data.Void                   (Void) import           Ide.Plugin.Config import           Ide.Plugin.Properties import           Ide.Types+import qualified Language.LSP.Protocol.Lens  as L+import           Language.LSP.Protocol.Types import           Language.LSP.Server-import           Language.LSP.Types              hiding-                                                 (SemanticTokenAbsolute (length, line),-                                                  SemanticTokenRelative (length),-                                                  SemanticTokensEdit (_start))-import qualified Language.LSP.Types              as J-import           Language.LSP.Types.Capabilities-import qualified Text.Megaparsec                 as P-import qualified Text.Megaparsec.Char            as P-import qualified Text.Megaparsec.Char.Lexer      as P+import qualified Text.Megaparsec             as P+import qualified Text.Megaparsec.Char        as P+import qualified Text.Megaparsec.Char.Lexer  as P  -- --------------------------------------------------------------------- @@ -92,39 +81,56 @@ extendLineStart (Range (Position sl _) e) =   Range (Position sl 0) e +-- | Extend 'Range' to include the start of the first line and start of the next line of the last line.+--+-- Caveat: It always extend the last line to the beginning of next line, even when the last position is at column 0.+-- This is to keep the compatibility with the implementation of old function @extractRange@.+--+-- >>> extendToFullLines (Range (Position 5 5) (Position 5 10))+-- Range (Position 5 0) (Position 6 0)+--+-- >>> extendToFullLines (Range (Position 5 5) (Position 7 2))+-- Range (Position 5 0) (Position 8 0)+--+-- >>> extendToFullLines (Range (Position 5 5) (Position 7 0))+-- Range (Position 5 0) (Position 8 0)+extendToFullLines :: Range -> Range+extendToFullLines = extendLineStart . extendNextLine++ -- ---------------------------------------------------------------------  data WithDeletions = IncludeDeletions | SkipDeletions-  deriving Eq+  deriving (Eq)  -- | Generate a 'WorkspaceEdit' value from a pair of source Text-diffText :: ClientCapabilities -> (Uri,T.Text) -> T.Text -> WithDeletions -> WorkspaceEdit+diffText :: ClientCapabilities -> (VersionedTextDocumentIdentifier, T.Text) -> T.Text -> WithDeletions -> WorkspaceEdit diffText clientCaps old new withDeletions =-  let-    supports = clientSupportsDocumentChanges clientCaps-  in diffText' supports old new withDeletions+  let supports = clientSupportsDocumentChanges clientCaps+   in diffText' supports old new withDeletions -makeDiffTextEdit :: T.Text -> T.Text -> List TextEdit+makeDiffTextEdit :: T.Text -> T.Text -> [TextEdit] makeDiffTextEdit f1 f2 = diffTextEdit f1 f2 IncludeDeletions -makeDiffTextEditAdditive :: T.Text -> T.Text -> List TextEdit+makeDiffTextEditAdditive :: T.Text -> T.Text -> [TextEdit] makeDiffTextEditAdditive f1 f2 = diffTextEdit f1 f2 SkipDeletions -diffTextEdit :: T.Text -> T.Text -> WithDeletions -> List TextEdit-diffTextEdit fText f2Text withDeletions = J.List r+diffTextEdit :: T.Text -> T.Text -> WithDeletions -> [TextEdit]+diffTextEdit fText f2Text withDeletions = r   where     r = map diffOperationToTextEdit diffOps     d = getGroupedDiff (lines $ T.unpack fText) (lines $ T.unpack f2Text) -    diffOps = filter (\x -> (withDeletions == IncludeDeletions) || not (isDeletion x))-                     (diffToLineRanges d)+    diffOps =+      filter+        (\x -> (withDeletions == IncludeDeletions) || not (isDeletion x))+        (diffToLineRanges d)      isDeletion (Deletion _ _) = True     isDeletion _              = False --    diffOperationToTextEdit :: DiffOperation LineRange -> J.TextEdit-    diffOperationToTextEdit (Change fm to) = J.TextEdit range nt+    diffOperationToTextEdit :: DiffOperation LineRange -> TextEdit+    diffOperationToTextEdit (Change fm to) = TextEdit range nt       where         range = calcRange fm         nt = T.pack $ init $ unlines $ lrContents to@@ -136,53 +142,54 @@       the line ending character(s) then use an end position denoting       the start of the next line"     -}-    diffOperationToTextEdit (Deletion (LineRange (sl, el) _) _) = J.TextEdit range ""+    diffOperationToTextEdit (Deletion (LineRange (sl, el) _) _) = TextEdit range ""       where-        range = J.Range (J.Position (fromIntegral $ sl - 1) 0)-                        (J.Position (fromIntegral el) 0)--    diffOperationToTextEdit (Addition fm l) = J.TextEdit range nt-    -- fm has a range wrt to the changed file, which starts in the current file at l + 1-    -- So the range has to be shifted to start at l + 1+        range =+          Range+            (Position (fromIntegral $ sl - 1) 0)+            (Position (fromIntegral el) 0)+    diffOperationToTextEdit (Addition fm l) = TextEdit range nt       where-        range = J.Range (J.Position (fromIntegral l) 0)-                        (J.Position (fromIntegral l) 0)-        nt = T.pack $ unlines $ lrContents fm+        -- fm has a range wrt to the changed file, which starts in the current file at l + 1+        -- So the range has to be shifted to start at l + 1 +        range =+          Range+            (Position (fromIntegral l) 0)+            (Position (fromIntegral l) 0)+        nt = T.pack $ unlines $ lrContents fm -    calcRange fm = J.Range s e+    calcRange fm = Range s e       where         sl = fst $ lrNumbers fm         sc = 0-        s = J.Position (fromIntegral $ sl - 1) sc -- Note: zero-based lines+        s = Position (fromIntegral $ sl - 1) sc -- Note: zero-based lines         el = snd $ lrNumbers fm         ec = fromIntegral $ length $ last $ lrContents fm-        e = J.Position (fromIntegral $ el - 1) ec  -- Note: zero-based lines-+        e = Position (fromIntegral $ el - 1) ec -- Note: zero-based lines  -- | A pure version of 'diffText' for testing-diffText' :: Bool -> (Uri,T.Text) -> T.Text -> WithDeletions -> WorkspaceEdit-diffText' supports (f,fText) f2Text withDeletions  =+diffText' :: Bool -> (VersionedTextDocumentIdentifier, T.Text) -> T.Text -> WithDeletions -> WorkspaceEdit+diffText' supports (verTxtDocId, fText) f2Text withDeletions =   if supports     then WorkspaceEdit Nothing (Just docChanges) Nothing     else WorkspaceEdit (Just h) Nothing Nothing   where     diff = diffTextEdit fText f2Text withDeletions-    h = H.singleton f diff-    docChanges = J.List [InL docEdit]-    docEdit = J.TextDocumentEdit (J.VersionedTextDocumentIdentifier f (Just 0)) $ fmap InL diff+    h = M.singleton (verTxtDocId ^. L.uri) diff+    docChanges = [InL docEdit]+    docEdit = TextDocumentEdit (verTxtDocId ^. re _versionedTextDocumentIdentifier) $ fmap InL diff  -- ---------------------------------------------------------------------  clientSupportsDocumentChanges :: ClientCapabilities -> Bool clientSupportsDocumentChanges caps =-  let ClientCapabilities mwCaps _ _ _ _ = caps+  let ClientCapabilities mwCaps _ _ _ _ _ = caps       supports = do         wCaps <- mwCaps         WorkspaceEditClientCapabilities mDc _ _ _ _ <- _workspaceEdit wCaps         mDc-  in-    Just True == supports+   in Just True == supports  -- --------------------------------------------------------------------- @@ -193,11 +200,11 @@ idePluginsToPluginDesc (IdePlugins pp) = pp  -- ---------------------------------------------------------------------+ -- | Returns the current client configuration. It is not wise to permanently -- cache the returned value of this function, as clients can at runtime change -- their configuration.----getClientConfig :: MonadLsp Config m => m Config+getClientConfig :: (MonadLsp Config m) => m Config getClientConfig = getConfig  -- ---------------------------------------------------------------------@@ -205,10 +212,10 @@ -- | Returns the current plugin configuration. It is not wise to permanently -- cache the returned value of this function, as clients can change their -- configuration at runtime.-getPluginConfig :: MonadLsp Config m => PluginDescriptor c -> m PluginConfig+getPluginConfig :: (MonadLsp Config m) => PluginDescriptor c -> m PluginConfig getPluginConfig plugin = do-    config <- getClientConfig-    return $ configForPlugin config plugin+  config <- getClientConfig+  return $ configForPlugin config plugin  -- --------------------------------------------------------------------- @@ -225,24 +232,33 @@  -- --------------------------------------------------------------------- -extractRange :: Range -> T.Text -> T.Text-extractRange (Range (Position sl _) (Position el _)) s = newS-  where focusLines = take (fromIntegral $ el-sl+1) $ drop (fromIntegral sl) $ T.lines s-        newS = T.unlines focusLines+-- | Extracts exact matching text in the range.+extractTextInRange :: Range -> T.Text -> T.Text+extractTextInRange (Range (Position sl sc) (Position el ec)) s = newS+  where+    focusLines = take (fromIntegral $ el - sl + 1) $ drop (fromIntegral sl) $ T.lines s+    -- NOTE: We have to trim the last line first to handle the single-line case+    newS =+      focusLines+        & _last %~ T.take (fromIntegral ec)+        & _head %~ T.drop (fromIntegral sc)+        -- NOTE: We cannot use unlines here, because we don't want to add trailing newline!+        & T.intercalate "\n"  -- | Gets the range that covers the entire text fullRange :: T.Text -> Range fullRange s = Range startPos endPos-  where startPos = Position 0 0-        endPos = Position lastLine 0-        {--        In order to replace everything including newline characters,-        the end range should extend below the last line. From the specification:-        "If you want to specify a range that contains a line including-        the line ending character(s) then use an end position denoting-        the start of the next line"-        -}-        lastLine = fromIntegral $ length $ T.lines s+  where+    startPos = Position 0 0+    endPos = Position lastLine 0+    {-+    In order to replace everything including newline characters,+    the end range should extend below the last line. From the specification:+    "If you want to specify a range that contains a line including+    the line ending character(s) then use an end position denoting+    the start of the next line"+    -}+    lastLine = fromIntegral $ length $ T.lines s  subRange :: Range -> Range -> Bool subRange = isSubrangeOf@@ -251,40 +267,16 @@  allLspCmdIds' :: T.Text -> IdePlugins ideState -> [T.Text] allLspCmdIds' pid (IdePlugins ls) =-    allLspCmdIds pid $ map (pluginId &&& pluginCommands) ls+  allLspCmdIds pid $ map (pluginId &&& pluginCommands) ls  allLspCmdIds :: T.Text -> [(PluginId, [PluginCommand ideState])] -> [T.Text] allLspCmdIds pid commands = concatMap go commands   where     go (plid, cmds) = map (mkLspCmdId pid plid . commandId) cmds - -- --------------------------------------------------------------------- -getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath-getNormalizedFilePath uri = handleMaybe errMsg-        $ uriToNormalizedFilePath-        $ toNormalizedUri uri-    where-        errMsg = T.unpack $ "Failed converting " <> getUri uri <> " to NormalizedFilePath" --- ----------------------------------------------------------------------throwPluginError :: Monad m => String -> ExceptT String m b-throwPluginError = throwE--handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b-handleMaybe msg = maybe (throwE msg) return--handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b-handleMaybeM msg act = maybeM (throwE msg) return $ lift act--pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a)-pluginResponse =-  fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing))-    . runExceptT---- ---------------------------------------------------------------------- type TextParser = P.Parsec Void T.Text  -- | Unescape printable escape sequences within double quotes.@@ -292,9 +284,9 @@ -- display as is. unescape :: T.Text -> T.Text unescape input =-    case P.runParser escapedTextParser "inline" input of-        Left _     -> input-        Right strs -> T.pack strs+  case P.runParser escapedTextParser "inline" input of+    Left _     -> input+    Right strs -> T.pack strs  -- | Parser for a string that contains double quotes. Returns unescaped string. escapedTextParser :: TextParser String@@ -305,11 +297,11 @@      stringLiteral :: TextParser String     stringLiteral = do-        inside <- P.char '"' >> P.manyTill P.charLiteral (P.char '"')-        let f '"' = "\\\"" -- double quote should still be escaped-            -- Despite the docs, 'showLitChar' and 'showLitString' from 'Data.Char' DOES ESCAPE unicode printable-            -- characters. So we need to call 'isPrint' from 'Data.Char' manually.-            f ch  = if isPrint ch then [ch] else showLitChar ch ""-            inside' = concatMap f inside+      inside <- P.char '"' >> P.manyTill P.charLiteral (P.char '"')+      let f '"' = "\\\"" -- double quote should still be escaped+      -- Despite the docs, 'showLitChar' and 'showLitString' from 'Data.Char' DOES ESCAPE unicode printable+      -- characters. So we need to call 'isPrint' from 'Data.Char' manually.+          f ch  = if isPrint ch then [ch] else showLitChar ch ""+          inside' = concatMap f inside -        pure $ "\"" <> inside' <> "\""+      pure $ "\"" <> inside' <> "\""
src/Ide/Types.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE BangPatterns               #-}+{-# LANGUAGE BlockArguments             #-} {-# LANGUAGE CPP                        #-} {-# LANGUAGE ConstraintKinds            #-} {-# LANGUAGE DefaultSignatures          #-}@@ -9,8 +10,10 @@ {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MonadComprehensions        #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE NamedFieldPuns             #-}+{-# LANGUAGE OverloadedLabels           #-} {-# LANGUAGE OverloadedStrings          #-} {-# LANGUAGE PatternSynonyms            #-} {-# LANGUAGE PolyKinds                  #-}@@ -19,7 +22,6 @@ {-# LANGUAGE TypeFamilies               #-} {-# LANGUAGE UndecidableInstances       #-} {-# LANGUAGE ViewPatterns               #-}- module Ide.Types ( PluginDescriptor(..), defaultPluginDescriptor, defaultCabalPluginDescriptor , defaultPluginPriority@@ -45,71 +47,65 @@ , PluginRequestMethod(..) , getProcessID, getPid , installSigUsr1Handler-, responseError , lookupCommandProvider+, ResolveFunction+, mkResolveHandler )     where  #ifdef mingw32_HOST_OS-import qualified System.Win32.Process            as P (getCurrentProcessId)++import qualified System.Win32.Process          as P (getCurrentProcessId)+ #else-import           Control.Monad                   (void)-import qualified System.Posix.Process            as P (getProcessID)++import qualified System.Posix.Process          as P (getProcessID) import           System.Posix.Signals+ #endif-import           Control.Applicative             ((<|>))-import           Control.Arrow                   ((&&&))-import           Control.Lens                    ((.~), (^.))-import           Data.Aeson                      hiding (defaultOptions)++import           Control.Applicative           ((<|>))+import           Control.Arrow                 ((&&&))+import           Control.Lens                  (_Just, (.~), (?~), (^.), (^?))+import           Control.Monad                 (void)+import           Control.Monad.Error.Class     (MonadError (throwError))+import           Control.Monad.Trans.Class     (MonadTrans (lift))+import           Control.Monad.Trans.Except    (ExceptT, runExceptT)+import           Data.Aeson                    hiding (Null, defaultOptions) import           Data.Default-import           Data.Dependent.Map              (DMap)-import qualified Data.Dependent.Map              as DMap-import qualified Data.DList                      as DList+import           Data.Dependent.Map            (DMap)+import qualified Data.Dependent.Map            as DMap+import qualified Data.DList                    as DList import           Data.GADT.Compare-import           Data.Hashable                   (Hashable)-import           Data.HashMap.Strict             (HashMap)-import qualified Data.HashMap.Strict             as HashMap-import           Data.List.Extra                 (find, sortOn)-import           Data.List.NonEmpty              (NonEmpty (..), toList)-import qualified Data.Map                        as Map+import           Data.Hashable                 (Hashable)+import           Data.HashMap.Strict           (HashMap)+import qualified Data.HashMap.Strict           as HashMap+import           Data.Kind                     (Type)+import           Data.List.Extra               (find, sortOn)+import           Data.List.NonEmpty            (NonEmpty (..), toList)+import qualified Data.Map                      as Map import           Data.Maybe import           Data.Ord import           Data.Semigroup import           Data.String-import qualified Data.Text                       as T-import           Data.Text.Encoding              (encodeUtf8)+import qualified Data.Text                     as T+import           Data.Text.Encoding            (encodeUtf8) import           Development.IDE.Graph-import           GHC                             (DynFlags)+import           GHC                           (DynFlags) import           GHC.Generics+import           Ide.Plugin.Error import           Ide.Plugin.Properties-import           Language.LSP.Server             (LspM, getVirtualFile)-import           Language.LSP.Types              hiding-                                                 (SemanticTokenAbsolute (length, line),-                                                  SemanticTokenRelative (length),-                                                  SemanticTokensEdit (_start))-import           Language.LSP.Types.Capabilities (ClientCapabilities (ClientCapabilities),-                                                  TextDocumentClientCapabilities (_codeAction, _documentSymbol))-import           Language.LSP.Types.Lens         as J (HasChildren (children),-                                                       HasCommand (command),-                                                       HasContents (contents),-                                                       HasDeprecated (deprecated),-                                                       HasEdit (edit),-                                                       HasKind (kind),-                                                       HasName (name),-                                                       HasOptions (..),-                                                       HasRange (range),-                                                       HasTextDocument (..),-                                                       HasTitle (title),-                                                       HasUri (..))-import qualified Language.LSP.Types.Lens         as J+import qualified Language.LSP.Protocol.Lens    as L+import           Language.LSP.Protocol.Message+import           Language.LSP.Protocol.Types+import           Language.LSP.Server           (LspM, LspT, getVirtualFile) import           Language.LSP.VFS import           Numeric.Natural import           OpenTelemetry.Eventlog-import           Options.Applicative             (ParserInfo)+import           Options.Applicative           (ParserInfo) import           System.FilePath import           System.IO.Unsafe-import           Text.Regex.TDFA.Text            ()-+import           Text.Regex.TDFA.Text          () -- ---------------------------------------------------------------------  data IdePlugins ideState = IdePlugins_@@ -170,7 +166,7 @@  -- | We (initially anyway) mirror the hie configuration, so that existing -- clients can simply switch executable and not have any nasty surprises.  There--- will be surprises relating to config options being ignored, initially though.+-- will initially be surprises relating to config options being ignored though. data Config =   Config     { checkParents            :: CheckParents@@ -200,6 +196,7 @@     -- , formattingProvider          = "floskell"     -- , formattingProvider          = "stylish-haskell"     , cabalFormattingProvider     = "cabal-fmt"+    -- this string value needs to kept in sync with the value provided in HlsPlugins     , maxCompletions              = 40     , plugins                     = mempty     }@@ -268,7 +265,7 @@  -- --------------------------------------------------------------------- -data PluginDescriptor (ideState :: *) =+data PluginDescriptor (ideState :: Type) =   PluginDescriptor { pluginId           :: !PluginId                    -- ^ Unique identifier of the plugin.                    , pluginPriority     :: Natural@@ -282,7 +279,7 @@                    , pluginCli            :: Maybe (ParserInfo (IdeCommand ideState))                    , pluginFileType       :: [T.Text]                    -- ^ File extension of the files the plugin is responsible for.-                   --   The plugin is only allowed to handle files with these extensions+                   --   The plugin is only allowed to handle files with these extensions.                    --   When writing handlers, etc. for this plugin it can be assumed that all handled files are of this type.                    --   The file extension must have a leading '.'.                    }@@ -301,8 +298,8 @@ -- | An existential wrapper of 'Properties' data CustomConfig = forall r. CustomConfig (Properties r) --- | Describes the configuration a plugin.--- A plugin may be configurable in such form:+-- | Describes the configuration of a plugin.+-- A plugin may be configurable as can be seen below: -- -- @ -- {@@ -317,7 +314,7 @@ -- } -- @ ----- @globalOn@, @codeActionsOn@, and @codeLensOn@ etc. are called generic configs,+-- @globalOn@, @codeActionsOn@, and @codeLensOn@ etc. are called generic configs -- which can be inferred from handlers registered by the plugin. -- @config@ is called custom config, which is defined using 'Properties'. data ConfigDescriptor = ConfigDescriptor {@@ -341,393 +338,431 @@ -- | Methods that can be handled by plugins. -- 'ExtraParams' captures any extra data the IDE passes to the handlers for this method -- Only methods for which we know how to combine responses can be instances of 'PluginMethod'-class HasTracing (MessageParams m) => PluginMethod (k :: MethodType) (m :: Method FromClient k) where+class HasTracing (MessageParams m) => PluginMethod (k :: MessageKind) (m :: Method ClientToServer k) where    -- | Parse the configuration to check if this plugin is enabled.-  -- Perform sanity checks on the message to see whether plugin is enabled+  -- Perform sanity checks on the message to see whether the plugin is enabled   -- for this message in particular.-  -- If a plugin is not enabled, its handlers, commands, etc... will not be+  -- If a plugin is not enabled, its handlers, commands, etc. will not be   -- run for the given message.   ---  -- Semantically, this method described whether a Plugin is enabled configuration wise+  -- Semantically, this method describes whether a plugin is enabled configuration wise   -- and is allowed to respond to the message. This might depend on the URI that is-  -- associated to the Message Parameters, but doesn't have to. There are requests-  -- with no associated URI that, consequentially, can't inspect the URI.+  -- associated to the Message Parameters. There are requests+  -- with no associated URI that, consequentially, cannot inspect the URI.   ---  -- Common reason why a plugin might not be allowed to respond although it is enabled:-  --   * Plugin can not handle requests associated to the specific URI+  -- A common reason why a plugin might not be allowed to respond although it is enabled:+  --   * The plugin cannot handle requests associated with the specific URI   --     * Since the implementation of [cabal plugins](https://github.com/haskell/haskell-language-server/issues/2940)-  --       HLS knows plugins specific for Haskell and specific for [Cabal file descriptions](https://cabal.readthedocs.io/en/3.6/cabal-package.html)+  --       HLS knows plugins specific to Haskell and specific to [Cabal file descriptions](https://cabal.readthedocs.io/en/3.6/cabal-package.html)   --   -- Strictly speaking, we are conflating two concepts here:-  --   * Dynamically enabled (e.g. enabled on a per-message basis)+  --   * Dynamically enabled (e.g. on a per-message basis)   --   * Statically enabled (e.g. by configuration in the lsp-client)   --     * Strictly speaking, this might also change dynamically   ---  -- But there is no use to split it up currently into two different methods for now.+  -- But there is no use to split it up into two different methods for now.   pluginEnabled     :: SMethod m     -- ^ Method type.     -> MessageParams m     -- ^ Whether a plugin is enabled might depend on the message parameters-    --   eg 'pluginFileType' specifies what file extension a plugin is allowed to handle+    --   e.g. 'pluginFileType' specifies which file extensions a plugin is allowed to handle     -> PluginDescriptor c-    -- ^ Contains meta information such as PluginId and what file types this+    -- ^ Contains meta information such as PluginId and which file types this     -- plugin is able to handle.     -> Config-    -- ^ Generic config description, expected to hold 'PluginConfig' configuration+    -- ^ Generic config description, expected to contain 'PluginConfig' configuration     -- for this plugin     -> Bool     -- ^ Is this plugin enabled and allowed to respond to the given request     -- with the given parameters? -  default pluginEnabled :: (HasTextDocument (MessageParams m) doc, HasUri doc Uri)+  default pluginEnabled :: (L.HasTextDocument (MessageParams m) doc, L.HasUri doc Uri)                               => SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool   pluginEnabled _ params desc conf = pluginResponsible uri desc && plcGlobalOn (configForPlugin conf desc)     where-        uri = params ^. J.textDocument . J.uri+        uri = params ^. L.textDocument . L.uri  -- --------------------------------------------------------------------- -- Plugin Requests -- --------------------------------------------------------------------- -class PluginMethod Request m => PluginRequestMethod (m :: Method FromClient Request) where+class PluginMethod Request m => PluginRequestMethod (m :: Method ClientToServer Request) where   -- | How to combine responses from different plugins.   --   -- For example, for Hover requests, we might have multiple producers of-  -- Hover information, we do not want to decide which one to display to the user-  -- but allow here to define how to merge two hover request responses into one+  -- Hover information. We do not want to decide which one to display to the user+  -- but instead allow to define how to merge two hover request responses into one   -- glorious hover box.   ---  -- However, sometimes only one handler of a request can realistically exist,-  -- such as TextDocumentFormatting, it is safe to just unconditionally report+  -- However, as sometimes only one handler of a request can realistically exist+  -- (such as TextDocumentFormatting), it is safe to just unconditionally report   -- back one arbitrary result (arbitrary since it should only be one anyway).   combineResponses     :: SMethod m     -> Config -- ^ IDE Configuration     -> ClientCapabilities     -> MessageParams m-    -> NonEmpty (ResponseResult m) -> ResponseResult m+    -> NonEmpty (MessageResult m) -> MessageResult m -  default combineResponses :: Semigroup (ResponseResult m)-    => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (ResponseResult m) -> ResponseResult m+  default combineResponses :: Semigroup (MessageResult m)+    => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (MessageResult m) -> MessageResult m   combineResponses _method _config _caps _params = sconcat -instance PluginMethod Request TextDocumentCodeAction where+instance PluginMethod Request Method_TextDocumentCodeAction where   pluginEnabled _ msgParams pluginDesc config =     pluginResponsible uri pluginDesc && pluginEnabledConfig plcCodeActionsOn (configForPlugin config pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri--instance PluginRequestMethod TextDocumentCodeAction where-  combineResponses _method _config (ClientCapabilities _ textDocCaps _ _ _) (CodeActionParams _ _ _ _ context) resps =-      fmap compat $ List $ filter wasRequested $ (\(List x) -> x) $ sconcat resps-    where-      compat :: (Command |? CodeAction) -> (Command |? CodeAction)-      compat x@(InL _) = x-      compat x@(InR action)-        | Just _ <- textDocCaps >>= _codeAction >>= _codeActionLiteralSupport-        = x-        | otherwise = InL cmd-        where-          cmd = mkLspCommand "hls" "fallbackCodeAction" (action ^. title) (Just cmdParams)-          cmdParams = [toJSON (FallbackCodeActionParams (action ^. edit) (action ^. command))]+      uri = msgParams ^. L.textDocument . L.uri -      wasRequested :: (Command |? CodeAction) -> Bool-      wasRequested (InL _) = True-      wasRequested (InR ca)-        | Nothing <- _only context = True-        | Just (List allowed) <- _only context-        -- See https://github.com/microsoft/language-server-protocol/issues/970-        -- This is somewhat vague, but due to the hierarchical nature of action kinds, we-        -- should check whether the requested kind is a *prefix* of the action kind.-        -- That means, for example, we will return actions with kinds `quickfix.import` and-        -- `quickfix.somethingElse` if the requested kind is `quickfix`.-        , Just caKind <- ca ^. kind = any (\k -> k `codeActionKindSubsumes` caKind) allowed-        | otherwise = False+instance PluginMethod Request Method_CodeActionResolve where+  -- See Note [Resolve in PluginHandlers]+  pluginEnabled _ msgParams pluginDesc config =+    pluginResolverResponsible (msgParams ^. L.data_) pluginDesc+    && pluginEnabledConfig plcCodeActionsOn (configForPlugin config pluginDesc) -instance PluginMethod Request TextDocumentDefinition where+instance PluginMethod Request Method_TextDocumentDefinition where   pluginEnabled _ msgParams pluginDesc _ =     pluginResponsible uri pluginDesc     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentTypeDefinition where+instance PluginMethod Request Method_TextDocumentTypeDefinition where   pluginEnabled _ msgParams pluginDesc _ =     pluginResponsible uri pluginDesc     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentDocumentHighlight where+instance PluginMethod Request Method_TextDocumentDocumentHighlight where   pluginEnabled _ msgParams pluginDesc _ =     pluginResponsible uri pluginDesc     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentReferences where+instance PluginMethod Request Method_TextDocumentReferences where   pluginEnabled _ msgParams pluginDesc _ =     pluginResponsible uri pluginDesc     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request WorkspaceSymbol where+instance PluginMethod Request Method_WorkspaceSymbol where   -- Unconditionally enabled, but should it really be?   pluginEnabled _ _ _ _ = True -instance PluginMethod Request TextDocumentCodeLens where+instance PluginMethod Request Method_TextDocumentCodeLens where   pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcCodeLensOn (configForPlugin config pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentRename where+instance PluginMethod Request Method_CodeLensResolve where+  -- See Note [Resolve in PluginHandlers]+  pluginEnabled _ msgParams pluginDesc config =+    pluginResolverResponsible (msgParams ^. L.data_) pluginDesc+    && pluginEnabledConfig plcCodeActionsOn (configForPlugin config pluginDesc)++instance PluginMethod Request Method_TextDocumentRename where   pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcRenameOn (configForPlugin config pluginDesc)    where-      uri = msgParams ^. J.textDocument . J.uri-instance PluginMethod Request TextDocumentHover where+      uri = msgParams ^. L.textDocument . L.uri+instance PluginMethod Request Method_TextDocumentHover where   pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcHoverOn (configForPlugin config pluginDesc)    where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentDocumentSymbol where+instance PluginMethod Request Method_TextDocumentDocumentSymbol where   pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcSymbolsOn (configForPlugin config pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request CompletionItemResolve where-  pluginEnabled _ msgParams pluginDesc config = pluginEnabledConfig plcCompletionOn (configForPlugin config pluginDesc)+instance PluginMethod Request Method_CompletionItemResolve where+  -- See Note [Resolve in PluginHandlers]+  pluginEnabled _ msgParams pluginDesc config = pluginResolverResponsible (msgParams ^. L.data_) pluginDesc+    && pluginEnabledConfig plcCompletionOn (configForPlugin config pluginDesc) -instance PluginMethod Request TextDocumentCompletion where+instance PluginMethod Request Method_TextDocumentCompletion where   pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcCompletionOn (configForPlugin config pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentFormatting where-  pluginEnabled STextDocumentFormatting msgParams pluginDesc conf =+instance PluginMethod Request Method_TextDocumentFormatting where+  pluginEnabled SMethod_TextDocumentFormatting msgParams pluginDesc conf =     pluginResponsible uri pluginDesc       && (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri       pid = pluginId pluginDesc -instance PluginMethod Request TextDocumentRangeFormatting where+instance PluginMethod Request Method_TextDocumentRangeFormatting where   pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc       && (PluginId (formattingProvider conf) == pid || PluginId (cabalFormattingProvider conf) == pid)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri       pid = pluginId pluginDesc -instance PluginMethod Request TextDocumentPrepareCallHierarchy where+instance PluginMethod Request Method_TextDocumentPrepareCallHierarchy where   pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentSelectionRange where+instance PluginMethod Request Method_TextDocumentSelectionRange where   pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcSelectionRangeOn (configForPlugin conf pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request TextDocumentFoldingRange where+instance PluginMethod Request Method_TextDocumentFoldingRange where   pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc       && pluginEnabledConfig plcFoldingRangeOn (configForPlugin conf pluginDesc)     where-      uri = msgParams ^. J.textDocument . J.uri+      uri = msgParams ^. L.textDocument . L.uri -instance PluginMethod Request CallHierarchyIncomingCalls where+instance PluginMethod Request Method_CallHierarchyIncomingCalls where   -- This method has no URI parameter, thus no call to 'pluginResponsible'   pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc) -instance PluginMethod Request CallHierarchyOutgoingCalls where+instance PluginMethod Request Method_CallHierarchyOutgoingCalls where   -- This method has no URI parameter, thus no call to 'pluginResponsible'   pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn (configForPlugin conf pluginDesc) -instance PluginMethod Request CustomMethod where+instance PluginMethod Request (Method_CustomMethod m) where   pluginEnabled _ _ _ _ = True  ----instance PluginRequestMethod TextDocumentDefinition where+instance PluginRequestMethod Method_TextDocumentCodeAction where+  combineResponses _method _config (ClientCapabilities _ textDocCaps _ _ _ _) (CodeActionParams _ _ _ _ context) resps =+      InL $ fmap compat $ filter wasRequested $ concat $ mapMaybe nullToMaybe $ toList resps+    where+      compat :: (Command |? CodeAction) -> (Command |? CodeAction)+      compat x@(InL _) = x+      compat x@(InR action)+        | Just _ <- textDocCaps >>= _codeAction >>= _codeActionLiteralSupport+        = x+        | otherwise = InL cmd+        where+          cmd = mkLspCommand "hls" "fallbackCodeAction" (action ^. L.title) (Just cmdParams)+          cmdParams = [toJSON (FallbackCodeActionParams (action ^. L.edit) (action ^. L.command))]++      wasRequested :: (Command |? CodeAction) -> Bool+      wasRequested (InL _) = True+      wasRequested (InR ca)+        | Nothing <- _only context = True+        | Just allowed <- _only context+        -- See https://github.com/microsoft/language-server-protocol/issues/970+        -- This is somewhat vague, but due to the hierarchical nature of action kinds, we+        -- should check whether the requested kind is a *prefix* of the action kind.+        -- That means, for example, we will return actions with kinds `quickfix.import` and+        -- `quickfix.somethingElse` if the requested kind is `quickfix`.+        , Just caKind <- ca ^. L.kind = any (\k -> k `codeActionKindSubsumes` caKind) allowed+        | otherwise = False++instance PluginRequestMethod Method_CodeActionResolve where+    -- A resolve request should only have one response.+    -- See Note [Resolve in PluginHandlers].+    combineResponses _ _ _ _ (x :| _) = x++instance PluginRequestMethod Method_TextDocumentDefinition where   combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentTypeDefinition where+instance PluginRequestMethod Method_TextDocumentTypeDefinition where   combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentDocumentHighlight where+instance PluginRequestMethod Method_TextDocumentDocumentHighlight where -instance PluginRequestMethod TextDocumentReferences where+instance PluginRequestMethod Method_TextDocumentReferences where -instance PluginRequestMethod WorkspaceSymbol where+instance PluginRequestMethod Method_WorkspaceSymbol where+    -- TODO: combine WorkspaceSymbol. Currently all WorkspaceSymbols are dumped+    -- as it is new of lsp-types 2.0.0.0+    combineResponses _ _ _ _ xs = InL $ mconcat $ takeLefts $ toList xs -instance PluginRequestMethod TextDocumentCodeLens where+instance PluginRequestMethod Method_TextDocumentCodeLens where -instance PluginRequestMethod TextDocumentRename where+instance PluginRequestMethod Method_CodeLensResolve where+    -- A resolve request should only ever get one response.+    -- See note Note [Resolve in PluginHandlers]+    combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentHover where-  combineResponses _ _ _ _ (catMaybes . toList -> hs) = h+instance PluginRequestMethod Method_TextDocumentRename where++instance PluginRequestMethod Method_TextDocumentHover where+  combineResponses _ _ _ _ (mapMaybe nullToMaybe . toList -> hs :: [Hover]) =+    if null hs+        then InR Null+        else InL $ Hover (InL mcontent) r     where-      r = listToMaybe $ mapMaybe (^. range) hs-      h = case foldMap (^. contents) hs of-            HoverContentsMS (List []) -> Nothing-            hh                        -> Just $ Hover hh r+      r = listToMaybe $ mapMaybe (^. L.range) hs+      -- We are only taking MarkupContent here, because MarkedStrings have been+      -- deprecated for a while and don't occur in the hls codebase+      mcontent :: MarkupContent+      mcontent = mconcat $ takeLefts $ map (^. L.contents) hs -instance PluginRequestMethod TextDocumentDocumentSymbol where-  combineResponses _ _ (ClientCapabilities _ tdc _ _ _) params xs = res+instance PluginRequestMethod Method_TextDocumentDocumentSymbol where+  combineResponses _ _ (ClientCapabilities _ tdc _ _ _ _) params xs = res     where-      uri' = params ^. textDocument . uri+      uri' = params ^. L.textDocument . L.uri       supportsHierarchy = Just True == (tdc >>= _documentSymbol >>= _hierarchicalDocumentSymbolSupport)-      dsOrSi = fmap toEither xs+      dsOrSi :: [Either [SymbolInformation] [DocumentSymbol]]+      dsOrSi =  toEither <$> mapMaybe nullToMaybe' (toList xs)+      res :: [SymbolInformation] |? ([DocumentSymbol] |? Null)       res-        | supportsHierarchy = InL $ sconcat $ fmap (either id (fmap siToDs)) dsOrSi-        | otherwise = InR $ sconcat $ fmap (either (List . concatMap dsToSi) id) dsOrSi-      siToDs (SymbolInformation name kind _tags dep (Location _uri range) cont)+        | supportsHierarchy = InR $ InL $ concatMap (either (fmap siToDs) id) dsOrSi+        | otherwise = InL $ concatMap (either id ( concatMap dsToSi)) dsOrSi+      -- Is this actually a good conversion? It's what there was before, but some+      -- things such as tags are getting lost+      siToDs :: SymbolInformation -> DocumentSymbol+      siToDs (SymbolInformation name kind _tags cont dep (Location _uri range) )         = DocumentSymbol name cont kind Nothing dep range range Nothing       dsToSi = go Nothing       go :: Maybe T.Text -> DocumentSymbol -> [SymbolInformation]       go parent ds =         let children' :: [SymbolInformation]-            children' = concatMap (go (Just name')) (fromMaybe mempty (ds ^. children))-            loc = Location uri' (ds ^. range)-            name' = ds ^. name-            si = SymbolInformation name' (ds ^. kind) Nothing (ds ^. deprecated) loc parent+            children' = concatMap (go (Just name')) (fromMaybe mempty (ds ^. L.children))+            loc = Location uri' (ds ^. L.range)+            name' = ds ^. L.name+            si = SymbolInformation name' (ds ^. L.kind) Nothing parent (ds ^. L.deprecated) loc         in [si] <> children' -instance PluginRequestMethod CompletionItemResolve where-  -- resolving completions can only change the detail, additionalTextEdit or documentation fields-  combineResponses _ _ _ _ (x :| xs) = go x xs-    where go :: CompletionItem -> [CompletionItem] -> CompletionItem-          go !comp [] = comp-          go !comp1 (comp2:xs)-            = go (comp1-                 & J.detail              .~ comp1 ^. J.detail <> comp2 ^. J.detail-                 & J.documentation       .~ ((comp1 ^. J.documentation) <|> (comp2 ^. J.documentation)) -- difficult to write generic concatentation for docs-                 & J.additionalTextEdits .~ comp1 ^. J.additionalTextEdits <> comp2 ^. J.additionalTextEdits)-                 xs+instance PluginRequestMethod Method_CompletionItemResolve where+  -- A resolve request should only have one response.+  -- See Note [Resolve in PluginHandlers]+  combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentCompletion where+instance PluginRequestMethod Method_TextDocumentCompletion where   combineResponses _ conf _ _ (toList -> xs) = snd $ consumeCompletionResponse limit $ combine xs       where         limit = maxCompletions conf-        combine :: [List CompletionItem |? CompletionList] -> (List CompletionItem |? CompletionList)+        combine :: [[CompletionItem] |? (CompletionList |? Null)] -> ([CompletionItem] |? (CompletionList |? Null))         combine cs = go True mempty cs +        go :: Bool -> DList.DList CompletionItem -> [[CompletionItem] |? (CompletionList |? Null)] -> ([CompletionItem] |? (CompletionList |? Null))         go !comp acc [] =-          InR (CompletionList comp (List $ DList.toList acc))-        go comp acc (InL (List ls) : rest) =+           InR (InL (CompletionList comp Nothing ( DList.toList acc)))+        go comp acc ((InL ls) : rest) =           go comp (acc <> DList.fromList ls) rest-        go comp acc (InR (CompletionList comp' (List ls)) : rest) =+        go comp acc ( (InR (InL (CompletionList comp' _ ls))) : rest) =           go (comp && comp') (acc <> DList.fromList ls) rest-+        go comp acc ( (InR (InR Null)) : rest) =+          go comp acc rest         -- boolean disambiguators         isCompleteResponse, isIncompleteResponse :: Bool         isIncompleteResponse = True         isCompleteResponse = False--        consumeCompletionResponse limit it@(InR (CompletionList _ (List xx))) =+        consumeCompletionResponse :: Int -> ([CompletionItem] |? (CompletionList |? Null)) -> (Int, [CompletionItem] |? (CompletionList |? Null))+        consumeCompletionResponse limit it@(InR (InL (CompletionList _ _ xx))) =           case splitAt limit xx of             -- consumed all the items, return the result as is             (_, []) -> (limit - length xx, it)             -- need to crop the response, set the 'isIncomplete' flag-            (xx', _) -> (0, InR (CompletionList isIncompleteResponse (List xx')))-        consumeCompletionResponse n (InL (List xx)) =-          consumeCompletionResponse n (InR (CompletionList isCompleteResponse (List xx)))--instance PluginRequestMethod TextDocumentFormatting where+            (xx', _) -> (0, InR (InL (CompletionList isIncompleteResponse Nothing xx')))+        consumeCompletionResponse n (InL xx) =+          consumeCompletionResponse n (InR (InL (CompletionList isCompleteResponse Nothing xx)))+        consumeCompletionResponse n (InR (InR Null)) = (n, InR (InR Null))+instance PluginRequestMethod Method_TextDocumentFormatting where   combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentRangeFormatting where+instance PluginRequestMethod Method_TextDocumentRangeFormatting where   combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentPrepareCallHierarchy where+instance PluginRequestMethod Method_TextDocumentPrepareCallHierarchy where -instance PluginRequestMethod TextDocumentSelectionRange where+instance PluginRequestMethod Method_TextDocumentSelectionRange where   combineResponses _ _ _ _ (x :| _) = x -instance PluginRequestMethod TextDocumentFoldingRange where+instance PluginRequestMethod Method_TextDocumentFoldingRange where   combineResponses _ _ _ _ x = sconcat x -instance PluginRequestMethod CallHierarchyIncomingCalls where+instance PluginRequestMethod Method_CallHierarchyIncomingCalls where -instance PluginRequestMethod CallHierarchyOutgoingCalls where+instance PluginRequestMethod Method_CallHierarchyOutgoingCalls where -instance PluginRequestMethod CustomMethod where+instance PluginRequestMethod (Method_CustomMethod m) where   combineResponses _ _ _ _ (x :| _) = x +takeLefts :: [a |? b] -> [a]+takeLefts = mapMaybe (\x -> [res | (InL res) <- Just x])++nullToMaybe' :: (a |? (b |? Null)) -> Maybe (a |? b)+nullToMaybe' (InL x)       = Just $ InL x+nullToMaybe' (InR (InL x)) = Just $ InR x+nullToMaybe' (InR (InR _)) = Nothing -- --------------------------------------------------------------------- -- Plugin Notifications -- ---------------------------------------------------------------------  -- | Plugin Notification methods. No specific methods at the moment, but -- might contain more in the future.-class PluginMethod Notification m => PluginNotificationMethod (m :: Method FromClient Notification)  where+class PluginMethod Notification m => PluginNotificationMethod (m :: Method ClientToServer Notification)  where  -instance PluginMethod Notification TextDocumentDidOpen where+instance PluginMethod Notification Method_TextDocumentDidOpen where -instance PluginMethod Notification TextDocumentDidChange where+instance PluginMethod Notification Method_TextDocumentDidChange where -instance PluginMethod Notification TextDocumentDidSave where+instance PluginMethod Notification Method_TextDocumentDidSave where -instance PluginMethod Notification TextDocumentDidClose where+instance PluginMethod Notification Method_TextDocumentDidClose where -instance PluginMethod Notification WorkspaceDidChangeWatchedFiles where+instance PluginMethod Notification Method_WorkspaceDidChangeWatchedFiles where   -- This method has no URI parameter, thus no call to 'pluginResponsible'.   pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf desc -instance PluginMethod Notification WorkspaceDidChangeWorkspaceFolders where+instance PluginMethod Notification Method_WorkspaceDidChangeWorkspaceFolders where   -- This method has no URI parameter, thus no call to 'pluginResponsible'.   pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf desc -instance PluginMethod Notification WorkspaceDidChangeConfiguration where+instance PluginMethod Notification Method_WorkspaceDidChangeConfiguration where   -- This method has no URI parameter, thus no call to 'pluginResponsible'.   pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf desc -instance PluginMethod Notification Initialized where+instance PluginMethod Notification Method_Initialized where   -- This method has no URI parameter, thus no call to 'pluginResponsible'.   pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf desc  -instance PluginNotificationMethod TextDocumentDidOpen where+instance PluginNotificationMethod Method_TextDocumentDidOpen where -instance PluginNotificationMethod TextDocumentDidChange where+instance PluginNotificationMethod Method_TextDocumentDidChange where -instance PluginNotificationMethod TextDocumentDidSave where+instance PluginNotificationMethod Method_TextDocumentDidSave where -instance PluginNotificationMethod TextDocumentDidClose where+instance PluginNotificationMethod Method_TextDocumentDidClose where -instance PluginNotificationMethod WorkspaceDidChangeWatchedFiles where+instance PluginNotificationMethod Method_WorkspaceDidChangeWatchedFiles where -instance PluginNotificationMethod WorkspaceDidChangeWorkspaceFolders where+instance PluginNotificationMethod Method_WorkspaceDidChangeWorkspaceFolders where -instance PluginNotificationMethod WorkspaceDidChangeConfiguration where+instance PluginNotificationMethod Method_WorkspaceDidChangeConfiguration where -instance PluginNotificationMethod Initialized where+instance PluginNotificationMethod Method_Initialized where  -- ---------------------------------------------------------------------  -- | Methods which have a PluginMethod instance-data IdeMethod (m :: Method FromClient Request) = PluginRequestMethod m => IdeMethod (SMethod m)+data IdeMethod (m :: Method ClientToServer Request) = PluginRequestMethod m => IdeMethod (SMethod m) instance GEq IdeMethod where   geq (IdeMethod a) (IdeMethod b) = geq a b instance GCompare IdeMethod where   gcompare (IdeMethod a) (IdeMethod b) = gcompare a b  -- | Methods which have a PluginMethod instance-data IdeNotification (m :: Method FromClient Notification) = PluginNotificationMethod m => IdeNotification (SMethod m)+data IdeNotification (m :: Method ClientToServer Notification) = PluginNotificationMethod m => IdeNotification (SMethod m) instance GEq IdeNotification where   geq (IdeNotification a) (IdeNotification b) = geq a b instance GCompare IdeNotification where   gcompare (IdeNotification a) (IdeNotification b) = gcompare a b  -- | Combine handlers for the-newtype PluginHandler a (m :: Method FromClient Request)-  = PluginHandler (PluginId -> a -> MessageParams m -> LspM Config (NonEmpty (Either ResponseError (ResponseResult m))))+newtype PluginHandler a (m :: Method ClientToServer Request)+  = PluginHandler (PluginId -> a -> MessageParams m -> LspM Config (NonEmpty (Either PluginError (MessageResult m)))) -newtype PluginNotificationHandler a (m :: Method FromClient Notification)+newtype PluginNotificationHandler a (m :: Method ClientToServer Notification)   = PluginNotificationHandler (PluginId -> a -> VFS -> MessageParams m -> LspM Config ())  newtype PluginHandlers a             = PluginHandlers             (DMap IdeMethod       (PluginHandler a))@@ -750,24 +785,51 @@ instance Monoid (PluginNotificationHandlers a) where   mempty = PluginNotificationHandlers mempty -type PluginMethodHandler a m = a -> PluginId -> MessageParams m -> LspM Config (Either ResponseError (ResponseResult m))+type PluginMethodHandler a m = a -> PluginId -> MessageParams m -> ExceptT PluginError (LspM Config) (MessageResult m)  type PluginNotificationMethodHandler a m = a -> VFS -> PluginId -> MessageParams m -> LspM Config () --- | Make a handler for plugins with no extra data+-- | Make a handler for plugins. For how resolve works with this see+-- Note [Resolve in PluginHandlers] mkPluginHandler-  :: PluginRequestMethod m+  :: forall ideState m. PluginRequestMethod m   => SClientMethod m   -> PluginMethodHandler ideState m   -> PluginHandlers ideState-mkPluginHandler m f = PluginHandlers $ DMap.singleton (IdeMethod m) (PluginHandler f')+mkPluginHandler m f = PluginHandlers $ DMap.singleton (IdeMethod m) (PluginHandler (f' m))   where-    f' pid ide params = pure <$> f ide pid params+    f' :: SMethod m -> PluginId -> ideState -> MessageParams m -> LspT Config IO (NonEmpty (Either PluginError (MessageResult m)))+    -- We need to have separate functions for each method that supports resolve, so far we only support CodeActions+    -- CodeLens, and Completion methods.+    f' SMethod_TextDocumentCodeAction pid ide params@CodeActionParams{_textDocument=TextDocumentIdentifier {_uri}} =+      pure . fmap (wrapCodeActions pid _uri) <$> runExceptT (f ide pid params)+    f' SMethod_TextDocumentCodeLens pid ide params@CodeLensParams{_textDocument=TextDocumentIdentifier {_uri}} =+      pure . fmap (wrapCodeLenses pid _uri) <$> runExceptT (f ide pid params)+    f' SMethod_TextDocumentCompletion pid ide params@CompletionParams{_textDocument=TextDocumentIdentifier {_uri}} =+      pure . fmap (wrapCompletions pid _uri) <$> runExceptT (f ide pid params) +    -- This is the default case for all other methods+    f' _ pid ide params = pure <$> runExceptT (f ide pid params)++    -- Todo: use fancy pancy lenses to make this a few lines+    wrapCodeActions pid uri (InL ls) =+      let wrapCodeActionItem pid uri (InR c) = InR $ wrapResolveData pid uri c+          wrapCodeActionItem _ _ command@(InL _) = command+      in InL $ wrapCodeActionItem pid uri <$> ls+    wrapCodeActions _ _ (InR r) = InR r++    wrapCodeLenses pid uri (InL ls) = InL $ wrapResolveData pid uri <$> ls+    wrapCodeLenses _ _ (InR r)      = InR r++    wrapCompletions pid uri (InL ls) = InL $ wrapResolveData pid uri <$> ls+    wrapCompletions pid uri (InR (InL cl@(CompletionList{_items}))) =+      InR $ InL $ cl & L.items .~ (wrapResolveData pid uri <$> _items)+    wrapCompletions _ _ (InR (InR r)) = InR $ InR r+ -- | Make a handler for plugins with no extra data mkPluginNotificationHandler   :: PluginNotificationMethod m-  => SClientMethod (m :: Method FromClient Notification)+  => SClientMethod (m :: Method ClientToServer Notification)   -> PluginNotificationMethodHandler ideState m   -> PluginNotificationHandlers ideState mkPluginNotificationHandler m f@@ -779,7 +841,7 @@ defaultPluginPriority = 1000  -- | Set up a plugin descriptor, initialized with default values.--- This is plugin descriptor is prepared for @haskell@ files, such as+-- This plugin descriptor is prepared for @haskell@ files, such as -- --   * @.hs@ --   * @.lhs@@@ -802,7 +864,7 @@     [".hs", ".lhs", ".hs-boot"]  -- | Set up a plugin descriptor, initialized with default values.--- This is plugin descriptor is prepared for @.cabal@ files and as such,+-- This plugin descriptor is prepared for @.cabal@ files and as such, -- will only respond / run when @.cabal@ files are currently in scope. -- -- Handles files with the following extensions:@@ -837,13 +899,69 @@ type CommandFunction ideState a   = ideState   -> a-  -> LspM Config (Either ResponseError Value)+  -> ExceptT PluginError (LspM Config) (Value |? Null)  -- --------------------------------------------------------------------- +type ResolveFunction ideState a (m :: Method ClientToServer Request) =+  ideState+  -> PluginId+  -> MessageParams m+  -> Uri+  -> a+  -> ExceptT PluginError (LspM Config) (MessageResult m)++-- | Make a handler for resolve methods. In here we take your provided ResolveFunction+-- and turn it into a PluginHandlers. See Note [Resolve in PluginHandlers]+mkResolveHandler+  :: forall ideState a m. (FromJSON a,  PluginRequestMethod m, L.HasData_ (MessageParams m) (Maybe Value))+  =>  SClientMethod m+  -> ResolveFunction ideState a m+  -> PluginHandlers ideState+mkResolveHandler m f = mkPluginHandler m $ \ideState plId params -> do+  case fromJSON <$> (params ^. L.data_) of+    (Just (Success (PluginResolveData owner uri value) )) -> do+      if owner == plId+      then+        case fromJSON value of+          Success decodedValue ->+            let newParams = params & L.data_ ?~ value+            in f ideState plId newParams uri decodedValue+          Error msg ->+            -- We are assuming that if we can't decode the data, that this+            -- request belongs to another resolve handler for this plugin.+            throwError (PluginRequestRefused (T.pack ("Unable to decode payload for handler, assuming that it's for a different handler" <> msg)))+      -- If we are getting an owner that isn't us, this means that there is an+      -- error, as we filter these our in `pluginEnabled`+      else throwError $ PluginInternalError invalidRequest+    -- If we are getting params without a decodable data field, this means that+    -- there is an error, as we filter these our in `pluginEnabled`+    (Just (Error err)) -> throwError $ PluginInternalError (parseError (params ^. L.data_) err)+    -- If there are no params at all, this also means that there is an error,+    -- as this is filtered out in `pluginEnabled`+    _ -> throwError $ PluginInternalError invalidRequest+  where invalidRequest = "The resolve request incorrectly got routed to the wrong resolve handler!"+        parseError value err = "Unable to decode: " <> (T.pack $ show value) <> ". Error: " <> (T.pack $ show err)++wrapResolveData :: L.HasData_ a (Maybe Value) => PluginId -> Uri -> a -> a+wrapResolveData pid uri hasData =+  hasData & L.data_ .~  (toJSON . PluginResolveData pid uri <$> data_)+  where data_ = hasData ^? L.data_ . _Just++-- |Allow plugins to "own" resolve data, allowing only them to be queried for+-- the resolve action. This design has added flexibility at the cost of nested+-- Value types+data PluginResolveData = PluginResolveData {+  resolvePlugin :: PluginId+, resolveURI    :: Uri+, resolveValue  :: Value+}+  deriving (Generic, Show)+  deriving anyclass (ToJSON, FromJSON)+ newtype PluginId = PluginId T.Text   deriving (Show, Read, Eq, Ord)-  deriving newtype (FromJSON, Hashable)+  deriving newtype (ToJSON, FromJSON, Hashable)  instance IsString PluginId where   fromString = PluginId . T.pack@@ -869,9 +987,9 @@   type FormattingMethod m =-  ( J.HasOptions (MessageParams m) FormattingOptions-  , J.HasTextDocument (MessageParams m) TextDocumentIdentifier-  , ResponseResult m ~ List TextEdit+  ( L.HasOptions (MessageParams m) FormattingOptions+  , L.HasTextDocument (MessageParams m) TextDocumentIdentifier+  , MessageResult m ~ ([TextEdit] |? Null)   )  type FormattingHandler a@@ -880,37 +998,33 @@   -> T.Text   -> NormalizedFilePath   -> FormattingOptions-  -> LspM Config (Either ResponseError (List TextEdit))+  -> ExceptT PluginError (LspM Config) ([TextEdit] |? Null)  mkFormattingHandlers :: forall a. FormattingHandler a -> PluginHandlers a-mkFormattingHandlers f = mkPluginHandler STextDocumentFormatting (provider STextDocumentFormatting)-                      <> mkPluginHandler STextDocumentRangeFormatting (provider STextDocumentRangeFormatting)+mkFormattingHandlers f = mkPluginHandler SMethod_TextDocumentFormatting ( provider SMethod_TextDocumentFormatting)+                      <> mkPluginHandler SMethod_TextDocumentRangeFormatting (provider SMethod_TextDocumentRangeFormatting)   where     provider :: forall m. FormattingMethod m => SMethod m -> PluginMethodHandler a m     provider m ide _pid params       | Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri = do-        mf <- getVirtualFile $ toNormalizedUri uri+        mf <- lift $ getVirtualFile $ toNormalizedUri uri         case mf of           Just vf -> do             let typ = case m of-                  STextDocumentFormatting -> FormatText-                  STextDocumentRangeFormatting -> FormatRange (params ^. J.range)-                  _ -> error "mkFormattingHandlers: impossible"+                  SMethod_TextDocumentFormatting -> FormatText+                  SMethod_TextDocumentRangeFormatting -> FormatRange (params ^. L.range)+                  _ -> Prelude.error "mkFormattingHandlers: impossible"             f ide typ (virtualFileText vf) nfp opts-          Nothing -> pure $ Left $ responseError $ T.pack $ "Formatter plugin: could not get file contents for " ++ show uri+          Nothing -> throwError $ PluginInvalidParams $ T.pack $ "Formatter plugin: could not get file contents for " ++ show uri -      | otherwise = pure $ Left $ responseError $ T.pack $ "Formatter plugin: uriToFilePath failed for: " ++ show uri+      | otherwise = throwError $ PluginInvalidParams $ T.pack $ "Formatter plugin: uriToFilePath failed for: " ++ show uri       where-        uri = params ^. J.textDocument . J.uri-        opts = params ^. J.options+        uri = params ^. L.textDocument . L.uri+        opts = params ^. L.options  -- --------------------------------------------------------------------- -responseError :: T.Text -> ResponseError-responseError txt = ResponseError InvalidParams txt Nothing --- ---------------------------------------------------------------------- data FallbackCodeActionParams =   FallbackCodeActionParams     { fallbackWorkspaceEdit :: Maybe WorkspaceEdit@@ -927,8 +1041,8 @@   traceWithSpan :: SpanInFlight -> a -> IO ()   traceWithSpan _ _ = pure () -instance {-# OVERLAPPABLE #-} (HasTextDocument a doc, HasUri doc Uri) => HasTracing a where-  traceWithSpan sp a = otSetUri sp (a ^. J.textDocument . J.uri)+instance {-# OVERLAPPABLE #-} (L.HasTextDocument a doc, L.HasUri doc Uri) => HasTracing a where+  traceWithSpan sp a = otSetUri sp (a ^. L.textDocument . L.uri)  instance HasTracing Value instance HasTracing ExecuteCommandParams@@ -938,24 +1052,29 @@ instance HasTracing DidChangeWorkspaceFoldersParams instance HasTracing DidChangeConfigurationParams instance HasTracing InitializeParams-instance HasTracing (Maybe InitializedParams)+instance HasTracing InitializedParams instance HasTracing WorkspaceSymbolParams where   traceWithSpan sp (WorkspaceSymbolParams _ _ query) = setTag sp "query" (encodeUtf8 query) instance HasTracing CallHierarchyIncomingCallsParams instance HasTracing CallHierarchyOutgoingCallsParams-instance HasTracing CompletionItem +-- Instances for resolve types+instance HasTracing CodeAction+instance HasTracing CodeLens+instance HasTracing CompletionItem+instance HasTracing DocumentLink+instance HasTracing InlayHint+instance HasTracing WorkspaceSymbol -- ----------------------------------------------------------------------+--Experimental resolve refactoring {-# NOINLINE pROCESS_ID #-} pROCESS_ID :: T.Text pROCESS_ID = unsafePerformIO getPid  mkLspCommand :: PluginId -> CommandId -> T.Text -> Maybe [Value] -> Command-mkLspCommand plid cn title args' = Command title cmdId args+mkLspCommand plid cn title args = Command title cmdId args   where     cmdId = mkLspCmdId pROCESS_ID plid cn-    args = List <$> args'  mkLspCmdId :: T.Text -> PluginId -> CommandId -> T.Text mkLspCmdId pid (PluginId plid) (CommandId cid)@@ -979,3 +1098,40 @@  installSigUsr1Handler h = void $ installHandler sigUSR1 (Catch h) Nothing #endif++-- |Determine whether this request should be routed to the plugin. Fails closed+-- if we can't determine which plugin it should be routed to.+pluginResolverResponsible :: Maybe Value -> PluginDescriptor c -> Bool+pluginResolverResponsible (Just (fromJSON -> (Success (PluginResolveData o _ _)))) pluginDesc =+  pluginId pluginDesc == o+-- We want to fail closed+pluginResolverResponsible _ _ = False++{- Note [Resolve in PluginHandlers]+  Resolve methods have a few guarantees that need to be made by HLS,+  specifically they need to only be called once, as neither their errors nor+  their responses can be easily combined. Whereas commands, which similarly have+  the same requirements have their own codepaths for execution, for resolve+  methods we are relying on the standard PluginHandlers codepath.+  That isn't a problem, but it does mean we need to do some things extra for+  these methods.+    - First of all, whenever a handler that can be resolved sets the data_ field+    in their response, we need to intercept it, and wrap it in a data type+    PluginResolveData that allows us to route the future resolve request to the+    specific plugin which is responsible for it. (We also throw in the URI for+    convenience, because everyone needs that). We do that in mkPluginHandler.+    - When we get any resolve requests we check their data field for our+    PluginResolveData that will allow us to route the request to the right+    plugin. If we can't find out which plugin to route the request to, then we+    just don't route it at all. This is done in pluginEnabled, and+    pluginResolverResponsible.+    - Finally we have mkResolveHandler, which takes the resolve request and+    unwraps the plugins data from our PluginResolveData, parses it and passes it+    it on to the registered handler.+  It should be noted that there are some restrictions with this approach: First,+  if a plugin does not set the data_ field, than the request will not be able+  to be resolved. This is because we only wrap data_ fields that have been set+  with our PluginResolvableData tag. Second, if a plugin were to register two+  resolve handlers for the same method, than our assumptions that we never have+  two responses break, and behavior is undefined.+  -}
test/Ide/PluginUtilsTest.hs view
@@ -5,13 +5,13 @@     ( tests     ) where -import           Data.Char             (isPrint)-import qualified Data.Set              as Set-import qualified Data.Text             as T-import qualified Ide.Plugin.RangeMap   as RangeMap-import           Ide.PluginUtils       (positionInRange, unescape)-import           Language.LSP.Types    (Position (..), Range (Range), UInt,-                                        isSubrangeOf)+import           Data.Char                   (isPrint)+import qualified Data.Set                    as Set+import qualified Data.Text                   as T+import qualified Ide.Plugin.RangeMap         as RangeMap+import           Ide.PluginUtils             (positionInRange, unescape)+import           Language.LSP.Protocol.Types (Position (..), Range (Range),+                                              UInt, isSubrangeOf) import           Test.Tasty import           Test.Tasty.HUnit import           Test.Tasty.QuickCheck