this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tala: Code dedupe, cargo fmt

+190 -207
+190 -207
crates/tala/src/main.rs
··· 174 174 Error(String), 175 175 } 176 176 177 - /// A segment of the source file: either a card fragment or unparsed text. 178 - #[derive(Clone)] 179 - enum Seg { 180 - Card { start: usize, end: usize }, 181 - Text { start: usize, end: usize }, 177 + /// Whether a paragraph segment is a card definition or plain text. 178 + #[derive(Clone, PartialEq)] 179 + enum SegKind { 180 + Card, 181 + Text, 182 182 } 183 183 184 - impl Seg { 185 - fn start(&self) -> usize { 186 - match self { Seg::Card { start, .. } | Seg::Text { start, .. } => *start } 187 - } 188 - fn end(&self) -> usize { 189 - match self { Seg::Card { end, .. } | Seg::Text { end, .. } => *end } 190 - } 184 + /// A blank-line-delimited paragraph in the source file. 185 + #[derive(Clone)] 186 + struct Seg { 187 + start: usize, 188 + end: usize, 189 + kind: SegKind, 191 190 } 192 191 193 192 /// True if a source fragment looks like a card definition by prefix alone. 194 193 fn is_card_frag(s: &str) -> bool { 195 194 let s = s.trim_start(); 196 - s.starts_with("#card[") || s.starts_with("#card(") 197 - || s.starts_with("#cloze[") || s.starts_with("#cloze(") 195 + s.starts_with("#card[") 196 + || s.starts_with("#card(") 197 + || s.starts_with("#cloze[") 198 + || s.starts_with("#cloze(") 198 199 || s.starts_with("#img_cloze(") 199 200 } 200 201 ··· 210 211 if bytes[i] == b'\n' && i + 1 < len && bytes[i + 1] == b'\n' { 211 212 let chunk = &source[seg_start..i]; 212 213 if !chunk.trim().is_empty() { 213 - if is_card_frag(chunk) { 214 - segs.push(Seg::Card { start: seg_start, end: i }); 214 + let kind = if is_card_frag(chunk) { 215 + SegKind::Card 215 216 } else { 216 - segs.push(Seg::Text { start: seg_start, end: i }); 217 - } 217 + SegKind::Text 218 + }; 219 + segs.push(Seg { 220 + start: seg_start, 221 + end: i, 222 + kind, 223 + }); 218 224 } 219 - while i < len && bytes[i] == b'\n' { i += 1; } 225 + while i < len && bytes[i] == b'\n' { 226 + i += 1; 227 + } 220 228 seg_start = i; 221 229 } else { 222 230 i += 1; 223 231 } 224 232 } 225 233 if seg_start < len && !source[seg_start..].trim().is_empty() { 226 - if is_card_frag(&source[seg_start..]) { 227 - segs.push(Seg::Card { start: seg_start, end: len }); 234 + let kind = if is_card_frag(&source[seg_start..]) { 235 + SegKind::Card 228 236 } else { 229 - segs.push(Seg::Text { start: seg_start, end: len }); 230 - } 237 + SegKind::Text 238 + }; 239 + segs.push(Seg { 240 + start: seg_start, 241 + end: len, 242 + kind, 243 + }); 231 244 } 232 245 segs 233 246 } ··· 248 261 #[component] 249 262 fn Editor() -> Element { 250 263 // ── Source & save ───────────────────────────────────────────────────────── 251 - let mut source = use_signal(|| { 252 - std::fs::read_to_string(cards_path()).unwrap_or_default() 253 - }); 264 + let mut source = use_signal(|| std::fs::read_to_string(cards_path()).unwrap_or_default()); 254 265 let mut save_status = use_signal(|| SaveStatus::Clean); 255 266 256 267 // Auto-save: debounce 1s after last edit. ··· 261 272 match tokio::task::spawn_blocking(move || std::fs::write(&path, &text)).await { 262 273 Ok(Ok(())) => save_status.set(SaveStatus::Saved), 263 274 Ok(Err(e)) => save_status.set(SaveStatus::Error(e.to_string())), 264 - Err(e) => save_status.set(SaveStatus::Error(e.to_string())), 275 + Err(e) => save_status.set(SaveStatus::Error(e.to_string())), 265 276 } 266 277 }); 267 278 268 279 // ── Multi-card state ─────────────────────────────────────────────────────── 269 280 let mut active_idx = use_signal(|| 0usize); 270 281 // Indexed by segment. None = text segment (no render attempt). 271 - let mut previews = use_signal(Vec::<Option<Result<PreviewData, String>>>::new); 282 + let mut previews = use_signal(Vec::<Option<Result<PreviewData, String>>>::new); 272 283 273 284 // Per-segment render resource: each card fragment compiled independently. 274 285 let _render = use_resource(move || async move { ··· 278 289 let results = tokio::task::spawn_blocking(move || { 279 290 make_segments(&text) 280 291 .into_iter() 281 - .map(|seg| match seg { 282 - Seg::Card { start, end } => Some(render_preview(&text[start..end], &dir)), 283 - Seg::Text { .. } => None, 292 + .map(|seg| match seg.kind { 293 + SegKind::Card => Some(render_preview(&text[seg.start..seg.end], &dir)), 294 + SegKind::Text => None, 284 295 }) 285 296 .collect::<Vec<_>>() 286 297 }) ··· 291 302 292 303 // ── Draw mode state ──────────────────────────────────────────────────────── 293 304 let mut blank_rects_sig = use_signal(Vec::<[f64; 4]>::new); 294 - let mut glyph_map_sig = use_signal(Vec::<([f64; 4], std::ops::Range<usize>, bool)>::new); 295 - let mut math_spans_sig = use_signal(Vec::<std::ops::Range<usize>>::new); 296 - let mut draw_mode = use_signal(|| false); 297 - let mut drag_start = use_signal(|| Option::<(f64, f64)>::None); 305 + let mut glyph_map_sig = use_signal(Vec::<([f64; 4], std::ops::Range<usize>, bool)>::new); 306 + let mut math_spans_sig = use_signal(Vec::<std::ops::Range<usize>>::new); 307 + let mut draw_mode = use_signal(|| false); 308 + let mut drag_start = use_signal(|| Option::<(f64, f64)>::None); 298 309 let mut drag_current = use_signal(|| Option::<(f64, f64)>::None); 299 - let mut drawn_boxes = use_signal(Vec::<[f64; 4]>::new); 300 - let cap_dims = use_signal(|| [1.0f64, 1.0, 1.0]); 310 + let mut drawn_boxes = use_signal(Vec::<[f64; 4]>::new); 311 + let cap_dims = use_signal(|| [1.0f64, 1.0, 1.0]); 301 312 let mut insert_error = use_signal(|| Option::<String>::None); 302 313 303 314 // Update glyph signals when the active card or its render result changes. 304 315 use_effect(move || { 305 - let idx = *active_idx.read(); // subscribe 316 + let idx = *active_idx.read(); // subscribe 306 317 let pvs = previews.read().clone(); // subscribe 307 - let text = source.peek().clone(); // no subscription 318 + let text = source.peek().clone(); // no subscription 308 319 let segs = make_segments(&text); 309 320 let ai = idx.min(segs.len().saturating_sub(1)); 310 321 match segs.get(ai) { 311 - Some(Seg::Card { .. }) => { 322 + Some(Seg { 323 + kind: SegKind::Card, 324 + .. 325 + }) => { 312 326 if let Some(Some(Ok(data))) = pvs.get(ai) { 313 327 blank_rects_sig.set(data.blank_rects.clone()); 314 328 glyph_map_sig.set(data.glyph_map.clone()); ··· 328 342 let key = e.data().key().to_string(); 329 343 if key == "ArrowUp" || key == "ArrowDown" { 330 344 let going_up = key == "ArrowUp"; 331 - let cur_idx = *active_idx.read(); 332 - let n = make_segments(&source.read().clone()).len(); 333 - let mut ai = active_idx; 345 + let cur_idx = *active_idx.read(); 346 + let n = make_segments(&source.read().clone()).len(); 347 + let mut ai = active_idx; 334 348 spawn(async move { 335 349 let js = if going_up { 336 350 "var t=document.querySelector('.card-row.active textarea');\ ··· 365 379 // spliced in and active_idx advances to the last one. 366 380 let on_input = move |e: FormEvent| { 367 381 let new_frag = e.value(); 368 - if new_frag.trim().is_empty() { return; } 369 - let cur = source.read().clone(); 382 + if new_frag.trim().is_empty() { 383 + return; 384 + } 385 + let cur = source.read().clone(); 370 386 let segs = make_segments(&cur); 371 - let ai = (*active_idx.read()).min(segs.len().saturating_sub(1)); 387 + let ai = (*active_idx.read()).min(segs.len().saturating_sub(1)); 372 388 if let Some(seg) = segs.get(ai) { 373 - let (cs, ce) = (seg.start(), seg.end()); 389 + let (cs, ce) = (seg.start, seg.end); 374 390 if ce <= cur.len() { 375 391 let new_src = format!("{}{}{}", &cur[..cs], new_frag, &cur[ce..]); 376 392 let new_segs = make_segments(&new_src); 377 393 source.set(new_src); 378 394 if new_segs.len() > segs.len() { 379 395 let edit_end = cs + new_frag.len(); 380 - let new_ai = new_segs.iter() 381 - .rposition(|s| s.start() <= edit_end) 396 + let new_ai = new_segs 397 + .iter() 398 + .rposition(|s| s.start <= edit_end) 382 399 .unwrap_or(ai); 383 400 active_idx.set(new_ai); 384 401 spawn(async move { ··· 394 411 }; 395 412 396 413 // ── Snapshot values for RSX ──────────────────────────────────────────────── 397 - let text_snap = source.read().clone(); 398 - let segs_snap = make_segments(&text_snap); 399 - let n_segs = segs_snap.len(); 400 - let active_i = (*active_idx.read()).min(n_segs.saturating_sub(1)); 414 + let text_snap = source.read().clone(); 415 + let segs_snap = make_segments(&text_snap); 416 + let n_segs = segs_snap.len(); 417 + let active_i = (*active_idx.read()).min(n_segs.saturating_sub(1)); 401 418 let active_is_cloze = match segs_snap.get(active_i) { 402 - Some(Seg::Card { start, end }) => matches!( 403 - tala_format::parse_cards(&text_snap[*start..*end]).first().map(|c| &c.kind), 419 + Some(Seg { 420 + start, 421 + end, 422 + kind: SegKind::Card, 423 + }) => matches!( 424 + tala_format::parse_cards(&text_snap[*start..*end]) 425 + .first() 426 + .map(|c| &c.kind), 404 427 Some(CardKind::Cloze { .. }) 405 428 ), 406 429 _ => false, 407 430 }; 408 - let pvs_snap = previews.read().clone(); 409 - let (active_cs, active_ce) = segs_snap.get(active_i) 410 - .map(|s| (s.start(), s.end())) 431 + let pvs_snap = previews.read().clone(); 432 + let (active_cs, active_ce) = segs_snap 433 + .get(active_i) 434 + .map(|s| (s.start, s.end)) 411 435 .unwrap_or((0, 0)); 412 436 let active_fragment = text_snap[active_cs..active_ce.min(text_snap.len())].to_string(); 413 - let in_draw = *draw_mode.read(); 414 - let ds = *drag_start.read(); 415 - let dc = *drag_current.read(); 416 - let drawn = drawn_boxes.read().clone(); 437 + let in_draw = *draw_mode.read(); 438 + let ds = *drag_start.read(); 439 + let dc = *drag_current.read(); 440 + let drawn = drawn_boxes.read().clone(); 417 441 let blank_rects = blank_rects_sig.read().clone(); 418 442 419 443 rsx! { ··· 452 476 { 453 477 segs_snap.iter().enumerate().map(|(i, seg)| { 454 478 let is_active = i == active_i; 455 - let frag_for_mount = active_fragment.clone(); 456 479 let row_class = if is_active { "card-row active" } else { "card-row" }; 480 + let seg_key = if seg.kind == SegKind::Card { format!("c{i}") } else { format!("t{i}") }; 457 481 458 - match seg { 459 - // ── Text segment (unparsed / mid-edit) ──────────────── 460 - Seg::Text { start, end } => { 461 - let preview_text = text_snap[*start..*end].to_string(); 462 - rsx! { 463 - div { 464 - key: "t{i}", 465 - class: row_class, 466 - onclick: move |_| { 467 - if !is_active { 468 - active_idx.set(i); 469 - spawn(async move { 470 - document::eval( 471 - "setTimeout(()=>document.querySelector('.card-row.active textarea')?.focus(),0)" 472 - ).await.ok(); 473 - }); 474 - } 475 - }, 476 - div { class: "card-actions", 477 - button { class: "btn-delete", title: "Delete segment", 478 - onclick: move |e| { 479 - e.stop_propagation(); 480 - let text = source.read().clone(); 481 - let segs = make_segments(&text); 482 - if let Some(seg) = segs.get(i) { 483 - let (cs, ce) = (seg.start(), seg.end()); 484 - let ce2 = if text[ce..].starts_with("\n\n") { ce + 2 } else { ce }; 485 - let cs2 = if ce2 == ce && cs >= 2 && &text[cs-2..cs] == "\n\n" { cs - 2 } else { cs }; 486 - source.set(format!("{}{}", &text[..cs2], &text[ce2..])); 487 - let new_n = segs.len().saturating_sub(1); 488 - if *active_idx.read() >= new_n { active_idx.set(new_n.saturating_sub(1)); } 489 - save_status.set(SaveStatus::Dirty); 490 - } 491 - }, 492 - "×" 493 - } 494 - } 495 - if is_active { 496 - textarea { 497 - class: "card-source", 498 - spellcheck: "false", 499 - onmounted: { 500 - let frag = frag_for_mount; 501 - move |_| { 502 - let b64 = base64::engine::general_purpose::STANDARD.encode(&frag); 503 - spawn(async move { 504 - document::eval(&format!( 505 - "var t=document.querySelector('.card-row.active textarea');\ 506 - if(t){{t.value=atob('{}');t.focus();}}", b64 507 - )).await.ok(); 508 - }); 509 - } 510 - }, 511 - oninput: on_input, 512 - onkeydown: on_keydown, 513 - } 514 - } else { 515 - pre { class: "text-seg-preview", "{preview_text}" } 516 - } 517 - } 518 - } 519 - } 520 - // ── Card segment ────────────────────────────────────── 521 - Seg::Card { .. } => { 522 - let card_result: Option<Result<PreviewData, String>> = 523 - pvs_snap.get(i).cloned().flatten(); 482 + // Render data — naturally None for text segments (previews stores None for them). 483 + let card_result: Option<Result<PreviewData, String>> = pvs_snap.get(i).cloned().flatten(); 524 484 let iw = card_result.as_ref().and_then(|r| r.as_ref().ok()).map(|d| d.img_w as f64).unwrap_or(1.0); 525 485 let ih = card_result.as_ref().and_then(|r| r.as_ref().ok()).map(|d| d.img_h as f64).unwrap_or(1.0); 526 486 let vb = format!("0 0 {} {}", iw as u32, ih as u32); ··· 537 497 .unwrap_or_default() 538 498 }; 539 499 let render_err = card_result.and_then(|r| r.err()); 500 + let preview_text = text_snap[seg.start..seg.end].to_string(); 501 + let frag_for_mount = active_fragment.clone(); 540 502 541 503 rsx! { 542 504 div { 543 - key: "c{i}", 505 + key: "{seg_key}", 544 506 class: row_class, 545 507 onclick: move |_| { 546 508 if !is_active { ··· 552 514 }); 553 515 } 554 516 }, 555 - // ── Card actions bar ────────────────────────── 556 517 div { class: "card-actions", 557 - button { 558 - class: "btn-delete", 559 - title: "Delete card", 518 + button { class: "btn-delete", title: "Delete segment", 560 519 onclick: move |e| { 561 520 e.stop_propagation(); 562 521 let text = source.read().clone(); 563 522 let segs = make_segments(&text); 564 523 if let Some(seg) = segs.get(i) { 565 - let (cs, ce) = (seg.start(), seg.end()); 524 + let (cs, ce) = (seg.start, seg.end); 566 525 let ce2 = if text[ce..].starts_with("\n\n") { ce + 2 } else { ce }; 567 526 let cs2 = if ce2 == ce && cs >= 2 && &text[cs-2..cs] == "\n\n" { cs - 2 } else { cs }; 568 527 source.set(format!("{}{}", &text[..cs2], &text[ce2..])); ··· 574 533 "×" 575 534 } 576 535 } 577 - // ── Source textarea (active card only) ──────── 578 536 if is_active { 579 537 textarea { 580 538 class: "card-source", 581 539 spellcheck: "false", 582 540 onmounted: { 583 - let frag = frag_for_mount.clone(); 541 + let frag = frag_for_mount; 584 542 move |_| { 585 - let b64 = base64::engine::general_purpose::STANDARD 586 - .encode(&frag); 543 + let b64 = base64::engine::general_purpose::STANDARD.encode(&frag); 587 544 spawn(async move { 588 545 document::eval(&format!( 589 546 "var t=document.querySelector('.card-row.active textarea');\ ··· 596 553 onkeydown: on_keydown, 597 554 } 598 555 } 599 - // ── Preview ─────────────────────────────────── 600 - if let Some(err) = render_err { 601 - pre { class: "render-error", "{err}" } 602 - } else if b64_src.is_empty() { 603 - span { class: "status", "Rendering…" } 604 - } else { 605 - div { class: "card-preview-wrap", 606 - img { class: "card-preview", src: "{b64_src}" } 607 - svg { 608 - class: "cloze-overlay", 609 - view_box: "{vb}", 610 - // Yellow blank highlights (all cards) 611 - for rect in &card_blank_rects { 612 - rect { 613 - x: "{rect[0] * iw}", y: "{rect[1] * ih}", 614 - width: "{rect[2] * iw}", height: "{rect[3] * ih}", 615 - fill: "rgba(255,220,50,0.35)", 616 - stroke: "rgb(200,150,0)", 617 - stroke_width: "1.5", rx: "3", 618 - } 619 - } 620 - // Draw overlays (active card only) 621 - if is_active { 622 - for rect in &drawn { 623 - rect { 624 - x: "{rect[0] * iw}", y: "{rect[1] * ih}", 625 - width: "{rect[2] * iw}", height: "{rect[3] * ih}", 626 - fill: "rgba(255,100,100,0.2)", 627 - stroke: "rgb(200,60,60)", 628 - stroke_width: "1.5", rx: "3", 556 + match seg.kind { 557 + SegKind::Text => rsx! { 558 + if !is_active { 559 + pre { class: "text-seg-preview", "{preview_text}" } 560 + } 561 + }, 562 + SegKind::Card => rsx! { 563 + if let Some(err) = render_err { 564 + pre { class: "render-error", "{err}" } 565 + } else if b64_src.is_empty() { 566 + span { class: "status", "Rendering…" } 567 + } else { 568 + div { class: "card-preview-wrap", 569 + img { class: "card-preview", src: "{b64_src}" } 570 + svg { 571 + class: "cloze-overlay", 572 + view_box: "{vb}", 573 + for rect in &card_blank_rects { 574 + rect { 575 + x: "{rect[0] * iw}", y: "{rect[1] * ih}", 576 + width: "{rect[2] * iw}", height: "{rect[3] * ih}", 577 + fill: "rgba(255,220,50,0.35)", 578 + stroke: "rgb(200,150,0)", 579 + stroke_width: "1.5", rx: "3", 580 + } 629 581 } 630 - } 631 - if let (Some((sx, sy)), Some((cx, cy))) = (ds, dc) { 632 - { 633 - let rx = sx.min(cx) * iw; 634 - let ry = sy.min(cy) * ih; 635 - let rw = (cx - sx).abs() * iw; 636 - let rh = (cy - sy).abs() * ih; 637 - rsx! { 582 + if is_active { 583 + for rect in &drawn { 638 584 rect { 639 - x: "{rx}", y: "{ry}", 640 - width: "{rw}", height: "{rh}", 641 - fill: "rgba(100,180,255,0.1)", 642 - stroke: "rgb(70,130,220)", 643 - stroke_width: "1.5", 644 - stroke_dasharray: "4 2", rx: "3", 585 + x: "{rect[0] * iw}", y: "{rect[1] * ih}", 586 + width: "{rect[2] * iw}", height: "{rect[3] * ih}", 587 + fill: "rgba(255,100,100,0.2)", 588 + stroke: "rgb(200,60,60)", 589 + stroke_width: "1.5", rx: "3", 590 + } 591 + } 592 + if let (Some((sx, sy)), Some((cx, cy))) = (ds, dc) { 593 + { 594 + let rx = sx.min(cx) * iw; 595 + let ry = sy.min(cy) * ih; 596 + let rw = (cx - sx).abs() * iw; 597 + let rh = (cy - sy).abs() * ih; 598 + rsx! { 599 + rect { 600 + x: "{rx}", y: "{ry}", 601 + width: "{rw}", height: "{rh}", 602 + fill: "rgba(100,180,255,0.1)", 603 + stroke: "rgb(70,130,220)", 604 + stroke_width: "1.5", 605 + stroke_dasharray: "4 2", rx: "3", 606 + } 607 + } 645 608 } 646 609 } 647 610 } 648 611 } 649 - } 650 - } 651 - // Draw-capture div (active cloze card in draw mode only) 652 - if is_active && in_draw && active_is_cloze { 653 - div { 654 - class: "draw-capture", 612 + if is_active && in_draw && active_is_cloze { 613 + div { 614 + class: "draw-capture", 655 615 onmousedown: move |e| { 656 616 let ox = e.data().element_coordinates().x; 657 617 let oy = e.data().element_coordinates().y; ··· 722 682 let cur = source.read().clone(); 723 683 let segs = make_segments(&cur); 724 684 let ai = (*active_idx.read()).min(segs.len().saturating_sub(1)); 725 - let (cs, ce) = segs.get(ai).map(|s| (s.start(), s.end())).unwrap_or((0, 0)); 685 + let (cs, ce) = segs.get(ai).map(|s| (s.start, s.end)).unwrap_or((0, 0)); 726 686 let fragment = cur[cs..ce].to_string(); 727 687 728 688 if all_math { ··· 783 743 } 784 744 } 785 745 } 746 + } 747 + }, 786 748 } 787 749 } 788 750 } 789 - } // end Seg::Card arm 790 - } // end match seg 791 751 }) 792 752 } 793 753 } ··· 831 791 ) 832 792 } 833 793 834 - 835 794 /// Given a raw glyph-based `sel_start..sel_end` within `eq_content` (the inner 836 795 /// text of an equation, excluding `$` delimiters), expand the selection so it: 837 796 /// 1. Extends `sel_end` to the end of any incomplete identifier token. ··· 857 816 while end < len && depth > 0 { 858 817 match b[end] { 859 818 b'(' => depth += 1, 860 - b')' => { depth -= 1; } 819 + b')' => { 820 + depth -= 1; 821 + } 861 822 _ => {} 862 823 } 863 824 end += 1; ··· 868 829 let (mut parens, mut brackets, mut braces) = (0i32, 0i32, 0i32); 869 830 for &c in &b[sel_start..end] { 870 831 match c { 871 - b'(' => parens += 1, b')' => parens -= 1, 872 - b'[' => brackets += 1, b']' => brackets -= 1, 873 - b'{' => braces += 1, b'}' => braces -= 1, 832 + b'(' => parens += 1, 833 + b')' => parens -= 1, 834 + b'[' => brackets += 1, 835 + b']' => brackets -= 1, 836 + b'{' => braces += 1, 837 + b'}' => braces -= 1, 874 838 _ => {} 875 839 } 876 840 } 877 841 while end < len && (parens > 0 || brackets > 0 || braces > 0) { 878 842 match b[end] { 879 - b'(' => parens += 1, b')' => parens -= 1, 880 - b'[' => brackets += 1, b']' => brackets -= 1, 881 - b'{' => braces += 1, b'}' => braces -= 1, 843 + b'(' => parens += 1, 844 + b')' => parens -= 1, 845 + b'[' => brackets += 1, 846 + b']' => brackets -= 1, 847 + b'{' => braces += 1, 848 + b'}' => braces -= 1, 882 849 _ => {} 883 850 } 884 851 end += 1; ··· 899 866 eq_end: usize, 900 867 ) -> String { 901 868 // Detect display math (`$ ... $`) vs inline math (`$...$`). 902 - let (open, close) = if source[eq_start..].starts_with("$ ") && source[..eq_end].ends_with(" $") { 869 + let (open, close) = if source[eq_start..].starts_with("$ ") && source[..eq_end].ends_with(" $") 870 + { 903 871 ("$ ", " $") 904 872 } else { 905 873 ("$", "$") 906 874 }; 907 875 let content_start = eq_start + open.len(); 908 - let content_end = eq_end - close.len(); 876 + let content_end = eq_end - close.len(); 909 877 910 - let left_inner = source[content_start..sel_start].trim_end(); 911 - let selected = source[sel_start..sel_end].trim(); 878 + let left_inner = source[content_start..sel_start].trim_end(); 879 + let selected = source[sel_start..sel_end].trim(); 912 880 let right_inner = source[sel_end..content_end].trim_start(); 913 881 914 - let left_part = if left_inner.is_empty() { String::new() } else { format!("{}{}{} ", open, left_inner, close) }; 882 + let left_part = if left_inner.is_empty() { 883 + String::new() 884 + } else { 885 + format!("{}{}{} ", open, left_inner, close) 886 + }; 915 887 let blank_part = format!("#blank[{}{}{}]", open, selected, close); 916 - let right_part = if right_inner.is_empty() { String::new() } else { format!(" {}{}{}", open, right_inner, close) }; 888 + let right_part = if right_inner.is_empty() { 889 + String::new() 890 + } else { 891 + format!(" {}{}{}", open, right_inner, close) 892 + }; 917 893 918 - format!("{}{}{}{}{}", &source[..eq_start], left_part, blank_part, right_part, &source[eq_end..]) 894 + format!( 895 + "{}{}{}{}{}", 896 + &source[..eq_start], 897 + left_part, 898 + blank_part, 899 + right_part, 900 + &source[eq_end..] 901 + ) 919 902 } 920 903 921 904 #[cfg(test)]