) => {
46 | if (
47 | (event.key === 'k' && event.metaKey !== event.ctrlKey && !event.altKey) ||
48 | (event.key === 'l' && event.ctrlKey && !event.metaKey && !event.altKey)
49 | ) {
50 | // Cmd+K, Ctrl+K or Ctrl+L to clear the buffer
51 | setOutput(null);
52 | return;
53 | }
54 | if (event.key === 'ArrowUp') {
55 | event.preventDefault();
56 | upArrow();
57 | return;
58 | }
59 | if (event.key === 'ArrowDown') {
60 | event.preventDefault();
61 | downArrow();
62 | return;
63 | }
64 |
65 | // allow multiple lines to be entered if shift, ctrl
66 | // or meta is held, otherwise evaluate the expression
67 | if (!(event.key === 'Enter' && !event.shiftKey && !event.ctrlKey && !event.metaKey && !event.altKey)) {
68 | return;
69 | }
70 | event.preventDefault();
71 | if (currentInput.trim() === 'clear') {
72 | onInput('');
73 | setOutput(null);
74 | return;
75 | }
76 |
77 | startTransition(async () => {
78 | const request = {`> ${currentInput}`}
;
79 | submit();
80 | const fendResult = await evaluate(currentInput);
81 | if (!fendResult.ok && fendResult.message === 'cancelled') {
82 | return;
83 | }
84 | onInput('');
85 | const result = {fendResult.ok ? fendResult.result : `Error: ${fendResult.message}`}
;
86 | setOutput(o => (
87 | <>
88 | {o}
89 | {request}
90 | {result}
91 | >
92 | ));
93 | setTimeout(() => {
94 | pendingOutput.current?.scrollIntoView({ behavior: 'smooth' });
95 | }, 50);
96 | });
97 | };
98 | useEffect(() => {
99 | const focus = () => {
100 | // allow the user to select text for copying and
101 | // pasting, but if text is deselected (collapsed)
102 | // refocus the input field
103 | if (document.activeElement !== inputText.current && document.getSelection()?.isCollapsed) {
104 | inputText.current?.focus();
105 | }
106 | };
107 | document.addEventListener('click', focus);
108 | return () => {
109 | document.removeEventListener('click', focus);
110 | };
111 | }, []);
112 | return (
113 |
114 | {!widget && (
115 |
116 | fend is an arbitrary-precision
117 | unit-aware calculator.
118 |
119 | )}
120 | {output}
121 |
139 |
140 | );
141 | }
142 |
--------------------------------------------------------------------------------
/core/src/scope.rs:
--------------------------------------------------------------------------------
1 | use crate::ident::Ident;
2 | use crate::result::FResult;
3 | use crate::serialize::{Deserialize, Serialize};
4 | use crate::value::Value;
5 | use crate::{Attrs, Context, Span};
6 | use crate::{ast::Expr, error::Interrupt};
7 | use std::io;
8 | use std::sync::Arc;
9 |
10 | #[derive(Debug, Clone)]
11 | enum ScopeValue {
12 | LazyVariable(Expr, Option>),
13 | }
14 |
15 | impl ScopeValue {
16 | pub(crate) fn compare(
17 | &self,
18 | other: &Self,
19 | ctx: &mut Context,
20 | int: &I,
21 | ) -> FResult {
22 | let Self::LazyVariable(a1, a2) = self;
23 | let Self::LazyVariable(b1, b2) = other;
24 | Ok(a1.compare(b1, ctx, int)?
25 | && compare_option_arc_scope(a2.as_ref(), b2.as_ref(), ctx, int)?)
26 | }
27 |
28 | fn eval(
29 | &self,
30 | attrs: Attrs,
31 | spans: &mut Vec,
32 | context: &mut crate::Context,
33 | int: &I,
34 | ) -> FResult {
35 | match self {
36 | Self::LazyVariable(expr, scope) => {
37 | let value =
38 | crate::ast::evaluate(expr.clone(), scope.clone(), attrs, spans, context, int)?;
39 | Ok(value)
40 | }
41 | }
42 | }
43 |
44 | pub(crate) fn serialize(&self, write: &mut impl io::Write) -> FResult<()> {
45 | match self {
46 | Self::LazyVariable(e, s) => {
47 | e.serialize(write)?;
48 | match s {
49 | None => false.serialize(write)?,
50 | Some(s) => {
51 | true.serialize(write)?;
52 | s.serialize(write)?;
53 | }
54 | }
55 | }
56 | }
57 | Ok(())
58 | }
59 |
60 | pub(crate) fn deserialize(read: &mut impl io::Read) -> FResult {
61 | Ok(Self::LazyVariable(Expr::deserialize(read)?, {
62 | if bool::deserialize(read)? {
63 | None
64 | } else {
65 | Some(Arc::new(Scope::deserialize(read)?))
66 | }
67 | }))
68 | }
69 | }
70 |
71 | #[derive(Debug, Clone)]
72 | pub(crate) struct Scope {
73 | ident: Ident,
74 | value: ScopeValue,
75 | inner: Option>,
76 | }
77 |
78 | pub(crate) fn compare_option_arc_scope(
79 | a: Option<&Arc>,
80 | b: Option<&Arc>,
81 | ctx: &mut Context,
82 | int: &I,
83 | ) -> FResult {
84 | Ok(match (a, b) {
85 | (None, None) => true,
86 | (Some(a), Some(b)) => a.compare(b, ctx, int)?,
87 | _ => false,
88 | })
89 | }
90 |
91 | impl Scope {
92 | pub(crate) fn compare(
93 | &self,
94 | other: &Self,
95 | ctx: &mut Context,
96 | int: &I,
97 | ) -> FResult {
98 | Ok(self.ident == other.ident
99 | && self.value.compare(&other.value, ctx, int)?
100 | && compare_option_arc_scope(self.inner.as_ref(), other.inner.as_ref(), ctx, int)?)
101 | }
102 |
103 | pub(crate) fn serialize(&self, write: &mut impl io::Write) -> FResult<()> {
104 | self.ident.serialize(write)?;
105 | self.value.serialize(write)?;
106 | match &self.inner {
107 | None => false.serialize(write)?,
108 | Some(s) => {
109 | true.serialize(write)?;
110 | s.serialize(write)?;
111 | }
112 | }
113 | Ok(())
114 | }
115 |
116 | pub(crate) fn deserialize(read: &mut impl io::Read) -> FResult {
117 | Ok(Self {
118 | ident: Ident::deserialize(read)?,
119 | value: ScopeValue::deserialize(read)?,
120 | inner: {
121 | if bool::deserialize(read)? {
122 | None
123 | } else {
124 | Some(Arc::new(Self::deserialize(read)?))
125 | }
126 | },
127 | })
128 | }
129 |
130 | const fn with_scope_value(ident: Ident, value: ScopeValue, inner: Option>) -> Self {
131 | Self {
132 | ident,
133 | value,
134 | inner,
135 | }
136 | }
137 |
138 | pub(crate) fn with_variable(
139 | name: Ident,
140 | expr: Expr,
141 | scope: Option>,
142 | inner: Option>,
143 | ) -> Self {
144 | Self::with_scope_value(name, ScopeValue::LazyVariable(expr, scope), inner)
145 | }
146 |
147 | pub(crate) fn get(
148 | &self,
149 | ident: &Ident,
150 | attrs: Attrs,
151 | spans: &mut Vec,
152 | context: &mut crate::Context,
153 | int: &I,
154 | ) -> FResult