diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs index 5defdbbe63..0e1053453b 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Completer/Paths.hs @@ -7,6 +7,7 @@ import Distribution.PackageDescription (Benchmark (..), BuildInfo (..), CondTree (condTreeData), Executable (..), + ForeignLib (..), GenericPackageDescription (..), Library (..), UnqualComponentName, @@ -118,6 +119,10 @@ sourceDirsExtractionTestSuite name gpd = extractRelativeDirsFromStanza name gpd sourceDirsExtractionBenchmark :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] sourceDirsExtractionBenchmark name gpd = extractRelativeDirsFromStanza name gpd condBenchmarks benchmarkBuildInfo +-- | Extracts the source directories of foreign-lib stanza with the given name. +sourceDirsExtractionForeignLib :: Maybe StanzaName -> GenericPackageDescription -> [FilePath] +sourceDirsExtractionForeignLib name gpd = extractRelativeDirsFromStanza name gpd condForeignLibs foreignLibBuildInfo + {- | 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. diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs index c27568d692..03e517eae2 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/Completion/Data.hs @@ -1,7 +1,4 @@ {-# LANGUAGE OverloadedStrings #-} -{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} - -{-# HLINT ignore "Redundant bracket" #-} module Ide.Plugin.Cabal.Completion.Data where @@ -19,6 +16,17 @@ import Ide.Plugin.Cabal.Completion.Completer.Types (Completer) import Ide.Plugin.Cabal.Completion.Types import Ide.Plugin.Cabal.LicenseSuggest (licenseNames) +-- | Ad-hoc data type for modelling the available top-level stanzas. +-- Not intended right now for anything else but to avoid string +-- comparisons in 'stanzaKeywordMap' and 'libExecTestBenchCommons'. +data TopLevelStanza + = Library + | Executable + | TestSuite + | Benchmark + | ForeignLib + | Common + -- ---------------------------------------------------------------- -- Completion Data -- ---------------------------------------------------------------- @@ -71,12 +79,13 @@ cabalKeywords = stanzaKeywordMap :: Map StanzaType (Map KeyWordName Completer) stanzaKeywordMap = Map.fromList - [ ("library", libraryFields <> libExecTestBenchCommons), - ("executable", executableFields <> libExecTestBenchCommons), - ("test-suite", testSuiteFields <> libExecTestBenchCommons), - ("benchmark", benchmarkFields <> libExecTestBenchCommons), - ("foreign-library", foreignLibraryFields <> libExecTestBenchCommons), - ("common", libExecTestBenchCommons), + [ ("library", libraryFields <> libExecTestBenchCommons Library), + ("executable", executableFields <> libExecTestBenchCommons Executable), + ("test-suite", testSuiteFields <> libExecTestBenchCommons TestSuite), + ("benchmark", benchmarkFields <> libExecTestBenchCommons Benchmark), + ("foreign-library", foreignLibraryFields <> libExecTestBenchCommons ForeignLib), + ("common", libExecTestBenchCommons Library), + ("common", libExecTestBenchCommons Common), ("flag", flagFields), ("source-repository", sourceRepositoryFields) ] @@ -162,8 +171,8 @@ flagFields = ("lib-version-linux:", noopCompleter) ] -libExecTestBenchCommons :: Map KeyWordName Completer -libExecTestBenchCommons = +libExecTestBenchCommons :: TopLevelStanza -> Map KeyWordName Completer +libExecTestBenchCommons st = Map.fromList [ ("import:", importCompleter), ("build-depends:", noopCompleter), @@ -183,6 +192,8 @@ libExecTestBenchCommons = ("includes:", filePathCompleter), ("install-includes:", filePathCompleter), ("include-dirs:", directoryCompleter), + ("autogen-includes:", filePathCompleter), + ("autogen-modules:", moduleCompleterByTopLevelStanza), ("c-sources:", filePathCompleter), ("cxx-sources:", filePathCompleter), ("asm-sources:", filePathCompleter), @@ -203,6 +214,26 @@ libExecTestBenchCommons = ("extra-framework-dirs:", directoryCompleter), ("mixins:", noopCompleter) ] + where + -- + moduleCompleterByTopLevelStanza = case st of + Library -> modulesCompleter sourceDirsExtractionLibrary + Executable -> modulesCompleter sourceDirsExtractionExecutable + TestSuite -> modulesCompleter sourceDirsExtractionTestSuite + Benchmark -> modulesCompleter sourceDirsExtractionBenchmark + ForeignLib -> modulesCompleter sourceDirsExtractionForeignLib + Common -> + -- TODO: We can't provide a module completer because we provide + -- module completions based on the "hs-source-dirs" after parsing the file, + -- i.e. based on the 'PackageDescription'. + -- "common" stanzas are erased in the 'PackageDescription' representation, + -- thus we can't provide accurate module completers right now, as we don't + -- know what the 'hs-source-dirs' in the "common" stanza are. + -- + -- A potential fix would be to introduce an intermediate representation that + -- parses the '.cabal' file s.t. that we have access to the 'hs-source-dirs', + -- but not have erased the "common" stanza. + noopCompleter -- | Contains a map of the most commonly used licenses, weighted by their popularity. -- diff --git a/plugins/hls-cabal-plugin/test/Completer.hs b/plugins/hls-cabal-plugin/test/Completer.hs index 6b1f772af0..ab7165b1ac 100644 --- a/plugins/hls-cabal-plugin/test/Completer.hs +++ b/plugins/hls-cabal-plugin/test/Completer.hs @@ -7,6 +7,7 @@ module Completer where import Control.Lens ((^.), (^?)) import Control.Lens.Prism +import Control.Monad (forM_) import qualified Data.ByteString as ByteString import qualified Data.ByteString.Char8 as BS8 import Data.Maybe (mapMaybe) @@ -40,7 +41,8 @@ completerTests = completionHelperTests, filePathExposedModulesTests, exposedModuleCompleterTests, - importCompleterTests + importCompleterTests, + autogenFieldCompletionTests ] basicCompleterTests :: TestTree @@ -336,6 +338,26 @@ importCompleterTests = [Syntax.SecArgName (Syntax.Position row (col + 7)) (BS8.pack name)] [] +autogenFieldCompletionTests :: TestTree +autogenFieldCompletionTests = + testGroup "Autogen Field Completer Tests" + [ testAutogenField "library" "completion/autogen-completion.cabal" (Position 6 9) ["autogen-modules:", "autogen-includes:"] + , testAutogenField "executable" "completion/autogen-completion.cabal" (Position 11 9) ["autogen-modules:", "autogen-includes:"] + , testAutogenField "test-suite" "completion/autogen-completion.cabal" (Position 16 9) ["autogen-modules:", "autogen-includes:"] + , testAutogenField "benchmark" "completion/autogen-completion.cabal" (Position 21 9) ["autogen-modules:", "autogen-includes:"] + , testAutogenField "common" "completion/autogen-completion.cabal" (Position 24 9) ["autogen-modules:", "autogen-includes:"] + ] + + where + testAutogenField :: String -> FilePath -> Position -> [T.Text] -> TestTree + testAutogenField section file pos expected = runCabalTestCaseSession ("autogen-modules completion in " <> section) "" $ do + doc <- openDoc file "cabal" + items <- getCompletions doc pos + let labels = map (^. L.label) items + liftIO $ forM_ expected $ \expect -> + assertBool (T.unpack expect <> " not found in " <> section) $ + any (expect `T.isInfixOf`) labels + simpleCompleterData :: Maybe StanzaName -> FilePath -> T.Text -> CompleterData simpleCompleterData sName dir pref = do CompleterData diff --git a/plugins/hls-cabal-plugin/test/testdata/completion/autogen-completion.cabal b/plugins/hls-cabal-plugin/test/testdata/completion/autogen-completion.cabal new file mode 100644 index 0000000000..dd5c86d339 --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/completion/autogen-completion.cabal @@ -0,0 +1,25 @@ +cabal-version: 3.0 +name: autogen-completion +version: 0.1.0.0 + +library + hs-source-dirs: src + autogen- + +executable autoexe + main-is: Main.hs + hs-source-dirs: src + autogen- + +test-suite autotest + type: exitcode-stdio-1.0 + hs-source-dirs: src + autogen- + +benchmark autobench + type: exitcode-stdio-1.0 + hs-source-dirs: src + autogen- + +common defaults + autogen-