packages feed

hls-plugin-api 1.4.0.0 → 1.5.0.0

raw patch · 5 files changed

+395/−91 lines, 5 filesdep +filepathdep ~hls-graphdep ~lspPVP ok

version bump matches the API change (PVP)

Dependencies added: filepath

Dependency ranges changed: hls-graph, lsp

API changes (from Hackage documentation)

- Ide.PluginUtils: response :: Monad m => ExceptT String m a -> m (Either ResponseError a)
- Ide.Types: IdePlugins :: [(PluginId, PluginDescriptor ideState)] -> IdePlugins ideState
- Ide.Types: [ipMap] :: IdePlugins ideState -> [(PluginId, PluginDescriptor ideState)]
- Ide.Types: instance GHC.Base.Monoid (Ide.Types.IdePlugins ideState)
- Ide.Types: instance GHC.Base.Semigroup (Ide.Types.IdePlugins ideState)
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.CallHierarchyIncomingCalls
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.CallHierarchyOutgoingCalls
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.CustomMethod
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentCodeAction
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentCodeLens
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentCompletion
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentDocumentSymbol
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentFormatting
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentHover
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentPrepareCallHierarchy
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentRangeFormatting
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentRename
- Ide.Types: instance Ide.Types.PluginMethod 'Language.LSP.Types.Method.TextDocumentSelectionRange
- Ide.Types: newtype IdePlugins ideState
- Ide.Types: otSetUri :: SpanInFlight -> Uri -> IO ()
- Ide.Types: pROCESS_ID :: Text
- Ide.Types: type PluginNotificationMethodHandler a m = a -> VFS -> PluginId -> MessageParams m -> LspM Config ()
+ Ide.PluginUtils: pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a)
+ Ide.PluginUtils: throwPluginError :: Monad m => String -> ExceptT String m b
+ Ide.Types: [pluginFileType] :: PluginDescriptor (ideState :: *) -> [Text]
+ Ide.Types: [pluginPriority] :: PluginDescriptor (ideState :: *) -> Natural
+ Ide.Types: class PluginMethod Request m => PluginRequestMethod (m :: Method FromClient Request)
+ Ide.Types: data IdePlugins ideState
+ Ide.Types: defaultCabalPluginDescriptor :: PluginId -> PluginDescriptor ideState
+ Ide.Types: defaultPluginPriority :: Natural
+ Ide.Types: instance Data.Aeson.Types.FromJSON.FromJSON Ide.Types.PluginId
+ Ide.Types: instance Data.Hashable.Class.Hashable Ide.Types.PluginId
+ Ide.Types: instance GHC.Base.Monoid (Ide.Types.IdePlugins a)
+ Ide.Types: instance GHC.Base.Semigroup (Ide.Types.IdePlugins a)
+ 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.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.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.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.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: lookupCommandProvider :: IdePlugins ideState -> CommandId -> Maybe PluginId
+ Ide.Types: pattern IdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState
- Ide.Plugin.Properties: [EnumMetaData] :: IsTEnum t ~ 'True => {defaultValue :: ToHsType t, description :: Text, enumValues :: [ToHsType t], enumDescriptions :: [Text]} -> MetaData t
+ Ide.Plugin.Properties: [EnumMetaData] :: IsTEnum t ~ 'True => ToHsType t -> Text -> [ToHsType t] -> [Text] -> MetaData t
- Ide.Plugin.Properties: [MetaData] :: IsTEnum t ~ 'False => {defaultValue :: ToHsType t, description :: Text} -> MetaData t
+ Ide.Plugin.Properties: [MetaData] :: IsTEnum t ~ 'False => ToHsType t -> Text -> MetaData t
- Ide.PluginUtils: getNormalizedFilePath :: Monad m => PluginId -> TextDocumentIdentifier -> ExceptT String m NormalizedFilePath
+ Ide.PluginUtils: getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath
- Ide.PluginUtils: pluginEnabled :: PluginMethod m => SMethod m -> PluginId -> Config -> Bool
+ Ide.PluginUtils: pluginEnabled :: PluginMethod k m => SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool
- Ide.Types: PluginDescriptor :: !PluginId -> !Rules () -> ![PluginCommand ideState] -> PluginHandlers ideState -> ConfigDescriptor -> PluginNotificationHandlers ideState -> DynFlagsModifications -> Maybe (ParserInfo (IdeCommand ideState)) -> PluginDescriptor ideState
+ Ide.Types: PluginDescriptor :: !PluginId -> Natural -> !Rules () -> ![PluginCommand ideState] -> PluginHandlers ideState -> ConfigDescriptor -> PluginNotificationHandlers ideState -> DynFlagsModifications -> Maybe (ParserInfo (IdeCommand ideState)) -> [Text] -> PluginDescriptor (ideState :: *)
- Ide.Types: [pluginCli] :: PluginDescriptor ideState -> Maybe (ParserInfo (IdeCommand ideState))
+ Ide.Types: [pluginCli] :: PluginDescriptor (ideState :: *) -> Maybe (ParserInfo (IdeCommand ideState))
- Ide.Types: [pluginCommands] :: PluginDescriptor ideState -> ![PluginCommand ideState]
+ Ide.Types: [pluginCommands] :: PluginDescriptor (ideState :: *) -> ![PluginCommand ideState]
- Ide.Types: [pluginConfigDescriptor] :: PluginDescriptor ideState -> ConfigDescriptor
+ Ide.Types: [pluginConfigDescriptor] :: PluginDescriptor (ideState :: *) -> ConfigDescriptor
- Ide.Types: [pluginHandlers] :: PluginDescriptor ideState -> PluginHandlers ideState
+ Ide.Types: [pluginHandlers] :: PluginDescriptor (ideState :: *) -> PluginHandlers ideState
- Ide.Types: [pluginId] :: PluginDescriptor ideState -> !PluginId
+ Ide.Types: [pluginId] :: PluginDescriptor (ideState :: *) -> !PluginId
- Ide.Types: [pluginModifyDynflags] :: PluginDescriptor ideState -> DynFlagsModifications
+ Ide.Types: [pluginModifyDynflags] :: PluginDescriptor (ideState :: *) -> DynFlagsModifications
- Ide.Types: [pluginNotificationHandlers] :: PluginDescriptor ideState -> PluginNotificationHandlers ideState
+ Ide.Types: [pluginNotificationHandlers] :: PluginDescriptor (ideState :: *) -> PluginNotificationHandlers ideState
- Ide.Types: [pluginRules] :: PluginDescriptor ideState -> !Rules ()
+ Ide.Types: [pluginRules] :: PluginDescriptor (ideState :: *) -> !Rules ()
- Ide.Types: class HasTracing (MessageParams m) => PluginMethod m
+ Ide.Types: class HasTracing (MessageParams m) => PluginMethod (k :: MethodType) (m :: Method FromClient k)
- Ide.Types: combineResponses :: (PluginMethod m, Semigroup (ResponseResult m)) => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (ResponseResult m) -> ResponseResult m
+ Ide.Types: combineResponses :: (PluginRequestMethod m, Semigroup (ResponseResult m)) => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (ResponseResult m) -> ResponseResult m
- Ide.Types: data PluginDescriptor ideState
+ Ide.Types: data PluginDescriptor (ideState :: *)
- Ide.Types: mkPluginHandler :: PluginMethod m => SClientMethod m -> PluginMethodHandler ideState m -> PluginHandlers ideState
+ Ide.Types: mkPluginHandler :: PluginRequestMethod m => SClientMethod m -> PluginMethodHandler ideState m -> PluginHandlers ideState
- Ide.Types: mkPluginNotificationHandler :: HasTracing (MessageParams m) => SClientMethod (m :: Method FromClient Notification) -> PluginNotificationMethodHandler ideState m -> PluginNotificationHandlers ideState
+ Ide.Types: mkPluginNotificationHandler :: PluginNotificationMethod m => SClientMethod (m :: Method FromClient Notification) -> PluginNotificationMethodHandler ideState m -> PluginNotificationHandlers ideState
- Ide.Types: pluginEnabled :: PluginMethod m => SMethod m -> PluginId -> Config -> Bool
+ Ide.Types: pluginEnabled :: (PluginMethod k m, HasTextDocument (MessageParams m) doc, HasUri doc Uri) => SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool

