Skip to content

Commit f5cadd5

Browse files
committed
feat: Add actions for modifying constructor field types
Signed-off-by: George Thomas <georgefsthomas@gmail.com>
1 parent 798af52 commit f5cadd5

12 files changed

Lines changed: 235 additions & 45 deletions

File tree

primer/src/Foreword.hs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ module Foreword (
3232
curry4,
3333
unsafeMaximum,
3434
spanMaybe,
35+
adjustAtA',
36+
findAndAdjustA',
3537
) where
3638

3739
-- In general, we should defer to "Protolude"'s exports and avoid name
@@ -130,6 +132,11 @@ adjustAtA n f xs = case splitAt n xs of
130132
(a, b : bs) -> f b <&> \b' -> Just $ a ++ [b'] ++ bs
131133
_ -> pure Nothing
132134

135+
adjustAtA' :: Applicative f => Int -> (a -> f (a, z)) -> [a] -> f (Maybe ([a], z))
136+
adjustAtA' n f xs = case splitAt n xs of
137+
(a, b : bs) -> f b <&> \(b', z) -> Just (a ++ [b'] ++ bs, z)
138+
_ -> pure Nothing
139+
133140
-- | Adjust the first element of the list which satisfies the
134141
-- predicate. Returns 'Nothing' if there is no such element.
135142
findAndAdjust :: (a -> Bool) -> (a -> a) -> [a] -> Maybe [a]
@@ -143,6 +150,11 @@ findAndAdjustA p f = \case
143150
[] -> pure Nothing
144151
x : xs -> if p x then Just . (: xs) <$> f x else (x :) <<$>> findAndAdjustA p f xs
145152

153+
findAndAdjustA' :: Applicative m => (a -> Bool) -> (a -> m (a, z)) -> [a] -> m (Maybe ([a], z))
154+
findAndAdjustA' p f = \case
155+
[] -> pure Nothing
156+
x : xs -> if p x then (\(x', z) -> Just . (,z) . (: xs) $ x') <$> f x else first (x :) <<$>> findAndAdjustA' p f xs
157+
146158
-- | Change the type of an error.
147159
modifyError :: MonadError e' m => (e -> e') -> ExceptT e m a -> m a
148160
modifyError f = runExceptT >=> either (throwError . f) pure

