I'd like to convert text-area value into the Markdown, I successfully managed to display pure text from text-area into output container however I stuck on binding parser inside closure, so how could I use properly parser and convert new text and display in output container?
lib.rs
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{Document};
use pulldown_cmark::{html, Options, Parser};
#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
let window = web_sys::window().expect("should have a window in this context");
let document = window.document().expect("window should have a document");
setup_preview(&document);
Ok(())
}
pub fn get_value(target: &web_sys::EventTarget) -> String {
if let Some(input) = target.dyn_ref::<web_sys::HtmlTextAreaElement>() {
return input.value();
}
"".into()
}
fn setup_preview(document: &Document) {
let input = document.query_selector("textarea").unwrap().unwrap();
let output = document.query_selector("output").unwrap().unwrap();
let text: &str = "### Init";
let parser = Parser::new_ext(text, Options::empty());
let mut html_output_buf = String::new();
html::push_html(&mut html_output_buf, parser);
output.set_inner_html(&html_output_buf);
let a = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
let val = get_value(&event.target().unwrap());
let mut new_html_output_buf: String = String::with_capacity(val.len() * 3 / 2);
html::push_html(&mut new_html_output_buf, parser); // <-- bad line here :(
output.set_inner_html(&new_html_output_buf);
}) as Box<dyn FnMut(_)>);
input.add_event_listener_with_callback("keydown", a.as_ref().unchecked_ref()).unwrap();
a.forget();
}
this is great that I can borrow/reuse multiple times parser object by ref but this version doesn't throw any error and it doesn't return any string in output container The second approach to copy 1:1 the same code works like a harm but I'm not sure if in Rust DRY principle is commonly used, maybe it's fair enough?
let mut parser = Parser::new_ext(text, Options::empty());
let mut html_output_buf = String::new();
html::push_html(&mut html_output_buf, parser.by_ref());
output.set_inner_html(&html_output_buf);
let a = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
let val = get_value(&event.target().unwrap());
// let mut new_html_output_buf: String = String::with_capacity(val.len() * 3 / 2);
// html::push_html(&mut new_html_output_buf, parser.by_ref());
let parser = Parser::new_ext(&val, Options::empty());
let mut new_html_output_buf = String::new();
html::push_html(&mut new_html_output_buf, parser);
output.set_inner_html(&new_html_output_buf);
}) as Box<dyn FnMut(_)>);
input.add_event_listener_with_callback("keyup", a.as_ref().unchecked_ref()).unwrap();
a.forget();
EDIT: it seems that by_ref can utilize parser only once
in near future I'd like to add some events mapper in one place, eg.
let parser = Parser::new_ext(text, Options::empty())
.map(|event| match event {
Event::Text(text) => Event::Text(text.replace("Peter", "John").into()),
_ => event,
})
I learned that I can clone parser and use one string buffer, but I'm still missing how to pass new value into cloned parser or how to use the same events mapper, do you have any clue?
fn setup_preview(document: &Document) {
let input = document.query_selector("textarea").unwrap().unwrap();
let output = document.query_selector("output").unwrap().unwrap();
let text: &str = "## This is Peter on .";
let events: Vec<_> = Parser::new_ext(text, Options::empty())
.map(|event| match event {
Event::Text(text) => Event::Text(text.replace("Peter", "John").into()),
_ => event,
})
.collect();
let mut buf = String::with_capacity(text.len() * 3 / 2);
html::push_html(&mut buf, events.clone().into_iter());
output.set_inner_html(&buf);
let a = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
let val = get_value(&event.target().unwrap());
buf.clear();
// html::push_html(&mut buf, events.clone().into_iter()); // cannot reuse parser with new val
html::push_html(&mut buf, Parser::new_ext(&val, Options::empty()));
output.set_inner_html(&buf);
}) as Box<dyn FnMut(_)>);
input.add_event_listener_with_callback("keyup", a.as_ref().unchecked_ref()).unwrap();
a.forget();
}
This way, the manual introduction followed by the conversion into a JS callback is simple:
handle_md_text(
"## This is Peter on ."
);
let a = Closure::wrap(Box::new(move |event: web_sys::KeyboardEvent| {
handle_md_text(&get_value(&event.target().unwrap()));
}) as Box<dyn FnMut(_)>);