packages feed

hls-cabal-plugin 2.1.0.0 → 2.2.0.0

raw patch · 13 files changed

+297/−235 lines, 13 filesdep ~ghcidedep ~hls-graphdep ~hls-plugin-apiPVP ok

version bump matches the API change (PVP)

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

API changes (from Hackage documentation)

- Ide.Plugin.Cabal.Completion.Completer.FilePath: PathCompletionInfo :: Text -> FilePath -> FilePath -> Maybe Apostrophe -> PathCompletionInfo
- Ide.Plugin.Cabal.Completion.Completer.FilePath: [isStringNotationPath] :: PathCompletionInfo -> Maybe Apostrophe
- Ide.Plugin.Cabal.Completion.Completer.FilePath: [pathSegment] :: PathCompletionInfo -> Text
- Ide.Plugin.Cabal.Completion.Completer.FilePath: [queryDirectory] :: PathCompletionInfo -> FilePath
- Ide.Plugin.Cabal.Completion.Completer.FilePath: [workingDirectory] :: PathCompletionInfo -> FilePath
- Ide.Plugin.Cabal.Completion.Completer.FilePath: data PathCompletionInfo
- Ide.Plugin.Cabal.Completion.Completer.FilePath: instance GHC.Classes.Eq Ide.Plugin.Cabal.Completion.Completer.FilePath.PathCompletionInfo
- Ide.Plugin.Cabal.Completion.Completer.FilePath: instance GHC.Show.Show Ide.Plugin.Cabal.Completion.Completer.FilePath.PathCompletionInfo
- Ide.Plugin.Cabal.Completion.Completer.FilePath: pathCompletionInfoFromCabalPrefixInfo :: CabalPrefixInfo -> PathCompletionInfo
- Ide.Plugin.Cabal.Completion.Completer.Module: extractRelativeDirsFromStanza :: Maybe StanzaName -> GenericPackageDescription -> (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) -> (a -> BuildInfo) -> [FilePath]
- Ide.Plugin.Cabal.Completion.Completer.Module: sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
- Ide.Plugin.Cabal.Completion.Completer.Module: sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
- Ide.Plugin.Cabal.Completion.Completer.Module: sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
- Ide.Plugin.Cabal.Completion.Completer.Module: sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
+ Ide.Plugin.Cabal.Completion.Completer.FilePath: mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer
+ Ide.Plugin.Cabal.Completion.Completer.Paths: PathCompletionInfo :: Text -> FilePath -> FilePath -> Maybe Apostrophe -> PathCompletionInfo
+ Ide.Plugin.Cabal.Completion.Completer.Paths: [isStringNotationPath] :: PathCompletionInfo -> Maybe Apostrophe
+ Ide.Plugin.Cabal.Completion.Completer.Paths: [pathSegment] :: PathCompletionInfo -> Text
+ Ide.Plugin.Cabal.Completion.Completer.Paths: [queryDirectory] :: PathCompletionInfo -> FilePath
+ Ide.Plugin.Cabal.Completion.Completer.Paths: [workingDirectory] :: PathCompletionInfo -> FilePath
+ Ide.Plugin.Cabal.Completion.Completer.Paths: data PathCompletionInfo
+ Ide.Plugin.Cabal.Completion.Completer.Paths: extractRelativeDirsFromStanza :: Maybe StanzaName -> GenericPackageDescription -> (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) -> (a -> BuildInfo) -> [FilePath]
+ Ide.Plugin.Cabal.Completion.Completer.Paths: instance GHC.Classes.Eq Ide.Plugin.Cabal.Completion.Completer.Paths.PathCompletionInfo
+ Ide.Plugin.Cabal.Completion.Completer.Paths: instance GHC.Show.Show Ide.Plugin.Cabal.Completion.Completer.Paths.PathCompletionInfo
+ Ide.Plugin.Cabal.Completion.Completer.Paths: pathCompletionInfoFromCabalPrefixInfo :: FilePath -> CabalPrefixInfo -> PathCompletionInfo
+ Ide.Plugin.Cabal.Completion.Completer.Paths: sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
+ Ide.Plugin.Cabal.Completion.Completer.Paths: sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
+ Ide.Plugin.Cabal.Completion.Completer.Paths: sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]
+ Ide.Plugin.Cabal.Completion.Completer.Paths: sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]

Files

