1use crate::*;
2use gloo_net::http::Request;
3use wasm_bindgen_futures::spawn_local;
4
5use crate::CURRENT_MECH;
6
7pub fn execute_repl_command(repl_cmd: ReplCommand) -> String {
8 match repl_cmd {
9 #[cfg(feature = "clear")]
10 ReplCommand::Clear(_) => {
11 CURRENT_MECH.with(|mech_ref| {
12 if let Some(ptr) = *mech_ref.borrow() {
13 unsafe {
14 let mut mech = &mut *ptr;
15 mech.interpreter.clear();
16 }
17 }
18 });
19 "".to_string()
20 }
21 #[cfg(feature = "clc")]
22 ReplCommand::Clc => {
23 CURRENT_MECH.with(|mech_ref| {
24 if let Some(ptr) = *mech_ref.borrow() {
25 unsafe {
26 let mut mech = &mut *ptr;
27 let window = web_sys::window().expect("global window does not exists");
28 let document = window.document().expect("expecting a document on window");
29 let output_element = document.get_element_by_id(&mech.repl_id.as_ref().unwrap().clone()).expect("REPL output element not found");
30 while output_element.child_nodes().length() > 0 {
32 let first_child = output_element
33 .first_child()
34 .expect("Expected a child node");
35 output_element
36 .remove_child(&first_child)
37 .expect("Failed to remove child");
38 }
39 }
40 return "".to_string();
41 }
42 "Error: No interpreter found.".to_string()
43 })
44 }
45 #[cfg(feature = "code")]
46 ReplCommand::Code(code) => {
47 CURRENT_MECH.with(|mech_ref| {
48 if let Some(ptr) = *mech_ref.borrow() {
49 unsafe {
50 let mut mech = &mut *ptr;
51 match run_mech_code(&mut mech.interpreter, &code) {
52 Ok(output) => {
53 let kind_str = html_escape(&format!("{}",output.kind()));
54 return format!("<div class=\"mech-output-kind\">{}</div><div class=\"mech-output-value\">{}</div>", kind_str, output.to_html());
55 },
56 Err(err) => { return format!("{:?}",err); }
57 }
58 }
59 }
60 "Error: No interpreter found.".to_string()
61 })
62 }
63 #[cfg(feature = "step")]
64 ReplCommand::Step(count) => {
65 CURRENT_MECH.with(|mech_ref| {
66 if let Some(ptr) = *mech_ref.borrow() {
67 unsafe {
68 let mut mech = &mut *ptr;
69 let n = match count {
70 Some(n) => n,
71 None => 1,
72 };
73 mech.interpreter.step(n as u64);
74 return format!("<div class=\"mech-output-kind\">Step</div><div class=\"mech-output-value\">Executed {} step(s).</div>",n);
75 }
76 }
77 "Error: No interpreter found.".to_string()
78 })
79 }
80 #[cfg(feature = "whos")]
81 ReplCommand::Whos(names) => {
82 CURRENT_MECH.with(|mech_ref| {
83 if let Some(ptr) = *mech_ref.borrow() {
84 unsafe {
85 let mut mech = &mut *ptr;
86 return whos_html(&mech.interpreter, names)
87 }
88 }
89 "Error: No interpreter found.".to_string()
90 })
91 }
92 #[cfg(feature = "help")]
93 ReplCommand::Help => {
94 help_html()
95 }
96 #[cfg(feature = "docs")]
97 ReplCommand::Docs(doc) => {
98 match doc {
99 Some(d) => {
100 CURRENT_MECH.with(|mech_ref| {
101 if let Some(ptr) = *mech_ref.borrow() {
102 unsafe {
103 let mut mech = &mut *ptr;
104 load_doc(&d, mech.repl_id.as_ref().unwrap().clone());
105 }
106 }
107 "Error: No interpreter found.".to_string()
108 });
109 format!("Fetching doc: {}...", d)
110 },
111 None => "Enter the name of a doc to load.".to_string(),
112 }
113 }
114 x => format!("Command {:?} not implemented for wasm", x),
115 }
116}
117
118#[cfg(feature = "help")]
120#[wasm_bindgen]
121pub fn help_html() -> String {
122 let text_logo = r#"
123┌─────────┐ ┌──────┐ ┌─┐ ┌──┐ ┌─┐ ┌─┐
124└───┐ ┌───┘ └──────┘ │ │ └┐ │ │ │ │ │
125┌─┐ │ │ ┌─┐ ┌──────┐ │ │ └─┘ │ └─┐│ │
126│ │ │ │ │ │ │ ┌────┘ │ │ ┌─┐ │ ┌─┘│ │
127│ │ └─┘ │ │ │ └────┐ │ └──┘ │ │ │ │ │
128└─┘ └─┘ └──────┘ └──────┘ └─┘ └─┘"#;
129 let version = env!("CARGO_PKG_VERSION");
130 let mut html = String::new();
131
132 html.push_str("<div class=\"mech-help\">");
133 html.push_str(&format!("<div class=\"mech-text-logo\">{}</div>", text_logo));
134 html.push_str(&format!("<div class=\"mech-version\">Version: <a href=\"https://github.com/mech-lang/mech/releases/tag/v{}-beta\">{}</a></div>", version, version));
135 html.push_str("<p>Welcome to the Mech REPL!</p>");
136 html.push_str("<p>Full documentation: <a href=\"https://docs.mech-lang.org\">docs.mech-lang.org</a>.</p>");
137 html.push_str("<table class=\"mech-help-table\">");
138 html.push_str("<thead><tr><th>Command</th><th>Short</th><th>Options</th><th>Description</th></tr></thead>");
139 html.push_str("<tbody>");
140 html.push_str("<tr><td><span class=\"mech-command\">:clc</span></td><td></span></td><td></td><td>Clear the REPL output.</td></tr>");
141 html.push_str("<tr><td><span class=\"mech-command\">:clear</span><td></span></td></td><td></td><td>Clear the interpreter state.</td></tr>");
142 html.push_str("<tr><td><span class=\"mech-command\">:docs</span></td><td></td><td><span class=\"mech-command\">[doc]</span></td><td>Display the given doc in the REPL.</td></tr>");
143 html.push_str("<tr><td><span class=\"mech-command\">:help</span></td><td><span class=\"mech-command\">:h</span></td><td></td><td>Show this help message.</td></tr>");
144 html.push_str("<tr><td><span class=\"mech-command\">:step</span></td><td></td><td><span class=\"mech-command\">[count]</span></td><td>Run the plan for a specified number of steps.</td></tr>");
145 html.push_str("<tr><td><span class=\"mech-command\">:whos</span></td><td><span class=\"mech-command\">:w</span></td><td><span class=\"mech-command\">[names...]</span></td><td>Show the current symbol directory.</td></tr>");
146 html.push_str("</tbody>");
147 html.push_str("</table>");
148 html.push_str("</div>");
149 html
150}
151
152#[cfg(feature = "whos")]
153pub fn whos_html(intrp: &Interpreter, names: Vec<String>) -> String {
154 let state_brrw = intrp.state.borrow();
155 let symbol_table = state_brrw.symbol_table.borrow();
156 let dictionary = symbol_table.dictionary.borrow();
157
158 let mut rows = Vec::new();
160
161 if !names.is_empty() {
162 for target_name in &names {
163 for (id, name) in dictionary.iter() {
164 if *name == *target_name {
165 if let Some(value_ref) = symbol_table.symbols.get(id) {
166 let value = value_ref.borrow();
167 rows.push((name.clone(), value.clone()));
168 }
169 break;
170 }
171 }
172 }
173 } else {
174 for (id, value_ref) in symbol_table.symbols.iter() {
176 if let Some(name) = dictionary.get(id) {
177 let value = value_ref.borrow();
178 rows.push((name.clone(), value.clone()));
179 }
180 }
181 }
182
183 if rows.is_empty() {
184 if names.is_empty() {
185 "No symbols found in the document.".to_string()
186 } else {
187 format!("Symbol(s) {:?} not found in the document.", names)
188 }
189 } else {
190 let mut html = String::new();
192 html.push_str("<table class=\"mech-table\">");
193 html.push_str("<thead class=\"mech-table-header\"><tr>");
194 html.push_str("<th class=\"mech-table-field\">Name</th>");
195 html.push_str("<th class=\"mech-table-field\">Size</th>");
196 html.push_str("<th class=\"mech-table-field\">Bytes</th>");
197 html.push_str("<th class=\"mech-table-field\">Kind</th>");
198 html.push_str("</tr></thead>");
199 html.push_str("<tbody class=\"mech-table-body\">");
200
201 for (name, value) in rows {
202 append_row(&mut html, &name, &value);
203 }
204
205 html.push_str("</tbody></table>");
206 html
207 }
208}
209
210
211fn append_row(html: &mut String, name: &str, value: &Value) {
212 let name = html_escape(name);
213 let size = html_escape(&format!("{:?}", value.shape()));
214 let bytes = html_escape(&format!("{}", value.size_of()));
215 let kind = html_escape(&format!("{}", value.kind()));
216
217 html.push_str("<tr class=\"mech-table-row\">");
218
219 let id = hash_str(&name);
220 html.push_str(&format!("<td class=\"mech-table-column\"><span class=\"mech-var-name mech-clickable\" id=\"{}:0\">{}</span></td>",id, name));
221 html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", size));
222 html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", bytes));
223 html.push_str(&format!("<td class=\"mech-table-column\">{}</td>", kind));
224 html.push_str("</tr>");
225}
226
227pub fn html_escape(input: &str) -> String {
228 input
229 .replace('&', "&")
230 .replace('<', "<")
231 .replace('>', ">")
232}