Skip to content

Commit ca476e8

Browse files
committed
implement isovector#21 : splitting on datatype
One code action per datatype constructor is produced.
1 parent a6e6c0b commit ca476e8

File tree

6 files changed

+55
-8
lines changed

6 files changed

+55
-8
lines changed

plugins/tactics/src/Ide/Plugin/Tactic.hs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Control.Monad.Trans
2020
import Control.Monad.Trans.Maybe
2121
import Data.Aeson
2222
import Data.Coerce
23+
import qualified Data.Foldable as F
2324
import qualified Data.Map as M
2425
import Data.Maybe
2526
import qualified Data.Set as S
@@ -92,6 +93,9 @@ commandProvider Auto = provide Auto ""
9293
commandProvider Intros =
9394
filterGoalType isFunction $
9495
provide Intros ""
96+
commandProvider Split =
97+
foldMapGoalType (F.fold . tyDataCons) $ \dc ->
98+
provide Split $ T.pack $ occNameString $ getOccName dc
9599
commandProvider Destruct =
96100
filterBindingType destructFilter $ \occ _ ->
97101
provide Destruct $ T.pack $ occNameString occ
@@ -110,11 +114,12 @@ commandProvider HomomorphismLambdaCase =
110114

111115
------------------------------------------------------------------------------
112116
-- | A mapping from tactic commands to actual tactics for refinery.
113-
commandTactic :: TacticCommand -> OccName -> TacticsM ()
117+
commandTactic :: TacticCommand -> String -> TacticsM ()
114118
commandTactic Auto = const auto
115119
commandTactic Intros = const intros
116-
commandTactic Destruct = destruct
117-
commandTactic Homomorphism = homo
120+
commandTactic Split = splitDataCon' . mkDataOcc
121+
commandTactic Destruct = destruct . mkVarOcc
122+
commandTactic Homomorphism = homo . mkVarOcc
118123
commandTactic DestructLambdaCase = const destructLambdaCase
119124
commandTactic HomomorphismLambdaCase = const homoLambdaCase
120125

@@ -185,6 +190,14 @@ requireExtension ext tp dflags plId uri range jdg =
185190
False -> pure []
186191

187192

193+
------------------------------------------------------------------------------
194+
-- | Create a 'TacticProvider' for each occurance of an 'a' in the foldable container
195+
-- extracted from the goal type. Useful instantiations for 't' include 'Maybe' or '[]'.
196+
foldMapGoalType :: Foldable t => (Type -> t a) -> (a -> TacticProvider) -> TacticProvider
197+
foldMapGoalType f tpa dflags plId uri range jdg =
198+
foldMap tpa (f $ unCType $ jGoal jdg) dflags plId uri range jdg
199+
200+
188201
------------------------------------------------------------------------------
189202
-- | Restrict a 'TacticProvider', making sure it appears only when the given
190203
-- predicate holds for the goal.
@@ -259,7 +272,7 @@ judgementForHole state nfp range = do
259272

260273

261274

262-
tacticCmd :: (OccName -> TacticsM ()) -> CommandFunction TacticParams
275+
tacticCmd :: (String -> TacticsM ()) -> CommandFunction TacticParams
263276
tacticCmd tac lf state (TacticParams uri range var_name)
264277
| Just nfp <- uriToNormalizedFilePath $ toNormalizedUri uri =
265278
fromMaybeT (Right Null, Nothing) $ do
@@ -268,7 +281,6 @@ tacticCmd tac lf state (TacticParams uri range var_name)
268281
pm <- MaybeT $ useAnnotatedSource "tacticsCmd" state nfp
269282
case runTactic ctx jdg
270283
$ tac
271-
$ mkVarOcc
272284
$ T.unpack var_name of
273285
Left err ->
274286
pure $ (, Nothing)

plugins/tactics/src/Ide/Plugin/Tactic/GHC.hs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module Ide.Plugin.Tactic.GHC where
44

