1{-# LANGUAGE OverloadedStrings #-}
2module Generate.JavaScript.Expression
14import qualified Data.IntMap as IntMap
15import qualified Data.List as List
16import qualified Data.Map as Map
17import qualified Data.Text.Encoding as Text
19import qualified AST.Canonical as Can
20import qualified AST.Optimized as Opt
21import qualified AST.Module.Name as ModuleName
22import qualified Data.Index as Index
23import qualified Elm.Compiler.Type as Type
24import qualified Elm.Compiler.Type.Extract as Extract
25import qualified Elm.Interface as I
26import qualified Elm.Name as N
27import qualified Elm.Package as Pkg
28import qualified Elm.Compiler.Version as Version
29import qualified Generate.JavaScript.Builder as JS
30import qualified Generate.JavaScript.Name as Name
31import qualified Json.Encode as Encode
32import qualified Optimize.DecisionTree as DT
33import qualified Reporting.Region as R
47generateJsExpr :: Mode -> Opt.Expr -> JS.Expr
48generateJsExpr mode expression =
49 codeToExpr (generate mode expression)
52generate :: Mode -> Opt.Expr -> Code
53generate mode expression =
62 JS.Call toChar [ JS.String (Text.encodeUtf8Builder char) ]
65 JS.String (Text.encodeUtf8Builder char)
68 JsExpr $ JS.String (Text.encodeUtf8Builder string)
74 JsExpr $ JS.Float float
77 JsExpr $ JS.Ref (Name.fromLocal name)
79 Opt.VarGlobal (Opt.Global home name) ->
80 JsExpr $ JS.Ref (Name.fromGlobal home name)
82 Opt.VarEnum (Opt.Global home name) index ->
85 JsExpr $ JS.Ref (Name.fromGlobal home name)
88 JsExpr $ JS.Int (Index.toMachine index)
90 Opt.VarBox (Opt.Global home name) ->
93 Name.Debug _ -> Name.fromGlobal home name
94 Name.Prod _ _ -> Name.fromGlobal ModuleName.basics N.identity
96 Opt.VarCycle home name ->
97 JsExpr $ JS.Call (JS.Ref (Name.fromCycle home name)) []
99 Opt.VarDebug name home region unhandledValueName ->
100 JsExpr $ generateDebug name home region unhandledValueName
102 Opt.VarKernel home name ->
103 JsExpr $ JS.Ref (Name.fromKernel home name)
108 JsExpr $ JS.Ref (Name.fromKernel N.list "Nil")
113 (JS.Ref (Name.fromKernel N.list "fromArray"))
114 [ JS.Array $ map (generateJsExpr mode) entries
117 Opt.Function args body ->
118 generateFunction (map Name.fromLocal args) (generate mode body)
120 Opt.Call func args ->
121 JsExpr $ generateCall mode func args
123 Opt.TailCall name args ->
124 JsBlock $ generateTailCall mode name args
126 Opt.If branches final ->
127 generateIf mode branches final
131 generateDef mode def : codeToStmtList (generate mode body)
133 Opt.Destruct (Opt.Destructor name path) body ->
135 pathExpr = generatePath mode path
136 pathDef = JS.Var [ (Name.fromLocal name, Just pathExpr) ]
138 JsBlock $ pathDef : codeToStmtList (generate mode body)
140 Opt.Case label root decider jumps ->
141 JsBlock $ generateCase mode label root decider jumps
143 Opt.Accessor field ->
144 JsExpr $ JS.Function Nothing [Name.dollar]
146 JS.Access (JS.Ref Name.dollar) (Name.fromField mode field)
149 Opt.Access record field ->
150 JsExpr $ JS.Access (generateJsExpr mode record) (Name.fromField mode field)
152 Opt.Update record fields ->
154 JS.Call (JS.Ref (Name.fromKernel N.utils "update"))
155 [ generateJsExpr mode record
156 , generateRecord mode fields
160 JsExpr $ generateRecord mode fields
165 JsExpr $ JS.Ref (Name.fromKernel N.utils "Tuple0")
170 Opt.Tuple a b maybeC ->
174 JS.Call (JS.Ref (Name.fromKernel N.utils "Tuple2"))
175 [ generateJsExpr mode a
176 , generateJsExpr mode b
180 JS.Call (JS.Ref (Name.fromKernel N.utils "Tuple3"))
181 [ generateJsExpr mode a
182 , generateJsExpr mode b
183 , generateJsExpr mode c
187 let string = JS.String (Text.encodeUtf8Builder src) in
188 JsExpr $ JS.Object [ ( Name.fromLocal "src", string ) ]
200codeToExpr :: Code -> JS.Expr
206 JsBlock [ JS.Return (Just expr) ] ->
210 JS.Call (JS.Function Nothing [] stmts) []
213codeToStmtList :: Code -> [JS.Stmt]
216 JsExpr (JS.Call (JS.Function Nothing [] stmts) []) ->
220 [ JS.Return (Just expr) ]
226codeToStmt :: Code -> JS.Stmt
229 JsExpr (JS.Call (JS.Function Nothing [] stmts) []) ->
233 JS.Return (Just expr)
246{-# NOINLINE toChar #-}
249 JS.Ref (Name.fromKernel N.utils "chr")
256generateCtor :: Mode -> N.Name -> Index.ZeroBased -> Int -> Code
257generateCtor mode name index arity =
260 Index.indexedMap (\i _ -> Name.fromIndex i) [1 .. arity]
264 Name.Debug _ -> JS.String (N.toBuilder name)
265 Name.Prod _ _ -> JS.Int (Index.toMachine index)
267 generateFunction argNames $ JsExpr $ JS.Object $
268 (Name.dollar, ctorTag) : map (\n -> (n, JS.Ref n)) argNames
275generateRecord :: Mode -> Map.Map N.Name Opt.Expr -> JS.Expr
276generateRecord mode fields =
278 toPair (field, value) =
279 (Name.fromField mode field, generateJsExpr mode value)
281 JS.Object (map toPair (Map.toList fields))
288generateDebug :: N.Name -> ModuleName.Canonical -> R.Region -> Maybe N.Name -> JS.Expr
289generateDebug name (ModuleName.Canonical _ home) region unhandledValueName =
290 if name /= "crash" then
291 JS.Ref (Name.fromGlobal ModuleName.debug name)
293 case unhandledValueName of
295 JS.Call (JS.Ref (Name.fromKernel N.utils "crash")) $
296 [ JS.String (N.toBuilder home)
297 , regionToJsExpr region
301 JS.Call (JS.Ref (Name.fromKernel N.utils "crashCase")) $
302 [ JS.String (N.toBuilder home)
303 , regionToJsExpr region
304 , JS.Ref (Name.fromLocal valueName)
308regionToJsExpr :: R.Region -> JS.Expr
309regionToJsExpr (R.Region start end) =
311 [ ( Name.fromLocal "start", positionToJsExpr start )
312 , ( Name.fromLocal "end", positionToJsExpr end )
316positionToJsExpr :: R.Position -> JS.Expr
317positionToJsExpr (R.Position line column) =
319 [ ( Name.fromLocal "line", JS.Int line )
320 , ( Name.fromLocal "column", JS.Int column )
328generateFunction :: [Name.Name] -> Code -> Code
329generateFunction args body =
330 case IntMap.lookup (length args) funcHelpers of
334 [ JS.Function Nothing args $
341 JsExpr $ JS.Function Nothing [arg] $
344 foldr addArg body args
347{-# NOINLINE funcHelpers #-}
348funcHelpers :: IntMap.IntMap JS.Expr
351 map (\n -> (n, JS.Ref (Name.makeF n))) [2..9]
358generateCall :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
359generateCall mode func args =
361 Opt.VarGlobal global@(Opt.Global (ModuleName.Canonical pkg _) _) | pkg == Pkg.core ->
362 generateCoreCall mode global args
367 generateCallHelp mode func args
372 generateJsExpr mode arg
375 generateCallHelp mode func args
378 generateCallHelp mode func args
381generateCallHelp :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
382generateCallHelp mode func args =
384 (generateJsExpr mode func)
385 (map (generateJsExpr mode) args)
388generateGlobalCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
389generateGlobalCall home name args =
390 generateNormalCall (JS.Ref (Name.fromGlobal home name)) args
393generateNormalCall :: JS.Expr -> [JS.Expr] -> JS.Expr
394generateNormalCall func args =
395 case IntMap.lookup (length args) callHelpers of
397 JS.Call helper (func:args)
400 List.foldl' (\f a -> JS.Call f [a]) func args
403{-# NOINLINE callHelpers #-}
404callHelpers :: IntMap.IntMap JS.Expr
407 map (\n -> (n, JS.Ref (Name.makeA n))) [2..9]
414generateCoreCall :: Mode -> Opt.Global -> [Opt.Expr] -> JS.Expr
415generateCoreCall mode (Opt.Global home@(ModuleName.Canonical _ moduleName) name) args =
416 if moduleName == N.basics then
417 generateBasicsCall mode home name args
419 else if moduleName == N.bitwise then
420 generateBitwiseCall home name (map (generateJsExpr mode) args)
422 else if moduleName == N.tuple then
423 generateTupleCall home name (map (generateJsExpr mode) args)
425 else if moduleName == N.jsArray then
426 generateJsArrayCall home name (map (generateJsExpr mode) args)
429 generateGlobalCall home name (map (generateJsExpr mode) args)
432generateTupleCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
433generateTupleCall home name args =
437 "first" -> JS.Access value (Name.fromLocal "a")
438 "second" -> JS.Access value (Name.fromLocal "b")
439 _ -> generateGlobalCall home name args
442 generateGlobalCall home name args
445generateJsArrayCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
446generateJsArrayCall home name args =
448 [entry] | name == "singleton" -> JS.Array [entry]
449 [index, array] | name == "unsafeGet" -> JS.Index array index
450 _ -> generateGlobalCall home name args
453generateBitwiseCall :: ModuleName.Canonical -> N.Name -> [JS.Expr] -> JS.Expr
454generateBitwiseCall home name args =
458 "complement" -> JS.Prefix JS.PrefixBNot arg
459 _ -> generateGlobalCall home name args
463 "and" -> JS.Infix JS.OpBAnd left right
464 "or" -> JS.Infix JS.OpBOr left right
465 "xor" -> JS.Infix JS.OpBXor left right
466 "shiftLeftBy" -> JS.Infix JS.OpLShift right left
467 "shiftRightBy" -> JS.Infix JS.OpSpRShift right left
468 "shiftRightZfBy" -> JS.Infix JS.OpZfRShift right left
469 _ -> generateGlobalCall home name args
472 generateGlobalCall home name args
475generateBasicsCall :: Mode -> ModuleName.Canonical -> N.Name -> [Opt.Expr] -> JS.Expr
476generateBasicsCall mode home name args =
479 let arg = generateJsExpr mode elmArg in
481 "not" -> JS.Prefix JS.PrefixLNot arg
482 "negate" -> JS.Prefix JS.PrefixMinus arg
484 "truncate" -> JS.Infix JS.OpBOr arg (JS.Int 0)
485 _ -> generateGlobalCall home name [arg]
487 [elmLeft, elmRight] ->
489 "composeL" -> decomposeL mode elmLeft [elmRight]
490 "composeR" -> decomposeR mode [elmLeft] elmRight
491 "append" -> append mode elmLeft elmRight
492 "apL" -> generateJsExpr mode $ apply elmLeft elmRight
493 "apR" -> generateJsExpr mode $ apply elmRight elmLeft
496 left = generateJsExpr mode elmLeft
497 right = generateJsExpr mode elmRight
500 "add" -> JS.Infix JS.OpAdd left right
501 "sub" -> JS.Infix JS.OpSub left right
502 "mul" -> JS.Infix JS.OpMul left right
503 "fdiv" -> JS.Infix JS.OpDiv left right
504 "idiv" -> JS.Infix JS.OpBOr (JS.Infix JS.OpDiv left right) (JS.Int 0)
505 "eq" -> equal left right
506 "neq" -> notEqual left right
507 "lt" -> cmp JS.OpLT JS.OpLT 0 left right
508 "gt" -> cmp JS.OpGT JS.OpGT 0 left right
509 "le" -> cmp JS.OpLEq JS.OpLT 1 left right
510 "ge" -> cmp JS.OpGEq JS.OpGT (-1) left right
511 "or" -> JS.Infix JS.OpLOr left right
512 "and" -> JS.Infix JS.OpLAnd left right
513 "xor" -> JS.Infix JS.OpStrictNEq left right
514 _ -> generateGlobalCall home name [left, right]
517 generateGlobalCall home name (map (generateJsExpr mode) args)
520equal :: JS.Expr -> JS.Expr -> JS.Expr
522 if isLiteral left || isLiteral right then
525 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
528notEqual :: JS.Expr -> JS.Expr -> JS.Expr
530 if isLiteral left || isLiteral right then
533 JS.Prefix JS.PrefixLNot $
534 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
537cmp :: JS.InfixOp -> JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> JS.Expr
538cmp idealOp backupOp backupInt left right =
539 if isLiteral left || isLiteral right then
540 JS.Infix idealOp left right
543 (JS.Call (JS.Ref (Name.fromKernel N.utils "cmp")) [left, right])
547isLiteral :: JS.Expr -> Bool
566decomposeL :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
567decomposeL mode expr funcs =
569 Opt.Call (Opt.VarGlobal (Opt.Global home "composeL")) [left, func]
570 | home == ModuleName.basics ->
571 decomposeL mode left (func:funcs)
574 generateJsExpr mode $
575 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
578decomposeR :: Mode -> [Opt.Expr] -> Opt.Expr -> JS.Expr
579decomposeR mode funcs expr =
581 Opt.Call (Opt.VarGlobal (Opt.Global home "composeR")) [func, right]
582 | home == ModuleName.basics ->
583 decomposeR mode (func:funcs) right
586 generateJsExpr mode $
587 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
590apply :: Opt.Expr -> Opt.Expr -> Opt.Expr
593 Opt.Accessor field ->
594 Opt.Access value field
597 Opt.Call f (args ++ [value])
600 Opt.Call func [value]
603append :: Mode -> Opt.Expr -> Opt.Expr -> JS.Expr
604append mode left right =
605 let seqs = generateJsExpr mode left : toSeqs mode right in
606 if any isStringLiteral seqs then
607 foldr1 (JS.Infix JS.OpAdd) seqs
612jsAppend :: JS.Expr -> JS.Expr -> JS.Expr
614 JS.Call (JS.Ref (Name.fromKernel N.utils "ap")) [a, b]
617toSeqs :: Mode -> Opt.Expr -> [JS.Expr]
620 Opt.Call (Opt.VarGlobal (Opt.Global home "append")) [left, right]
621 | home == ModuleName.basics ->
622 generateJsExpr mode left : toSeqs mode right
625 [generateJsExpr mode expr]
628isStringLiteral :: JS.Expr -> Bool
629isStringLiteral expr =
639-- SIMPLIFY INFIX OPERATORS
642strictEq :: JS.Expr -> JS.Expr -> JS.Expr
646 JS.Prefix JS.PrefixLNot right
649 if bool then right else JS.Prefix JS.PrefixLNot right
654 JS.Prefix JS.PrefixLNot left
657 if bool then left else JS.Prefix JS.PrefixLNot left
660 JS.Infix JS.OpStrictEq left right
663strictNEq :: JS.Expr -> JS.Expr -> JS.Expr
664strictNEq left right =
670 if bool then JS.Prefix JS.PrefixLNot right else right
678 if bool then JS.Prefix JS.PrefixLNot left else left
681 JS.Infix JS.OpStrictNEq left right
688-- TODO check if JS minifiers collapse unnecessary temporary variables
690generateTailCall :: Mode -> N.Name -> [(N.Name, Opt.Expr)] -> [JS.Stmt]
691generateTailCall mode name args =
693 toTempVars (argName, arg) =
694 ( Name.makeTemp argName, Just (generateJsExpr mode arg) )
696 toRealVars (argName, _) =
698 JS.Assign (JS.LRef (Name.fromLocal argName)) (JS.Ref (Name.makeTemp argName))
700 JS.Var (map toTempVars args)
701 : map toRealVars args
702 ++ [ JS.Continue (Just (Name.fromLocal name)) ]
709generateDef :: Mode -> Opt.Def -> JS.Stmt
710generateDef mode def =
713 JS.Var [ (Name.fromLocal name, Just (generateJsExpr mode body)) ]
715 Opt.TailDef name argNames body ->
716 JS.Var [ (Name.fromLocal name, Just (codeToExpr (generateTailDef mode name argNames body))) ]
719generateTailDef :: Mode -> N.Name -> [N.Name] -> Opt.Expr -> Code
720generateTailDef mode name argNames body =
721 generateFunction (map Name.fromLocal argNames) $ JsBlock $
722 [ JS.Labelled (Name.fromLocal name) $
723 JS.While (JS.Bool True) $
724 codeToStmt $ generate mode body
732generatePath :: Mode -> Opt.Path -> JS.Expr
733generatePath mode path =
735 Opt.Index index subPath ->
736 JS.Access (generatePath mode subPath) (Name.fromIndex index)
739 JS.Ref (Name.fromLocal name)
741 Opt.Field field subPath ->
742 JS.Access (generatePath mode subPath) (Name.fromField mode field)
747 JS.Access (generatePath mode subPath) (Name.fromIndex Index.first)
750 generatePath mode subPath
757generateIf :: Mode -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Code
758generateIf mode givenBranches givenFinal =
761 crushIfs givenBranches givenFinal
763 convertBranch (condition, expr) =
764 ( generateJsExpr mode condition
768 branchExprs = map convertBranch branches
769 finalCode = generate mode final
771 if isBlock finalCode || any (isBlock . snd) branchExprs then
772 JsBlock [ foldr addStmtIf (codeToStmt finalCode) branchExprs ]
774 JsExpr $ foldr addExprIf (codeToExpr finalCode) branchExprs
777addExprIf :: (JS.Expr, Code) -> JS.Expr -> JS.Expr
778addExprIf (condition, branch) final =
779 JS.If condition (codeToExpr branch) final
782addStmtIf :: (JS.Expr, Code) -> JS.Stmt -> JS.Stmt
783addStmtIf (condition, branch) final =
784 JS.IfStmt condition (codeToStmt branch) final
787isBlock :: Code -> Bool
794crushIfs :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
795crushIfs branches final =
796 crushIfsHelp [] branches final
800 :: [(Opt.Expr, Opt.Expr)]
801 -> [(Opt.Expr, Opt.Expr)]
803 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
804crushIfsHelp visitedBranches unvisitedBranches final =
805 case unvisitedBranches of
808 Opt.If subBranches subFinal ->
809 crushIfsHelp visitedBranches subBranches subFinal
812 (reverse visitedBranches, final)
814 visiting : unvisited ->
815 crushIfsHelp (visiting : visitedBranches) unvisited final
822generateCase :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> [JS.Stmt]
823generateCase mode label root decider jumps =
824 foldr (goto mode label) (generateDecider mode label root decider) jumps
827goto :: Mode -> N.Name -> (Int, Opt.Expr) -> [JS.Stmt] -> [JS.Stmt]
828goto mode label (index, branch) stmts =
832 (Name.makeLabel label index)
833 (JS.While (JS.Bool True) (JS.Block stmts))
835 labeledDeciderStmt : codeToStmtList (generate mode branch)
838generateDecider :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [JS.Stmt]
839generateDecider mode label root decisionTree =
841 Opt.Leaf (Opt.Inline branch) ->
842 codeToStmtList (generate mode branch)
844 Opt.Leaf (Opt.Jump index) ->
845 [ JS.Break (Just (Name.makeLabel label index)) ]
847 Opt.Chain testChain success failure ->
849 (List.foldl1' (JS.Infix JS.OpLAnd) (map (generateIfTest mode root) testChain))
850 (JS.Block $ generateDecider mode label root success)
851 (JS.Block $ generateDecider mode label root failure)
854 Opt.FanOut path edges fallback ->
856 (generateCaseTest mode root path (fst (head edges)))
858 (\edge cases -> generateCaseBranch mode label root edge : cases)
859 [ JS.Default (generateDecider mode label root fallback) ]
865generateIfTest :: Mode -> N.Name -> (DT.Path, DT.Test) -> JS.Expr
866generateIfTest mode root (path, test) =
868 value = pathToJsExpr mode root path
871 DT.IsCtor name index _ _ ->
872 strictEq (JS.Access value Name.dollar) $
874 Name.Debug _ -> JS.String (N.toBuilder name)
875 Name.Prod _ _ -> JS.Int (Index.toMachine index)
878 strictEq value (JS.Int int)
881 strictEq (JS.String (Text.encodeUtf8Builder char)) $
883 Name.Debug _ -> JS.Call (JS.Access value (Name.fromLocal "valueOf")) []
884 Name.Prod _ _ -> value
887 strictEq value (JS.String (Text.encodeUtf8Builder string))
890 JS.Access value (Name.fromLocal "b")
893 JS.Prefix JS.PrefixLNot $
894 JS.Access value (Name.fromLocal "b")
897 error "COMPILER BUG - there should never be tests on a tuple"
901generateCaseBranch :: Mode -> N.Name -> N.Name -> (DT.Test, Opt.Decider Opt.Choice) -> JS.Case
902generateCaseBranch mode label root (test, subTree) =
904 (generateCaseValue mode test)
905 (generateDecider mode label root subTree)
908generateCaseValue :: Mode -> DT.Test -> JS.Expr
909generateCaseValue mode test =
911 DT.IsCtor name index _ _ ->
913 Name.Debug _ -> JS.String (N.toBuilder name)
914 Name.Prod _ _ -> JS.Int (Index.toMachine index)
920 JS.String (Text.encodeUtf8Builder char)
923 JS.String (Text.encodeUtf8Builder string)
926 error "COMPILER BUG - there should never be three tests on a list"
929 error "COMPILER BUG - there should never be three tests on a list"
932 error "COMPILER BUG - there should never be three tests on a tuple"
935generateCaseTest :: Mode -> N.Name -> DT.Path -> DT.Test -> JS.Expr
936generateCaseTest mode root path exampleTest =
938 value = pathToJsExpr mode root path
941 DT.IsCtor _ _ _ opts ->
944 JS.Access value Name.dollar
949 JS.Access value Name.dollar
966 JS.Call (JS.Access value (Name.fromLocal "valueOf")) []
972 error "COMPILER BUG - there should never be three tests on a list"
975 error "COMPILER BUG - there should never be three tests on a list"
978 error "COMPILER BUG - there should never be three tests on a list"
985pathToJsExpr :: Mode -> N.Name -> DT.Path -> JS.Expr
986pathToJsExpr mode root path =
988 DT.Index index subPath ->
989 JS.Access (pathToJsExpr mode root subPath) (Name.fromIndex index)
994 JS.Access (pathToJsExpr mode root subPath) (Name.fromIndex Index.first)
997 pathToJsExpr mode root subPath
1000 JS.Ref (Name.fromLocal root)
1007generateMain :: Mode -> I.Interfaces -> ModuleName.Canonical -> Opt.Main -> JS.Expr
1008generateMain mode interfaces home main =
1011 JS.Ref (Name.fromKernel N.browser "staticPage")
1012 # JS.Ref (Name.fromGlobal home "main")
1016 Opt.Dynamic msgType decoder ->
1017 JS.Ref (Name.fromGlobal home "main")
1018 # generateJsExpr mode decoder
1019 # toDebugMetadata mode interfaces msgType
1022(#) :: JS.Expr -> JS.Expr -> JS.Expr
1027toDebugMetadata :: Mode -> I.Interfaces -> Can.Type -> JS.Expr
1028toDebugMetadata mode interfaces msgType =
1034 JS.Json $ Encode.object $
1035 [ ("versions", Encode.object [ ("elm", Pkg.encodeVersion Version.version) ])
1036 , ("types", Type.encodeMetadata (Extract.fromMsg interfaces msgType))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list