primer/src/Primer/API.hs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ import Primer.App (
127127
newApp,
128128
progAllDefs,
129129
progAllTypeDefs,
130+
progAllTypeDefsMeta,
130131
progCxt,
131132
progImports,
132133
progLog,
@@ -157,6 +158,7 @@ import Primer.Core (
157158
TyVarName,
158159
Type,
159160
Type' (..),
161+
TypeMeta,
160162
ValConName,
161163
getID,
162164
unLocalName,
@@ -218,7 +220,7 @@ import Primer.Log (
218220
import Primer.Module (moduleDefsQualified, moduleName, moduleTypesQualifiedMeta)
219221
import Primer.Name qualified as Name
220222
import Primer.Primitives (primDefType)
221-
import Primer.TypeDef (ASTTypeDef (..), typeDefNameHints, typeDefParameters)
223+
import Primer.TypeDef (ASTTypeDef (..), forgetTypeDefMetadata, typeDefNameHints, typeDefParameters)
222224
import Primer.TypeDef qualified as TypeDef
223225
import StmContainers.Map qualified as StmMap
224226

@@ -1077,23 +1079,23 @@ availableActions ::
10771079
availableActions = curry3 $ logAPI (noError AvailableActions) $ \(sid, level, selection) -> do
10781080
prog <- getProgram sid
10791081
let allDefs = progAllDefs prog
1080-
allTypeDefs = progAllTypeDefs prog
1082+
allTypeDefs = progAllTypeDefsMeta prog
10811083
case selection of
10821084
SelectionDef sel -> do
10831085
(editable, ASTDef{astDefType = type_, astDefExpr = expr}) <- findASTDef allDefs sel.def
10841086
pure $ case sel.node of
10851087
Nothing -> Available.forDef (snd <$> allDefs) level editable sel.def
10861088
Just NodeSelection{..} -> case nodeType of
10871089
SigNode -> Available.forSig level editable type_ meta
1088-
BodyNode -> Available.forBody (snd <$> allTypeDefs) level editable expr meta
1090+
BodyNode -> Available.forBody (forgetTypeDefMetadata . snd <$> allTypeDefs) level editable expr meta
10891091
SelectionTypeDef sel -> do
1090-
(editable, _def) <- findASTTypeDef allTypeDefs sel.def
1092+
(editable, def) <- findASTTypeDef allTypeDefs sel.def
10911093
pure $ case sel.node of
10921094
Nothing -> Available.forTypeDef level editable
10931095
Just (TypeDefParamNodeSelection _) -> Available.forTypeDefParamNode level editable
10941096
Just (TypeDefConsNodeSelection s) -> case s.field of
10951097
Nothing -> Available.forTypeDefConsNode level editable
1096-
Just _ -> Available.forTypeDefConsFieldNode level editable
1098+
Just field -> Available.forTypeDefConsFieldNode level editable def s.con field.index field.meta
10971099

10981100
actionOptions ::
10991101
(MonadIO m, MonadThrow m, MonadAPILog l m) =>
@@ -1117,16 +1119,16 @@ findASTDef allDefs def = case allDefs Map.!? def of
11171119
Just (_, Def.DefPrim _) -> throwM $ UnexpectedPrimDef def
11181120
Just (editable, Def.DefAST d) -> pure (editable, d)
11191121

1120-
findASTTypeDef :: MonadThrow m => Map TyConName (Editable, TypeDef.TypeDef ()) -> TyConName -> m (Editable, ASTTypeDef ())
1122+
findASTTypeDef :: MonadThrow m => Map TyConName (Editable, TypeDef.TypeDef a) -> TyConName -> m (Editable, ASTTypeDef a)
11211123
findASTTypeDef allTypeDefs def = case allTypeDefs Map.!? def of
11221124
Nothing -> throwM $ UnknownTypeDef def
11231125
Just (_, TypeDef.TypeDefPrim _) -> throwM $ UnexpectedPrimTypeDef def
11241126
Just (editable, TypeDef.TypeDefAST d) -> pure (editable, d)
11251127

1126-
findASTTypeOrTermDef :: MonadThrow f => App.Prog -> Selection' a -> f (Editable, Either (ASTTypeDef ()) ASTDef)
1128+
findASTTypeOrTermDef :: MonadThrow f => App.Prog -> Selection -> f (Editable, Either (ASTTypeDef TypeMeta) ASTDef)
11271129
findASTTypeOrTermDef prog = \case
11281130
App.SelectionTypeDef sel ->
1129-
Left <<$>> findASTTypeDef (progAllTypeDefs prog) sel.def
1131+
Left <<$>> findASTTypeDef (progAllTypeDefsMeta prog) sel.def
11301132
App.SelectionDef sel ->
11311133
Right <<$>> findASTDef (progAllDefs prog) sel.def
11321134

primer/src/Primer/Action.hs

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
{-# LANGUAGE BlockArguments #-}
12
{-# LANGUAGE GADTs #-}
3+
{-# LANGUAGE OverloadedLabels #-}
24
{-# LANGUAGE OverloadedRecordDot #-}
35

46
module Primer.Action (
@@ -17,6 +19,7 @@ module Primer.Action (
1719
uniquifyDefName,
1820
toProgActionInput,
1921
toProgActionNoInput,
22+
applyActionsToField,
2023
) where
2124

2225
import Foreword hiding (mod)
@@ -27,10 +30,11 @@ import Data.Bifunctor.Swap qualified as Swap
2730
import Data.Generics.Product (typed)
2831
import Data.List (findIndex)
2932
import Data.List.NonEmpty qualified as NE
33+
import Data.Map (insert)
3034
import Data.Map.Strict qualified as Map
3135
import Data.Set qualified as Set
3236
import Data.Text qualified as T
33-
import Optics (set, (%), (?~), (^.), (^?), _Just)
37+
import Optics (over, set, (%), (?~), (^.), (^?), _Just)
3438
import Primer.Action.Actions (Action (..), Movement (..), QualifiedText)
3539
import Primer.Action.Available qualified as Available
3640
import Primer.Action.Errors (ActionError (..))
@@ -40,6 +44,7 @@ import Primer.App.Base (
4044
NodeSelection (..),
4145
NodeType (..),
4246
Selection' (..),
47+
TypeDefConsFieldSelection (..),
4348
TypeDefConsSelection (..),
4449
TypeDefNodeSelection (..),
4550
TypeDefSelection (..),
@@ -60,6 +65,7 @@ import Primer.Core (
6065
Type' (..),
6166
TypeCache (..),
6267
TypeCacheBoth (..),
68+
TypeMeta,
6369
ValConName,
6470
baseName,
6571
bindName,
@@ -102,7 +108,7 @@ import Primer.Def (
102108
Def (..),
103109
DefMap,
104110
)
105-
import Primer.Module (Module, insertDef)
111+
import Primer.Module (Module (moduleTypes), insertDef)
106112
import Primer.Name (Name, NameCounter, unName, unsafeMkName)
107113
import Primer.Name.Fresh (
108114
isFresh,
@@ -238,6 +244,49 @@ applyActionsToTypeSig smartHoles imports (mod, mods) (defName, def) actions =
238244
-- In this case we just refocus on the top of the type.
239245
z -> maybe unwrapError pure (focusType (unfocusLoc z))
240246

247+
applyActionsToField ::
248+
(MonadFresh ID m, MonadFresh NameCounter m) =>
249+
SmartHoles ->
250+
[Module] ->
251+
(Module, [Module]) ->
252+
(Name, ValConName, Int, ASTTypeDef TypeMeta) ->
253+
[Action] ->
254+
m (Either ActionError ([Module], TypeZ))
255+
applyActionsToField smartHoles imports (mod, mods) (tyName, conName', index, tyDef) actions =
256+
runReaderT
257+
go
258+
(buildTypingContextFromModules (mod : mods <> imports) smartHoles)
259+
& runExceptT
260+
where
261+
go :: ActionM m => m ([Module], TypeZ)
262+
go = do
263+
(valCons, zt) <-
264+
(maybe (throwError $ InternalFailure "applyActionsToField: con name not found") pure =<<) $
265+
flip (findAndAdjustA' ((== conName') . valConName)) (astTypeDefConstructors tyDef) \(ValCon _ ts) -> do
266+
(t, zt) <-
267+
maybe (throwError $ InternalFailure "applyActionsToField: con field index out of bounds") pure
268+
=<< flip (adjustAtA' index) ts \fieldType -> do
269+
zt <- withWrappedType fieldType \zt ->
270+
foldlM (\l -> local addParamsToCxt . flip applyActionAndSynth l) (InType zt) actions
271+
pure (target (top zt), zt)
272+
pure (ValCon conName' t, zt)
273+
let mod' = mod{moduleTypes = insert tyName (TypeDefAST tyDef{astTypeDefConstructors = valCons}) $ moduleTypes mod}
274+
(,zt) <$> checkEverything smartHoles (CheckEverything{trusted = imports, toCheck = mod' : mods})
275+
addParamsToCxt :: TC.Cxt -> TC.Cxt
276+
addParamsToCxt = over #localCxt (<> Map.fromList (map (bimap unLocalName TC.K) $ astTypeDefParameters tyDef))
277+
withWrappedType :: ActionM m => Type -> (TypeZ -> m Loc) -> m TypeZ
278+
withWrappedType ty f = do
279+
wrappedType <- ann emptyHole (pure ty)
280+
let unwrapError = throwError $ InternalFailure "applyActionsToField: failed to unwrap type"
281+
wrapError = throwError $ InternalFailure "applyActionsToField: failed to wrap type"
282+
focusedType = focusType $ focus wrappedType
283+
case focusedType of
284+
Nothing -> wrapError
285+
Just wrappedTy ->
286+
f wrappedTy >>= \case
287+
InType zt -> pure zt
288+
z -> maybe unwrapError pure (focusType (unfocusLoc z))
289+
241290
data Refocus = Refocus
242291
{ pre :: Loc
243292
, post :: Expr
@@ -866,7 +915,7 @@ renameForall b zt = case target zt of
866915
-- | Convert a high-level 'Available.NoInputAction' to a concrete sequence of 'ProgAction's.
867916
toProgActionNoInput ::
868917
DefMap ->
869-
Either (ASTTypeDef ()) ASTDef ->
918+
Either (ASTTypeDef a) ASTDef ->
870919
Selection' ID ->
871920
Available.NoInputAction ->
872921
Either ActionError [ProgAction]
@@ -958,15 +1007,21 @@ toProgActionNoInput defs def0 sel0 = \case
9581007
typeNodeSel >>= \case
9591008
(s0, TypeDefConsNodeSelection s) -> pure (s0, s)
9601009
_ -> Left NeedTypeDefConsSelection
1010+
conFieldSel = do
1011+
(ty, s) <- conSel
1012+
maybe (Left NeedTypeDefConsFieldSelection) (pure . (ty,s.con,)) s.field
9611013
toProgAction actions = do
962-
sel <- termSel
963-
toProg' actions sel.def <$> maybeToEither NoNodeSelection sel.node
1014+
case sel0 of
1015+
SelectionDef sel -> toProg' actions sel.def <$> maybeToEither NoNodeSelection sel.node
1016+
SelectionTypeDef _ -> do
1017+
(t, c, f) <- conFieldSel
1018+
pure [ConFieldAction t c f.index $ SetCursor f.meta : actions]
9641019
termDef = first (const NeedTermDef) def0
9651020

9661021
-- | Convert a high-level 'Available.InputAction', and associated 'Available.Option',
9671022
-- to a concrete sequence of 'ProgAction's.
9681023
toProgActionInput ::
969-
Either (ASTTypeDef ()) ASTDef ->
1024+
Either (ASTTypeDef a) ASTDef ->
9701025
Selection' ID ->
9711026
Available.Option ->
9721027
Available.InputAction ->
@@ -1079,9 +1134,15 @@ toProgActionInput def0 sel0 opt0 = \case
10791134
optGlobal = case opt0.context of
10801135
Nothing -> Left $ NeedLocal opt0
10811136
Just q -> pure (q, opt0.option)
1137+
conFieldSel = do
1138+
(ty, s) <- conSel
1139+
maybe (Left NeedTypeDefConsFieldSelection) (pure . (ty,s.con,)) s.field
10821140
toProg actions = do
1083-
sel <- termSel
1084-
toProg' actions sel.def <$> maybeToEither NoNodeSelection sel.node
1141+
case sel0 of
1142+
SelectionDef sel -> toProg' actions sel.def <$> maybeToEither NoNodeSelection sel.node
1143+
SelectionTypeDef _ -> do
1144+
(t, c, f) <- conFieldSel
1145+
pure [ConFieldAction t c f.index $ SetCursor f.meta : actions]
10851146
offerRefined = do
10861147
id <- nodeID
10871148
def <- termDef

primer/src/Primer/Action/Available.hs

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ import Primer.App.Base (
4343
NodeSelection (..),
4444
NodeType (..),
4545
Selection' (..),
46+
TypeDefConsFieldSelection (..),
47+
TypeDefConsSelection (..),
48+
TypeDefNodeSelection (..),
49+
TypeDefSelection (..),
50+
getTypeDefConFieldType,
4651
)
4752
import Primer.Core (
4853
Expr,
@@ -53,6 +58,8 @@ import Primer.Core (
5358
ModuleName (unModuleName),
5459
Type,
5560
Type' (..),
61+
TypeMeta,
62+
ValConName,
5663
getID,
5764
unLocalName,
5865
_bindMeta,
@@ -98,6 +105,7 @@ import Primer.Zipper (
98105
focusOn,
99106
focusOnTy,
100107
locToEither,
108+
target,
101109
)
102110

103111
-- | An offered action.
@@ -337,9 +345,15 @@ forTypeDefConsNode l Editable =
337345
forTypeDefConsFieldNode ::
338346
Level ->
339347
Editable ->
348+
ASTTypeDef TypeMeta ->
349+
ValConName ->
350+
Int ->
351+
ID ->
340352
[Action]
341-
forTypeDefConsFieldNode _ NonEditable = mempty
342-
forTypeDefConsFieldNode l Editable = sortByPriority l []
353+
forTypeDefConsFieldNode _ NonEditable _ _ _ _ = mempty
354+
forTypeDefConsFieldNode l Editable def con index id =
355+
maybe mempty (sortByPriority l . forType l) $
356+
findType id =<< getTypeDefConFieldType def con index
343357

344358
-- | An input for an 'InputAction'.
345359
data Option = Option
@@ -375,7 +389,7 @@ options ::
375389
DefMap ->
376390
Cxt ->
377391
Level ->
378-
Either (ASTTypeDef ()) ASTDef ->
392+
Either (ASTTypeDef TypeMeta) ASTDef ->
379393
Selection' ID ->
380394
InputAction ->
381395
-- | Returns 'Nothing' if an ID was required but not passed, passed but not found in the tree,
@@ -445,9 +459,6 @@ options typeDefs defs cxt level def0 sel0 = \case
445459
AddCon ->
446460
pure $ freeVar []
447461
where
448-
defSel = case sel0 of
449-
SelectionDef s -> pure s
450-
SelectionTypeDef _ -> Nothing
451462
freeVar opts = Options{opts, free = FreeVarName}
452463
noFree opts = Options{opts, free = FreeNone}
453464
localOpt = flip Option Nothing . unName
@@ -461,30 +472,47 @@ options typeDefs defs cxt level def0 sel0 = \case
461472
pure $
462473
(first (localOpt . unLocalName) <$> locals)
463474
<> (first globalOpt <$> globals)
464-
findNode = do
465-
sel <- defSel
466-
s <- sel.node
467-
def <- eitherToMaybe def0
468-
case s.nodeType of
469-
BodyNode -> fst <$> findNodeWithParent s.meta (astDefExpr def)
470-
SigNode -> TypeNode <$> findType s.meta (astDefType def)
471-
genNames typeOrKind = do
472-
sel <- defSel
473-
z <- focusNode =<< sel.node
474-
pure $ map localOpt $ flip runReader cxt $ case z of
475-
Left zE -> generateNameExpr typeOrKind zE
476-
Right zT -> generateNameTy typeOrKind zT
477-
varsInScope = do
478-
sel <- defSel
479-
nodeSel <- sel.node
480-
focusNode nodeSel <&> \case
481-
Left zE -> variablesInScopeExpr defs zE
482-
Right zT -> (variablesInScopeTy zT, [], [])
475+
findNode = case sel0 of
476+
SelectionDef sel -> do
477+
nodeSel <- sel.node
478+
def <- eitherToMaybe def0
479+
case nodeSel.nodeType of
480+
BodyNode -> fst <$> findNodeWithParent nodeSel.meta (astDefExpr def)
481+
SigNode -> TypeNode <$> findType nodeSel.meta (astDefType def)
482+
SelectionTypeDef sel -> do
483+
(_, zT) <- conField sel
484+
pure $ TypeNode $ target zT
485+
genNames typeOrKind =
486+
map localOpt . flip runReader cxt <$> case sel0 of
487+
SelectionDef sel -> do
488+
z <- focusNode =<< sel.node
489+
pure $ case z of
490+
Left zE -> generateNameExpr typeOrKind zE
491+
Right zT -> generateNameTy typeOrKind zT
492+
SelectionTypeDef sel -> do
493+
(_, zT) <- conField sel
494+
pure $ generateNameTy typeOrKind zT
495+
varsInScope = case sel0 of
496+
SelectionDef sel -> do
497+
nodeSel <- sel.node
498+
focusNode nodeSel <&> \case
499+
Left zE -> variablesInScopeExpr defs zE
500+
Right zT -> (variablesInScopeTy zT, [], [])
501+
SelectionTypeDef sel -> do
502+
(def, zT) <- conField sel
503+
pure (astTypeDefParameters def <> variablesInScopeTy zT, [], [])
483504
focusNode nodeSel = do
484505
def <- eitherToMaybe def0
485506
case nodeSel.nodeType of
486507
BodyNode -> Left . locToEither <$> focusOn nodeSel.meta (astDefExpr def)
487508
SigNode -> fmap Right $ focusOnTy nodeSel.meta $ astDefType def
509+
conField sel = do
510+
(con, field) <- case sel of
511+
TypeDefSelection _ (Just (TypeDefConsNodeSelection (TypeDefConsSelection con (Just field)))) ->
512+
Just (con, field)
513+
_ -> Nothing
514+
def <- either Just (const Nothing) def0
515+
map (def,) $ focusOnTy field.meta =<< getTypeDefConFieldType def con field.index
488516
-- Extract the source of the function type we were checked at
489517
-- i.e. the type that a lambda-bound variable would have here
490518
lamVarTy = \case

0 commit comments

Comments
 (0)