Skip to content

Add "Refine hole" code action #1463

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

Merged
merged 11 commits into from
Mar 1, 2021
1 change: 1 addition & 0 deletions plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ mkWorkspaceEdits
-> Either ResponseError (Maybe WorkspaceEdit)
mkWorkspaceEdits span dflags ccs uri pm rtr = do
for_ (rtr_other_solns rtr) $ traceMX "other solution"
traceMX "solution" $ rtr_extract rtr
let g = graftHole (RealSrcSpan span) rtr
response = transform dflags ccs uri g pm
in case response of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import qualified Data.Text as T
------------------------------------------------------------------------------
-- | All the available features. A 'FeatureSet' describes the ones currently
-- available to the user.
data Feature = FeatureUseDataCon
data Feature
= FeatureUseDataCon
| FeatureRefineHole
deriving (Eq, Ord, Show, Read, Enum, Bounded)


Expand Down
3 changes: 2 additions & 1 deletion plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic/GHC.hs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,12 @@ tacticsThetaTy (tcSplitSigmaTy -> (_, theta, _)) = theta
------------------------------------------------------------------------------
-- | Get the data cons of a type, if it has any.
tacticsGetDataCons :: Type -> Maybe ([DataCon], [Type])
tacticsGetDataCons ty =
tacticsGetDataCons ty | Just _ <- algebraicTyCon ty =
splitTyConApp_maybe ty <&> \(tc, apps) ->
( filter (not . dataConCannotMatch apps) $ tyConDataCons tc
, apps
)
tacticsGetDataCons _ = Nothing

------------------------------------------------------------------------------
-- | Instantiate all of the quantified type variables in a type with fresh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ commandTactic Homomorphism = useNameFromHypothesis homo
commandTactic DestructLambdaCase = const destructLambdaCase
commandTactic HomomorphismLambdaCase = const homoLambdaCase
commandTactic UseDataCon = userSplit
commandTactic Refine = const refine


------------------------------------------------------------------------------
Expand Down Expand Up @@ -89,6 +90,9 @@ commandProvider UseDataCon =
. occNameString
. occName
$ dataConName dcon
commandProvider Refine =
requireFeature FeatureRefineHole $
provide Refine ""


------------------------------------------------------------------------------
Expand Down
24 changes: 24 additions & 0 deletions plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic/Tactics.hs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,19 @@ splitAuto = requireConcreteHole $ tracing "split(auto)" $ do
splitDataCon dc


------------------------------------------------------------------------------
-- | Like 'split', but only works if there is a single matching data
-- constructor for the goal.
splitSingle :: TacticsM ()
splitSingle = tracing "splitSingle" $ do
jdg <- goal
let g = jGoal jdg
case tacticsGetDataCons $ unCType g of
Just ([dc], _) -> do
splitDataCon dc
_ -> throwError $ GoalMismatch "splitSingle" g


------------------------------------------------------------------------------
-- | Allow the given tactic to proceed if and only if it introduces holes that
-- have a different goal than current goal.
Expand Down Expand Up @@ -268,6 +281,17 @@ localTactic t f = do
runStateT (unTacticT t) $ f jdg


refine :: TacticsM ()
refine = go 3
where
go 0 = pure ()
go n = do
let try_that_doesnt_suck t = commit t $ pure ()
try_that_doesnt_suck intros
try_that_doesnt_suck splitSingle
go $ n - 1


auto' :: Int -> TacticsM ()
auto' 0 = throwError NoProgress
auto' n = do
Expand Down
2 changes: 2 additions & 0 deletions plugins/hls-tactics-plugin/src/Ide/Plugin/Tactic/TestTypes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ data TacticCommand
| DestructLambdaCase
| HomomorphismLambdaCase
| UseDataCon
| Refine
deriving (Eq, Ord, Show, Enum, Bounded)

-- | Generate a title for the command.
Expand All @@ -31,6 +32,7 @@ tacticTitle Homomorphism var = "Homomorphic case split on " <> var
tacticTitle DestructLambdaCase _ = "Lambda case split"
tacticTitle HomomorphismLambdaCase _ = "Homomorphic lambda case split"
tacticTitle UseDataCon dcon = "Use constructor " <> dcon
tacticTitle Refine _ = "Refine hole"


------------------------------------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions plugins/hls-tactics-plugin/test/GoldenSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ spec = do
useTest "Left" "UseConLeft.hs" 2 8
useTest "Right" "UseConRight.hs" 2 8

-- test via:
-- stack test hls-tactics-plugin --test-arguments '--match "Golden/refine/"'
describe "refine" $ do
let refineTest = mkGoldenTest allFeatures Refine ""
describe "golden" $ do
refineTest "RefineIntro.hs" 2 8
refineTest "RefineCon.hs" 2 8
refineTest "RefineReader.hs" 4 8
refineTest "RefineGADT.hs" 8 8


describe "golden tests" $ do
let autoTest = mkGoldenTest allFeatures Auto ""

Expand Down
3 changes: 3 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineCon.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test :: ((), (b, c), d)
test = _

3 changes: 3 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineCon.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test :: ((), (b, c), d)
test = ((), (_, _), _)

9 changes: 9 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineGADT.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{-# LANGUAGE GADTs #-}

data GADT a where
One :: (b -> Int) -> GADT Int
Two :: GADT Bool

test :: z -> GADT Int
test = _

9 changes: 9 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineGADT.hs.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{-# LANGUAGE GADTs #-}

data GADT a where
One :: (b -> Int) -> GADT Int
Two :: GADT Bool

test :: z -> GADT Int
test z = One (\ b -> _)

2 changes: 2 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineIntro.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test :: a -> Either a b
test = _
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test :: a -> Either a b
test a = _
5 changes: 5 additions & 0 deletions plugins/hls-tactics-plugin/test/golden/RefineReader.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
newtype Reader r a = Reader (r -> a)

test :: b -> Reader r a
test = _

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
newtype Reader r a = Reader (r -> a)

test :: b -> Reader r a
test b = Reader (\ r -> _)