Highlight code, start code tooltip

This commit is contained in:
Adrian Woźniak 2020-08-13 23:44:37 +02:00
parent 331acd4574
commit 8deab1c2d5
8 changed files with 330 additions and 59 deletions

139
Cargo.lock generated
View File

@ -544,10 +544,25 @@ version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"serde", "serde",
] ]
[[package]]
name = "bit-set"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.2.1" version = "1.2.1"
@ -1067,8 +1082,8 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
"synstructure", "synstructure",
] ]
[[package]] [[package]]
@ -1077,13 +1092,23 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fancy-regex"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae91abf6555234338687bb47913978d275539235fcb77ba9863b779090b42b14"
dependencies = [
"bit-set",
"regex",
]
[[package]] [[package]]
name = "fast_chemail" name = "fast_chemail"
version = "0.9.6" version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" checksum = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4"
dependencies = [ dependencies = [
"ascii_utils", "ascii_utils",
] ]
[[package]] [[package]]
@ -1687,19 +1712,21 @@ dependencies = [
name = "jirs_client" name = "jirs_client"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"chrono", "chrono",
"comrak", "comrak",
"futures 0.1.29", "futures 0.1.29",
"jirs-data", "jirs-data",
"js-sys", "js-sys",
"seed", "lazy_static",
"serde", "seed",
"uuid 0.8.1", "serde",
"wasm-bindgen", "syntect",
"wasm-bindgen-test", "uuid 0.8.1",
"web-sys", "wasm-bindgen",
"wee_alloc", "wasm-bindgen-test",
"web-sys",
"wee_alloc",
] ]
[[package]] [[package]]
@ -1767,8 +1794,8 @@ dependencies = [
"email", "email",
"lettre", "lettre",
"mime", "mime",
"time 0.1.43", "time 0.1.43",
"uuid 0.7.4", "uuid 0.7.4",
] ]
[[package]] [[package]]
@ -1777,6 +1804,15 @@ version = "0.2.74"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
[[package]]
name = "line-wrap"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
dependencies = [
"safemem",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.3" version = "0.5.3"
@ -2247,6 +2283,20 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33" checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
[[package]]
name = "plist"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b336d94e8e4ce29bf15bba393164629764744c567e8ad306cc1fdd0119967fd"
dependencies = [
"base64 0.12.3",
"chrono",
"indexmap",
"line-wrap",
"serde",
"xml-rs",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.8" version = "0.2.8"
@ -3003,10 +3053,32 @@ version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn",
"unicode-xid", "unicode-xid",
]
[[package]]
name = "syntect"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b57a45fdcf4891bc79f635be5c559210a4cfa464891f969724944c713282eedb"
dependencies = [
"bincode",
"bitflags",
"fancy-regex",
"flate2",
"fnv",
"lazy_static",
"lazycell",
"plist",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"walkdir",
"yaml-rust",
] ]
[[package]] [[package]]
@ -3015,9 +3087,9 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"rand 0.7.3", "rand 0.7.3",
"redox_syscall", "redox_syscall",
"remove_dir_all", "remove_dir_all",
"winapi 0.3.9", "winapi 0.3.9",
@ -3686,8 +3758,8 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [ dependencies = [
"winapi 0.2.8", "winapi 0.2.8",
"winapi-build", "winapi-build",
] ]
[[package]] [[package]]
@ -3696,6 +3768,15 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
[[package]]
name = "yaml-rust"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.1.0" version = "1.1.0"

View File

@ -11,7 +11,7 @@ license = "MPL-2.0"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
name = "jirs_client" name = "jirs_client"
path = "./src/lib.rs" path = "src/lib.rs"
[profile.dev] [profile.dev]
opt-level = 0 # Use slightly better optimizations. opt-level = 0 # Use slightly better optimizations.
@ -32,6 +32,9 @@ futures = "^0.1.26"
comrak = "*" comrak = "*"
wee_alloc = "*" wee_alloc = "*"
lazy_static = "*"
syntect = { version = "*", default-features = false, features = ["default-fancy"] }
[dependencies.wasm-bindgen] [dependencies.wasm-bindgen]
version = "0.2.66" version = "0.2.66"
features = ["enable-interning"] features = ["enable-interning"]

125
jirs-client/src/elements.rs Normal file
View File

