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

Port "available actions" code from Vonnegut frontend #159

Merged
23 commits merged into from
Oct 12, 2021
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8aeb322
[refactor] Move expr/type metadata lenses in to `Core`
georgefst Sep 28, 2021
9f9446e
Implement some useful lenses and traversals
georgefst Sep 29, 2021
ea17808
Add a function for unfolding function types
georgefst Oct 4, 2021
e6aae32
[refactor] Move some types from `Primer.App` module to `Primer.Action`
georgefst Oct 5, 2021
9ac4b95
Copy some useful definitions from old frontend's `Vonnegut.Projection…
georgefst Oct 5, 2021
fb863b4
Port action priorities from old frontend
georgefst Oct 4, 2021
f1b4c2a
Port functions for computing available actions from old frontend
georgefst Oct 5, 2021
389c718
Remove unused definition `findNode`
georgefst Oct 5, 2021
3eeaa8e
[refactor] Parameterise `OfferedAction` over the underlying action type
georgefst Oct 6, 2021
43ea107
Remove unused metadata arguments
georgefst Oct 6, 2021
0d8e966
Silence unused variable warnings
georgefst Oct 6, 2021
cb4bd98
Use Haddock syntax in all code recently ported from old frontend
georgefst Oct 7, 2021
ea5499e
HLint pass
georgefst Oct 7, 2021
c2c3042
Use haddock subheadings in `Primer.Action.Priorities`
georgefst Oct 7, 2021
05a1456
Add un-utilised action modules to weeder roots
georgefst Oct 7, 2021
65d246d
Add warnings about `_exprMetaLens` and `_typeMetaLens` not being recu…
georgefst Oct 7, 2021
031429b
[refactor] Implement more lenses via generics
georgefst Oct 12, 2021
4326bbf
Remove no-longer relevant comment about concurrency
georgefst Oct 12, 2021
3f4a1b4
Replace `destructive` tag on actions with a bespoke enum
georgefst Oct 12, 2021
d4b3038
Clarify comment about `optics` lacking `failing` for `AffineTraversal`s
georgefst Oct 12, 2021
0fd4b96
[refactor] Move some definitions out to new `Primer.Name.Fresh` module
georgefst Oct 12, 2021
eee3d36
Move `Question` type in to `Questions` module
georgefst Oct 12, 2021
ad10683
Avoid using partial field selectors in `UserInput`
georgefst Oct 12, 2021
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
111 changes: 111 additions & 0 deletions primer/src/Primer/Action.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ module Primer.Action (
moveExpr,
mkAvoidForFreshName,
mkAvoidForFreshNameTy,
OfferedAction (..),
FunctionFiltering (..),
UserInput (..),
ActionInput (..),
ActionName (..),
Level (..),
nameString,
uniquifyDefName,
) where

import Foreword
Expand All @@ -26,6 +34,7 @@ import Data.Generics.Product (typed)
import Data.List (delete, findIndex, lookup)
import qualified Data.Map.Strict as Map
import qualified Data.Set as S
import qualified Data.Text as T
import Optics (set, (%), (?~))
import Primer.Core (
Def (..),
Expand Down Expand Up @@ -119,6 +128,108 @@ import Primer.Zipper (
)
import Primer.ZipperCxt (localVariablesInScopeExpr)

-- An OfferedAction is an option that we show to the user.
-- It may require some user input (e.g. to choose what to name a binder, or
-- choose which variable to insert).
-- If picked, it will submit a particular set of actions to the backend.
data OfferedAction = OfferedAction
{ name :: ActionName
, description :: Text
, input :: ActionInput
, priority :: Int
, -- XXX dhess: this is a hack so that we can render "destructive"
-- actions differently than the others. I suggest we find a better
-- way to do this in the new frontend.
destructive :: Bool
}
georgefst marked this conversation as resolved.
Show resolved Hide resolved

-- | Filter on variables and constructors according to whether they
-- | have a function type.
data FunctionFiltering
= Everything
| OnlyFunctions
| NoFunctions

-- Further user input is sometimes required to construct an action.
-- For example, when inserting a constructor the user must tell us what
-- constructor.
-- This type models that input and the corresponding output.
-- Currently we can only take a single input per action - in the future this
-- may need to be extended to support multiple inputs.
-- This type is parameterised because we may need it for other things in
-- future, and because it lets us derive a useful functor instance.
data UserInput a
= ChooseConstructor FunctionFiltering (Text -> a)
| ChooseTypeConstructor (Text -> a)
| -- ChooseOrEnterName: Renders a choice between some options (as buttons),
-- plus a textbox to manually enter a name
ChooseOrEnterName
-- prompt: prompt to show the user,
-- e.g. "choose a name, or enter your own"
{ prompt :: Text
, -- A bunch of options
options :: [Name]
, -- What to do with whatever name is chosen
choose :: Name -> a
georgefst marked this conversation as resolved.
Show resolved Hide resolved
}
| ChooseVariable FunctionFiltering (Either Text ID -> a)
| ChooseTypeVariable (Text -> a)
deriving (Functor)

data ActionInput where
InputRequired :: UserInput [ProgAction] -> ActionInput
NoInputRequired :: [ProgAction] -> ActionInput
AskQuestion :: Question a -> (a -> ActionInput) -> ActionInput

-- | Some actions' names are meant to be rendered as code, others as
-- | prose.
data ActionName
= Code Text
| Prose Text

-- | The current programming "level". This setting determines which
-- | actions are displayed to the student, the labels on UI elements,
-- | etc.
data Level
= -- | Bare minimum features to define sum types, and functions on
-- | those types using simple pattern matching.
Beginner
| -- | Function application & monomorphic HoF. (Support for the latter
-- | should probably be split into a separate level.)
Intermediate
| -- | All features.
Expert

-- | Sigh, yes, this is required so that Safari doesn't try to
-- | autocomplete these fields with your contact data.
-- |
-- | See
-- | https://stackoverflow.com/questions/43058018/how-to-disable-autocomplete-in-address-fields-for-safari
-- |
-- | Note that, according to a comment in the above StackOverflow
-- | post, this is screenreader-safe.
nameString :: Text
nameString = "n" <> T.singleton '\x200C' <> "ame"

-- | Given a definition name and a program, return a unique variant of
-- | that name. Note that if no definition of the given name already
-- | exists in the program, this function will return the same name
-- | it's been given.
-- |
-- | Note: this is not concurrency-safe! There's probably no
-- | reasonable way to do this atomically without also creating the
-- | definition at the same time.
uniquifyDefName :: Text -> Map ID Def -> Text
georgefst marked this conversation as resolved.
Show resolved Hide resolved
uniquifyDefName name' defs =
if notElem name' avoid
then name'
else
let go i = if notElem (name' <> "_" <> show i) avoid then (name' <> "_" <> show i) else go (i + 1)
in go (1 :: Int)
where
avoid :: [Text]
avoid = Map.elems $ map (unName . defName) defs

Copy link
Contributor Author

@georgefst georgefst Oct 7, 2021

Choose a reason for hiding this comment

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

This and Level possibly belong somewhere more general than Primer.Action. But this seemed a convenient place to put them for now.

-- | Core actions.
-- These describe edits to the core AST.
-- Some of them take Text arguments rather than Name because they represent
Expand Down