mirror of
https://github.com/unisonweb/unison.git
synced 2024-09-27 02:07:14 +03:00
Revamping editor, added Editor module which is currently compiling, still need to wire up all events
This commit is contained in:
parent
0468249700
commit
54489f9986
36
editor/Elmz/Trie.elm
Normal file
36
editor/Elmz/Trie.elm
Normal 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
103
editor/Unison/Editor.elm
Normal 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
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user