@ -0,0 +1,125 @@
use syntect::easy::HighlightLines;
use syntect::highlighting::{FontStyle, Style};
use wasm_bindgen::prelude::*;
#[wasm_bindgen(final, js_name = JirsCodeBuilder)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JirsCodeBuilder {}
#[wasm_bindgen]
impl JirsCodeBuilder {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {}
}
#[wasm_bindgen]
pub fn hi(&mut self, lang: &str, line: &str) -> String {
let syntax = match crate::hi::SYNTAX_SET.find_syntax_by_name(lang) {
Some(s) => s,
_ => {
return line.to_string();
}
};
let mut h = HighlightLines::new(syntax, &crate::hi::THEME_SET.themes["base16-ocean-dark"]); // inspired-github
let tokens = h.highlight(line, &crate::hi::SYNTAX_SET);
let parts: Vec<String> = tokens
.into_iter()
.map(|(style, token)| {
let Style {
foreground: f,
background: b,
font_style,
} = style;
let fs = if font_style == FontStyle::BOLD {
"font-weight: bold"
} else if font_style == FontStyle::ITALIC {
"font-style: italic"
} else {
"font-decoration: underline"
};
let f = format!("rgba({}, {}, {}, {})", f.r, f.g, f.b, f.a);
let b = format!("rgba({}, {}, {}, {})", b.r, b.g, b.b, b.a);
format!(
r#"<span style="color: {f};background:{b}; {fs}">{t}</span>"#,
t = if token.is_empty() { "&nbsp;" } else { token },
f = f,
b = b,
fs = fs
)
})
.collect();
parts.join("")
}
}
pub fn define() {
create_custom_element(
"jirs-code-view",
"JirsCodeView",
r#"
<style>
:host { display: block; border: 1px solid black; background: rgba(43, 48, 59, 255); padding: 1rem; }
:host { margin-left: 400px; }
#view span { white-space: pre; }
</style>
<div id='view'></div>
"#,
);
}
fn create_custom_element(tag: &str, name: &str, html: &str) {
let source = format!(
r#"
class {name} extends HTMLElement {{
static RUNTIME = Symbol();
static SHADOW = Symbol();
static get observedAttributes() {{ return ['lang']; }}
constructor() {{
super();
this[ {name} . SHADOW] = this.attachShadow({{ 'mode': 'closed' }});
this[ {name} . SHADOW].innerHTML = `{html}`;
}}
connectedCallback() {{
this[ {name} . RUNTIME] = new JirsCodeBuilder();
const view = this[ {name} . SHADOW].querySelector('#view');
view.innerHTML = '';
const lang = this.getAttribute('lang') || '';
setTimeout(() => {{
const hi = () => {{
const line = code.shift();
if (line === undefined) return;
const s = this[ {name} . RUNTIME].hi(lang, line);
view.innerHTML += `${{s}}<br />`;
setTimeout(() => hi(), 10);
}};
hi();
}}, 10);
}}
disconnectedCallback() {{
this[ {name} . RUNTIME].free();
}}
attributeChangedCallback(name, oldV, newV) {{
}}
}}
customElements.define( '{tag}', {name});
"#,
name = name,
tag = tag,
html = html,
);
{
use seed::*;
match js_sys::eval(source.as_str()) {
Ok(_v) => (),
Err(e) => error!(e),
};
};
}

Binary file not shown.

View File

@ -0,0 +1,9 @@
use lazy_static::lazy_static;
use syntect::dumps::from_binary;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
lazy_static! {
pub static ref SYNTAX_SET: SyntaxSet = from_binary(include_bytes!("./newlines.packdump"));
pub static ref THEME_SET: ThemeSet = from_binary(include_bytes!("./all.themedump"));
}

Binary file not shown.

View File