Files

hls-plugin-api.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name:          hls-plugin-api-version:       1.4.0.0+version:       1.5.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>@@ -39,17 +39,18 @@     , containers     , data-default     , dependent-map-    , dependent-sum+    , dependent-sum         >=0.7     , Diff                  ^>=0.4.0     , dlist     , extra+    , filepath     , ghc     , hashable-    , hls-graph             ^>= 1.7+    , hls-graph             ^>= 1.8     , lens     , lens-aeson-    , lsp                   >=1.4.0.0 && < 1.6-    , opentelemetry+    , lsp                   ^>=1.6.0.0+    , opentelemetry         >=0.4     , optparse-applicative     , process     , regex-tdfa            >=1.3.1.0
src/Ide/Plugin/ConfigUtils.hs view
@@ -34,7 +34,7 @@   A.toJSON defaultConfig & ix "haskell" . _Object . at "plugin" ?~ elems   where     defaultConfig@Config {} = def-    elems = A.object $ mconcat $ singlePlugin <$> map snd ipMap+    elems = A.object $ mconcat $ singlePlugin <$> ipMap     -- Splice genericDefaultConfig and dedicatedDefaultConfig     -- Example:     --@@ -96,7 +96,7 @@ -- | Generates json schema used in haskell vscode extension -- Similar to 'pluginsToDefaultConfig' but simpler, since schema has a flatten structure pluginsToVSCodeExtensionSchema :: IdePlugins a -> A.Value-pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlugin <$> map snd ipMap+pluginsToVSCodeExtensionSchema IdePlugins {..} = A.object $ mconcat $ singlePlugin <$> ipMap   where     singlePlugin PluginDescriptor {pluginConfigDescriptor = ConfigDescriptor {..}, ..} = genericSchema <> dedicatedSchema       where
src/Ide/PluginUtils.hs view
@@ -28,22 +28,23 @@     positionInRange,     usePropertyLsp,     getNormalizedFilePath,-    response,+    pluginResponse,     handleMaybe,     handleMaybeM,+    throwPluginError,     ) where  -import           Control.Lens                    ((^.))+import           Control.Arrow                   ((&&&)) import           Control.Monad.Extra             (maybeM) import           Control.Monad.Trans.Class       (lift) import           Control.Monad.Trans.Except      (ExceptT, runExceptT, throwE) import           Data.Algorithm.Diff import           Data.Algorithm.DiffOutput import           Data.Bifunctor                  (Bifunctor (first))-import           Data.Containers.ListUtils       (nubOrdOn) import qualified Data.HashMap.Strict             as H+import           Data.List                       (find) import           Data.String                     (IsString (fromString)) import qualified Data.Text                       as T import           Ide.Plugin.Config@@ -56,7 +57,6 @@                                                   SemanticTokensEdit (_start)) import qualified Language.LSP.Types              as J import           Language.LSP.Types.Capabilities-import           Language.LSP.Types.Lens         (uri)  -- --------------------------------------------------------------------- @@ -160,11 +160,10 @@ -- ---------------------------------------------------------------------  pluginDescToIdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState-pluginDescToIdePlugins plugins =-    IdePlugins $ map (\p -> (pluginId p, p)) $ nubOrdOn pluginId plugins+pluginDescToIdePlugins = IdePlugins  idePluginsToPluginDesc :: IdePlugins ideState -> [PluginDescriptor ideState]-idePluginsToPluginDesc (IdePlugins pp) = map snd pp+idePluginsToPluginDesc (IdePlugins pp) = pp  -- --------------------------------------------------------------------- -- | Returns the current client configuration. It is not wise to permanently@@ -219,49 +218,40 @@         lastLine = fromIntegral $ length $ T.lines s  subRange :: Range -> Range -> Bool-subRange smallRange range =-     positionInRange (_start smallRange) range-  && positionInRange (_end smallRange) range--positionInRange :: Position -> Range -> Bool-positionInRange p (Range sp ep) = sp <= p && p <= ep+subRange = isSubrangeOf  -- ---------------------------------------------------------------------  allLspCmdIds' :: T.Text -> IdePlugins ideState -> [T.Text]-allLspCmdIds' pid (IdePlugins ls) = mkPlugin (allLspCmdIds pid) (Just . pluginCommands)-    where-        justs (p, Just x)  = [(p, x)]-        justs (_, Nothing) = []---        mkPlugin maker selector-            = maker $ concatMap (\(pid, p) -> justs (pid, selector p)) ls-+allLspCmdIds' pid (IdePlugins 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 => PluginId -> TextDocumentIdentifier -> ExceptT String m NormalizedFilePath-getNormalizedFilePath (PluginId plId) docId = handleMaybe errMsg+getNormalizedFilePath :: Monad m => Uri -> ExceptT String m NormalizedFilePath+getNormalizedFilePath uri = handleMaybe errMsg         $ uriToNormalizedFilePath-        $ toNormalizedUri uri'+        $ toNormalizedUri uri     where-        errMsg = T.unpack $ "Error(" <> plId <> "): converting " <> getUri uri' <> " to NormalizedFilePath"-        uri' = docId ^. uri+        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 -response :: Monad m => ExceptT String m a -> m (Either ResponseError a)-response =+pluginResponse :: Monad m => ExceptT String m a -> m (Either ResponseError a)+pluginResponse =   fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing))     . runExceptT
src/Ide/Types.hs view
@@ -9,8 +9,10 @@ {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GADTs                      #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE NamedFieldPuns             #-} {-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE PatternSynonyms            #-} {-# LANGUAGE PolyKinds                  #-} {-# LANGUAGE RecordWildCards            #-} {-# LANGUAGE ScopedTypeVariables        #-}@@ -19,6 +21,32 @@ {-# LANGUAGE ViewPatterns               #-}  module Ide.Types+( PluginDescriptor(..), defaultPluginDescriptor, defaultCabalPluginDescriptor+, defaultPluginPriority+, IdeCommand(..)+, IdeMethod(..)+, IdeNotification(..)+, IdePlugins(IdePlugins, ipMap)+, DynFlagsModifications(..)+, ConfigDescriptor(..), defaultConfigDescriptor, configForPlugin, pluginEnabledConfig+, CustomConfig(..), mkCustomConfig+, FallbackCodeActionParams(..)+, FormattingType(..), FormattingMethod, FormattingHandler, mkFormattingHandlers+, HasTracing(..)+, PluginCommand(..), CommandId(..), CommandFunction, mkLspCommand, mkLspCmdId+, PluginId(..)+, PluginHandler(..), mkPluginHandler+, PluginHandlers(..)+, PluginMethod(..)+, PluginMethodHandler+, PluginNotificationHandler(..), mkPluginNotificationHandler+, PluginNotificationHandlers(..)+, PluginRequestMethod(..)+, getProcessID, getPid+, installSigUsr1Handler+, responseError+, lookupCommandProvider+)     where  #ifdef mingw32_HOST_OS@@ -28,16 +56,22 @@ import qualified System.Posix.Process            as P (getProcessID) import           System.Posix.Signals #endif+import           Control.Arrow                   ((&&&)) import           Control.Lens                    ((^.)) import           Data.Aeson                      hiding (defaultOptions)-import qualified Data.DList                      as DList import qualified Data.Default 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                 (sortOn, find) 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@@ -67,17 +101,42 @@                                                        HasTitle (title),                                                        HasUri (..)) import           Language.LSP.VFS+import           Numeric.Natural import           OpenTelemetry.Eventlog import           Options.Applicative             (ParserInfo)+import           System.FilePath import           System.IO.Unsafe import           Text.Regex.TDFA.Text            ()+import           Control.Applicative             ((<|>))  -- --------------------------------------------------------------------- -newtype IdePlugins ideState = IdePlugins-  { ipMap :: [(PluginId, PluginDescriptor ideState)]}-  deriving newtype (Monoid, Semigroup)+data IdePlugins ideState = IdePlugins_+  { ipMap_ :: HashMap PluginId (PluginDescriptor ideState)+  , lookupCommandProvider :: CommandId -> Maybe PluginId+  } +-- | Smart constructor that deduplicates plugins+pattern IdePlugins :: [PluginDescriptor ideState] -> IdePlugins ideState+pattern IdePlugins{ipMap} <- IdePlugins_ (sortOn (Down . pluginPriority) . HashMap.elems -> ipMap) _+  where+    IdePlugins ipMap = IdePlugins_{ipMap_ = HashMap.fromList $ (pluginId &&& id) <$> ipMap+                                  , lookupCommandProvider = lookupPluginId ipMap+                                  }+{-# COMPLETE IdePlugins #-}++instance Semigroup (IdePlugins a) where+  (IdePlugins_ a f) <> (IdePlugins_ b g) = IdePlugins_ (a <> b) (\x -> f x <|> g x)++instance Monoid (IdePlugins a) where+  mempty = IdePlugins_ mempty (const Nothing)++-- | Lookup the plugin that exposes a particular command+lookupPluginId :: [PluginDescriptor a] -> CommandId -> Maybe PluginId+lookupPluginId ls cmd = pluginId <$> find go ls+  where+    go desc = cmd `elem` map commandId (pluginCommands desc)+ -- | Hooks for modifying the 'DynFlags' at different times of the compilation -- process. Plugins can install a 'DynFlagsModifications' via -- 'pluginModifyDynflags' in their 'PluginDescriptor'.@@ -108,8 +167,11 @@  -- --------------------------------------------------------------------- -data PluginDescriptor ideState =+data PluginDescriptor (ideState :: *) =   PluginDescriptor { pluginId           :: !PluginId+                   -- ^ Unique identifier of the plugin.+                   , pluginPriority     :: Natural+                   -- ^ Plugin handlers are called in priority order, higher priority first                    , pluginRules        :: !(Rules ())                    , pluginCommands     :: ![PluginCommand ideState]                    , pluginHandlers     :: PluginHandlers ideState@@ -117,13 +179,30 @@                    , pluginNotificationHandlers :: PluginNotificationHandlers ideState                    , pluginModifyDynflags :: DynFlagsModifications                    , 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+                   --   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 '.'.                    } +-- | Check whether the given plugin descriptor is responsible for the file with the given path.+--   Compares the file extension of the file at the given path with the file extension+--   the plugin is responsible for.+pluginResponsible :: Uri -> PluginDescriptor c -> Bool+pluginResponsible uri pluginDesc+    | Just fp <- mfp+    , T.pack (takeExtension fp) `elem` pluginFileType pluginDesc = True+    | otherwise = False+    where+      mfp = uriToFilePath uri+ -- | 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:+-- -- @ -- { --  "plugin-id": {@@ -136,6 +215,7 @@ --   } -- } -- @+-- -- @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'.@@ -159,12 +239,67 @@ -- | 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 m where+class HasTracing (MessageParams m) => PluginMethod (k :: MethodType) (m :: Method FromClient k) where -  -- | Parse the configuration to check if this plugin is enabled-  pluginEnabled :: SMethod m -> PluginId -> Config -> Bool+  -- | Parse the configuration to check if this plugin is enabled.+  -- Perform sanity checks on the message to see whether plugin is enabled+  -- for this message in particular.+  -- 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+  -- 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.+  --+  -- 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+  --     * 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)+  --+  -- Strictly speaking, we are conflating two concepts here:+  --   * Dynamically enabled (e.g. enabled 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.+  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+    -> PluginDescriptor c+    -- ^ Contains meta information such as PluginId and what file types this+    -- plugin is able to handle.+    -> Config+    -- ^ Generic config description, expected to hold 'PluginConfig' configuration+    -- for this plugin+    -> Bool+    -- ^ Is this plugin enabled and allowed to respond to the given request+    -- with the given parameters? -  -- | How to combine responses from different plugins+  default pluginEnabled :: (HasTextDocument (MessageParams m) doc, HasUri doc Uri)+                              => SMethod m -> MessageParams m -> PluginDescriptor c -> Config -> Bool+  pluginEnabled _ params desc conf = pluginResponsible uri desc && plcGlobalOn (configForPlugin conf (pluginId desc))+    where+        uri = params ^. J.textDocument . J.uri++-- ---------------------------------------------------------------------+-- Plugin Requests+-- ---------------------------------------------------------------------++class PluginMethod Request m => PluginRequestMethod (m :: Method FromClient 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+  -- glorious hover box.+  --+  -- However, 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@@ -176,12 +311,16 @@     => SMethod m -> Config -> ClientCapabilities -> MessageParams m -> NonEmpty (ResponseResult m) -> ResponseResult m   combineResponses _method _config _caps _params = sconcat -instance PluginMethod TextDocumentCodeAction where-  pluginEnabled _ = pluginEnabledConfig plcCodeActionsOn+instance PluginMethod Request TextDocumentCodeAction where+  pluginEnabled _ msgParams pluginDesc config =+    pluginResponsible uri pluginDesc && pluginEnabledConfig plcCodeActionsOn (pluginId pluginDesc) config+    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)@@ -205,12 +344,124 @@         , Just caKind <- ca ^. kind = any (\k -> k `codeActionKindSubsumes` caKind) allowed         | otherwise = False -instance PluginMethod TextDocumentCodeLens where-  pluginEnabled _ = pluginEnabledConfig plcCodeLensOn-instance PluginMethod TextDocumentRename where-  pluginEnabled _ = pluginEnabledConfig plcRenameOn-instance PluginMethod TextDocumentHover where-  pluginEnabled _ = pluginEnabledConfig plcHoverOn+instance PluginMethod Request TextDocumentDefinition where+  pluginEnabled _ msgParams pluginDesc _ =+    pluginResponsible uri pluginDesc+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentTypeDefinition where+  pluginEnabled _ msgParams pluginDesc _ =+    pluginResponsible uri pluginDesc+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentDocumentHighlight where+  pluginEnabled _ msgParams pluginDesc _ =+    pluginResponsible uri pluginDesc+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentReferences where+  pluginEnabled _ msgParams pluginDesc _ =+    pluginResponsible uri pluginDesc+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request WorkspaceSymbol where+  -- Unconditionally enabled, but should it really be?+  pluginEnabled _ _ _ _ = True++instance PluginMethod Request TextDocumentCodeLens where+  pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcCodeLensOn (pluginId pluginDesc) config+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentRename where+  pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcRenameOn (pluginId pluginDesc) config+   where+      uri = msgParams ^. J.textDocument . J.uri+instance PluginMethod Request TextDocumentHover where+  pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcHoverOn (pluginId pluginDesc) config+   where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentDocumentSymbol where+  pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcSymbolsOn (pluginId pluginDesc) config+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentCompletion where+  pluginEnabled _ msgParams pluginDesc config = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcCompletionOn (pluginId pluginDesc) config+    where+      uri = msgParams ^. J.textDocument . J.uri++instance PluginMethod Request TextDocumentFormatting where+  pluginEnabled STextDocumentFormatting msgParams pluginDesc conf =+    pluginResponsible uri pluginDesc && PluginId (formattingProvider conf) == pid+    where+      uri = msgParams ^. J.textDocument . J.uri+      pid = pluginId pluginDesc++instance PluginMethod Request TextDocumentRangeFormatting where+  pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc+      && PluginId (formattingProvider conf) == pid+    where+      uri = msgParams ^. J.textDocument . J.uri+      pid = pluginId pluginDesc++instance PluginMethod Request TextDocumentPrepareCallHierarchy where+  pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcCallHierarchyOn pid conf+    where+      uri = msgParams ^. J.textDocument . J.uri+      pid = pluginId pluginDesc++instance PluginMethod Request TextDocumentSelectionRange where+  pluginEnabled _ msgParams pluginDesc conf = pluginResponsible uri pluginDesc+      && pluginEnabledConfig plcSelectionRangeOn pid conf+    where+      uri = msgParams ^. J.textDocument . J.uri+      pid = pluginId pluginDesc++instance PluginMethod Request CallHierarchyIncomingCalls where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'+  pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn pid conf+    where+      pid = pluginId pluginDesc++instance PluginMethod Request CallHierarchyOutgoingCalls where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'+  pluginEnabled _ _ pluginDesc conf = pluginEnabledConfig plcCallHierarchyOn pid conf+    where+      pid = pluginId pluginDesc++instance PluginMethod Request CustomMethod where+  pluginEnabled _ _ _ _ = True++---+instance PluginRequestMethod TextDocumentDefinition where+  combineResponses _ _ _ _ (x :| _) = x++instance PluginRequestMethod TextDocumentTypeDefinition where+  combineResponses _ _ _ _ (x :| _) = x++instance PluginRequestMethod TextDocumentDocumentHighlight where++instance PluginRequestMethod TextDocumentReferences where++instance PluginRequestMethod WorkspaceSymbol where++instance PluginRequestMethod TextDocumentCodeLens where++instance PluginRequestMethod TextDocumentRename where++instance PluginRequestMethod TextDocumentHover where   combineResponses _ _ _ _ (catMaybes . toList -> hs) = h     where       r = listToMaybe $ mapMaybe (^. range) hs@@ -218,8 +469,7 @@             HoverContentsMS (List []) -> Nothing             hh                        -> Just $ Hover hh r -instance PluginMethod TextDocumentDocumentSymbol where-  pluginEnabled _ = pluginEnabledConfig plcSymbolsOn+instance PluginRequestMethod TextDocumentDocumentSymbol where   combineResponses _ _ (ClientCapabilities _ tdc _ _ _) params xs = res     where       uri' = params ^. textDocument . uri@@ -240,8 +490,7 @@             si = SymbolInformation name' (ds ^. kind) Nothing (ds ^. deprecated) loc parent         in [si] <> children' -instance PluginMethod TextDocumentCompletion where-  pluginEnabled _ = pluginEnabledConfig plcCompletionOn+instance PluginRequestMethod TextDocumentCompletion where   combineResponses _ conf _ _ (toList -> xs) = snd $ consumeCompletionResponse limit $ combine xs       where         limit = maxCompletions conf@@ -269,42 +518,85 @@         consumeCompletionResponse n (InL (List xx)) =           consumeCompletionResponse n (InR (CompletionList isCompleteResponse (List xx))) -instance PluginMethod TextDocumentFormatting where-  pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid+instance PluginRequestMethod TextDocumentFormatting where   combineResponses _ _ _ _ (x :| _) = x -instance PluginMethod TextDocumentRangeFormatting where-  pluginEnabled _ pid conf = (PluginId $ formattingProvider conf) == pid+instance PluginRequestMethod TextDocumentRangeFormatting where   combineResponses _ _ _ _ (x :| _) = x -instance PluginMethod TextDocumentPrepareCallHierarchy where-  pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn+instance PluginRequestMethod TextDocumentPrepareCallHierarchy where -instance PluginMethod TextDocumentSelectionRange where-  pluginEnabled _ = pluginEnabledConfig plcSelectionRangeOn+instance PluginRequestMethod TextDocumentSelectionRange where   combineResponses _ _ _ _ (x :| _) = x -instance PluginMethod CallHierarchyIncomingCalls where-  pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn+instance PluginRequestMethod CallHierarchyIncomingCalls where -instance PluginMethod CallHierarchyOutgoingCalls where-  pluginEnabled _ = pluginEnabledConfig plcCallHierarchyOn+instance PluginRequestMethod CallHierarchyOutgoingCalls where -instance PluginMethod CustomMethod where-  pluginEnabled _ _ _ = True+instance PluginRequestMethod CustomMethod where   combineResponses _ _ _ _ (x :| _) = x  -- ---------------------------------------------------------------------+-- 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+++instance PluginMethod Notification TextDocumentDidOpen where++instance PluginMethod Notification TextDocumentDidChange where++instance PluginMethod Notification TextDocumentDidSave where++instance PluginMethod Notification TextDocumentDidClose where++instance PluginMethod Notification WorkspaceDidChangeWatchedFiles where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'.+  pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)++instance PluginMethod Notification WorkspaceDidChangeWorkspaceFolders where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'.+  pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)++instance PluginMethod Notification WorkspaceDidChangeConfiguration where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'.+  pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)++instance PluginMethod Notification Initialized where+  -- This method has no URI parameter, thus no call to 'pluginResponsible'.+  pluginEnabled _ _ desc conf = plcGlobalOn $ configForPlugin conf (pluginId desc)+++instance PluginNotificationMethod TextDocumentDidOpen where++instance PluginNotificationMethod TextDocumentDidChange where++instance PluginNotificationMethod TextDocumentDidSave where++instance PluginNotificationMethod TextDocumentDidClose where++instance PluginNotificationMethod WorkspaceDidChangeWatchedFiles where++instance PluginNotificationMethod WorkspaceDidChangeWorkspaceFolders where++instance PluginNotificationMethod WorkspaceDidChangeConfiguration where++instance PluginNotificationMethod Initialized where++-- ---------------------------------------------------------------------+ -- | Methods which have a PluginMethod instance-data IdeMethod (m :: Method FromClient Request) = PluginMethod m => IdeMethod (SMethod m)+data IdeMethod (m :: Method FromClient 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) = HasTracing (MessageParams m) => IdeNotification (SMethod m)+data IdeNotification (m :: Method FromClient Notification) = PluginNotificationMethod m => IdeNotification (SMethod m) instance GEq IdeNotification where   geq (IdeNotification a) (IdeNotification b) = geq a b instance GCompare IdeNotification where@@ -343,7 +635,7 @@  -- | Make a handler for plugins with no extra data mkPluginHandler-  :: PluginMethod m+  :: PluginRequestMethod m   => SClientMethod m   -> PluginMethodHandler ideState m   -> PluginHandlers ideState@@ -353,7 +645,7 @@  -- | Make a handler for plugins with no extra data mkPluginNotificationHandler-  :: HasTracing (MessageParams m)+  :: PluginNotificationMethod m   => SClientMethod (m :: Method FromClient Notification)   -> PluginNotificationMethodHandler ideState m   -> PluginNotificationHandlers ideState@@ -362,10 +654,23 @@   where     f' pid ide vfs = f ide vfs pid +defaultPluginPriority :: Natural+defaultPluginPriority = 1000++-- | Set up a plugin descriptor, initialized with default values.+-- This is plugin descriptor is prepared for @haskell@ files, such as+--+--   * @.hs@+--   * @.lhs@+--   * @.hs-boot@+--+-- and handlers will be enabled for files with the appropriate file+-- extensions. defaultPluginDescriptor :: PluginId -> PluginDescriptor ideState defaultPluginDescriptor plId =   PluginDescriptor     plId+    defaultPluginPriority     mempty     mempty     mempty@@ -373,7 +678,28 @@     mempty     mempty     Nothing+    [".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,+-- will only respond / run when @.cabal@ files are currently in scope.+--+-- Handles files with the following extensions:+--   * @.cabal@+defaultCabalPluginDescriptor :: PluginId -> PluginDescriptor ideState+defaultCabalPluginDescriptor plId =+  PluginDescriptor+    plId+    defaultPluginPriority+    mempty+    mempty+    mempty+    defaultConfigDescriptor+    mempty+    mempty+    Nothing+    [".cabal"]+ newtype CommandId = CommandId T.Text   deriving (Show, Read, Eq, Ord) instance IsString CommandId where@@ -396,6 +722,7 @@  newtype PluginId = PluginId T.Text   deriving (Show, Read, Eq, Ord)+  deriving newtype (FromJSON, Hashable)  instance IsString PluginId where   fromString = PluginId . T.pack
test/Ide/PluginUtilsTest.hs view
@@ -9,19 +9,5 @@  tests :: TestTree tests = testGroup "PluginUtils"-    [ positionInRangeTest-    ]--positionInRangeTest :: TestTree-positionInRangeTest = testGroup "positionInRange"-    [ testCase "single line, after the end" $-        positionInRange (Position 1 10) (Range (Position 1 1) (Position 1 3)) @?= False-    , testCase "single line, before the begining" $-        positionInRange (Position 1 0) (Range (Position 1 1) (Position 1 6)) @?= False-    , testCase "single line, in range" $-        positionInRange (Position 1 5) (Range (Position 1 1) (Position 1 6)) @?= True-    , testCase "multiline, in range" $-        positionInRange (Position 3 5) (Range (Position 1 1) (Position 5 6)) @?= True-    , testCase "multiline, out of range" $-        positionInRange (Position 3 5) (Range (Position 3 6) (Position 4 10)) @?= False+    [     ]