lean4monaco 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/monaco-lean4/lean4-infoview/src/index.d.ts +16 -0
  2. package/dist/monaco-lean4/lean4-infoview/src/index.js +29 -0
  3. package/dist/monaco-lean4/lean4-infoview/src/infoview/collapsing.d.ts +12 -0
  4. package/dist/monaco-lean4/lean4-infoview/src/infoview/collapsing.js +37 -0
  5. package/dist/monaco-lean4/lean4-infoview/src/infoview/contexts.d.ts +40 -0
  6. package/dist/monaco-lean4/lean4-infoview/src/infoview/contexts.js +44 -0
  7. package/dist/monaco-lean4/lean4-infoview/src/infoview/editorConnection.d.ts +22 -0
  8. package/dist/monaco-lean4/lean4-infoview/src/infoview/editorConnection.js +41 -0
  9. package/dist/monaco-lean4/lean4-infoview/src/infoview/errors.d.ts +14 -0
  10. package/dist/monaco-lean4/lean4-infoview/src/infoview/errors.js +24 -0
  11. package/dist/monaco-lean4/lean4-infoview/src/infoview/event.d.ts +33 -0
  12. package/dist/monaco-lean4/lean4-infoview/src/infoview/event.js +57 -0
  13. package/dist/monaco-lean4/lean4-infoview/src/infoview/goalLocation.d.ts +61 -0
  14. package/dist/monaco-lean4/lean4-infoview/src/infoview/goalLocation.js +87 -0
  15. package/dist/monaco-lean4/lean4-infoview/src/infoview/goals.d.ts +11 -0
  16. package/dist/monaco-lean4/lean4-infoview/src/infoview/goals.js +141 -0
  17. package/dist/monaco-lean4/lean4-infoview/src/infoview/info.d.ts +18 -0
  18. package/dist/monaco-lean4/lean4-infoview/src/infoview/info.js +278 -0
  19. package/dist/monaco-lean4/lean4-infoview/src/infoview/infos.d.ts +2 -0
  20. package/dist/monaco-lean4/lean4-infoview/src/infoview/infos.js +113 -0
  21. package/dist/monaco-lean4/lean4-infoview/src/infoview/interactiveCode.d.ts +18 -0
  22. package/dist/monaco-lean4/lean4-infoview/src/infoview/interactiveCode.js +164 -0
  23. package/dist/monaco-lean4/lean4-infoview/src/infoview/main.d.ts +13 -0
  24. package/dist/monaco-lean4/lean4-infoview/src/infoview/main.js +97 -0
  25. package/dist/monaco-lean4/lean4-infoview/src/infoview/messages.d.ts +16 -0
  26. package/dist/monaco-lean4/lean4-infoview/src/infoview/messages.js +151 -0
  27. package/dist/monaco-lean4/lean4-infoview/src/infoview/rpcSessions.d.ts +21 -0
  28. package/dist/monaco-lean4/lean4-infoview/src/infoview/rpcSessions.js +67 -0
  29. package/dist/monaco-lean4/lean4-infoview/src/infoview/serverVersion.d.ts +10 -0
  30. package/dist/monaco-lean4/lean4-infoview/src/infoview/serverVersion.js +25 -0
  31. package/dist/monaco-lean4/lean4-infoview/src/infoview/tooltips.d.ts +23 -0
  32. package/dist/monaco-lean4/lean4-infoview/src/infoview/tooltips.js +231 -0
  33. package/dist/monaco-lean4/lean4-infoview/src/infoview/traceExplorer.d.ts +11 -0
  34. package/dist/monaco-lean4/lean4-infoview/src/infoview/traceExplorer.js +115 -0
  35. package/dist/monaco-lean4/lean4-infoview/src/infoview/userWidget.d.ts +48 -0
  36. package/dist/monaco-lean4/lean4-infoview/src/infoview/userWidget.js +54 -0
  37. package/dist/monaco-lean4/lean4-infoview/src/infoview/util.d.ts +144 -0
  38. package/dist/monaco-lean4/lean4-infoview/src/infoview/util.js +366 -0
  39. package/package.json +1 -2
