/- Copyright (c) E.W.Ayers. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: E.W.Ayers -/ prelude import init.meta.widget.basic import init.meta.widget.tactic_component import init.meta.tagged_format import init.data.punit import init.meta.mk_dec_eq_instance meta def subexpr := (expr × expr.address) namespace widget open tagged_format open html attr namespace interactive_expression /-- eformat but without any of the formatting stuff like highlighting, groups etc. -/ meta inductive sf : Type | tag_expr : expr.address → expr → sf → sf | compose : sf → sf → sf | of_string : string → sf meta def sf.repr : sf → format | (sf.tag_expr addr e a) := format.group $ format.nest 2 $ "(tag_expr " ++ to_fmt addr ++ format.line ++ "`(" ++ to_fmt e ++ ")" ++ format.line ++ a.repr ++ ")" | (sf.compose a b) := a.repr ++ format.line ++ b.repr | (sf.of_string s) := repr s meta instance : has_to_format sf := ⟨sf.repr⟩ meta instance : has_to_string sf := ⟨λ s, s.repr.to_string⟩ meta instance : has_repr sf := ⟨λ s, s.repr.to_string⟩ meta def sf.of_eformat : eformat → sf | (tag ⟨ea,e⟩ m) := sf.tag_expr ea e $ sf.of_eformat m | (group m) := sf.of_eformat m | (nest i m) := sf.of_eformat m | (highlight i m) := sf.of_eformat m | (of_format f) := sf.of_string $ format.to_string f | (compose x y) := sf.compose (sf.of_eformat x) (sf.of_eformat y) meta def sf.flatten : sf → sf | (sf.tag_expr e ea m) := (sf.tag_expr e ea $ sf.flatten m) | (sf.compose x y) := match (sf.flatten x), (sf.flatten y) with | (sf.of_string sx), (sf.of_string sy) := sf.of_string (sx ++ sy) | (sf.of_string sx), (sf.compose (sf.of_string sy) z) := sf.compose (sf.of_string (sx ++ sy)) z | (sf.compose x (sf.of_string sy)), (sf.of_string sz) := sf.compose x (sf.of_string (sy ++ sz)) | (sf.compose x (sf.of_string sy1)), (sf.compose (sf.of_string sy2) z) := sf.compose x (sf.compose (sf.of_string (sy1 ++ sy2)) z) | x, y := sf.compose x y end | (sf.of_string s) := sf.of_string s meta inductive action (γ : Type) | on_mouse_enter : subexpr → action | on_mouse_leave_all : action | on_click : subexpr → action | on_tooltip_action : γ → action | on_close_tooltip : action meta def view {γ} (tooltip_component : tc subexpr (action γ)) (click_address : option expr.address) (select_address : option expr.address) : subexpr → sf → tactic (list (html (action γ))) | ⟨ce, current_address⟩ (sf.tag_expr ea e m) := do let new_address := current_address ++ ea, let select_attrs : list (attr (action γ)) := if some new_address = select_address then [className "highlight"] else [], click_attrs : list (attr (action γ)) ← if some new_address = click_address then do content ← tc.to_html tooltip_component (e, new_address), pure [tooltip $ h "div" [] [ h "button" [cn "fr pointer ba br3", on_click (λ _, action.on_close_tooltip)] ["x"], content ]] else pure [], let as := [className "expr-boundary", key (ea)] ++ select_attrs ++ click_attrs, inner ← view (e,new_address) m, pure [h "span" as inner] | ca (sf.compose x y) := pure (++) <*> view ca x <*> view ca y | ca (sf.of_string s) := pure [h "span" [ on_mouse_enter (λ _, action.on_mouse_enter ca), on_click (λ _, action.on_click ca), key s ] [html.of_string s]] /-- Make an interactive expression. -/ meta def mk {γ} (tooltip : tc subexpr γ) : tc expr γ := let tooltip_comp := component.with_should_update (λ (x y : tactic_state × expr × expr.address), x.2.2 ≠ y.2.2) $ component.map_action (action.on_tooltip_action) tooltip in tc.mk_simple (action γ) (option subexpr × option subexpr) (λ e, pure $ (none, none)) (λ e ⟨ca, sa⟩ act, pure $ match act with | (action.on_mouse_enter ⟨e, ea⟩) := ((ca, some (e, ea)), none) | (action.on_mouse_leave_all) := ((ca, none), none) | (action.on_click ⟨e, ea⟩) := if some (e,ea) = ca then ((none, sa), none) else ((some (e, ea), sa), none) | (action.on_tooltip_action g) := ((none, sa), some g) | (action.on_close_tooltip) := ((none, sa), none) end ) (λ e ⟨ca, sa⟩, do ts ← tactic.read, let m : sf := sf.flatten $ sf.of_eformat $ tactic_state.pp_tagged ts e, let m : sf := sf.tag_expr [] e m, -- [hack] in pp.cpp I forgot to add an expr-boundary for the root expression. v ← view tooltip_comp (prod.snd <$> ca) (prod.snd <$> sa) ⟨e, []⟩ m, pure $ [ h "span" [ className "expr", key e.hash, on_mouse_leave (λ _, action.on_mouse_leave_all) ] $ v ] ) /-- Render the implicit arguments for an expression in fancy, little pills. -/ meta def implicit_arg_list (tooltip : tc subexpr empty) (e : expr) : tactic $ html empty := do fn ← (mk tooltip) $ expr.get_app_fn e, args ← list.mmap (mk tooltip) $ expr.get_app_args e, pure $ h "div" [] ( (h "span" [className "bg-blue br3 ma1 ph2 white"] [fn]) :: list.map (λ a, h "span" [className "bg-gray br3 ma1 ph2 white"] [a]) args ) meta def type_tooltip : tc subexpr empty := tc.stateless (λ ⟨e,ea⟩, do y ← tactic.infer_type e, y_comp ← mk type_tooltip y, implicit_args ← implicit_arg_list type_tooltip e, pure [ h "div" [] [ h "div" [] [y_comp], h "hr" [] [], implicit_args ] ] ) end interactive_expression @[derive decidable_eq] meta inductive filter_type | none | no_instances | only_props meta def filter_local : filter_type → expr → tactic bool | (filter_type.none) e := pure tt | (filter_type.no_instances) e := do t ← tactic.infer_type e, bnot <$> tactic.is_class t | (filter_type.only_props) e := do t ← tactic.infer_type e, tactic.is_prop t meta def filter_component : component filter_type filter_type := component.stateless (λ lf, [ h "label" [] ["filter: "], select [ ⟨filter_type.none, "0", ["no filter"]⟩, ⟨filter_type.no_instances, "1", ["no instances"]⟩, ⟨filter_type.only_props, "2", ["only props"]⟩ ] lf ] ) meta def html.of_name {α : Type} : name → html α | n := html.of_string $ name.to_string n open tactic meta def show_type_component : tc expr empty := tc.stateless (λ x, do y ← infer_type x, y_comp ← interactive_expression.mk interactive_expression.type_tooltip $ y, pure y_comp ) /-- A group of local constants in the context that should be rendered as one line. -/ @[derive decidable_eq] meta structure local_collection := (key : string) (locals : list expr) (type : expr) /-- Group consecutive locals according to whether they have the same type -/ meta def to_local_collection : list local_collection → list expr → tactic (list local_collection) | acc [] := pure $ list.map (λ lc : local_collection, {locals := lc.locals.reverse, ..lc}) $ list.reverse $ acc | acc (l::ls) := do l_type ← infer_type l, (do (⟨k,ns,t⟩::acc) ← pure acc, is_def_eq t l_type, to_local_collection (⟨k,l::ns,t⟩::acc) ls) <|> (to_local_collection (⟨to_string $ expr.local_uniq_name $ l, [l], l_type⟩::acc) ls) /-- Component that displays the main (first) goal. -/ meta def tactic_view_goal {γ} (local_c : tc local_collection γ) (target_c : tc expr γ) : tc filter_type γ := tc.stateless $ λ ft, do g@(expr.mvar u_n pp_n y) ← main_goal, t ← get_tag g, let case_tag : list (html γ) := match interactive.case_tag.parse t with | some t := [h "li" [key "_case"] $ [h "span" [cn "goal-case b"] ["case"]] ++ (t.case_names.bind $ λ n, [" ", n])] | none := [] end, lcs ← local_context, lcs ← list.mfilter (filter_local ft) lcs, lcs ← to_local_collection [] lcs, lchs ← lcs.mmap (λ lc, do lh ← local_c lc, ns ← pure $ lc.locals.map (λ n, h "span" [cn "goal-hyp b pr2"] [html.of_name $ expr.local_pp_name n]), pure $ h "li" [key lc.key] (ns ++ [": ", h "span" [cn "goal-hyp-type"] [lh]])), t_comp ← target_c g, pure $ h "ul" [key g.hash, className "list pl0 font-code"] $ case_tag ++ lchs ++ [ h "li" [key u_n] [ h "span" [cn "goal-vdash b"] ["⊢ "], t_comp ]] meta inductive tactic_view_action (γ : Type) | out (a:γ): tactic_view_action | filter (f: filter_type): tactic_view_action /-- Component that displays all goals, together with the `$n goals` message. -/ meta def tactic_view_component {γ} (local_c : tc local_collection γ) (target_c : tc expr γ) : tc unit γ := tc.mk_simple (tactic_view_action γ) (filter_type) (λ _, pure $ filter_type.none) (λ ⟨⟩ ft a, match a with | (tactic_view_action.out a) := pure (ft, some a) | (tactic_view_action.filter ft) := pure (ft, none) end) (λ ⟨⟩ ft, do gs ← get_goals, hs ← gs.mmap (λ g, do set_goals [g], flip tc.to_html ft $ tactic_view_goal local_c target_c), set_goals gs, let goal_message := if gs.length = 0 then "goals accomplished" else if gs.length = 1 then "1 goal" else to_string gs.length ++ " goals", let goal_message : html γ := h "strong" [cn "goal-goals"] [goal_message], let goals : html γ := h "ul" [className "list pl0"] $ list.map_with_index (λ i x, h "li" [className $ "lh-copy mt2", key i] [x]) $ (goal_message :: hs), pure [ h "div" [className "fr"] [html.of_component ft $ component.map_action tactic_view_action.filter filter_component], html.map_action tactic_view_action.out goals ]) /-- Component that displays the term-mode goal. -/ meta def tactic_view_term_goal {γ} (local_c : tc local_collection γ) (target_c : tc expr γ) : tc unit γ := tc.stateless $ λ _, do goal ← flip tc.to_html (filter_type.none) $ tactic_view_goal local_c target_c, pure [h "ul" [className "list pl0"] [ h "li" [className "lh-copy"] [h "strong" [cn "goal-goals"] ["expected type:"]], h "li" [className "lh-copy"] [goal]]] meta def show_local_collection_component : tc local_collection empty := tc.stateless (λ lc, do (l::_) ← pure lc.locals, c ← show_type_component l, pure [c] ) meta def tactic_render : tc unit empty := component.ignore_action $ tactic_view_component show_local_collection_component show_type_component meta def tactic_state_widget : component tactic_state empty := tc.to_component tactic_render /-- Widget used to display term-proof goals. -/ meta def term_goal_widget : component tactic_state empty := (tactic_view_term_goal show_local_collection_component show_type_component).to_component end widget