Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for multiple build targets at cabal-add #4393

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,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

-- | Handler for hover messages.
Expand Down
64 changes: 24 additions & 40 deletions plugins/hls-cabal-plugin/src/Ide/Plugin/Cabal/CabalAdd.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 (..))
Expand Down Expand Up @@ -76,7 +76,6 @@ import Language.LSP.Protocol.Types (ApplyWorkspaceEd
import System.Directory (doesFileExist,
listDirectory)
import System.FilePath (dropFileName,
makeRelative,
splitPath,
takeExtension,
(</>))
Expand Down Expand Up @@ -122,54 +121,39 @@ 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
-> [(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

Expand Down
11 changes: 8 additions & 3 deletions plugins/hls-cabal-plugin/test/CabalAdd.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@ 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])
, runHaskellTestCaseSession "Code Actions - Can add hidden package to a test" ("cabal-add-testdata" </> "cabal-add-tests")
(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'"
Expand Down Expand Up @@ -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
Comment on lines +123 to +126
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let runAvait codeAction = do
executeCodeAction codeAction
skipManyTill anyMessage $ getDocumentEdit cabDoc
mapM_ runAvait selectedCas
let runAvail codeAction = do
executeCodeAction codeAction
skipManyTill anyMessage $ getDocumentEdit cabDoc
mapM_ runAvail selectedCas

Maybe?

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -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


Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Main where

import Data.List.Split

main = putStrLn "Hello, Haskell!"
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ packages: cabal-add-exe
cabal-add-lib
cabal-add-tests
cabal-add-bench
cabal-add-multitarget
Loading