hls-cabal-plugin.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               hls-cabal-plugin-version:            2.1.0.0+version:            2.2.0.0 synopsis:           Cabal integration plugin with Haskell Language Server description:   Please see the README on GitHub at <https://github.com/haskell/haskell-language-server#readme>@@ -29,6 +29,7 @@     Ide.Plugin.Cabal.Diagnostics     Ide.Plugin.Cabal.Completion.Completer.FilePath     Ide.Plugin.Cabal.Completion.Completer.Module+    Ide.Plugin.Cabal.Completion.Completer.Paths     Ide.Plugin.Cabal.Completion.Completer.Simple     Ide.Plugin.Cabal.Completion.Completer.Snippet     Ide.Plugin.Cabal.Completion.Completer.Types@@ -48,13 +49,13 @@     , directory     , filepath     , extra                 >=1.7.4-    , ghcide                == 2.1.0.0+    , ghcide                == 2.2.0.0     , hashable-    , hls-plugin-api        == 2.1.0.0-    , hls-graph             == 2.1.0.0+    , hls-plugin-api        == 2.2.0.0+    , hls-graph             == 2.2.0.0     , lens-    , lsp                   ^>=2.1.0.0-    , lsp-types             ^>=2.0.1.0+    , lsp                   ^>=2.2+    , lsp-types             ^>=2.0.2     , regex-tdfa            ^>=1.3.1     , stm     , text@@ -83,7 +84,7 @@     , filepath     , ghcide     , hls-cabal-plugin-    , hls-test-utils    == 2.1.0.0+    , hls-test-utils    == 2.2.0.0     , lens     , lsp     , lsp-types
src/Ide/Plugin/Cabal.hs view
@@ -75,7 +75,7 @@       "Determined completion context:"         <+> viaShow context         <+> "for cursor position:"-        <+> viaShow position+        <+> pretty position     LogCompletions logs -> pretty logs  descriptor :: Recorder (WithPriority Log) -> PluginId -> PluginDescriptor IdeState
src/Ide/Plugin/Cabal/Completion/Completer/FilePath.hs view
@@ -5,9 +5,11 @@  import           Control.Exception                            (evaluate, try) import           Control.Monad                                (filterM)-import           Control.Monad.Extra                          (forM)+import           Control.Monad.Extra                          (concatForM, forM) import qualified Data.Text                                    as T+import           Distribution.PackageDescription              (GenericPackageDescription) import           Ide.Logger+import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completer.Simple import           Ide.Plugin.Cabal.Completion.Completer.Types import           Ide.Plugin.Cabal.Completion.Types@@ -23,7 +25,7 @@ filePathCompleter :: Completer filePathCompleter recorder cData = do   let prefInfo = cabalPrefixInfo cData-      complInfo = pathCompletionInfoFromCabalPrefixInfo prefInfo+      complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo   filePathCompletions <- listFileCompletions recorder complInfo   let scored =         Fuzzy.simpleFilter@@ -39,12 +41,44 @@         pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath     ) +mainIsCompleter :: (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> Completer+mainIsCompleter extractionFunction recorder cData = do+  mGPD <- getLatestGPD cData+  case mGPD of+    Just gpd -> do+      let srcDirs = extractionFunction sName gpd+      concatForM srcDirs+        (\dir' -> do+        let dir = FP.normalise dir'+        let pathInfo = pathCompletionInfoFromCabalPrefixInfo dir prefInfo+        completions <- listFileCompletions recorder pathInfo+        let scored = Fuzzy.simpleFilter+              Fuzzy.defChunkSize+              Fuzzy.defMaxResults+              (pathSegment pathInfo)+              (map T.pack completions)+        forM+          scored+          ( \compl' -> do+              let compl = Fuzzy.original compl'+              fullFilePath <- mkFilePathCompletion pathInfo compl+              pure $ mkCompletionItem (completionRange prefInfo) fullFilePath fullFilePath+          )+        )+    Nothing -> do+      logWith recorder Debug LogUseWithStaleFastNoResult+      pure []+  where+    sName = stanzaName cData+    prefInfo = cabalPrefixInfo cData++ -- | Completer to be used when a directory can be completed for the field. --  Only completes directories. directoryCompleter :: Completer directoryCompleter recorder cData = do   let prefInfo = cabalPrefixInfo cData-      complInfo = pathCompletionInfoFromCabalPrefixInfo prefInfo+      complInfo = pathCompletionInfoFromCabalPrefixInfo "" prefInfo   directoryCompletions <- listDirectoryCompletions recorder complInfo   let scored =         Fuzzy.simpleFilter@@ -73,33 +107,6 @@   be used for file path completions to be written to the cabal file. -} --- | Information used to query and build path completions.------  Note that pathSegment  combined with queryDirectory  results in---  the original prefix.------  Example:---  When given the written prefix, @dir1\/dir2\/fi@, the---  resulting PathCompletionInfo would be:------  @---    pathSegment = "fi"---    queryDirectory  = "dir1\/dir2\/fi"---    ...---  @-data PathCompletionInfo = PathCompletionInfo-  { -- | partly written segment of the next part of the path-    pathSegment          :: T.Text,-    -- | written part of path, platform dependent-    queryDirectory       :: FilePath,-    -- | directory relative to which relative paths are interpreted, platform dependent-    workingDirectory     :: FilePath,-    -- | Did the completion happen in the context of a string notation,-    -- if yes, contains the state of the string notation-    isStringNotationPath :: Maybe Apostrophe-  }-  deriving (Eq, Show)- -- | Takes a PathCompletionInfo and returns the list of files and directories --  in the directory which match the path completion info in posix style. --@@ -125,18 +132,6 @@ listDirectoryCompletions recorder complInfo = do   filepaths <- listFileCompletions recorder complInfo   filterM (doesDirectoryExist . mkDirFromCWD complInfo) filepaths--pathCompletionInfoFromCabalPrefixInfo :: CabalPrefixInfo -> PathCompletionInfo-pathCompletionInfoFromCabalPrefixInfo ctx =-  PathCompletionInfo-    { pathSegment = T.pack pathSegment',-      queryDirectory = queryDirectory',-      workingDirectory = completionWorkingDir ctx,-      isStringNotationPath = isStringNotation ctx-    }-  where-    prefix = T.unpack $ completionPrefix ctx-    (queryDirectory', pathSegment') = Posix.splitFileName prefix  -- | Returns the directory where files and directories can be queried from --  for the passed PathCompletionInfo.
src/Ide/Plugin/Cabal/Completion/Completer/Module.hs view
@@ -6,26 +6,16 @@ import           Control.Monad.Extra                            (concatForM,                                                                  forM) import           Data.List                                      (stripPrefix)-import qualified Data.List                                      as List import           Data.Maybe                                     (fromMaybe) import qualified Data.Text                                      as T-import           Distribution.PackageDescription                (Benchmark (..),-                                                                 BuildInfo (..),-                                                                 CondTree (condTreeData),-                                                                 Executable (..),-                                                                 GenericPackageDescription (..),-                                                                 Library (..),-                                                                 UnqualComponentName,-                                                                 mkUnqualComponentName,-                                                                 testBuildInfo)-import           Distribution.Utils.Path                        (getSymbolicPath)+import           Distribution.PackageDescription                (GenericPackageDescription) import           Ide.Logger                                     (Priority (..),                                                                  Recorder,                                                                  WithPriority,                                                                  logWith)-import           Ide.Plugin.Cabal.Completion.Completer.FilePath (PathCompletionInfo (..),-                                                                 listFileCompletions,+import           Ide.Plugin.Cabal.Completion.Completer.FilePath (listFileCompletions,                                                                  mkCompletionDirectory)+import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completer.Simple import           Ide.Plugin.Cabal.Completion.Completer.Types import           Ide.Plugin.Cabal.Completion.Types@@ -53,56 +43,6 @@     sName = stanzaName cData     prefInfo = cabalPrefixInfo cData --- | Extracts the source directories of the library stanza.-sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]-sourceDirsExtractionLibrary Nothing gpd =-  -- we use condLibrary to get the information contained in the library stanza-  -- since the library in PackageDescription is not populated by us-  case libM of-    Just lib -> do-      map getSymbolicPath $ hsSourceDirs $ libBuildInfo $ condTreeData lib-    Nothing -> []-  where-    libM = condLibrary gpd-sourceDirsExtractionLibrary name gpd = extractRelativeDirsFromStanza name gpd condSubLibraries libBuildInfo---- | Extracts the source directories of the executable stanza with the given name.-sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]-sourceDirsExtractionExecutable name gpd = extractRelativeDirsFromStanza name gpd condExecutables buildInfo---- | Extracts the source directories of the test suite stanza with the given name.-sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]-sourceDirsExtractionTestSuite name gpd = extractRelativeDirsFromStanza name gpd condTestSuites testBuildInfo---- | Extracts the source directories of benchmark stanza with the given name.-sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]-sourceDirsExtractionBenchmark name gpd = extractRelativeDirsFromStanza name gpd condBenchmarks benchmarkBuildInfo---- | Takes a possible stanza name, a GenericPackageDescription,---  a function to access the stanza information we are interested in---  and a function to access the build info from the specific stanza.------  Returns a list of relative source directory paths specified for the extracted stanza.-extractRelativeDirsFromStanza ::-  Maybe StanzaName ->-  GenericPackageDescription ->-  (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) ->-  (a -> BuildInfo) ->-  [FilePath]-extractRelativeDirsFromStanza Nothing _ _ _ = []-extractRelativeDirsFromStanza (Just name) gpd getStanza getBuildInfo-  | Just stanza <- stanzaM = map getSymbolicPath $ hsSourceDirs $ getBuildInfo stanza-  | otherwise = []-  where-    stanzaM = fmap (condTreeData . snd) res-    allStanzasM = getStanza gpd-    res =-      List.find-        ( \(n, _) ->-            n == mkUnqualComponentName (T.unpack name)-        )-        allStanzasM- -- | Takes a list of source directories and returns a list of path completions --  relative to any of the passed source directories which fit the passed prefix info. filePathsForExposedModules :: Recorder (WithPriority Log) -> [FilePath] -> CabalPrefixInfo -> IO [T.Text]@@ -111,34 +51,36 @@     srcDirs     ( \dir' -> do         let dir = FP.normalise dir'-        let pInfo =-              PathCompletionInfo-                { isStringNotationPath = Nothing,-                  pathSegment = T.pack $ FP.takeFileName prefix,-                  queryDirectory = FP.addTrailingPathSeparator $ FP.takeDirectory prefix,-                  workingDirectory = completionWorkingDir prefInfo FP.</> dir-                }-        completions <- listFileCompletions recorder pInfo-        validExposedCompletions <- filterM (isValidExposedModulePath pInfo) completions-        let toMatch = pathSegment pInfo-            scored = Fuzzy.simpleFilter Fuzzy.defChunkSize Fuzzy.defMaxResults toMatch (map T.pack validExposedCompletions)+            pathInfo = pathCompletionInfoFromCabalPrefixInfo dir modPrefInfo+        completions <- listFileCompletions recorder pathInfo+        validExposedCompletions <- filterM (isValidExposedModulePath pathInfo) completions+        let toMatch = pathSegment pathInfo+            scored = Fuzzy.simpleFilter+              Fuzzy.defChunkSize+              Fuzzy.defMaxResults+              toMatch+              (map T.pack validExposedCompletions)         forM           scored           ( \compl' -> do               let compl = Fuzzy.original compl'-              fullFilePath <- mkExposedModulePathCompletion pInfo $ T.unpack compl+              fullFilePath <- mkExposedModulePathCompletion pathInfo $ T.unpack compl               pure fullFilePath           )     )   where     prefix =-      exposedModulePathToFp $+      T.pack $ exposedModulePathToFp $         completionPrefix prefInfo-    -- \| Takes a PathCompletionInfo and a path segment and checks whether+    -- build completion info relative to the source dir,+    -- we overwrite the prefix written in the cabal file with its translation+    -- to filepath syntax, since it is in exposed module syntax+    modPrefInfo = prefInfo{completionPrefix=prefix}++    --    Takes a PathCompletionInfo and a path segment and checks whether     --    the path segment can be completed for an exposed module.     --     --    This is the case if the segment represents either a directory or a Haskell file.-    --     isValidExposedModulePath :: PathCompletionInfo -> FilePath -> IO Bool     isValidExposedModulePath pInfo path = do       let dir = mkCompletionDirectory pInfo
+ src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs view
@@ -0,0 +1,118 @@+module Ide.Plugin.Cabal.Completion.Completer.Paths where++import qualified Data.List                         as List+import qualified Data.Text                         as T+import           Distribution.PackageDescription   (Benchmark (..),+                                                    BuildInfo (..),+                                                    CondTree (condTreeData),+                                                    Executable (..),+                                                    GenericPackageDescription (..),+                                                    Library (..),+                                                    UnqualComponentName,+                                                    mkUnqualComponentName,+                                                    testBuildInfo)+import           Distribution.Utils.Path           (getSymbolicPath)+import           Ide.Plugin.Cabal.Completion.Types+import qualified System.FilePath                   as FP+import qualified System.FilePath.Posix             as Posix+++{- | Information used to query and build path completions.++  Note that pathSegment  combined with queryDirectory  results in+  the original prefix.++  Example:+  When given the written prefix, @dir1\/dir2\/fi@, the+  resulting PathCompletionInfo would be:++  @+    pathSegment = "fi"+    queryDirectory  = "dir1\/dir2\/fi"+    ...+  @+-}+data PathCompletionInfo = PathCompletionInfo+  { pathSegment          :: T.Text,+    -- ^ Partly written segment of the next part of the path.+    queryDirectory       :: FilePath,+    -- ^ Written part of path, in posix format.+    workingDirectory     :: FilePath,+    -- ^ Directory relative to which relative paths are interpreted, platform dependent.+    isStringNotationPath :: Maybe Apostrophe+    -- ^ Did the completion happen in the context of a string notation,+    -- if yes, contains the state of the string notation.+  }+  deriving (Eq, Show)++{- | Takes an optional source subdirectory and a prefix info+  and creates a path completion info accordingly.++  The source directory represents some subdirectory of the working directory such as a+  path from the field @hs-source-dirs@.++  If the source subdirectory is empty, then the working directory is simply set to+  the currently handled cabal file's directory.+-}+pathCompletionInfoFromCabalPrefixInfo :: FilePath -> CabalPrefixInfo -> PathCompletionInfo+pathCompletionInfoFromCabalPrefixInfo srcDir prefInfo =+  PathCompletionInfo+    { pathSegment = T.pack pathSegment',+      queryDirectory = queryDirectory',+      workingDirectory = completionWorkingDir prefInfo FP.</> srcDir,+      isStringNotationPath = isStringNotation prefInfo+    }+  where+    prefix = T.unpack $ completionPrefix prefInfo+    (queryDirectory', pathSegment') = Posix.splitFileName prefix++-- | Extracts the source directories of the library stanza.+sourceDirsExtractionLibrary :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]+sourceDirsExtractionLibrary Nothing gpd =+  -- we use condLibrary to get the information contained in the library stanza+  -- since the library in PackageDescription is not populated by us+  case libM of+    Just lib -> do+      map getSymbolicPath $ hsSourceDirs $ libBuildInfo $ condTreeData lib+    Nothing -> []+  where+    libM = condLibrary gpd+sourceDirsExtractionLibrary name gpd = extractRelativeDirsFromStanza name gpd condSubLibraries libBuildInfo++-- | Extracts the source directories of the executable stanza with the given name.+sourceDirsExtractionExecutable :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]+sourceDirsExtractionExecutable name gpd = extractRelativeDirsFromStanza name gpd condExecutables buildInfo++-- | Extracts the source directories of the test suite stanza with the given name.+sourceDirsExtractionTestSuite :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]+sourceDirsExtractionTestSuite name gpd = extractRelativeDirsFromStanza name gpd condTestSuites testBuildInfo++-- | Extracts the source directories of benchmark stanza with the given name.+sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath]+sourceDirsExtractionBenchmark name gpd = extractRelativeDirsFromStanza name gpd condBenchmarks benchmarkBuildInfo++{- | Takes a possible stanza name, a GenericPackageDescription,+  a function to access the stanza information we are interested in+  and a function to access the build info from the specific stanza.++  Returns a list of relative source directory paths specified for the extracted stanza.+-}+extractRelativeDirsFromStanza ::+  Maybe StanzaName ->+  GenericPackageDescription ->+  (GenericPackageDescription -> [(UnqualComponentName, CondTree b c a)]) ->+  (a -> BuildInfo) ->+  [FilePath]+extractRelativeDirsFromStanza Nothing _ _ _ = []+extractRelativeDirsFromStanza (Just name) gpd getStanza getBuildInfo+  | Just stanza <- stanzaM = map getSymbolicPath $ hsSourceDirs $ getBuildInfo stanza+  | otherwise = []+  where+    stanzaM = fmap (condTreeData . snd) res+    allStanzasM = getStanza gpd+    res =+      List.find+        ( \(n, _) ->+            n == mkUnqualComponentName (T.unpack name)+        )+        allStanzasM
src/Ide/Plugin/Cabal/Completion/Completions.hs view
@@ -49,7 +49,7 @@ contextToCompleter (Stanza s _, None) =   case Map.lookup s stanzaKeywordMap of     Nothing -> errorNoopCompleter (LogUnknownStanzaNameInContextError s)-    Just l  -> constantCompleter $ Map.keys l ++ Map.keys stanzaKeywordMap+    Just l  -> constantCompleter $ Map.keys l -- if we are in a stanza's keyword's context we can complete possible values of that keyword contextToCompleter (Stanza s _, KeyWord kw) =   case Map.lookup s stanzaKeywordMap of
src/Ide/Plugin/Cabal/Completion/Data.hs view
@@ -14,6 +14,7 @@                                                                  showCabalSpecVersion) import           Ide.Plugin.Cabal.Completion.Completer.FilePath import           Ide.Plugin.Cabal.Completion.Completer.Module+import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completer.Simple import           Ide.Plugin.Cabal.Completion.Completer.Types    (Completer) import           Ide.Plugin.Cabal.Completion.Types@@ -92,16 +93,16 @@ executableFields :: Map KeyWordName Completer executableFields =   Map.fromList-    [ ("main-is:", filePathCompleter),+    [ ("main-is:", mainIsCompleter sourceDirsExtractionExecutable),       ("scope:", constantCompleter ["public", "private"]),-      ("other-modules:", modulesCompleter (sourceDirsExtractionExecutable))+      ("other-modules:", modulesCompleter sourceDirsExtractionExecutable)     ]  testSuiteFields :: Map KeyWordName Completer testSuiteFields =   Map.fromList     [ ("type:", constantCompleter ["exitcode-stdio-1.0", "detailed-0.9"]),-      ("main-is:", filePathCompleter),+      ("main-is:", mainIsCompleter sourceDirsExtractionTestSuite),       ("other-modules:", modulesCompleter sourceDirsExtractionTestSuite)     ] @@ -109,8 +110,8 @@ benchmarkFields =   Map.fromList     [ ("type:", noopCompleter),-      ("main-is:", filePathCompleter),-      ("other-modules:", modulesCompleter (sourceDirsExtractionBenchmark))+      ("main-is:", mainIsCompleter sourceDirsExtractionBenchmark),+      ("other-modules:", modulesCompleter sourceDirsExtractionBenchmark)     ]  foreignLibraryFields :: Map KeyWordName Completer
src/Ide/Plugin/Cabal/Completion/Types.hs view
@@ -27,15 +27,15 @@  instance Pretty Log where   pretty = \case-    LogFileSplitError pos -> "An error occured when trying to separate the lines of the cabal file at position:" <+> viaShow pos+    LogFileSplitError pos -> "An error occured when trying to separate the lines of the cabal file at position:" <+> pretty pos     LogUnknownKeyWordInContextError kw ->       "Lookup of key word failed for:" <+> viaShow kw     LogUnknownStanzaNameInContextError sn ->       "Lookup of stanza name failed for:" <+> viaShow sn     LogFilePathCompleterIOError fp ioErr ->-      "When trying to complete the file path:" <+> viaShow fp <+> "the following unexpected IO error occured" <+> viaShow ioErr+      "When trying to complete the file path:" <+> pretty fp <+> "the following unexpected IO error occured" <+> viaShow ioErr     LogUseWithStaleFastNoResult -> "Package description couldn't be read"-    LogMapLookUpOfKnownKeyFailed key -> "Lookup of key in map failed even though it should exist" <+> viaShow key+    LogMapLookUpOfKnownKeyFailed key -> "Lookup of key in map failed even though it should exist" <+> pretty key  type instance RuleResult ParseCabal = Parse.GenericPackageDescription 
test/Completer.hs view
@@ -3,15 +3,19 @@  module Completer where -import           Control.Lens                                   ((^.))+import           Control.Lens                                   ((^.), (^?))+import           Control.Lens.Prism import qualified Data.ByteString                                as ByteString+import           Data.Maybe                                     (mapMaybe) import qualified Data.Text                                      as T import           Distribution.PackageDescription.Parsec         (parseGenericPackageDescriptionMaybe) import           Ide.Plugin.Cabal.Completion.Completer.FilePath import           Ide.Plugin.Cabal.Completion.Completer.Module+import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completer.Types    (CompleterData (..)) import           Ide.Plugin.Cabal.Completion.Completions-import           Ide.Plugin.Cabal.Completion.Types+import           Ide.Plugin.Cabal.Completion.Types              (CabalPrefixInfo (..),+                                                                 StanzaName) import           Ide.Plugin.Cabal.Parse                         (GenericPackageDescription) import qualified Language.LSP.Protocol.Lens                     as L import qualified Language.LSP.VFS                               as VFS@@ -23,7 +27,8 @@ completerTests =   testGroup     "Completer Tests"-    [ fileCompleterTests,+    [ basicCompleterTests,+      fileCompleterTests,       filePathCompletionContextTests,       directoryCompleterTests,       completionHelperTests,@@ -31,41 +36,59 @@       exposedModuleCompleterTests     ] +basicCompleterTests :: TestTree+basicCompleterTests =+  testGroup+    "Basic Completer Tests"+    [ runCabalTestCaseSession "In stanza context - stanza should not be suggested" "" $ do+        doc <- openDoc "completer.cabal" "cabal"+        compls <- getCompletions doc (Position 11 7)+        let complTexts = getTextEditTexts compls+        liftIO $ assertBool "does not suggest library" $ "library" `notElem` complTexts+        liftIO $ assertBool "suggests library keyword" $ "extra-libraries:" `elem` complTexts+    , runCabalTestCaseSession "In top level context - stanza should be suggested" "" $ do+        doc <- openDoc "completer.cabal" "cabal"+        compls <- getCompletions doc (Position 8 2)+        let complTexts = getTextEditTexts compls+        liftIO $ assertBool "suggests benchmark" $ "benchmark" `elem` complTexts+    , runCabalTestCaseSession "Main-is completions should be relative to hs-source-dirs of same stanza" "filepath-completions" $ do+        doc <- openDoc "main-is.cabal" "cabal"+        compls <- getCompletions doc (Position 10 12)+        let complTexts = getTextEditTexts compls+        liftIO $ assertBool "suggests f2" $ "./f2.hs" `elem` complTexts+        liftIO $ assertBool "does not suggest" $ "./Content.hs" `notElem` complTexts+    ]+    where+      getTextEditTexts :: [CompletionItem] -> [T.Text]+      getTextEditTexts compls = mapMaybe (^? L.textEdit . _Just . _L . L.newText) compls+ fileCompleterTests :: TestTree fileCompleterTests =   testGroup     "File Completer Tests"     [ testCase "Current Directory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "" testDir-        completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt"],+        completions <- completeFilePath "" filePathComplTestDir+        completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"],       testCase "Current Directory - alternative writing" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "./" testDir-        completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt"],+        completions <- completeFilePath "./" filePathComplTestDir+        completions @?== ["./.hidden", "./Content.hs", "./dir1/", "./dir2/", "./textfile.txt", "./main-is.cabal"],       testCase "Current Directory - hidden file start" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "." testDir-        completions @?== ["./Content.hs", "./.hidden", "./textfile.txt"],+        completions <- completeFilePath "." filePathComplTestDir+        completions @?== ["./Content.hs", "./.hidden", "./textfile.txt", "./main-is.cabal"],       testCase "Current Directory - incomplete directory path written" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "di" testDir+        completions <- completeFilePath "di" filePathComplTestDir         completions @?== ["./dir1/", "./dir2/"],       testCase "Current Directory - incomplete filepath written" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "te" testDir+        completions <- completeFilePath "te" filePathComplTestDir         completions @?== ["./Content.hs", "./textfile.txt"],       testCase "Subdirectory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "dir1/" testDir+        completions <- completeFilePath "dir1/" filePathComplTestDir         completions @?== ["dir1/f1.txt", "dir1/f2.hs"],       testCase "Subdirectory - incomplete filepath written" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "dir2/dir3/MA" testDir+        completions <- completeFilePath "dir2/dir3/MA" filePathComplTestDir         completions @?== ["dir2/dir3/MARKDOWN.md"],       testCase "Nonexistent directory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeFilePath "dir2/dir4/" testDir+        completions <- completeFilePath "dir2/dir4/" filePathComplTestDir         completions @?== []     ]   where@@ -106,7 +129,6 @@         let complContext = getCabalPrefixInfo "" (simplePosPrefixInfo "\"fp.txt\" \"src fp2.txt" 0 13)         completionPrefix complContext @?= "src",       testCase "Current Directory" $ do-        testDir <- getFilePathComplTestDir         compls <-           listFileCompletions             mempty@@ -114,11 +136,10 @@               { isStringNotationPath = Nothing,                 pathSegment = "",                 queryDirectory = "",-                workingDirectory = testDir+                workingDirectory = filePathComplTestDir               }-        compls @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt"],+        compls @?== [".hidden", "Content.hs", "dir1/", "dir2/", "textfile.txt", "main-is.cabal"],       testCase "In directory" $ do-        testDir <- getFilePathComplTestDir         compls <-           listFileCompletions             mempty@@ -126,7 +147,7 @@               { isStringNotationPath = Nothing,                 pathSegment = "",                 queryDirectory = "dir1/",-                workingDirectory = testDir+                workingDirectory = filePathComplTestDir               }         compls @?== ["f1.txt", "f2.hs"]     ]@@ -145,32 +166,25 @@   testGroup     "Directory Completer Tests"     [ testCase "Current Directory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "" testDir+        completions <- completeDirectory "" filePathComplTestDir         completions @?== ["./dir1/", "./dir2/"],       testCase "Current Directory - alternative writing" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "./" testDir+        completions <- completeDirectory "./" filePathComplTestDir         completions @?== ["./dir1/", "./dir2/"],       testCase "Current Directory - incomplete directory path written" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "di" testDir+        completions <- completeDirectory "di" filePathComplTestDir         completions @?== ["./dir1/", "./dir2/"],       testCase "Current Directory - incomplete filepath written" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "te" testDir+        completions <- completeDirectory "te" filePathComplTestDir         completions @?== [],       testCase "Subdirectory - no more directories found" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "dir1/" testDir+        completions <- completeDirectory "dir1/" filePathComplTestDir         completions @?== [],       testCase "Subdirectory - available subdirectory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "dir2/" testDir+        completions <- completeDirectory "dir2/" filePathComplTestDir         completions @?== ["dir2/dir3/"],       testCase "Nonexistent directory" $ do-        testDir <- getFilePathComplTestDir-        completions <- completeDirectory "dir2/dir4/" testDir+        completions <- completeDirectory "dir2/dir4/" filePathComplTestDir         completions @?== []     ]   where@@ -241,8 +255,7 @@   where     callFilePathsForExposedModules :: [FilePath] -> IO [T.Text]     callFilePathsForExposedModules srcDirs = do-      cwd <- getExposedTestDir-      let prefInfo = simpleCabalPrefixInfoFromFp "" cwd+      let prefInfo = simpleCabalPrefixInfoFromFp "" exposedTestDir       filePathsForExposedModules mempty srcDirs prefInfo  exposedModuleCompleterTests :: TestTree@@ -277,25 +290,21 @@       CompleterData         { cabalPrefixInfo = simpleExposedCabalPrefixInfo pref dir,           getLatestGPD = do-            testDir <- getTestDir-            cabalContents <- ByteString.readFile $ testDir </> "exposed.cabal"+            cabalContents <- ByteString.readFile $ testDataDir </> "exposed.cabal"             pure $ parseGenericPackageDescriptionMaybe cabalContents,           stanzaName = sName         }     callModulesCompleter :: Maybe StanzaName -> (Maybe StanzaName -> GenericPackageDescription -> [FilePath]) -> T.Text -> IO [T.Text]     callModulesCompleter sName func prefix = do-      cwd <- getTestDir-      let cData = simpleCompleterData sName cwd prefix+      let cData = simpleCompleterData sName testDataDir prefix       completer <- modulesCompleter func mempty cData       pure $ fmap extract completer  mkCompleterData :: CabalPrefixInfo -> CompleterData mkCompleterData prefInfo = CompleterData {getLatestGPD = undefined, cabalPrefixInfo = prefInfo, stanzaName = Nothing} -getExposedTestDir :: IO FilePath-getExposedTestDir = do-  testDir <- getTestDir-  pure $ addTrailingPathSeparator $ testDir </> "src-modules"+exposedTestDir :: FilePath+exposedTestDir = addTrailingPathSeparator $ testDataDir </> "src-modules"  simpleExposedCabalPrefixInfo :: T.Text -> FilePath -> CabalPrefixInfo simpleExposedCabalPrefixInfo prefix fp =
test/Context.hs view
@@ -4,17 +4,17 @@  module Context where -import           Control.Monad.Trans.Maybe                      (runMaybeT)-import qualified Data.Text                                      as T-import qualified Data.Text.Utf16.Rope                           as Rope+import           Control.Monad.Trans.Maybe                   (runMaybeT)+import qualified Data.Text                                   as T+import qualified Data.Text.Utf16.Rope                        as Rope import           Ide.Plugin.Cabal-import           Ide.Plugin.Cabal.Completion.Completer.FilePath+import           Ide.Plugin.Cabal.Completion.Completer.Paths import           Ide.Plugin.Cabal.Completion.Completions-import           Ide.Plugin.Cabal.Completion.Types              (Context,-                                                                 FieldContext (KeyWord, None),-                                                                 StanzaContext (Stanza, TopLevel))+import           Ide.Plugin.Cabal.Completion.Types           (Context,+                                                              FieldContext (KeyWord, None),+                                                              StanzaContext (Stanza, TopLevel)) import           Test.Hls-import           Utils                                          as T+import           Utils                                       as T  cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log cabalPlugin = mkPluginTestDescriptor descriptor "cabal context"@@ -32,31 +32,25 @@     testGroup         "Completion Info to Completion Context Tests"         [ testCase "Current Directory" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "" testDataDir             queryDirectory complInfo @?= "./"         , testCase "Current Directory - partly written next" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "di" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "di" testDataDir             queryDirectory complInfo @?= "./"             pathSegment complInfo @?= "di"         , testCase "Current Directory - alternative writing" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "./" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "./" testDataDir             queryDirectory complInfo @?= "./"         , testCase "Subdirectory" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/" testDataDir             queryDirectory complInfo @?= "dir1/"             pathSegment complInfo @?= ""         , testCase "Subdirectory - partly written next" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/d" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/d" testDataDir             queryDirectory complInfo @?= "dir1/"             pathSegment complInfo @?= "d"         , testCase "Subdirectory - partly written next" $ do-            testDir <- getTestDir-            let complInfo = pathCompletionInfoFromCabalPrefixInfo $ simpleCabalPrefixInfoFromFp "dir1/dir2/d" testDir+            let complInfo = pathCompletionInfoFromCabalPrefixInfo "" $ simpleCabalPrefixInfoFromFp "dir1/dir2/d" testDataDir             queryDirectory complInfo @?= "dir1/dir2/"             pathSegment complInfo @?= "d"         ]@@ -172,6 +166,7 @@ -- ------------------------------------------------------------------------ -- Test Data -- ------------------------------------------------------------------------+ libraryStanzaData :: [T.Text] libraryStanzaData =     [ "cabal-version:      3.0"
test/Main.hs view
@@ -17,15 +17,12 @@ import           Data.Row import qualified Data.Text                       as T import qualified Data.Text                       as Text-import           Ide.Plugin.Cabal import           Ide.Plugin.Cabal.LicenseSuggest (licenseErrorSuggestion) import qualified Ide.Plugin.Cabal.Parse          as Lib import qualified Language.LSP.Protocol.Lens      as L import           System.FilePath import           Test.Hls--cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log-cabalPlugin = mkPluginTestDescriptor descriptor "cabal"+import           Utils  main :: IO () main = do@@ -195,17 +192,3 @@         InR action@CodeAction{_title} <- codeActions         guard (_title == "Replace with " <> license)         pure action---- --------------------------------------------------------------------------- Runner utils--- --------------------------------------------------------------------------runCabalTestCaseSession :: TestName -> FilePath -> Session () -> TestTree-runCabalTestCaseSession title subdir = testCase title . runCabalSession subdir--runCabalSession :: FilePath -> Session a -> IO a-runCabalSession subdir =-    failIfSessionTimeout . runSessionWithServer cabalPlugin (testDataDir </> subdir)--testDataDir :: FilePath-testDataDir = "test" </> "testdata"
test/Utils.hs view
@@ -5,11 +5,14 @@  import           Data.List                         (sort) import qualified Data.Text                         as T+import           Ide.Plugin.Cabal                  (descriptor)+import qualified Ide.Plugin.Cabal import           Ide.Plugin.Cabal.Completion.Types-import           System.Directory                  (getCurrentDirectory) import           System.FilePath import           Test.Hls +cabalPlugin :: PluginTestDescriptor Ide.Plugin.Cabal.Log+cabalPlugin = mkPluginTestDescriptor descriptor "cabal"  simpleCabalPrefixInfoFromPos :: Position -> T.Text -> CabalPrefixInfo simpleCabalPrefixInfoFromPos pos prefix =@@ -33,15 +36,18 @@         , completionFileName = "test"         } -getTestDir :: IO FilePath-getTestDir = do-    cwd <- getCurrentDirectory-    pure $ addTrailingPathSeparator $ cwd </> "test" </> "testdata"+filePathComplTestDir :: FilePath+filePathComplTestDir = addTrailingPathSeparator $ testDataDir </> "filepath-completions" -getFilePathComplTestDir :: IO FilePath-getFilePathComplTestDir = do-    testDir <- getTestDir-    pure $ addTrailingPathSeparator $ testDir </> "filepath-completions"+runCabalTestCaseSession :: TestName -> FilePath -> Session () -> TestTree+runCabalTestCaseSession title subdir = testCase title . runCabalSession subdir++runCabalSession :: FilePath -> Session a -> IO a+runCabalSession subdir =+    failIfSessionTimeout . runSessionWithServer def cabalPlugin (testDataDir </> subdir)++testDataDir :: FilePath+testDataDir = "test" </> "testdata"  -- | list comparison where the order in the list is irrelevant (@?==) :: (HasCallStack, Ord a, Show a) => [a] -> [a] -> Assertion
+ test/testdata/completer.cabal view
@@ -0,0 +1,12 @@+cabal-version: 3.4+name:          test-hls+version:       0.1.0.0+maintainer:    milky+synopsis:      example cabal file :)+license:       Apache-2.0+build-type:    Simple++be++library+    lib