-- Copyright (c) 2019 The DAML Authors. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE RankNTypes            #-}

module Development.IDE.LSP.Notifications
    ( whenUriFile
    , descriptor
    , Log(..)
    ) where

import           Language.LSP.Types
import qualified Language.LSP.Types                    as LSP

import           Control.Concurrent.STM.Stats          (atomically)
import           Control.Monad.Extra
import           Control.Monad.IO.Class
import qualified Data.HashMap.Strict                   as HM
import qualified Data.HashSet                          as S
import qualified Data.Text                             as Text
import           Development.IDE.Core.FileExists       (modifyFileExists,
                                                        watchedGlobs)
import           Development.IDE.Core.FileStore        (registerFileWatches,
                                                        resetFileStore,
                                                        setFileModified,
                                                        setSomethingModified)
import qualified Development.IDE.Core.FileStore        as FileStore
import           Development.IDE.Core.IdeConfiguration
import           Development.IDE.Core.OfInterest       hiding (Log, LogShake)
import           Development.IDE.Core.RuleTypes        (GetClientSettings (..))
import           Development.IDE.Core.Service          hiding (Log, LogShake)
import           Development.IDE.Core.Shake            hiding (Log, Priority)
import qualified Development.IDE.Core.Shake            as Shake
import           Development.IDE.Types.Location
import           Development.IDE.Types.Logger
import           Development.IDE.Types.Shake           (toKey)
import           Ide.Types

data Log
  = LogShake Shake.Log
  | LogFileStore FileStore.Log
  deriving Int -> Log -> ShowS
[Log] -> ShowS
Log -> String
(Int -> Log -> ShowS)
-> (Log -> String) -> ([Log] -> ShowS) -> Show Log
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Log] -> ShowS
$cshowList :: [Log] -> ShowS
show :: Log -> String
$cshow :: Log -> String
showsPrec :: Int -> Log -> ShowS
$cshowsPrec :: Int -> Log -> ShowS
Show

instance Pretty Log where
  pretty :: Log -> Doc ann
pretty = \case
    LogShake Log
log     -> Log -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Log
log
    LogFileStore Log
log -> Log -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Log
log

whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile Uri
uri NormalizedFilePath -> IO ()
act = Maybe String -> (String -> IO ()) -> IO ()
forall (m :: * -> *) a.
Applicative m =>
Maybe a -> (a -> m ()) -> m ()
whenJust (Uri -> Maybe String
LSP.uriToFilePath Uri
uri) ((String -> IO ()) -> IO ()) -> (String -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ NormalizedFilePath -> IO ()
act (NormalizedFilePath -> IO ())
-> (String -> NormalizedFilePath) -> String -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> NormalizedFilePath
toNormalizedFilePath'

descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
descriptor :: Recorder (WithPriority Log)
-> PluginId -> PluginDescriptor IdeState
descriptor Recorder (WithPriority Log)
recorder PluginId
plId = (PluginId -> PluginDescriptor IdeState
forall ideState. PluginId -> PluginDescriptor ideState
defaultPluginDescriptor PluginId
plId) { pluginNotificationHandlers :: PluginNotificationHandlers IdeState
pluginNotificationHandlers = [PluginNotificationHandlers IdeState]
-> PluginNotificationHandlers IdeState
forall a. Monoid a => [a] -> a
mconcat
  [ SClientMethod 'TextDocumentDidOpen
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidOpen
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'TextDocumentDidOpen
LSP.STextDocumentDidOpen (PluginNotificationMethodHandler IdeState 'TextDocumentDidOpen
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidOpen
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
vfs PluginId
_ (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
      STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ IdeState
-> VersionedTextDocumentIdentifier
-> List TextDocumentContentChangeEvent
-> STM ()
updatePositionMapping IdeState
ide (Uri -> TextDocumentVersion -> VersionedTextDocumentIdentifier
VersionedTextDocumentIdentifier Uri
_uri (Int32 -> TextDocumentVersion
forall a. a -> Maybe a
Just Int32
_version)) ([TextDocumentContentChangeEvent]
-> List TextDocumentContentChangeEvent
forall a. [a] -> List a
List [])
      Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile Uri
_uri ((NormalizedFilePath -> IO ()) -> IO ())
-> (NormalizedFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \NormalizedFilePath
file -> do
          -- We don't know if the file actually exists, or if the contents match those on disk
          -- For example, vscode restores previously unsaved contents on open
          IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO ()
addFileOfInterest IdeState
ide NormalizedFilePath
file Modified :: Bool -> FileOfInterestStatus
Modified{firstOpen :: Bool
firstOpen=Bool
True}
          Recorder (WithPriority Log)
-> VFSModified -> IdeState -> Bool -> NormalizedFilePath -> IO ()
setFileModified ((Log -> Log)
-> Recorder (WithPriority Log) -> Recorder (WithPriority Log)
forall a b.
(a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
cmapWithPrio Log -> Log
LogFileStore Recorder (WithPriority Log)
recorder) (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide Bool
False NormalizedFilePath
file
          Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Opened text document: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Uri -> Text
getUri Uri
_uri

  , SClientMethod 'TextDocumentDidChange
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidChange
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'TextDocumentDidChange
LSP.STextDocumentDidChange (PluginNotificationMethodHandler IdeState 'TextDocumentDidChange
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidChange
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
vfs PluginId
_ (DidChangeTextDocumentParams identifier@VersionedTextDocumentIdentifier{_uri} changes) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
        STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ IdeState
-> VersionedTextDocumentIdentifier
-> List TextDocumentContentChangeEvent
-> STM ()
updatePositionMapping IdeState
ide VersionedTextDocumentIdentifier
identifier List TextDocumentContentChangeEvent
changes
        Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile Uri
_uri ((NormalizedFilePath -> IO ()) -> IO ())
-> (NormalizedFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \NormalizedFilePath
file -> do
          IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO ()
addFileOfInterest IdeState
ide NormalizedFilePath
file Modified :: Bool -> FileOfInterestStatus
Modified{firstOpen :: Bool
firstOpen=Bool
False}
          Recorder (WithPriority Log)
-> VFSModified -> IdeState -> Bool -> NormalizedFilePath -> IO ()
setFileModified ((Log -> Log)
-> Recorder (WithPriority Log) -> Recorder (WithPriority Log)
forall a b.
(a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
cmapWithPrio Log -> Log
LogFileStore Recorder (WithPriority Log)
recorder) (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide Bool
False NormalizedFilePath
file
        Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Modified text document: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Uri -> Text
getUri Uri
_uri

  , SClientMethod 'TextDocumentDidSave
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidSave
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'TextDocumentDidSave
LSP.STextDocumentDidSave (PluginNotificationMethodHandler IdeState 'TextDocumentDidSave
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidSave
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
vfs PluginId
_ (DidSaveTextDocumentParams TextDocumentIdentifier{_uri} _) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
        Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile Uri
_uri ((NormalizedFilePath -> IO ()) -> IO ())
-> (NormalizedFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \NormalizedFilePath
file -> do
            IdeState -> NormalizedFilePath -> FileOfInterestStatus -> IO ()
addFileOfInterest IdeState
ide NormalizedFilePath
file FileOfInterestStatus
OnDisk
            Recorder (WithPriority Log)
-> VFSModified -> IdeState -> Bool -> NormalizedFilePath -> IO ()
setFileModified ((Log -> Log)
-> Recorder (WithPriority Log) -> Recorder (WithPriority Log)
forall a b.
(a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
cmapWithPrio Log -> Log
LogFileStore Recorder (WithPriority Log)
recorder) (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide Bool
True NormalizedFilePath
file
        Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Saved text document: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Uri -> Text
getUri Uri
_uri

  , SClientMethod 'TextDocumentDidClose
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidClose
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'TextDocumentDidClose
LSP.STextDocumentDidClose (PluginNotificationMethodHandler IdeState 'TextDocumentDidClose
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler IdeState 'TextDocumentDidClose
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
        \IdeState
ide VFS
vfs PluginId
_ (DidCloseTextDocumentParams TextDocumentIdentifier{_uri}) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
          Uri -> (NormalizedFilePath -> IO ()) -> IO ()
whenUriFile Uri
_uri ((NormalizedFilePath -> IO ()) -> IO ())
-> (NormalizedFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \NormalizedFilePath
file -> do
              IdeState -> NormalizedFilePath -> IO ()
deleteFileOfInterest IdeState
ide NormalizedFilePath
file
              let msg :: Text
msg = Text
"Closed text document: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Uri -> Text
getUri Uri
_uri
              IdeState -> IO ()
scheduleGarbageCollection IdeState
ide
              VFSModified -> IdeState -> [Key] -> String -> IO ()
setSomethingModified (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide [] (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ Text -> String
Text.unpack Text
msg
              Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) Text
msg

  , SClientMethod 'WorkspaceDidChangeWatchedFiles
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeWatchedFiles
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'WorkspaceDidChangeWatchedFiles
LSP.SWorkspaceDidChangeWatchedFiles (PluginNotificationMethodHandler
   IdeState 'WorkspaceDidChangeWatchedFiles
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeWatchedFiles
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
vfs PluginId
_ (DidChangeWatchedFilesParams (List fileEvents)) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
        -- See Note [File existence cache and LSP file watchers] which explains why we get these notifications and
        -- what we do with them
        -- filter out files of interest, since we already know all about those
        -- filter also uris that do not map to filenames, since we cannot handle them
        HashMap NormalizedFilePath FileOfInterestStatus
filesOfInterest <- IdeState -> IO (HashMap NormalizedFilePath FileOfInterestStatus)
getFilesOfInterest IdeState
ide
        let fileEvents' :: [(NormalizedFilePath, FileChangeType)]
fileEvents' =
                [ (NormalizedFilePath
nfp, FileChangeType
event) | (FileEvent Uri
uri FileChangeType
event) <- [FileEvent]
fileEvents
                , Just String
fp <- [Uri -> Maybe String
uriToFilePath Uri
uri]
                , let nfp :: NormalizedFilePath
nfp = String -> NormalizedFilePath
toNormalizedFilePath String
fp
                , Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ NormalizedFilePath
-> HashMap NormalizedFilePath FileOfInterestStatus -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
HM.member NormalizedFilePath
nfp HashMap NormalizedFilePath FileOfInterestStatus
filesOfInterest
                ]
        Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([(NormalizedFilePath, FileChangeType)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(NormalizedFilePath, FileChangeType)]
fileEvents') (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
            let msg :: String
msg = [(NormalizedFilePath, FileChangeType)] -> String
forall a. Show a => a -> String
show [(NormalizedFilePath, FileChangeType)]
fileEvents'
            Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Watched file events: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack String
msg
            IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO ()
modifyFileExists IdeState
ide [(NormalizedFilePath, FileChangeType)]
fileEvents'
            IdeState -> [(NormalizedFilePath, FileChangeType)] -> IO ()
resetFileStore IdeState
ide [(NormalizedFilePath, FileChangeType)]
fileEvents'
            VFSModified -> IdeState -> [Key] -> String -> IO ()
setSomethingModified (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide [] String
msg

  , SClientMethod 'WorkspaceDidChangeWorkspaceFolders
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeWorkspaceFolders
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'WorkspaceDidChangeWorkspaceFolders
LSP.SWorkspaceDidChangeWorkspaceFolders (PluginNotificationMethodHandler
   IdeState 'WorkspaceDidChangeWorkspaceFolders
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeWorkspaceFolders
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
_ PluginId
_ (DidChangeWorkspaceFoldersParams events) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
        let add :: HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
add       = HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
forall a. (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
S.union
            substract :: HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
substract = (HashSet NormalizedUri
 -> HashSet NormalizedUri -> HashSet NormalizedUri)
-> HashSet NormalizedUri
-> HashSet NormalizedUri
-> HashSet NormalizedUri
forall a b c. (a -> b -> c) -> b -> a -> c
flip HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
forall a. (Eq a, Hashable a) => HashSet a -> HashSet a -> HashSet a
S.difference
        IdeState
-> (HashSet NormalizedUri -> HashSet NormalizedUri) -> IO ()
modifyWorkspaceFolders IdeState
ide
          ((HashSet NormalizedUri -> HashSet NormalizedUri) -> IO ())
-> (HashSet NormalizedUri -> HashSet NormalizedUri) -> IO ()
forall a b. (a -> b) -> a -> b
$ HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
add       ((WorkspaceFolder -> HashSet NormalizedUri)
-> List WorkspaceFolder -> HashSet NormalizedUri
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (NormalizedUri -> HashSet NormalizedUri
forall a. Hashable a => a -> HashSet a
S.singleton (NormalizedUri -> HashSet NormalizedUri)
-> (WorkspaceFolder -> NormalizedUri)
-> WorkspaceFolder
-> HashSet NormalizedUri
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WorkspaceFolder -> NormalizedUri
parseWorkspaceFolder) (WorkspaceFoldersChangeEvent -> List WorkspaceFolder
_added   WorkspaceFoldersChangeEvent
events))
          (HashSet NormalizedUri -> HashSet NormalizedUri)
-> (HashSet NormalizedUri -> HashSet NormalizedUri)
-> HashSet NormalizedUri
-> HashSet NormalizedUri
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashSet NormalizedUri
-> HashSet NormalizedUri -> HashSet NormalizedUri
substract ((WorkspaceFolder -> HashSet NormalizedUri)
-> List WorkspaceFolder -> HashSet NormalizedUri
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (NormalizedUri -> HashSet NormalizedUri
forall a. Hashable a => a -> HashSet a
S.singleton (NormalizedUri -> HashSet NormalizedUri)
-> (WorkspaceFolder -> NormalizedUri)
-> WorkspaceFolder
-> HashSet NormalizedUri
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WorkspaceFolder -> NormalizedUri
parseWorkspaceFolder) (WorkspaceFoldersChangeEvent -> List WorkspaceFolder
_removed WorkspaceFoldersChangeEvent
events))

  , SClientMethod 'WorkspaceDidChangeConfiguration
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeConfiguration
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'WorkspaceDidChangeConfiguration
LSP.SWorkspaceDidChangeConfiguration (PluginNotificationMethodHandler
   IdeState 'WorkspaceDidChangeConfiguration
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler
     IdeState 'WorkspaceDidChangeConfiguration
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$
      \IdeState
ide VFS
vfs PluginId
_ (DidChangeConfigurationParams cfg) -> IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ do
        let msg :: Text
msg = String -> Text
Text.pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Value -> String
forall a. Show a => a -> String
show Value
cfg
        Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) (Text -> IO ()) -> Text -> IO ()
forall a b. (a -> b) -> a -> b
$ Text
"Configuration changed: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
msg
        IdeState -> (Maybe Value -> Maybe Value) -> IO ()
modifyClientSettings IdeState
ide (Maybe Value -> Maybe Value -> Maybe Value
forall a b. a -> b -> a
const (Maybe Value -> Maybe Value -> Maybe Value)
-> Maybe Value -> Maybe Value -> Maybe Value
forall a b. (a -> b) -> a -> b
$ Value -> Maybe Value
forall a. a -> Maybe a
Just Value
cfg)
        VFSModified -> IdeState -> [Key] -> String -> IO ()
setSomethingModified (VFS -> VFSModified
VFSModified VFS
vfs) IdeState
ide [GetClientSettings -> NormalizedFilePath -> Key
forall k. ShakeValue k => k -> NormalizedFilePath -> Key
toKey GetClientSettings
GetClientSettings NormalizedFilePath
emptyFilePath] String
"config change"

  , SClientMethod 'Initialized
-> PluginNotificationMethodHandler IdeState 'Initialized
-> PluginNotificationHandlers IdeState
forall (m :: Method 'FromClient 'Notification) ideState.
HasTracing (MessageParams m) =>
SClientMethod m
-> PluginNotificationMethodHandler ideState m
-> PluginNotificationHandlers ideState
mkPluginNotificationHandler SClientMethod 'Initialized
LSP.SInitialized (PluginNotificationMethodHandler IdeState 'Initialized
 -> PluginNotificationHandlers IdeState)
-> PluginNotificationMethodHandler IdeState 'Initialized
-> PluginNotificationHandlers IdeState
forall a b. (a -> b) -> a -> b
$ \IdeState
ide VFS
_ PluginId
_ MessageParams 'Initialized
_ -> do
      --------- Initialize Shake session --------------------------------------------------------------------
      IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ Recorder (WithPriority Log) -> IdeState -> IO ()
shakeSessionInit ((Log -> Log)
-> Recorder (WithPriority Log) -> Recorder (WithPriority Log)
forall a b.
(a -> b) -> Recorder (WithPriority b) -> Recorder (WithPriority a)
cmapWithPrio Log -> Log
LogShake Recorder (WithPriority Log)
recorder) IdeState
ide

      --------- Set up file watchers ------------------------------------------------------------------------
      IdeOptions
opts <- IO IdeOptions -> LspT Config IO IdeOptions
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO IdeOptions -> LspT Config IO IdeOptions)
-> IO IdeOptions -> LspT Config IO IdeOptions
forall a b. (a -> b) -> a -> b
$ ShakeExtras -> IO IdeOptions
getIdeOptionsIO (ShakeExtras -> IO IdeOptions) -> ShakeExtras -> IO IdeOptions
forall a b. (a -> b) -> a -> b
$ IdeState -> ShakeExtras
shakeExtras IdeState
ide
        -- See Note [Which files should we watch?] for an explanation of why the pattern is the way that it is
        -- The patterns will be something like "**/.hs", i.e. "any number of directory segments,
        -- followed by a file with an extension 'hs'.
        -- We use multiple watchers instead of one using '{}' because lsp-test doesn't
        -- support that: https://github.com/bubba/lsp-test/issues/77
      let globs :: [String]
globs = IdeOptions -> [String]
watchedGlobs IdeOptions
opts
      Bool
success <- [String] -> LspT Config IO Bool
registerFileWatches [String]
globs
      Bool -> LspT Config IO () -> LspT Config IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
success (LspT Config IO () -> LspT Config IO ())
-> LspT Config IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$
        IO () -> LspT Config IO ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> LspT Config IO ()) -> IO () -> LspT Config IO ()
forall a b. (a -> b) -> a -> b
$ Logger -> Text -> IO ()
logDebug (IdeState -> Logger
ideLogger IdeState
ide) Text
"Warning: Client does not support watched files. Falling back to OS polling"
  ]
    }