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.Dev _ -> 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.Dev _ -> 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 /= "todo" then
291 JS.Ref (Name.fromGlobal ModuleName.debug name)
293 case unhandledValueName of
295 JS.Call (JS.Ref (Name.fromKernel N.debug "todo")) $
296 [ JS.String (N.toBuilder home)
297 , regionToJsExpr region
301 JS.Call (JS.Ref (Name.fromKernel N.debug "todoCase")) $
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 "remainderBy" -> JS.Infix JS.OpMod right left
515 _ -> generateGlobalCall home name [left, right]
518 generateGlobalCall home name (map (generateJsExpr mode) args)
521equal :: JS.Expr -> JS.Expr -> JS.Expr
523 if isLiteral left || isLiteral right then
526 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
529notEqual :: JS.Expr -> JS.Expr -> JS.Expr
531 if isLiteral left || isLiteral right then
534 JS.Prefix JS.PrefixLNot $
535 JS.Call (JS.Ref (Name.fromKernel N.utils "eq")) [left, right]
538cmp :: JS.InfixOp -> JS.InfixOp -> Int -> JS.Expr -> JS.Expr -> JS.Expr
539cmp idealOp backupOp backupInt left right =
540 if isLiteral left || isLiteral right then
541 JS.Infix idealOp left right
544 (JS.Call (JS.Ref (Name.fromKernel N.utils "cmp")) [left, right])
548isLiteral :: JS.Expr -> Bool
567decomposeL :: Mode -> Opt.Expr -> [Opt.Expr] -> JS.Expr
568decomposeL mode expr funcs =
570 Opt.Call (Opt.VarGlobal (Opt.Global home "composeL")) [left, func]
571 | home == ModuleName.basics ->
572 decomposeL mode left (func:funcs)
575 generateJsExpr mode $
576 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
579decomposeR :: Mode -> [Opt.Expr] -> Opt.Expr -> JS.Expr
580decomposeR mode funcs expr =
582 Opt.Call (Opt.VarGlobal (Opt.Global home "composeR")) [func, right]
583 | home == ModuleName.basics ->
584 decomposeR mode (func:funcs) right
587 generateJsExpr mode $
588 Opt.Function [N.dollar] (foldr apply (Opt.VarLocal N.dollar) (expr:funcs))
591apply :: Opt.Expr -> Opt.Expr -> Opt.Expr
594 Opt.Accessor field ->
595 Opt.Access value field
598 Opt.Call f (args ++ [value])
601 Opt.Call func [value]
604append :: Mode -> Opt.Expr -> Opt.Expr -> JS.Expr
605append mode left right =
606 let seqs = generateJsExpr mode left : toSeqs mode right in
607 if any isStringLiteral seqs then
608 foldr1 (JS.Infix JS.OpAdd) seqs
613jsAppend :: JS.Expr -> JS.Expr -> JS.Expr
615 JS.Call (JS.Ref (Name.fromKernel N.utils "ap")) [a, b]
618toSeqs :: Mode -> Opt.Expr -> [JS.Expr]
621 Opt.Call (Opt.VarGlobal (Opt.Global home "append")) [left, right]
622 | home == ModuleName.basics ->
623 generateJsExpr mode left : toSeqs mode right
626 [generateJsExpr mode expr]
629isStringLiteral :: JS.Expr -> Bool
630isStringLiteral expr =
640-- SIMPLIFY INFIX OPERATORS
643strictEq :: JS.Expr -> JS.Expr -> JS.Expr
647 JS.Prefix JS.PrefixLNot right
650 if bool then right else JS.Prefix JS.PrefixLNot right
655 JS.Prefix JS.PrefixLNot left
658 if bool then left else JS.Prefix JS.PrefixLNot left
661 JS.Infix JS.OpStrictEq left right
664strictNEq :: JS.Expr -> JS.Expr -> JS.Expr
665strictNEq left right =
671 if bool then JS.Prefix JS.PrefixLNot right else right
679 if bool then JS.Prefix JS.PrefixLNot left else left
682 JS.Infix JS.OpStrictNEq left right
689-- TODO check if JS minifiers collapse unnecessary temporary variables
691generateTailCall :: Mode -> N.Name -> [(N.Name, Opt.Expr)] -> [JS.Stmt]
692generateTailCall mode name args =
694 toTempVars (argName, arg) =
695 ( Name.makeTemp argName, Just (generateJsExpr mode arg) )
697 toRealVars (argName, _) =
699 JS.Assign (JS.LRef (Name.fromLocal argName)) (JS.Ref (Name.makeTemp argName))
701 JS.Var (map toTempVars args)
702 : map toRealVars args
703 ++ [ JS.Continue (Just (Name.fromLocal name)) ]
710generateDef :: Mode -> Opt.Def -> JS.Stmt
711generateDef mode def =
714 JS.Var [ (Name.fromLocal name, Just (generateJsExpr mode body)) ]
716 Opt.TailDef name argNames body ->
717 JS.Var [ (Name.fromLocal name, Just (codeToExpr (generateTailDef mode name argNames body))) ]
720generateTailDef :: Mode -> N.Name -> [N.Name] -> Opt.Expr -> Code
721generateTailDef mode name argNames body =
722 generateFunction (map Name.fromLocal argNames) $ JsBlock $
723 [ JS.Labelled (Name.fromLocal name) $
724 JS.While (JS.Bool True) $
725 codeToStmt $ generate mode body
733generatePath :: Mode -> Opt.Path -> JS.Expr
734generatePath mode path =
736 Opt.Index index subPath ->
737 JS.Access (generatePath mode subPath) (Name.fromIndex index)
740 JS.Ref (Name.fromLocal name)
742 Opt.Field field subPath ->
743 JS.Access (generatePath mode subPath) (Name.fromField mode field)
748 JS.Access (generatePath mode subPath) (Name.fromIndex Index.first)
751 generatePath mode subPath
758generateIf :: Mode -> [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> Code
759generateIf mode givenBranches givenFinal =
762 crushIfs givenBranches givenFinal
764 convertBranch (condition, expr) =
765 ( generateJsExpr mode condition
769 branchExprs = map convertBranch branches
770 finalCode = generate mode final
772 if isBlock finalCode || any (isBlock . snd) branchExprs then
773 JsBlock [ foldr addStmtIf (codeToStmt finalCode) branchExprs ]
775 JsExpr $ foldr addExprIf (codeToExpr finalCode) branchExprs
778addExprIf :: (JS.Expr, Code) -> JS.Expr -> JS.Expr
779addExprIf (condition, branch) final =
780 JS.If condition (codeToExpr branch) final
783addStmtIf :: (JS.Expr, Code) -> JS.Stmt -> JS.Stmt
784addStmtIf (condition, branch) final =
785 JS.IfStmt condition (codeToStmt branch) final
788isBlock :: Code -> Bool
795crushIfs :: [(Opt.Expr, Opt.Expr)] -> Opt.Expr -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
796crushIfs branches final =
797 crushIfsHelp [] branches final
801 :: [(Opt.Expr, Opt.Expr)]
802 -> [(Opt.Expr, Opt.Expr)]
804 -> ([(Opt.Expr, Opt.Expr)], Opt.Expr)
805crushIfsHelp visitedBranches unvisitedBranches final =
806 case unvisitedBranches of
809 Opt.If subBranches subFinal ->
810 crushIfsHelp visitedBranches subBranches subFinal
813 (reverse visitedBranches, final)
815 visiting : unvisited ->
816 crushIfsHelp (visiting : visitedBranches) unvisited final
823generateCase :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [(Int, Opt.Expr)] -> [JS.Stmt]
824generateCase mode label root decider jumps =
825 foldr (goto mode label) (generateDecider mode label root decider) jumps
828goto :: Mode -> N.Name -> (Int, Opt.Expr) -> [JS.Stmt] -> [JS.Stmt]
829goto mode label (index, branch) stmts =
833 (Name.makeLabel label index)
834 (JS.While (JS.Bool True) (JS.Block stmts))
836 labeledDeciderStmt : codeToStmtList (generate mode branch)
839generateDecider :: Mode -> N.Name -> N.Name -> Opt.Decider Opt.Choice -> [JS.Stmt]
840generateDecider mode label root decisionTree =
842 Opt.Leaf (Opt.Inline branch) ->
843 codeToStmtList (generate mode branch)
845 Opt.Leaf (Opt.Jump index) ->
846 [ JS.Break (Just (Name.makeLabel label index)) ]
848 Opt.Chain testChain success failure ->
850 (List.foldl1' (JS.Infix JS.OpLAnd) (map (generateIfTest mode root) testChain))
851 (JS.Block $ generateDecider mode label root success)
852 (JS.Block $ generateDecider mode label root failure)
855 Opt.FanOut path edges fallback ->
857 (generateCaseTest mode root path (fst (head edges)))
859 (\edge cases -> generateCaseBranch mode label root edge : cases)
860 [ JS.Default (generateDecider mode label root fallback) ]
866generateIfTest :: Mode -> N.Name -> (DT.Path, DT.Test) -> JS.Expr
867generateIfTest mode root (path, test) =
869 value = pathToJsExpr mode root path
872 DT.IsCtor name index _ _ ->
873 strictEq (JS.Access value Name.dollar) $
875 Name.Dev _ -> JS.String (N.toBuilder name)
876 Name.Prod _ _ -> JS.Int (Index.toMachine index)
879 strictEq value (JS.Int int)
882 strictEq (JS.String (Text.encodeUtf8Builder char)) $
884 Name.Dev _ -> JS.Call (JS.Access value (Name.fromLocal "valueOf")) []
885 Name.Prod _ _ -> value
888 strictEq value (JS.String (Text.encodeUtf8Builder string))
891 JS.Access value (Name.fromLocal "b")
894 JS.Prefix JS.PrefixLNot $
895 JS.Access value (Name.fromLocal "b")
898 error "COMPILER BUG - there should never be tests on a tuple"
902generateCaseBranch :: Mode -> N.Name -> N.Name -> (DT.Test, Opt.Decider Opt.Choice) -> JS.Case
903generateCaseBranch mode label root (test, subTree) =
905 (generateCaseValue mode test)
906 (generateDecider mode label root subTree)
909generateCaseValue :: Mode -> DT.Test -> JS.Expr
910generateCaseValue mode test =
912 DT.IsCtor name index _ _ ->
914 Name.Dev _ -> JS.String (N.toBuilder name)
915 Name.Prod _ _ -> JS.Int (Index.toMachine index)
921 JS.String (Text.encodeUtf8Builder char)
924 JS.String (Text.encodeUtf8Builder string)
927 error "COMPILER BUG - there should never be three tests on a list"
930 error "COMPILER BUG - there should never be three tests on a list"
933 error "COMPILER BUG - there should never be three tests on a tuple"
936generateCaseTest :: Mode -> N.Name -> DT.Path -> DT.Test -> JS.Expr
937generateCaseTest mode root path exampleTest =
939 value = pathToJsExpr mode root path
942 DT.IsCtor _ _ _ opts ->
945 JS.Access value Name.dollar
950 JS.Access value Name.dollar
967 JS.Call (JS.Access value (Name.fromLocal "valueOf")) []
973 error "COMPILER BUG - there should never be three tests on a list"
976 error "COMPILER BUG - there should never be three tests on a list"
979 error "COMPILER BUG - there should never be three tests on a list"
986pathToJsExpr :: Mode -> N.Name -> DT.Path -> JS.Expr
987pathToJsExpr mode root path =
989 DT.Index index subPath ->
990 JS.Access (pathToJsExpr mode root subPath) (Name.fromIndex index)
995 JS.Access (pathToJsExpr mode root subPath) (Name.fromIndex Index.first)
998 pathToJsExpr mode root subPath
1001 JS.Ref (Name.fromLocal root)
1008generateMain :: Mode -> I.Interfaces -> ModuleName.Canonical -> Opt.Main -> JS.Expr
1009generateMain mode interfaces home main =
1012 JS.Ref (Name.fromKernel N.virtualDom "init")
1013 # JS.Ref (Name.fromGlobal home "main")
1017 Opt.Dynamic msgType decoder ->
1018 JS.Ref (Name.fromGlobal home "main")
1019 # generateJsExpr mode decoder
1020 # toDebugMetadata mode interfaces msgType
1023(#) :: JS.Expr -> JS.Expr -> JS.Expr
1028toDebugMetadata :: Mode -> I.Interfaces -> Can.Type -> JS.Expr
1029toDebugMetadata mode interfaces msgType =
1035 JS.Json $ Encode.object $
1036 [ ("versions", Encode.object [ ("elm", Pkg.encodeVersion Version.version) ])
1037 , ("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