@@ -0,0 +1,231 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import * as ReactDOM from 'react-dom';
4
+ import { arrow, autoPlacement, autoUpdate, FloatingArrow, offset, shift, size, useFloating } from '@floating-ui/react';
5
+ import { ConfigContext } from './contexts';
6
+ import { forwardAndUseRef, forwardAndUseStateRef, LogicalDomContext, useLogicalDomObserver, useOnClickOutside, } from './util';
7
+ export function Tooltip(props_) {
8
+ const { reference, children, style, ...props } = props_;
9
+ const arrowRef = React.useRef(null);
10
+ const { refs, floatingStyles, context } = useFloating({
11
+ elements: { reference },
12
+ placement: 'top',
13
+ middleware: [
14
+ offset(8),
15
+ shift(),
16
+ autoPlacement({
17
+ padding: 10,
18
+ }),
19
+ size({
20
+ apply({ availableHeight, elements }) {
21
+ elements.floating.style.maxHeight = `${Math.min(availableHeight, 300)}px`;
22
+ },
23
+ padding: 10,
24
+ }),
25
+ // NOTE: `padding` should be `tooltip.borderRadius` or more so that the arrow
26
+ // doesn't overflow the rounded corner.
27
+ arrow({ element: arrowRef, padding: 6 }),
28
+ ],
29
+ whileElementsMounted: autoUpdate,
30
+ });
31
+ const logicalDom = React.useContext(LogicalDomContext);
32
+ const logicalDomCleanupFn = React.useRef(() => { });
33
+ const floating = (_jsxs("div", { ref: node => {
34
+ refs.setFloating(node);
35
+ logicalDomCleanupFn.current();
36
+ if (node)
37
+ logicalDomCleanupFn.current = logicalDom.registerDescendant(node);
38
+ else
39
+ logicalDomCleanupFn.current = () => { };
40
+ }, style: { ...style, ...floatingStyles }, className: "tooltip", ...props, children: [_jsx(FloatingArrow, { ref: arrowRef, context: context, fill: "var(--vscode-editorHoverWidget-background)", strokeWidth: 1, stroke: "var(--vscode-editorHoverWidget-border)" }), _jsx("div", { className: "tooltip-content", children: children })] }));
41
+ // Append the tooltip to the end of document body to avoid layout issues.
42
+ // (https://github.com/leanprover/vscode-lean4/issues/51)
43
+ return ReactDOM.createPortal(floating, document.body);
44
+ }
45
+ export const WithToggleableTooltip = forwardAndUseStateRef((props_, ref, setRef) => {
46
+ const { isTooltipShown, hideTooltip, tooltipChildren, ...props } = props_;
47
+ // Since we do not want to hide the tooltip if the user is trying to select text in it,
48
+ // we need both the "click outside" and "click inside" handlers here because they
49
+ // play nicer with existing selections than a global click handler.
50
+ // With a single global click handler, any selection anywhere in the InfoView could block
51
+ // the tooltip from being hidden. This is especially annoying because right-clicking any
52
+ // element also selects it.
53
+ // With both inside and outside click handlers, the outside click handler can simply disregard
54
+ // selections, whereas React ensures that only a selection in the tooltip itself can block
55
+ // the inside click handler from hiding the tooltip, since the outer selection is removed
56
+ // before the inside click handler fires.
57
+ const [logicalSpanElt, logicalDomStorage] = useLogicalDomObserver({ current: ref });
58
+ const onClickOutside = React.useCallback(() => {
59
+ hideTooltip();
60
+ }, [hideTooltip]);
61
+ useOnClickOutside(logicalSpanElt, onClickOutside);
62
+ return (_jsx(LogicalDomContext.Provider, { value: logicalDomStorage, children: _jsxs("span", { ref: setRef, onClick: () => {
63
+ if (!window.getSelection()?.toString())
64
+ hideTooltip();
65
+ }, ...props, children: [isTooltipShown && _jsx(Tooltip, { reference: ref, children: tooltipChildren }), props.children] }) }));
66
+ });
67
+ /** An element which calls `setHoverState` when the hover state of its DOM children changes.
68
+ *
69
+ * It is implemented with JS rather than CSS in order to allow nesting of these elements. When nested,
70
+ * only the smallest (deepest in the DOM tree) {@link DetectHoverSpan} has an enabled hover state. */
71
+ export const DetectHoverSpan = forwardAndUseRef((props_, ref, setRef) => {
72
+ const { setHoverState, ...props } = props_;
73
+ const onPointerEvent = (b, e) => {
74
+ // It's more composable to let pointer events bubble up rather than to call `stopPropagation`,
75
+ // but we only want to handle hovers in the innermost component. So we record that the
76
+ // event was handled with a property.
77
+ // The `contains` check ensures that the node hovered over is a child in the DOM
78
+ // tree and not just a logical React child (see useLogicalDom and
79
+ // https://reactjs.org/docs/portals.html#event-bubbling-through-portals).
80
+ if (ref.current && e.target instanceof Node && ref.current.contains(e.target)) {
81
+ if ('_DetectHoverSpanSeen' in e)
82
+ return;
83
+ e._DetectHoverSpanSeen = {};
84
+ if (!b)
85
+ setHoverState('off');
86
+ else if (e.ctrlKey || e.metaKey)
87
+ setHoverState('ctrlOver');
88
+ else
89
+ setHoverState('over');
90
+ }
91
+ };
92
+ React.useEffect(() => {
93
+ const onKeyDown = (e) => {
94
+ if (e.key === 'Control' || e.key === 'Meta')
95
+ setHoverState(st => (st === 'over' ? 'ctrlOver' : st));
96
+ };
97
+ const onKeyUp = (e) => {
98
+ if (e.key === 'Control' || e.key === 'Meta')
99
+ setHoverState(st => (st === 'ctrlOver' ? 'over' : st));
100
+ };
101
+ // Note: In VSCode these events do not fire when the webview is not in focus.
102
+ document.addEventListener('keydown', onKeyDown);
103
+ document.addEventListener('keyup', onKeyUp);
104
+ return () => {
105
+ document.removeEventListener('keydown', onKeyDown);
106
+ document.removeEventListener('keyup', onKeyUp);
107
+ };
108
+ }, [setHoverState]);
109
+ return (_jsx("span", { ...props, ref: setRef, onPointerOver: e => {
110
+ onPointerEvent(true, e);
111
+ if (props.onPointerOver)
112
+ props.onPointerOver(e);
113
+ }, onPointerOut: e => {
114
+ onPointerEvent(false, e);
115
+ if (props.onPointerOut)
116
+ props.onPointerOut(e);
117
+ }, onPointerMove: e => {
118
+ if (e.ctrlKey || e.metaKey)
119
+ setHoverState(st => (st === 'over' ? 'ctrlOver' : st));
120
+ else
121
+ setHoverState(st => (st === 'ctrlOver' ? 'over' : st));
122
+ if (props.onPointerMove)
123
+ props.onPointerMove(e);
124
+ }, children: props.children }));
125
+ });
126
+ const TipChainContext = React.createContext({ pinParent: () => { } });
127
+ /** Shows a tooltip when the children are hovered over or clicked.
128
+ *
129
+ * An `onClick` middleware can optionally be given in order to control what happens when the
130
+ * hoverable area is clicked. The middleware can invoke `next` to execute the default action
131
+ * which is to pin the tooltip open. */
132
+ export const WithTooltipOnHover = forwardAndUseStateRef((props_, ref, setRef) => {
133
+ const { tooltipChildren, onClick: onClickProp, ...props } = props_;
134
+ const config = React.useContext(ConfigContext);
135
+ const [state, setState] = React.useState('hide');
136
+ const shouldShow = state !== 'hide';
137
+ const tipChainCtx = React.useContext(TipChainContext);
138
+ React.useEffect(() => {
139
+ if (state === 'pin')
140
+ tipChainCtx.pinParent();
141
+ }, [state, tipChainCtx]);
142
+ const newTipChainCtx = React.useMemo(() => ({
143
+ pinParent: () => {
144
+ setState('pin');
145
+ tipChainCtx.pinParent();
146
+ },
147
+ }), [tipChainCtx]);
148
+ // Note: because tooltips are attached to `document.body`, they are not descendants of the
149
+ // hoverable area in the DOM tree. Thus the `contains` check fails for elements within tooltip
150
+ // contents and succeeds for elements within the hoverable. We can use this to distinguish them.
151
+ const isWithinHoverable = (el) => ref && el instanceof Node && ref.contains(el);
152
+ const [logicalSpanElt, logicalDomStorage] = useLogicalDomObserver({ current: ref });
153
+ // We use timeouts for debouncing hover events.
154
+ const timeout = React.useRef();
155
+ const clearTimeout = () => {
156
+ if (timeout.current) {
157
+ window.clearTimeout(timeout.current);
158
+ timeout.current = undefined;
159
+ }
160
+ };
161
+ const showDelay = 500;
162
+ const hideDelay = 300;
163
+ const isModifierHeld = (e) => e.altKey || e.ctrlKey || e.shiftKey || e.metaKey;
164
+ const onClick = (e) => {
165
+ clearTimeout();
166
+ setState(state => (state === 'pin' ? 'hide' : 'pin'));
167
+ e.stopPropagation();
168
+ };
169
+ const onClickOutside = React.useCallback(() => {
170
+ clearTimeout();
171
+ setState('hide');
172
+ }, []);
173
+ useOnClickOutside(logicalSpanElt, onClickOutside);
174
+ const isPointerOverTooltip = React.useRef(false);
175
+ const startShowTimeout = () => {
176
+ clearTimeout();
177
+ if (!config.showTooltipOnHover)
178
+ return;
179
+ timeout.current = window.setTimeout(() => {
180
+ setState(state => (state === 'hide' ? 'show' : state));
181
+ timeout.current = undefined;
182
+ }, showDelay);
183
+ };
184
+ const startHideTimeout = () => {
185
+ clearTimeout();
186
+ timeout.current = window.setTimeout(() => {
187
+ if (!isPointerOverTooltip.current)
188
+ setState(state => (state === 'show' ? 'hide' : state));
189
+ timeout.current = undefined;
190
+ }, hideDelay);
191
+ };
192
+ const onPointerEnter = (e) => {
193
+ isPointerOverTooltip.current = true;
194
+ clearTimeout();
195
+ };
196
+ const onPointerLeave = (e) => {
197
+ isPointerOverTooltip.current = false;
198
+ startHideTimeout();
199
+ };
200
+ function guardMouseEvent(act, e) {
201
+ if ('_WithTooltipOnHoverSeen' in e)
202
+ return;
203
+ if (!isWithinHoverable(e.target))
204
+ return;
205
+ e._WithTooltipOnHoverSeen = {};
206
+ act(e);
207
+ }
208
+ return (_jsx(LogicalDomContext.Provider, { value: logicalDomStorage, children: _jsxs("span", { ...props, ref: setRef, onClick: e => {
209
+ guardMouseEvent(e => {
210
+ if (onClickProp !== undefined)
211
+ onClickProp(e, onClick);
212
+ else
213
+ onClick(e);
214
+ }, e);
215
+ }, onPointerDown: e => {
216
+ // We have special handling for some modifier+click events, so prevent default browser
217
+ // events from interfering when a modifier is held.
218
+ if (isModifierHeld(e))
219
+ e.preventDefault();
220
+ }, onPointerOver: e => {
221
+ if (!isModifierHeld(e)) {
222
+ guardMouseEvent(_ => startShowTimeout(), e);
223
+ }
224
+ if (props.onPointerOver !== undefined)
225
+ props.onPointerOver(e);
226
+ }, onPointerOut: e => {
227
+ guardMouseEvent(_ => startHideTimeout(), e);
228
+ if (props.onPointerOut !== undefined)
229
+ props.onPointerOut(e);
230
+ }, children: [shouldShow && (_jsx(TipChainContext.Provider, { value: newTipChainCtx, children: _jsx(Tooltip, { reference: ref, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, children: tooltipChildren }) })), props.children] }) }));
231
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Traces of any substantial compilation or elaboration process are usually extremely verbose,
3
+ * which makes them slow (or even infeasible) to pretty-print and difficult to understand.
4
+ * Instead, we provide a "TraceExplorer" UI which allows users to lazily expand trace subtrees,
5
+ * and (TODO) execute search queries.
6
+ *
7
+ * @module
8
+ */
9
+ import { MsgEmbed } from '@leanprover/infoview-api';
10
+ import { InteractiveTextComponentProps } from './interactiveCode';
11
+ export declare function InteractiveMessage({ fmt }: InteractiveTextComponentProps<MsgEmbed>): any;
@@ -0,0 +1,115 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * Traces of any substantial compilation or elaboration process are usually extremely verbose,
4
+ * which makes them slow (or even infeasible) to pretty-print and difficult to understand.
5
+ * Instead, we provide a "TraceExplorer" UI which allows users to lazily expand trace subtrees,
6
+ * and (TODO) execute search queries.
7
+ *
8
+ * @module
9
+ */
10
+ import { InteractiveDiagnostics_msgToInteractive, lazyTraceChildrenToInteractive, } from '@leanprover/infoview-api';
11
+ import * as React from 'react';
12
+ import { Goal } from './goals';
13
+ import { InteractiveCode, InteractiveTaggedText, } from './interactiveCode';
14
+ import { useRpcSession } from './rpcSessions';
15
+ import { DynamicComponent } from './userWidget';
16
+ import { mapRpcError, useAsyncWithTrigger } from './util';
17
+ function LazyTrace({ col, cls, msg }) {
18
+ const rs = useRpcSession();
19
+ const [tt, fetchTrace] = useAsyncWithTrigger(() => InteractiveDiagnostics_msgToInteractive(rs, msg, col), [rs, msg, col]);
20
+ const [open, setOpen] = React.useState(false);
21
+ if (!open)
22
+ return (_jsxs("span", { className: "underline-hover pointer", onClick: ev => {
23
+ void fetchTrace();
24
+ setOpen(true);
25
+ ev.stopPropagation();
26
+ }, children: ["[", cls, "] >"] }));
27
+ else if (tt.state === 'resolved')
28
+ return (_jsxs(_Fragment, { children: [_jsxs("span", { className: "underline-hover pointer", onClick: ev => {
29
+ setOpen(false);
30
+ ev.stopPropagation();
31
+ }, children: ["[", cls, "] \u2228"] }), _jsx(InteractiveMessage, { fmt: tt.value })] }));
32
+ else if (tt.state === 'rejected')
33
+ return (_jsxs(_Fragment, { children: [_jsxs("span", { className: "underline-hover pointer", onClick: ev => {
34
+ void fetchTrace();
35
+ ev.stopPropagation();
36
+ }, children: ["[", cls, "] Error (click to retry):"] }), ' ', mapRpcError(tt.error)] }));
37
+ else
38
+ return _jsxs("span", { children: ["[", cls, "] Loading.."] });
39
+ }
40
+ const TraceClassContext = React.createContext('');
41
+ function abbreviateCommonPrefix(parent, cls) {
42
+ const parentParts = parent.split('.');
43
+ const clsParts = cls.split('.');
44
+ let i = 0;
45
+ for (; i < parentParts.length && i < clsParts.length && parentParts[i] === clsParts[i]; i++)
46
+ ;
47
+ return clsParts.slice(i).join('.');
48
+ }
49
+ function TraceLine({ indent, cls, msg, icon }) {
50
+ const spaces = ' '.repeat(indent);
51
+ const abbrCls = abbreviateCommonPrefix(React.useContext(TraceClassContext), cls);
52
+ return (_jsxs("div", { className: "trace-line", children: [spaces, _jsxs("span", { className: "trace-class", title: cls, children: ["[", abbrCls, "]"] }), ' ', _jsx(InteractiveMessage, { fmt: msg }), " ", icon] }));
53
+ }
54
+ function ChildlessTraceNode(traceEmbed) {
55
+ return _jsx(TraceLine, { ...traceEmbed, icon: "" });
56
+ }
57
+ function CollapsibleTraceNode(traceEmbed) {
58
+ const { cls, collapsed: collapsedByDefault, children: lazyKids } = traceEmbed;
59
+ const rs = useRpcSession();
60
+ const [children, fetchChildren] = useAsyncWithTrigger(async () => {
61
+ if ('strict' in lazyKids) {
62
+ return lazyKids.strict;
63
+ }
64
+ else {
65
+ return lazyTraceChildrenToInteractive(rs, lazyKids.lazy);
66
+ }
67
+ }, [rs, lazyKids]);
68
+ const [open, setOpen] = React.useState(!collapsedByDefault); // TODO: reset when collapsedByDefault changes?
69
+ if (open && children.state === 'notStarted')
70
+ void fetchChildren();
71
+ let icon = open ? '▼' : '▶';
72
+ if (children.state === 'loading')
73
+ icon += ' ⋯';
74
+ const onClick = React.useCallback((ev) => {
75
+ if (!(ev.target instanceof Node))
76
+ return;
77
+ if (!ev.currentTarget || !ev.target)
78
+ return;
79
+ // Don't handle clicks within React portals nested in this div (notably, tooltips).
80
+ if (!ev.currentTarget.contains(ev.target))
81
+ return;
82
+ ev.stopPropagation();
83
+ if (!open)
84
+ void fetchChildren();
85
+ setOpen(o => !o);
86
+ }, [open, fetchChildren]);
87
+ return (_jsxs("div", { children: [_jsx("div", { className: "pointer", onClick: onClick, children: _jsx(TraceLine, { ...traceEmbed, icon: icon }) }), _jsx("div", { style: { display: open ? 'block' : 'none' }, children: _jsx(TraceClassContext.Provider, { value: cls, children: children.state === 'resolved' ? (children.value.map((tt, i) => _jsx(InteractiveMessage, { fmt: tt }, i))) : children.state === 'rejected' ? (mapRpcError(children.error).toString()) : (_jsx(_Fragment, {})) }) })] }));
88
+ }
89
+ function Trace(traceEmbed) {
90
+ const noChildren = 'strict' in traceEmbed.children && traceEmbed.children.strict.length === 0;
91
+ return noChildren ? _jsx(ChildlessTraceNode, { ...traceEmbed }) : _jsx(CollapsibleTraceNode, { ...traceEmbed });
92
+ }
93
+ function InteractiveMessageTag({ tag: embed }) {
94
+ if ('expr' in embed)
95
+ return _jsx(InteractiveCode, { fmt: embed.expr });
96
+ else if ('goal' in embed)
97
+ return (_jsx(Goal, { goal: embed.goal, filter: {
98
+ reverse: false,
99
+ showType: true,
100
+ showInstance: true,
101
+ showHiddenAssumption: true,
102
+ showLetValue: true,
103
+ }, additionalClassNames: "" }));
104
+ else if ('widget' in embed)
105
+ return _jsx(DynamicComponent, { hash: embed.widget.wi.javascriptHash, props: embed.widget.wi.props });
106
+ else if ('lazyTrace' in embed)
107
+ return _jsx(LazyTrace, { col: embed.lazyTrace[0], cls: embed.lazyTrace[1], msg: embed.lazyTrace[2] });
108
+ else if ('trace' in embed)
109
+ return _jsx(Trace, { ...embed.trace });
110
+ else
111
+ return _jsxs("div", { children: ["malformed MsgEmbed: ", JSON.stringify(embed)] });
112
+ }
113
+ export function InteractiveMessage({ fmt }) {
114
+ return InteractiveTaggedText({ fmt, InnerTagUi: InteractiveMessageTag });
115
+ }
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import { InteractiveGoal, InteractiveTermGoal, RpcSessionAtPos, UserWidgetInstance } from '@leanprover/infoview-api';
3
+ import { GoalsLocation } from './goalLocation';
4
+ import { DocumentPosition } from './util';
5
+ /**
6
+ * Fetch source code from Lean and dynamically import it as a JS module.
7
+ *
8
+ * The source must hash to `hash` (in Lean) and must have been annotated with `@[widget]`
9
+ * or `@[widget_module]` at some point before `pos`. */
10
+ export declare function importWidgetModule(rs: RpcSessionAtPos, pos: DocumentPosition, hash: string): Promise<any>;
11
+ export interface DynamicComponentProps {
12
+ hash: string;
13
+ props: any;
14
+ /** @deprecated set {@link EnvPosContext} instead */
15
+ pos?: DocumentPosition;
16
+ }
17
+ /**
18
+ * Use {@link importWidgetModule} to import a module
19
+ * which must `export default` a React component,
20
+ * and render that with `props`.
21
+ * Errors in the component are caught in an error boundary.
22
+ *
23
+ * The {@link EnvPosContext} must be set.
24
+ * It is used to retrieve the `Lean.Environment`
25
+ * from which the widget module identified by `hash`
26
+ * is obtained.
27
+ */
28
+ export declare function DynamicComponent(props_: React.PropsWithChildren<DynamicComponentProps>): any;
29
+ interface PanelWidgetDisplayProps {
30
+ pos: DocumentPosition;
31
+ goals: InteractiveGoal[];
32
+ termGoal?: InteractiveTermGoal;
33
+ selectedLocations: GoalsLocation[];
34
+ widget: UserWidgetInstance;
35
+ }
36
+ /** Props that every infoview panel widget receives as input to its `default` export. */
37
+ export interface PanelWidgetProps {
38
+ /** Cursor position in the file at which the widget is being displayed. */
39
+ pos: DocumentPosition;
40
+ /** The current tactic-mode goals. */
41
+ goals: InteractiveGoal[];
42
+ /** The current term-mode goal, if any. */
43
+ termGoal?: InteractiveTermGoal;
44
+ /** Locations currently selected in the goal state. */
45
+ selectedLocations: GoalsLocation[];
46
+ }
47
+ export declare function PanelWidgetDisplay({ pos, goals, termGoal, selectedLocations, widget }: PanelWidgetDisplayProps): any;
48
+ export {};
@@ -0,0 +1,54 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from 'react';
3
+ import { Widget_getWidgetSource, } from '@leanprover/infoview-api';
4
+ import { EnvPosContext } from './contexts';
5
+ import { ErrorBoundary } from './errors';
6
+ import { useRpcSession } from './rpcSessions';
7
+ import { mapRpcError, useAsyncPersistent } from './util';
8
+ async function dynamicallyLoadModule(hash, code) {
9
+ const file = new File([code], `widget_${hash}.js`, { type: 'text/javascript' });
10
+ const url = URL.createObjectURL(file);
11
+ return await import(url);
12
+ }
13
+ const moduleCache = new Map();
14
+ /**
15
+ * Fetch source code from Lean and dynamically import it as a JS module.
16
+ *
17
+ * The source must hash to `hash` (in Lean) and must have been annotated with `@[widget]`
18
+ * or `@[widget_module]` at some point before `pos`. */
19
+ export async function importWidgetModule(rs, pos, hash) {
20
+ if (moduleCache.has(hash)) {
21
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22
+ return moduleCache.get(hash);
23
+ }
24
+ const resp = await Widget_getWidgetSource(rs, pos, hash);
25
+ const mod = await dynamicallyLoadModule(hash, resp.sourcetext);
26
+ moduleCache.set(hash, mod);
27
+ return mod;
28
+ }
29
+ /**
30
+ * Use {@link importWidgetModule} to import a module
31
+ * which must `export default` a React component,
32
+ * and render that with `props`.
33
+ * Errors in the component are caught in an error boundary.
34
+ *
35
+ * The {@link EnvPosContext} must be set.
36
+ * It is used to retrieve the `Lean.Environment`
37
+ * from which the widget module identified by `hash`
38
+ * is obtained.
39
+ */
40
+ export function DynamicComponent(props_) {
41
+ const { hash, props, children } = props_;
42
+ const rs = useRpcSession();
43
+ const pos = React.useContext(EnvPosContext);
44
+ const state = useAsyncPersistent(() => {
45
+ if (!pos)
46
+ throw new Error('position context is not set');
47
+ return importWidgetModule(rs, pos, hash);
48
+ }, [rs, pos, hash]);
49
+ return (_jsx(React.Suspense, { fallback: `Loading component '${hash}'..`, children: _jsxs(ErrorBoundary, { children: [state.state === 'resolved' && React.createElement(state.value.default, props, children), state.state === 'rejected' && _jsxs("span", { className: "red", children: ["Error: ", mapRpcError(state.error).message] })] }) }));
50
+ }
51
+ export function PanelWidgetDisplay({ pos, goals, termGoal, selectedLocations, widget }) {
52
+ const componentProps = { pos, goals, termGoal, selectedLocations, ...widget.props };
53
+ return _jsx(DynamicComponent, { hash: widget.javascriptHash, props: componentProps });
54
+ }
@@ -0,0 +1,144 @@
1
+ import * as React from 'react';
2
+ import type { DocumentUri, Position, Range, TextDocumentPositionParams } from 'vscode-languageserver-protocol';
3
+ import { EventEmitter } from './event';
4
+ /** A document URI and a position in that document. */
5
+ export interface DocumentPosition extends Position {
6
+ uri: DocumentUri;
7
+ }
8
+ export declare namespace DocumentPosition {
9
+ function isEqual(p1: DocumentPosition, p2: DocumentPosition): boolean;
10
+ function toTdpp(p: DocumentPosition): TextDocumentPositionParams;
11
+ function toString(p: DocumentPosition): string;
12
+ }
13
+ export declare namespace PositionHelpers {
14
+ function isLessThanOrEqual(p1: Position, p2: Position): boolean;
15
+ }
16
+ export declare namespace RangeHelpers {
17
+ function contains(range: Range, pos: Position, ignoreCharacter?: boolean): boolean;
18
+ }
19
+ export declare function escapeHtml(s: string): string;
20
+ /** @deprecated (unused) */
21
+ export declare function colorizeMessage(goal: string): string;
22
+ export declare function basename(path: string): string;
23
+ /**
24
+ * A specialization of {@link React.useEffect} which executes `f` with the event data
25
+ * whenever `ev` fires.
26
+ * If `key` is provided, `f` is only invoked on events fired with that key.
27
+ */
28
+ export declare function useEvent<E, K>(ev: EventEmitter<E, K>, f: (_: E) => void, dependencies?: React.DependencyList, key?: K): void;
29
+ /**
30
+ * A piece of React {@link React.useState} which returns the data that `ev` most recently fired with.
31
+ * If `f` is provided, the data is mapped through `f` first. */
32
+ export declare function useEventResult<E, K>(ev: EventEmitter<E, K>): E | undefined;
33
+ export declare function useEventResult<E, K, T>(ev: EventEmitter<E, K>, f: (newVal: E) => T): T | undefined;
34
+ export declare function useServerNotificationEffect<T>(method: string, f: (params: T) => void, deps?: React.DependencyList): void;
35
+ /**
36
+ * Returns the same tuple as `setState` such that whenever a server notification with `method`
37
+ * arrives at the editor, the state will be updated according to `f`.
38
+ */
39
+ export declare function useServerNotificationState<S, T>(method: string, initial: S, f: (params: T) => Promise<(state: S) => S>, deps?: React.DependencyList): [S, React.Dispatch<React.SetStateAction<S>>];
40
+ export declare function useClientNotificationEffect<T>(method: string, f: (params: T) => void, deps?: React.DependencyList): void;
41
+ /**
42
+ * Like {@link useServerNotificationState} but for client->server notifications sent by the editor.
43
+ */
44
+ export declare function useClientNotificationState<S, T>(method: string, initial: S, f: (state: S, params: T) => S, deps?: React.DependencyList): [S, React.Dispatch<React.SetStateAction<S>>];
45
+ /** Useful for controlling {@link usePausableState} from child components. */
46
+ export interface PausableProps {
47
+ isPaused: boolean;
48
+ setPaused: React.Dispatch<React.SetStateAction<boolean>>;
49
+ }
50
+ /**
51
+ * Returns `[{ isPaused, setPaused }, tPausable, tRef]` s.t.
52
+ * - `[isPaused, setPaused]` are the paused status state
53
+ * - for as long as `isPaused` is set, `tPausable` holds its initial value (the `t` passed before pausing)
54
+ * rather than updates with changes to `t`.
55
+ * - `tRef` can be used to overwrite the paused state
56
+ *
57
+ * To pause child components, `startPaused` can be passed in their props.
58
+ */
59
+ export declare function usePausableState<T>(startPaused: boolean, t: T): [PausableProps, T, React.MutableRefObject<T>];
60
+ export type Keyed<T> = T & {
61
+ key: string;
62
+ };
63
+ /**
64
+ * Adds a unique `key` property to each element in `elems` using
65
+ * the values of (possibly non-injective) `getId`.
66
+ */
67
+ export declare function addUniqueKeys<T>(elems: T[], getId: (el: T) => string): Keyed<T>[];
68
+ /** Like `React.forwardRef`, but also allows reading the ref inside the forwarding component.
69
+ * Adapted from https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd */
70
+ export declare function forwardAndUseRef<T, P>(render: (props: P, ref: React.RefObject<T>, setRef: (_: T | null) => void) => React.ReactElement | null): React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<T>>;
71
+ /** Like `forwardAndUseRef`, but the ref is stored in state so that setting it triggers a render.
72
+ * Should only be used if re-rendering is necessary. */
73
+ export declare function forwardAndUseStateRef<T, P>(render: (props: P, ref: T | null, setRef: (_: T | null) => void) => React.ReactElement | null): React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<T>>;
74
+ export interface LogicalDomElement {
75
+ contains(el: Node): boolean;
76
+ }
77
+ export interface LogicalDomStorage {
78
+ /** Registers a descendant in the logical DOM.
79
+ * Returns a function which disposes of the registration. */
80
+ registerDescendant(el: LogicalDomElement): () => void;
81
+ }
82
+ export declare const LogicalDomContext: any;
83
+ /** Suppose a component B appears as a React descendant of the component A. For layout reasons,
84
+ * we sometimes don't want B to appear as a descendant of A in the DOM, so we use `createPortal`.
85
+ * We may still however want to carry out `contains` checks as if B were there, i.e. according to
86
+ * the React tree structure rather than the DOM structure. While React already correctly propagates
87
+ * DOM events up the React tree, other functionality such as `contains` is not provided. We provide
88
+ * it in this hook.
89
+ *
90
+ * Accepts a ref to the observed {@link HTMLElement} (A in the example). Returns:
91
+ * - a {@link LogicalDomElement} which provides `contains` checks for that {@link HTMLElement}; and
92
+ * - a {@link LogicalDomStorage} which MUST be passed to a {@link LogicalDomContext} enclosing
93
+ * the observed {@link HTMLElement}.
94
+ *
95
+ * Additionally, any component which introduces a portal MUST call `registerDescendant` in the
96
+ * {@link LogicalDomContext} with a ref to the portalled component (B in the example). */
97
+ export declare function useLogicalDomObserver(elt: React.RefObject<HTMLElement>): [LogicalDomElement, LogicalDomStorage];
98
+ /**
99
+ * An effect which calls `onClickOutside` whenever an element not logically descending from `ld`
100
+ * (see {@link useLogicalDomObserver}) is clicked. Note that `onClickOutside` is not called on clicks
101
+ * on the scrollbar since these should usually not impact the app's state. */
102
+ export declare function useOnClickOutside(ld: LogicalDomElement, onClickOutside: (_: PointerEvent) => void): void;
103
+ /** Sends an exception object to a throwable error.
104
+ * Maps JSON Rpc errors to throwable errors.
105
+ */
106
+ export declare function mapRpcError(err: unknown): Error;
107
+ /** Catch handler for RPC methods that just returns undefined if the method is not found.
108
+ * This is useful for compatibility with versions of Lean that do not yet have the given RPC method.
109
+ */
110
+ export declare function discardMethodNotFound(e: unknown): undefined;
111
+ export type AsyncState<T> = {
112
+ state: 'loading';
113
+ } | {
114
+ state: 'resolved';
115
+ value: T;
116
+ } | {
117
+ state: 'rejected';
118
+ error: any;
119
+ };
120
+ export type AsyncWithTriggerState<T> = {
121
+ state: 'notStarted';
122
+ } | AsyncState<T>;
123
+ export declare function useAsyncWithTrigger<T>(fn: () => Promise<T>, deps?: React.DependencyList): [AsyncWithTriggerState<T>, () => Promise<void>];
124
+ /** This React hook will run the given promise function `fn` whenever the deps change
125
+ * and use it to update the status and result when the promise resolves.
126
+ *
127
+ * This function prevents race conditions if the requests resolve in a
128
+ * different order to that which they were requested in:
129
+ *
130
+ * - Request 1 is sent with, say, line=42.
131
+ * - Request 2 is sent with line=90.
132
+ * - Request 2 returns with diags=[].
133
+ * - Request 1 returns with diags=['error'].
134
+ *
135
+ * Without `useAsync` we would now return the diagnostics for line 42 even though we're at line 90.
136
+ *
137
+ * When the deps change, the function immediately returns `{ state: 'loading' }`.
138
+ */
139
+ export declare function useAsync<T>(fn: () => Promise<T>, deps?: React.DependencyList): AsyncState<T>;
140
+ /** Like {@link useAsync} but never transitions from `resolved` to `loading` by internally storing
141
+ * the latest `resolved` state and continuing to return it while an update is in flight. The lower
142
+ * amount of re-renders tends to be less visually jarring.
143
+ */
144
+ export declare function useAsyncPersistent<T>(fn: () => Promise<T>, deps?: React.DependencyList): AsyncState<T>;