55
import Data.Maybe (isJust)
6+
import DataCon
67
import TcType
78
import TyCoRep
89
import TyCon
@@ -67,3 +68,11 @@ lambdaCaseable (splitFunTy_maybe -> Just (arg, res))
6768
= Just $ isJust $ algebraicTyCon res
6869
lambdaCaseable _ = Nothing
6970

71+
72+
------------------------------------------------------------------------------
73+
-- | What data-constructor, if any, does the type have?
74+
tyDataCons :: Type -> Maybe [DataCon]
75+
tyDataCons g = do
76+
(tc, _) <- splitTyConApp_maybe g
77+
pure $ tyConDataCons tc
78+

plugins/tactics/src/Ide/Plugin/Tactic/Tactics.hs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,9 @@ split :: TacticsM ()
155155
split = do
156156
jdg <- goal
157157
let g = jGoal jdg
158-
case splitTyConApp_maybe $ unCType g of
158+
case tyDataCons $ unCType g of
159159
Nothing -> throwError $ GoalMismatch "getGoalTyCon" g
160-
Just (tc, _) -> do
161-
let dcs = tyConDataCons tc
160+
Just dcs -> do
162161
choice $ fmap splitDataCon dcs
163162

164163

@@ -175,6 +174,22 @@ splitDataCon dc = rule $ \jdg -> do
175174
Nothing -> throwError $ GoalMismatch "splitDataCon" g
176175

177176

177+
------------------------------------------------------------------------------
178+
-- | Attempt to instantiate the named data constructor to solve the goal.
179+
splitDataCon' :: OccName -> TacticsM ()
180+
splitDataCon' dcn = do
181+
jdg <- goal
182+
let g = jGoal jdg
183+
case splitTyConApp_maybe $ unCType g of
184+
Nothing -> throwError $ GoalMismatch ("splitDataCon'" ++ unsafeRender dcn) g
185+
Just (tc, _) -> do
186+
let dcs = tyConDataCons tc
187+
mdc = find ((== dcn) . getOccName) dcs
188+
case mdc of
189+
Nothing -> throwError $ GoalMismatch ("splitDataCon'" ++ unsafeRender dcn) g
190+
Just dc -> splitDataCon dc
191+
192+
178193
------------------------------------------------------------------------------
179194
-- | @matching f@ takes a function from a judgement to a @Tactic@, and
180195
-- then applies the resulting @Tactic@.

plugins/tactics/src/Ide/Plugin/Tactic/TestTypes.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import qualified Data.Text as T
1111
data TacticCommand
1212
= Auto
1313
| Intros
14+
| Split
1415
| Destruct
1516
| Homomorphism
1617
| DestructLambdaCase
@@ -21,6 +22,7 @@ data TacticCommand
2122
tacticTitle :: TacticCommand -> T.Text -> T.Text
2223
tacticTitle Auto _ = "Attempt to fill hole"
2324
tacticTitle Intros _ = "Introduce lambda"
25+
tacticTitle Split cname = "Introduce constructor " <> cname
2426
tacticTitle Destruct var = "Case split on " <> var
2527
tacticTitle Homomorphism var = "Homomorphic case split on " <> var
2628
tacticTitle DestructLambdaCase _ = "Lambda case split"

test/functional/Tactic.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ tests = testGroup
8080
"T3.hs" 7 13
8181
[ (id, DestructLambdaCase, "")
8282
]
83+
, mkTest
84+
"Produces datatype split actions"
85+
"T2.hs" 16 14
86+
[ (id, Split, "T")
87+
]
8388
, mkTest
8489
"Doesn't suggest lambdacase without -XLambdaCase"
8590
"T2.hs" 11 25

test/testdata/tactic/T2.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ foo = _
1010
dontSuggestLambdaCase :: Either a b -> Int
1111
dontSuggestLambdaCase = _
1212

13+
data T = T !(Int, Int)
14+
15+
suggestCon :: T
16+
suggestCon = _

0 commit comments

Comments
 (0)