Revamping editor, added Editor module which is currently compiling, still need to wire up all events

This commit is contained in:
Paul Chiusano 2015-01-29 11:44:23 -05:00
parent 0468249700
commit 54489f9986
5 changed files with 209 additions and 58 deletions

36
editor/Elmz/Trie.elm Normal file
View File

@ -0,0 +1,36 @@
module Elmz.Trie where
import List
import List ((::))
type Trie k v = Trie (Maybe v) (List (k, Trie k v))
empty : Trie k v
empty = Trie Nothing []
insert : List k -> v -> Trie k v -> Trie k v
insert k v (Trie v0 children) = case k of
[] -> Trie (Just v) children
h :: t -> let (yes,no) = List.partition (\(k2,_) -> k2 == h) children
in case yes of
[] -> Trie v0 ((h, insert t v empty) :: no)
(_,child) :: _ -> Trie v0 ((h, insert t v child) :: no)
lookup : List k -> Trie k v -> Maybe v
lookup k (Trie v children) = case k of
[] -> v
h :: t -> let f (k,v) = if k == h then Just v else Nothing
in case List.filterMap f children of
child :: _ -> lookup t child
[] -> Nothing
delete : List k -> Trie k v -> Trie k v
delete k (Trie v children) = case k of
[] -> Trie Nothing children
h :: t -> let f2 (k,child) = if k == h then (k, delete t child) else (k, child)
in Trie v (List.map f2 children)
contains : List k -> Trie k v -> Bool
contains k t = case lookup k t of
Nothing -> False
Just _ -> True

103
editor/Unison/Editor.elm Normal file
View File

@ -0,0 +1,103 @@
module Unison.Editor (Model) where
import Dict
import Dict (Dict)
import Elmz.Layout (Containment,Layout,Pt,Region)
import Elmz.Layout as Layout
import Elmz.Selection1D as Selection1D
import Elmz.Trie (Trie)
import Elmz.Trie as Trie
import Graphics.Input.Field as Field
import Graphics.Element as Element
import Graphics.Element (Element)
import Result
import Unison.Explorer as Explorer
import Unison.Hash (Hash)
import Unison.Metadata as Metadata
import Unison.Path (Path)
import Unison.Path as Path
import Unison.Scope (Scope)
import Unison.Scope as Scope
import Unison.Styles as Styles
import Unison.Term (Term)
import Unison.Term as Term
import Unison.View as View
import Signal
type alias Model =
{ overall : Term
, scope : Maybe Scope
, explorer : Explorer.Model
, explorerValues : List Term
, explorerSelection : Selection1D.Model
, dependents : Trie Path.E (List Path)
, overrides : Trie Path.E (Layout View.L)
, hashes : Trie Path.E Hash }
type alias Action = Model -> Model
viewTerm : Int -> Term -> Layout View.L
viewTerm availableWidth term =
View.layout term { rootMetadata = Metadata.anonymousTerm
, availableWidth = availableWidth
, metadata h = Metadata.anonymousTerm
, overrides x = Nothing }
type alias Sink a = a -> Signal.Message
type alias Context =
{ availableWidth : Int
, searchbox : Sink Field.Content
, explorerActive : Sink Bool }
view : Context -> Model -> (Layout View.L, Layout (Result Containment Int))
view ctx model =
let termLayout = viewTerm ctx.availableWidth model.overall
highlighted : Maybe Region
highlighted = case model.scope of
Nothing -> Nothing
Just scope -> Layout.region Path.startsWith .path termLayout scope.focus
|> Layout.selectableLub .selectable
highlightLayer : Element
highlightLayer = case highlighted of
Nothing -> Element.empty
Just region -> Styles.selection region
highlightedTermLayout : Layout View.L
highlightedTermLayout =
Layout.transform (\e -> Element.layers [e, highlightLayer]) termLayout
explorerTopLeft : Pt
explorerTopLeft = case highlighted of
Nothing -> Pt 0 0
Just region -> { x = region.topLeft.x, y = region.topLeft.y + region.height }
explorerLayout : Layout (Result Containment Int)
explorerLayout =
Explorer.view explorerTopLeft
ctx.searchbox
ctx.explorerActive
model.explorer
in (highlightedTermLayout, explorerLayout)
--
-- viewExplorer : Model -> Layout
-- view : Int -> Term -> Layout View.L
-- view availableWidth term =
-- let rendered : Signal (L.Layout { path : Path, selectable : Bool })
-- rendered = layout <~ Signals.steady (100 * Time.millisecond) Window.width ~ term
-- in
todo : a
todo = todo
-- might want to just add Panel, Cell constructors
-- Panel Term
{-| For each subpanel path, what are the paths to other subpanels that it depends on? -}
-- localDependencies : (Path -> Maybe (Hash,Term)) -> Term -> Path -> [Path]
-- localDependencies sourceOf e = todo

