From 7c5c43b58f4f8d2c49a923c119675267f278bf5e Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Fri, 30 Aug 2024 02:08:38 +0300 Subject: [PATCH 1/3] fixed build targets lookup --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal.hs | 6 +- .../src/Ide/Plugin/Cabal/CabalAdd.hs | 55 ++++++++----------- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs index 8973f4401d..f406abb309 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs @@ -322,10 +322,8 @@ cabalAddCodeAction state plId (CodeActionParams _ _ (TextDocumentIdentifier uri) case mbGPD of Nothing -> pure $ InL [] Just (gpd, _) -> do - actions <- liftIO $ CabalAdd.addDependencySuggestCodeAction plId verTxtDocId - suggestions - haskellFilePath cabalFilePath - gpd + actions <- liftIO $ CabalAdd.addDependencySuggestCodeAction plId verTxtDocId suggestions + cabalFilePath gpd pure $ InL $ fmap InR actions diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs index e60d06db78..187a8cacac 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs @@ -27,6 +27,7 @@ import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as B import Data.List.NonEmpty (NonEmpty (..), fromList) +import Data.Maybe (catMaybes) import Data.String (IsString) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) @@ -37,18 +38,17 @@ import Development.IDE.Core.Rules (runAction) import Development.IDE.Core.RuleTypes (GetFileContents (..)) import Distribution.Client.Add as Add import Distribution.Compat.Prelude (Generic) -import Distribution.PackageDescription (GenericPackageDescription, +import Distribution.PackageDescription (ComponentName, + GenericPackageDescription, + PackageDescription (..), packageDescription, specVersion) import Distribution.PackageDescription.Configuration (flattenPackageDescription) import Distribution.PackageDescription.Quirks (patchQuirks) import qualified Distribution.Pretty as Pretty -import Distribution.Simple.BuildTarget (BuildTarget, - buildTargetComponentName, - readBuildTargets) import Distribution.Simple.Utils (safeHead) -import Distribution.Verbosity (silent, - verboseNoStderr) +import Distribution.Types.Component (Component (..), + componentName) import Ide.Logger import Ide.Plugin.Cabal.Completion.Types (ParseCabalFields (..), ParseCabalFile (..)) @@ -76,7 +76,6 @@ import Language.LSP.Protocol.Types (ApplyWorkspaceEd import System.Directory (doesFileExist, listDirectory) import System.FilePath (dropFileName, - makeRelative, splitPath, takeExtension, ()) @@ -134,42 +133,34 @@ addDependencySuggestCodeAction :: PluginId -> VersionedTextDocumentIdentifier -- ^ Cabal's versioned text identifier -> [(T.Text, T.Text)] -- ^ A dependency-version suggestion pairs - -> FilePath -- ^ Path to the haskell file (source of diagnostics) -> FilePath -- ^ Path to the cabal file (that will be edited) -> GenericPackageDescription -> IO [CodeAction] -addDependencySuggestCodeAction plId verTxtDocId suggestions haskellFilePath cabalFilePath gpd = do - buildTargets <- liftIO $ getBuildTargets gpd cabalFilePath haskellFilePath - case buildTargets of - -- If there are no build targets found, run `cabal-add` command with default behaviour - [] -> pure $ mkCodeAction cabalFilePath Nothing <$> suggestions - -- Otherwise provide actions for all found targets - targets -> pure $ concat [mkCodeAction cabalFilePath (Just $ buildTargetToStringRepr target) <$> - suggestions | target <- targets] - where +addDependencySuggestCodeAction plId verTxtDocId suggestions cabalFilePath gpd = do -- | Note the use of `pretty` function. - -- It converts the `BuildTarget` to an acceptable string representation. + -- It converts the `ComponentName` to an acceptable string representation. -- It will be used in as the input for `cabal-add`'s `executeConfig`. - buildTargetToStringRepr target = render $ Pretty.pretty $ buildTargetComponentName target - - -- | Gives the build targets that are used in the `CabalAdd`. - -- Note the unorthodox usage of `readBuildTargets`: - -- If the relative path to the haskell file is provided, - -- the `readBuildTargets` will return a main build target. - -- This behaviour is acceptable for now, but changing to a way of getting - -- all build targets in a file is advised. - getBuildTargets :: GenericPackageDescription -> FilePath -> FilePath -> IO [BuildTarget] - getBuildTargets gpd cabalFilePath haskellFilePath = do - let haskellFileRelativePath = makeRelative (dropFileName cabalFilePath) haskellFilePath - readBuildTargets (verboseNoStderr silent) (flattenPackageDescription gpd) [haskellFileRelativePath] + pure $ concat [mkCodeAction cabalFilePath (Just $ render $ Pretty.pretty cNames) <$> + suggestions | cNames <- getBuildTargetComponentNames gpd] + where + getBuildTargetComponentNames :: GenericPackageDescription -> [ComponentName] + getBuildTargetComponentNames gpd = map componentName components + where PackageDescription{..} = flattenPackageDescription gpd + components = catMaybes $ + [CLib <$> library] <> + map (Just . CLib) subLibraries <> + map (Just . CFLib) foreignLibs <> + map (Just . CExe) executables <> + map (Just . CTest) testSuites <> + map (Just . CBench) benchmarks mkCodeAction :: FilePath -> Maybe String -> (T.Text, T.Text) -> CodeAction mkCodeAction cabalFilePath target (suggestedDep, suggestedVersion) = let - versionTitle = if T.null suggestedVersion then T.empty else " version " <> suggestedVersion + versionTitle = if T.null suggestedVersion then T.empty else "-" <> suggestedVersion targetTitle = case target of Nothing -> T.empty - Just t -> " target " <> T.pack t + Just t -> " at " <> T.pack t title = "Add dependency " <> suggestedDep <> versionTitle <> targetTitle version = if T.null suggestedVersion then Nothing else Just suggestedVersion From 4f3171d852d60e313c976a2b49ee699dcb948930 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Fri, 30 Aug 2024 16:06:29 +0300 Subject: [PATCH 2/3] new multitarget tests --- plugins/hls-cabal-plugin/test/CabalAdd.hs | 11 +++++-- .../cabal-add-exe/cabal-add-exe.cabal | 4 --- .../cabal-add-multitarget.cabal | 31 +++++++++++++++++++ .../cabal-add-multitarget/src/Main.hs | 5 +++ .../testdata/cabal-add-testdata/cabal.project | 1 + 5 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/cabal-add-multitarget.cabal create mode 100644 plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/src/Main.hs diff --git a/plugins/hls-cabal-plugin/test/CabalAdd.hs b/plugins/hls-cabal-plugin/test/CabalAdd.hs index f6bc7dbde0..9287da685a 100644 --- a/plugins/hls-cabal-plugin/test/CabalAdd.hs +++ b/plugins/hls-cabal-plugin/test/CabalAdd.hs @@ -28,7 +28,7 @@ cabalAddTests :: TestTree cabalAddTests = testGroup "CabalAdd Tests" - [ runHaskellTestCaseSession "Code Actions - Can add hidden package" ("cabal-add-testdata" "cabal-add-exe") + [ runHaskellTestCaseSession "Code Actions - Can add hidden packages" ("cabal-add-testdata" "cabal-add-exe") (generateAddDependencyTestSession "cabal-add-exe.cabal" ("src" "Main.hs") "split" [253]) , runHaskellTestCaseSession "Code Actions - Can add hidden package to a library" ("cabal-add-testdata" "cabal-add-lib") (generateAddDependencyTestSession "cabal-add-lib.cabal" ("src" "MyLib.hs") "split" [348]) @@ -36,6 +36,9 @@ cabalAddTests = (generateAddDependencyTestSession "cabal-add-tests.cabal" ("test" "Main.hs") "split" [478]) , runHaskellTestCaseSession "Code Actions - Can add hidden package to a benchmark" ("cabal-add-testdata" "cabal-add-bench") (generateAddDependencyTestSession "cabal-add-bench.cabal" ("bench" "Main.hs") "split" [403]) + , runHaskellTestCaseSession "Code Actions - Can add hidden packages for multiple targets" ("cabal-add-testdata" "cabal-add-multitarget") + (generateAddDependencyTestSession "cabal-add-multitarget.cabal" ("src" "Main.hs") "split" [261,345,590,754]) + , testHiddenPackageSuggestions "Check CabalAdd's parser, no version" [ "It is a member of the hidden package 'base'" , "It is a member of the hidden package 'Blammo-wai'" @@ -117,8 +120,10 @@ cabalAddTests = _ <- waitForDiagnosticsFrom hsdoc cas <- Maybe.mapMaybe (^? _R) <$> getAllCodeActions hsdoc let selectedCas = filter (\ca -> "Add dependency" `T.isPrefixOf` (ca ^. L.title)) cas - mapM_ executeCodeAction selectedCas - _ <- skipManyTill anyMessage $ getDocumentEdit cabDoc -- Wait for the changes in cabal file + let runAvait codeAction = do + executeCodeAction codeAction + skipManyTill anyMessage $ getDocumentEdit cabDoc + mapM_ runAvait selectedCas contents <- documentContents cabDoc liftIO $ assertEqual (T.unpack dependency <> " isn't found in the cabal file") indicesRes (T.indices dependency contents) testHiddenPackageSuggestions :: String -> [T.Text] -> [(T.Text, T.Text)] -> TestTree diff --git a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-exe/cabal-add-exe.cabal b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-exe/cabal-add-exe.cabal index a3499bbf97..5567facd47 100644 --- a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-exe/cabal-add-exe.cabal +++ b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-exe/cabal-add-exe.cabal @@ -9,7 +9,3 @@ executable cabal-add-exe ghc-options: -Wall build-depends: base default-language: Haskell2010 - -library - build-depends: base >= 4 && < 5 - ghc-options: -Wall diff --git a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/cabal-add-multitarget.cabal b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/cabal-add-multitarget.cabal new file mode 100644 index 0000000000..9afd7db5ea --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/cabal-add-multitarget.cabal @@ -0,0 +1,31 @@ +cabal-version: 2.4 +name: cabal-add-multitarget +version: 0.1.0.0 +build-type: Simple + +executable cabal-add-exe + main-is: Main.hs + hs-source-dirs: src + ghc-options: -Wall + build-depends: base + default-language: Haskell2010 + +library + build-depends: base >= 4 && < 5 + ghc-options: -Wall + +test-suite cabal-add-tests-test + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: base + +benchmark benchmark + type: exitcode-stdio-1.0 + ghc-options: -threaded + main-is: Main.hs + hs-source-dirs: bench + build-depends: base + + diff --git a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/src/Main.hs b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/src/Main.hs new file mode 100644 index 0000000000..0bf3e99dae --- /dev/null +++ b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal-add-multitarget/src/Main.hs @@ -0,0 +1,5 @@ +module Main where + +import Data.List.Split + +main = putStrLn "Hello, Haskell!" \ No newline at end of file diff --git a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal.project b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal.project index dfa2feed39..8ebe942fcc 100644 --- a/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal.project +++ b/plugins/hls-cabal-plugin/test/testdata/cabal-add-testdata/cabal.project @@ -2,3 +2,4 @@ packages: cabal-add-exe cabal-add-lib cabal-add-tests cabal-add-bench + cabal-add-multitarget From 60f776a085b7e814bb2f26b7050bd70805204148 Mon Sep 17 00:00:00 2001 From: George Gerasev Date: Fri, 30 Aug 2024 16:12:11 +0300 Subject: [PATCH 3/3] docs update --- .../hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs index 187a8cacac..724a7800e1 100644 --- a/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs +++ b/plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs @@ -121,14 +121,7 @@ instance Pretty CabalAddCommandParams where -- | Creates a code action that calls the `cabalAddCommand`, -- using dependency-version suggestion pairs as input. -- --- Returns disabled action if no cabal files given. --- --- Takes haskell file and cabal file paths to create a relative path --- to the haskell file, which is used to get a `BuildTarget`. --- --- In current implementation the dependency is being added to the main found --- build target, but if there will be a way to get all build targets from a file --- it will be possible to support addition to a build target of choice. +-- Gives a code action for all found build targets. addDependencySuggestCodeAction :: PluginId -> VersionedTextDocumentIdentifier -- ^ Cabal's versioned text identifier