@ -15,7 +15,9 @@ use crate::shared::{go_to_board, go_to_login, styled_tooltip};
use crate::ws::{flush_queue, open_socket, read_incoming, send_ws_msg}; use crate::ws::{flush_queue, open_socket, read_incoming, send_ws_msg};
mod changes; mod changes;
pub mod elements;
mod fields; mod fields;
pub mod hi;
mod invite; mod invite;
mod modal; mod modal;
mod model; mod model;
@ -261,6 +263,7 @@ pub static mut WS_URL: String = String::new();
#[wasm_bindgen] #[wasm_bindgen]
pub fn render(host_url: String, ws_url: String) { pub fn render(host_url: String, ws_url: String) {
elements::define();
unsafe { unsafe {
HOST_URL = host_url; HOST_URL = host_url;
WS_URL = ws_url; WS_URL = ws_url;

View File

@ -70,12 +70,17 @@ pub enum RteMsg {
RemoveFormat, RemoveFormat,
Subscript, Subscript,
Superscript, Superscript,
// table
TableSetVisibility(bool),
TableSetRows(u16), TableSetRows(u16),
TableSetColumns(u16), TableSetColumns(u16),
TableSetVisibility(bool),
InsertTable { rows: u16, cols: u16 }, InsertTable { rows: u16, cols: u16 },
ChangeIndent(RteIndentMsg), ChangeIndent(RteIndentMsg),
// code
InsertCode(bool),
RequestFocus(uuid::Uuid), RequestFocus(uuid::Uuid),
} }
@ -139,6 +144,8 @@ impl RteMsg {
RteMsg::Subscript => Some(ExecCommand::new("subscript")), RteMsg::Subscript => Some(ExecCommand::new("subscript")),
RteMsg::Superscript => Some(ExecCommand::new("superscript")), RteMsg::Superscript => Some(ExecCommand::new("superscript")),
RteMsg::InsertTable { .. } => None, RteMsg::InsertTable { .. } => None,
// code
RteMsg::InsertCode(_) => None,
// indent // indent
RteMsg::ChangeIndent(RteIndentMsg::Increase) => Some(ExecCommand::new("indent")), RteMsg::ChangeIndent(RteIndentMsg::Increase) => Some(ExecCommand::new("indent")),
@ -171,11 +178,18 @@ pub struct StyledRteTableState {
pub cols: u16, pub cols: u16,
} }
#[derive(Debug, Clone)]
pub struct StyledRteCodeState {
pub visible: bool,
pub lang: String,
}
#[derive(Debug)] #[derive(Debug)]
pub struct StyledRteState { pub struct StyledRteState {
pub value: String, pub value: String,
pub field_id: FieldId, pub field_id: FieldId,
pub table_tooltip: StyledRteTableState, pub table_tooltip: StyledRteTableState,
pub code_tooltip: StyledRteCodeState,
range: Option<web_sys::Range>, range: Option<web_sys::Range>,
identifier: uuid::Uuid, identifier: uuid::Uuid,
} }
@ -190,6 +204,10 @@ impl StyledRteState {
rows: 3, rows: 3,
cols: 3, cols: 3,
}, },
code_tooltip: StyledRteCodeState {
visible: false,
lang: "".to_string(),
},
range: None, range: None,
identifier: uuid::Uuid::new_v4(), identifier: uuid::Uuid::new_v4(),
} }
@ -217,6 +235,12 @@ impl StyledRteState {
self.schedule_focus(orders); self.schedule_focus(orders);
} }
_ => match m { _ => match m {
RteMsg::InsertCode(b) => {
if *b {
self.store_range();
}
self.code_tooltip.visible = *b;
}
RteMsg::TableSetRows(n) => { RteMsg::TableSetRows(n) => {
self.table_tooltip.rows = *n; self.table_tooltip.rows = *n;
} }
@ -772,41 +796,48 @@ fn second_row(values: &StyledRte) -> Node<Msg> {
None as Option<Msg> None as Option<Msg>
}), }),
);*/ );*/
let field_id = values.field_id.clone();
let mut table_button = styled_rte_button( let mut table_button = {
"Table", let field_id = values.field_id.clone();
Icon::Table, styled_rte_button(
mouse_ev(Ev::Click, move |ev| { "Table",
ev.prevent_default(); Icon::Table,
Some(Msg::Rte(RteMsg::TableSetVisibility(true), field_id)) mouse_ev(Ev::Click, move |ev| {
}), ev.prevent_default();
); Some(Msg::Rte(RteMsg::TableSetVisibility(true), field_id))
}),
)
};
table_button.add_child(table_tooltip); table_button.add_child(table_tooltip);
let field_id = values.field_id.clone(); let paragraph_button = {
let paragraph_button = styled_rte_button( let field_id = values.field_id.clone();
"Paragraph", styled_rte_button(
Icon::Paragraph, "Paragraph",
mouse_ev(Ev::Click, move |ev| { Icon::Paragraph,
ev.prevent_default(); mouse_ev(Ev::Click, move |ev| {
Some(Msg::Rte(RteMsg::InsertParagraph, field_id)) ev.prevent_default();
}), Some(Msg::Rte(RteMsg::InsertParagraph, field_id))
); }),
// let field_id = values.field_id.clone(); )
// let code_alt_button = styled_rte_button( };
// "Insert code", let code_alt_button = {
// Icon::CodeAlt, let field_id = values.field_id.clone();
// mouse_ev(Ev::Click, move |ev| { styled_rte_button(
// ev.prevent_default(); "Insert code",
// None as Option<Msg> Icon::CodeAlt,
// }), mouse_ev(Ev::Click, move |ev| {
// ); ev.prevent_default();
Some(Msg::Rte(RteMsg::InsertCode(true), field_id))
}),
)
};
div![ div![
class!["group insert"], class!["group insert"],
paragraph_button, paragraph_button,
table_button, table_button,
// code_alt_button, code_alt_button,
listing_dots, listing_dots,
listing_number, listing_number,
// sub_listing_button, // sub_listing_button,
@ -931,3 +962,22 @@ fn styled_rte_button(title: &str, icon: Icon, handler: EventHandler<Msg>) -> Nod
button button
] ]
} }
fn insert_code() -> Node<Msg> {
let mut languages: Vec<&str> = crate::hi::SYNTAX_SET
.syntaxes()
.iter()
.map(|s| s.name.as_str())
.collect();
languages.sort();
let options: Vec<Node<Msg>> = languages
.into_iter()
.map(|name| option![attrs![At::Value => name], name])
.collect();
seed::select![options]
}
pub fn code_to_tag(code: &str) -> Node<Msg> {
custom!["jirs-code-view", code]
}