View File

@ -1,7 +1,7 @@
module Unison.Explorer where
import Debug
import Elmz.Layout (Layout,Pt,Region)
import Elmz.Layout (Layout,Pt,Region,Containment(Inside,Outside))
import Elmz.Layout as Layout
import Elmz.Maybe
import Elmz.Movement as Movement
@ -24,14 +24,6 @@ import Time
import Unison.Styles as Styles
import Window
-- model for overall editor is
-- Term
-- Maybe Scope.Model
-- Explorer.Model
-- can get a bit fancier with tracking dependencies,
-- result of previous evaluations
-- mapping from paths to hashes
type alias Model = Maybe
{ isKeyboardOpen : Bool
, prompt : String
@ -42,9 +34,6 @@ type alias Model = Maybe
type alias Action = Model -> Model
-- actions for setting the instructions,
-- completions, invalid completions, input, and keyboard open
zero : Model
zero = Just
{ isKeyboardOpen = False
@ -54,6 +43,36 @@ zero = Just
, completions = []
, invalidCompletions = [] }
setPrompt : Signal String -> Signal Action
setPrompt event =
let f s model = Maybe.map (\m -> { m | prompt <- s }) model
in Signal.map f event
openKeyboard : Signal () -> Signal Action
openKeyboard event =
let f _ model = Maybe.map (\m -> { m | isKeyboardOpen <- True }) model
in Signal.map f event
setInput : Signal Field.Content -> Signal Action
setInput content =
let f c model = Maybe.map (\m -> { m | input <- c }) model
in Signal.map f content
setInstructions : Signal Element -> Signal Action
setInstructions e =
let f e model = Maybe.map (\m -> { m | instructions <- e }) model
in Signal.map f e
setCompletions : Signal (List Element) -> Signal Action
setCompletions e =
let f e model = Maybe.map (\m -> { m | completions <- e }) model
in Signal.map f e
setInvalidCompletions : Signal (List Element) -> Signal Action
setInvalidCompletions e =
let f e model = Maybe.map (\m -> { m | invalidCompletions <- e }) model
in Signal.map f e
clicks : { tl | click : Signal (), inside : Signal Bool, allowOpen : Signal Bool } -> Signal Action
clicks {click,inside,allowOpen} =
let f inside allowOpen model = case model of
@ -68,35 +87,11 @@ enters {down,allowOpen} =
Just _ -> Nothing
in Signal.sampleOn down (Signal.map2 f down allowOpen)
-- may need another Signal Bool input, which lets the explorer be closed 'externally'
-- or may want a `Signal Pt` which lets origin be moved
-- todo: can replace all Err with the zero Model, and foldp over this to get our Model states
{-
actions : { enter : Signal Bool
, click : Signal ()
, mouse : Signal (Int,Int)
, isOpen : Signal Bool
, upDown : Signal Movement.D1
, completions : Signal
-> Signal ()
-> Signal (Int,Int)
-> Signal Bool
-> Signal Movement.D1
-> Signal (List v)
-> Signal (Action v)
actions {enter,click,mouse,isOpen,upDown} values =
let merge = Signals.mergeWith (\a1 a2 model -> a1 model `Result.andThen` a2)
in completions values `merge`
movements upDown `merge`
clicks click isOpen values `merge`
enters enter values
-}
type alias Sink a = a -> Signal.Message
view : Pt -> Sink Field.Content -> Sink Bool -> Model -> Layout (Maybe Int)
view : Pt -> Sink Field.Content -> Sink Bool -> Model -> Layout (Result Containment Int)
view origin searchbox active model = case model of
Nothing -> Layout.empty Nothing
Nothing -> Layout.empty (Result.Err Outside)
Just s ->
let ok = not (List.isEmpty s.completions)
statusColor = Styles.statusColor ok
@ -105,20 +100,26 @@ view origin searchbox active model = case model of
s.prompt
s.input
insertion = Styles.carotUp 7 statusColor
status = Layout.embed Nothing s.instructions
inside = Result.Err Inside
status = Layout.embed inside s.instructions
|> Layout.transform (Input.clickable (active True))
renderCompletion i e = Layout.embed (Just i) (Input.clickable (active False) e)
invalids = List.map (Layout.embed Nothing) s.invalidCompletions
top = Layout.embed Nothing (Input.clickable (active True) fld)
renderCompletion i e = Layout.embed (Result.Ok i)
(Input.clickable (active False) e)
invalids = List.map (Layout.embed inside) s.invalidCompletions
top = Layout.embed inside (Input.clickable (active True) fld)
|> Layout.transform (Input.clickable (active True))
spacer = Layout.embed Nothing (E.spacer 1 7)
bot = Styles.explorerCells Nothing <|
spacer = Layout.embed inside (E.spacer 1 7)
bot = Styles.explorerCells inside <|
status :: List.indexedMap renderCompletion s.completions
`List.append` invalids
top' = Layout.transform (E.width (Layout.widthOf bot)) top
box = Layout.above Nothing
(Layout.embed Nothing (E.beside (E.spacer 14 1) insertion))
(Layout.above Nothing (Layout.above (Layout.tag top) top' spacer) bot)
box = Layout.above inside
(Layout.embed inside (E.beside (E.spacer 14 1) insertion))
(Layout.above inside (Layout.above (Layout.tag top) top' spacer) bot)
boxTopLeft = origin
h = boxTopLeft.y + Layout.heightOf box + 50
in Layout.container Nothing (boxTopLeft.x + Layout.widthOf box) h boxTopLeft box
in Layout.container (Result.Err Outside)
(boxTopLeft.x + Layout.widthOf box)
h
boxTopLeft
box

View File

@ -9,6 +9,7 @@ import Elmz.Json.Encoder (Encoder)
import Elmz.Json.Decoder as Decoder
import Json.Decode (Decoder)
import Json.Decode as Decode
import String
type E
= Fn -- ^ Points at function in a function application
@ -17,6 +18,16 @@ type E
| Index Int -- ^ Points into a `Vector` literal
type alias Path = List E
type alias Key = String
key : Path -> Key
key path =
let f e = case e of
Fn -> "Fn"
Arg -> "Arg"
Body -> "Body"
Index i -> toString i
in String.join "," (List.map f path)
snoc : Path -> E -> Path
snoc p e = p ++ [e]

View File

@ -97,19 +97,19 @@ explorerCells k ls =
in L.intersperseVertical hsep cs |>
L.transform (\e -> E.layers [e, outlineOf midnightBlue 6 e])
selection : Element -> Region -> Element
selection e = selectionLayer highlight { width = E.widthOf e, height = E.heightOf e }
selection : Region -> Element
selection = selectionLayer highlight
explorerSelection : Element -> Region -> Element
explorerSelection e = selectionLayer highlightExplorer { width = E.widthOf e, height = E.heightOf e }
explorerSelection : Region -> Element
explorerSelection = selectionLayer highlightExplorer
selectionLayer : (Int -> Int -> Element) -> { width : Int, height : Int } -> Region -> Element
selectionLayer highlight l r =
selectionLayer : (Int -> Int -> Element) -> Region -> Element
selectionLayer highlight r =
let
hl = highlight r.width r.height
n = 1
in E.container l.width
l.height
in E.container (r.topLeft.x + r.width)
(r.topLeft.y + r.height)
(E.topLeftAt (E.absolute (r.topLeft.x)) (E.absolute (r.topLeft.y)))
hl
@ -149,8 +149,8 @@ contain e =
spinner : Signal Element
spinner =
let pct n = toFloat (n%30) / 30.0
t = Signal.map pct (Signals.count (Time.fps 30))
let pct n = toFloat (n%60) / 60.0
t = Signal.map pct (Signals.count (Time.fps 60))
rect = E.color midnightBlue (E.spacer 5 10)
sep = E.spacer 1 1
render pct = E.flow E.right