From d8f632086338ea1a7b2527e4478174f52acd9ddc Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Mon, 10 Aug 2026 06:08:35 +0200 Subject: [PATCH 01/14] prepare v0.5.20 --- Cargo.lock | 26 +++++++++++++------------- Cargo.toml | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 555f2e2102..7e0a55b538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -872,7 +872,7 @@ dependencies = [ [[package]] name = "corcovado" -version = "0.5.19" +version = "0.5.20" dependencies = [ "criterion", "libc", @@ -2516,7 +2516,7 @@ dependencies = [ [[package]] name = "librio" -version = "0.5.19" +version = "0.5.20" dependencies = [ "bitflags 2.13.1", "corcovado", @@ -2530,7 +2530,7 @@ dependencies = [ [[package]] name = "librio-wasm" -version = "0.5.19" +version = "0.5.20" dependencies = [ "js-sys", "librio", @@ -3865,7 +3865,7 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "rio-backend" -version = "0.5.19" +version = "0.5.20" dependencies = [ "base64", "dirs", @@ -3884,11 +3884,11 @@ dependencies = [ [[package]] name = "rio-fonts" -version = "0.5.19" +version = "0.5.20" [[package]] name = "rio-grapheme-width" -version = "0.5.19" +version = "0.5.20" dependencies = [ "phf", "ucd-trie", @@ -3896,7 +3896,7 @@ dependencies = [ [[package]] name = "rio-graphics" -version = "0.5.19" +version = "0.5.20" dependencies = [ "image", "parking_lot", @@ -3909,7 +3909,7 @@ dependencies = [ [[package]] name = "rio-notifier" -version = "0.5.19" +version = "0.5.20" dependencies = [ "block2 0.5.1", "objc-rs 0.3.2", @@ -3922,7 +3922,7 @@ dependencies = [ [[package]] name = "rio-vt" -version = "0.5.19" +version = "0.5.20" dependencies = [ "base64", "bitflags 2.13.1", @@ -3955,7 +3955,7 @@ dependencies = [ [[package]] name = "rio-window" -version = "0.5.19" +version = "0.5.20" dependencies = [ "ahash", "atomic-waker", @@ -4010,7 +4010,7 @@ dependencies = [ [[package]] name = "rioterm" -version = "0.5.19" +version = "0.5.20" dependencies = [ "ahash", "bitflags 2.13.1", @@ -4545,7 +4545,7 @@ dependencies = [ [[package]] name = "sugarloaf" -version = "0.5.19" +version = "0.5.20" dependencies = [ "approx", "ash", @@ -4678,7 +4678,7 @@ dependencies = [ [[package]] name = "teletypewriter" -version = "0.5.19" +version = "0.5.20" dependencies = [ "corcovado", "dirs", diff --git a/Cargo.toml b/Cargo.toml index 1e0e5b13d6..50dd01b2ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.5.19" +version = "0.5.20" authors = ["Raphael Amorim "] edition = "2021" license = "MIT" @@ -32,16 +32,16 @@ readme = "README.md" # Note: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#multiple-locations # Sugarloaf example uses path when used locally, but uses # version from crates.io when published. -teletypewriter = { path = "teletypewriter", version = "0.5.19" } -rio-backend = { path = "rio-backend", version = "0.5.19" } -rio-window = { path = "rio-window", version = "0.5.19", default-features = false } -rio-notifier = { path = "rio-notifier", version = "0.5.19" } -rio-grapheme-width = { path = "rio-grapheme-width", version = "0.5.19" } -sugarloaf = { path = "sugarloaf", version = "0.5.19" } -corcovado = { path = "corcovado", version = "0.5.19" } -rio-graphics = { path = "rio-graphics", version = "0.5.19" } -rio-fonts = { path = "rio-fonts", version = "0.5.19" } -rio-vt = { path = "rio-vt", version = "0.5.19" } +teletypewriter = { path = "teletypewriter", version = "0.5.20" } +rio-backend = { path = "rio-backend", version = "0.5.20" } +rio-window = { path = "rio-window", version = "0.5.20", default-features = false } +rio-notifier = { path = "rio-notifier", version = "0.5.20" } +rio-grapheme-width = { path = "rio-grapheme-width", version = "0.5.20" } +sugarloaf = { path = "sugarloaf", version = "0.5.20" } +corcovado = { path = "corcovado", version = "0.5.20" } +rio-graphics = { path = "rio-graphics", version = "0.5.20" } +rio-fonts = { path = "rio-fonts", version = "0.5.20" } +rio-vt = { path = "rio-vt", version = "0.5.20" } raw-window-handle = { version = "0.6.2", features = ["std"] } parking_lot = { version = "0.12.5", features = [ From 4e2eb9b1a7a9582fefd666ce4af4dc7c669ed19a Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Mon, 10 Aug 2026 06:23:36 +0200 Subject: [PATCH 02/14] windows: mask the composition-window flag off lparam, not wparam ISC_SHOWUICOMPOSITIONWINDOW lives in lparam; masking wparam meant the OS floating composition window was never actually suppressed. Same fix as upstream winit. Only correct to ship together with inline preedit rendering: without it this would remove the only visible composition UI on Windows. --- rio-window/src/platform_impl/windows/event_loop.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rio-window/src/platform_impl/windows/event_loop.rs b/rio-window/src/platform_impl/windows/event_loop.rs index ffc2939284..fd9d615e1c 100644 --- a/rio-window/src/platform_impl/windows/event_loop.rs +++ b/rio-window/src/platform_impl/windows/event_loop.rs @@ -1745,9 +1745,14 @@ unsafe fn public_window_callback_inner( } WM_IME_SETCONTEXT => { - // Hide composing text drawn by IME. - let wparam = wparam & (!ISC_SHOWUICOMPOSITIONWINDOW as usize); - result = ProcResult::DefWindowProc(wparam); + // Suppress the OS-drawn floating composition window — rio + // renders the composition inline. The ISC_* UI flags live + // in `lparam` (`wparam` is the window-active BOOL); this + // used to mask `wparam` and therefore never suppressed + // anything. Same fix as upstream winit. + let lparam = lparam & !(ISC_SHOWUICOMPOSITIONWINDOW as isize); + result = + ProcResult::Value(unsafe { DefWindowProcW(window, msg, wparam, lparam) }); } // this is necessary for us to maintain minimize/restore state From 13f8cbe8e20d671b9a3c7ff45d32aa856f96808b Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Mon, 10 Aug 2026 06:23:36 +0200 Subject: [PATCH 03/14] rioterm: render the ime composition inline at the cursor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single-row wezterm-style block anchored at the cursor: slides left at the right edge, keeps the tail (where the caret lives) when wider than the row, and never wraps or silently truncates. Text is laid out and shaped per grapheme cluster so ZWJ emoji and combining marks hold together. The caret renders as a thick underline in the text color on composition cells (a beam there would be cursor-on-cursor, invisible) and as a beam past the end where the normal background is behind it. Ime state moves from Context to Screen: the platform IME composes into the focused view, one composition per window, so per-context state could go stale on a background split — keep painting, and swallow keys on refocus through the preedit gate. Window-level state makes that unrepresentable. The overlay is also never built over scrollback, where the cursor row is off-viewport and the anchor would lie. The composition row (and the row it just left) is force-rebuilt every frame while composing, so cursor-only damage or an ended composition can't leave stale block cells. Composition runs use their own run-cache namespace, and the block fill resolves the cursor color the same way the cursor-block uniforms do, OSC 12 included. --- Cargo.lock | 1 + frontends/rioterm/Cargo.toml | 1 + frontends/rioterm/src/application.rs | 51 +-- frontends/rioterm/src/context/mod.rs | 5 - frontends/rioterm/src/context/renderable.rs | 2 - frontends/rioterm/src/grid_emit.rs | 327 +++++++++++++++++++- frontends/rioterm/src/ime.rs | 51 +-- frontends/rioterm/src/renderer/mod.rs | 16 +- frontends/rioterm/src/renderer/preedit.rs | 293 ++++++++++++++++++ frontends/rioterm/src/screen/mod.rs | 73 ++++- 10 files changed, 751 insertions(+), 69 deletions(-) create mode 100644 frontends/rioterm/src/renderer/preedit.rs diff --git a/Cargo.lock b/Cargo.lock index 7e0a55b538..770b52445e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4045,6 +4045,7 @@ dependencies = [ "tinyvec", "tracing", "tracing-subscriber", + "unicode-segmentation", "unicode-width-16", "url", "wgpu", diff --git a/frontends/rioterm/Cargo.toml b/frontends/rioterm/Cargo.toml index d5ead4aab2..5e06e80834 100644 --- a/frontends/rioterm/Cargo.toml +++ b/frontends/rioterm/Cargo.toml @@ -45,6 +45,7 @@ parking_lot = { workspace = true } serde = { workspace = true } taffy = { version = "0.10.1", features = ["flexbox","grid"] } teletypewriter = { workspace = true } +unicode-segmentation = "1.12.0" unicode-width = { workspace = true } url = { workspace = true } smallvec = { workspace = true } diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs index 5f396f2aa5..e6cf5da241 100644 --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -1937,36 +1937,49 @@ impl ApplicationHandler for Application<'_> { Some(Preedit::new(text, cursor_offset.map(|offset| offset.0))) }; - if route.window.screen.context_manager.current().ime.preedit() - != preedit.as_ref() - { + if route.window.screen.ime.preedit() != preedit.as_ref() { + route.window.screen.ime.set_preedit(preedit); + // The overlay lives in the grid's CPU cell + // buffers: force the current panel to + // repaint so the composition (or its + // removal) is visible this frame, not + // whenever the terminal next damages. route .window .screen .context_manager .current_mut() - .ime - .set_preedit(preedit); + .renderable_content + .pending_update + .set_terminal_damage( + rio_backend::event::TerminalDamage::Full, + ); route.request_redraw(); } } Ime::Enabled => { - route - .window - .screen - .context_manager - .current_mut() - .ime - .set_enabled(true); + route.window.screen.ime.set_enabled(true); } Ime::Disabled => { - route - .window - .screen - .context_manager - .current_mut() - .ime - .set_enabled(false); + // Disabling wipes any live preedit (input + // source switched mid-composition): damage and + // repaint like the Preedit arm, or the block + // ghosts on screen until unrelated damage. + let had_preedit = route.window.screen.ime.preedit().is_some(); + route.window.screen.ime.set_enabled(false); + if had_preedit { + route + .window + .screen + .context_manager + .current_mut() + .renderable_content + .pending_update + .set_terminal_damage( + rio_backend::event::TerminalDamage::Full, + ); + route.request_redraw(); + } } } } diff --git a/frontends/rioterm/src/context/mod.rs b/frontends/rioterm/src/context/mod.rs index 5f5944375f..93e40468ef 100644 --- a/frontends/rioterm/src/context/mod.rs +++ b/frontends/rioterm/src/context/mod.rs @@ -7,7 +7,6 @@ use crate::context::title::{ }; use crate::event::sync::FairMutex; use crate::event::{Msg, RioEvent}; -use crate::ime::Ime; pub use crate::layout::{ContextDimension, ContextGrid, ContextGridItem}; use crate::messenger::Messenger; use crate::performer::{self, Machine}; @@ -57,7 +56,6 @@ pub struct Context { pub rich_text_id: usize, pub dimension: ContextDimension, pub title: ContextTitle, - pub ime: Ime, _io_thread: Option, performer::State)>>, } @@ -116,7 +114,6 @@ impl Context { state: self.renderable_content.cursor.state.new_from_self(), content: self.renderable_content.cursor.content_ref, content_ref: self.renderable_content.cursor.content_ref, - is_ime_enabled: false, } } } @@ -190,7 +187,6 @@ pub fn create_dead_context( rich_text_id, dimension, title: ContextTitle::default(), - ime: Ime::new(), _io_thread: None, } } @@ -349,7 +345,6 @@ impl ContextManager { renderable_content: RenderableContent::new(cursor_state.0.clone()), dimension, title: ContextTitle::default(), - ime: Ime::new(), _io_thread: io_thread, }) } diff --git a/frontends/rioterm/src/context/renderable.rs b/frontends/rioterm/src/context/renderable.rs index d645e2eb98..987c2b1693 100644 --- a/frontends/rioterm/src/context/renderable.rs +++ b/frontends/rioterm/src/context/renderable.rs @@ -27,7 +27,6 @@ pub struct Cursor { pub state: CursorState, pub content: char, pub content_ref: char, - pub is_ime_enabled: bool, } #[derive(Clone, Copy, Debug)] @@ -145,7 +144,6 @@ impl RenderableContent { content: config_cursor.shape.into(), content_ref: config_cursor.shape.into(), state: CursorState::new(config_cursor.shape.into()), - is_ime_enabled: false, }; Self::new(cursor) } diff --git a/frontends/rioterm/src/grid_emit.rs b/frontends/rioterm/src/grid_emit.rs index 82238da233..155dc6ac22 100644 --- a/frontends/rioterm/src/grid_emit.rs +++ b/frontends/rioterm/src/grid_emit.rs @@ -39,8 +39,22 @@ use smallvec::SmallVec; /// cells. The renderer reads via `extras.get(&id)`. pub(crate) type ExtrasMap = FxHashMap; +use crate::renderer::preedit::{PreeditCaret, PreeditCell, PreeditLine}; use crate::renderer::Renderer; +/// The IME composition threaded into a row build. Only handed to the +/// emit passes for the single row the composition lives on. +pub struct PreeditRow<'a> { + pub line: &'a PreeditLine, +} + +impl PreeditRow<'_> { + #[inline] + fn cell(&self, col: usize) -> Option { + self.line.cell(col) + } +} + #[inline(always)] pub(crate) fn resolve_style(style_table: &[Style], sq: Square) -> Style { if sq.is_bg_only() { @@ -399,6 +413,15 @@ enum DecorationStyle { DashedUnderline = 3, CurlyUnderline = 4, Strikethrough = 5, + /// Thick underline marking the IME caret on a composition cell. + /// Drawn in the terminal background color so it reads against the + /// cursor-colored block — a beam there would be cursor-on-cursor + /// and invisible. + ImeCaretUnderline = 6, + /// Vertical beam marking the IME caret one cell past the + /// composition, where there is no block behind it; drawn in the + /// cursor color against the normal background. + ImeCaretBeam = 7, } /// Sentinel font_id base for decoration sprites. Real font_ids come @@ -889,6 +912,22 @@ fn rasterize_decoration( let bearing_y = center_from_bottom as i16 + (thickness as i16 + 1) / 2; (bytes, cell_w, thickness, bearing_y) } + DecorationStyle::ImeCaretUnderline => { + // Double-thickness underline at the underline position — + // the ghostty presentation for the caret inside a + // composition. + let h = (thickness * 2).max(2).min(cell_h); + let bytes = vec![0xFFu8; (cell_w * h) as usize]; + let bearing_y = (h + underline_gap_below(cell_h)) as i16; + (bytes, cell_w, h, bearing_y) + } + DecorationStyle::ImeCaretBeam => { + // Full-height beam pinned to the cell's left edge, in the + // same visual weight as the underline decorations. + let w = thickness.max(1).min(cell_w); + let bytes = vec![0xFFu8; (w * cell_h) as usize]; + (bytes, w, cell_h, cell_h as i16) + } } } @@ -1059,18 +1098,30 @@ pub fn build_row_bg( term_colors: &TermColors, row_sel: Option, row_hints: &[RowHint], + preedit: Option<&PreeditRow<'_>>, bg_scratch: &mut Vec, ) { bg_scratch.clear(); - // Fast path: row has no selection and no color-changing hints - // (HyperlinkHover only contributes an underline, never bg). The - // overwhelming majority of rows in idle terminals hit this path — - // strip the per-cell `cell_in_row_sel` / `cell_in_row_hints` - // checks and just walk cells. + // Block fill behind every composition cell — the same resolved + // cursor color the cursor-block uniforms use (OSC 12 wins, then + // the theme), so the block and the forced block cursor on the + // first composition cell can never be two different colors. + let preedit_block_bg = preedit.map(|_| { + normalized_to_u8( + term_colors[NamedColor::Cursor as usize] + .unwrap_or(renderer.named_colors.cursor), + ) + }); + + // Fast path: row has no selection, no color-changing hints, and no + // composition. (HyperlinkHover only contributes an underline, + // never bg.) The overwhelming majority of rows in idle terminals + // hit this path — strip the per-cell `cell_in_row_sel` / + // `cell_in_row_hints` checks and just walk cells. let has_sel = row_sel.is_some(); let has_color_hints = row_hints.iter().any(|rh| rh.tag != HintTag::HyperlinkHover); - if !has_sel && !has_color_hints { + if !has_sel && !has_color_hints && preedit.is_none() { bg_scratch.reserve(cols); for x in 0..cols { let sq = row[Column(x)]; @@ -1103,7 +1154,17 @@ pub fn build_row_bg( let sq = row[Column(x)]; let style = resolve_style(style_table, sq); let col = x as u16; - let rgba = if cell_in_row_sel(row_sel, col) { + // The composition wins over selection / hint backgrounds: the + // user is actively typing here, that signal reads first. Both + // Start and Continuation cells take the fill so wide clusters + // span one continuous block. + let preedit_here = match (preedit, preedit_block_bg) { + (Some(p), Some(bg)) if p.cell(x).is_some() => Some(bg), + _ => None, + }; + let rgba = if let Some(bg) = preedit_here { + bg + } else if cell_in_row_sel(row_sel, col) { // Selection bg wins over hint bg and the cell's own bg, // matching `generic.zig:2775-2800` (selection check // runs before highlight check). @@ -1629,6 +1690,7 @@ pub fn build_row_fg( cell_h: f32, row_sel: Option, row_hints: &[RowHint], + preedit: Option<&PreeditRow<'_>>, font_library: &FontLibrary, route_id: usize, // Column of the cursor on this row, or `None` if the cursor isn't @@ -1683,6 +1745,7 @@ pub fn build_row_fg( thickness, row_sel, row_hints, + preedit, fg_scratch, ); @@ -1698,6 +1761,12 @@ pub fn build_row_fg( let mut x: usize = 0; while x < max { let sq = row[Column(x)]; + // Composition cells emit in their own pass below, shaped per + // grapheme cluster so they can't ligate with terminal text. + if preedit.is_some_and(|p| p.cell(x).is_some()) { + x += 1; + continue; + } if is_run_breaker(sq) { x += 1; continue; @@ -1908,6 +1977,11 @@ pub fn build_row_fg( // Extend the run while (font_id, style_flags) match. let mut end = x + 1; while end < cols { + // Stop before stepping onto composition cells — those are + // taken over by the preedit pass. + if preedit.is_some_and(|p| p.cell(end).is_some()) { + break; + } let sq2 = row[Column(end)]; if is_run_breaker(sq2) { break; @@ -2201,6 +2275,34 @@ pub fn build_row_fg( x = end; } + // Phase 2.5: composition pass. Every grapheme cluster shapes as + // its own run — never ligating with the surrounding terminal text + // — with the foreground forced to the terminal background and + // BOOL_IS_CURSOR_GLYPH set, so the glyph reads inverted against + // the cursor-colored block painted in `build_row_bg`. + if let Some(pre) = preedit { + let text_fg = normalized_to_u8(renderer.named_colors.background.0); + for col in pre.line.start_col..pre.line.end_col().min(cols) { + let Some(PreeditCell::Start(cluster)) = pre.cell(col) else { + continue; + }; + emit_preedit_cluster( + pre.line.cluster(cluster), + col as u16, + y, + rasterizer, + grid, + font_library, + route_id, + size_u16, + size_bucket, + cell_h, + text_fg, + fg_scratch, + ); + } + } + // Phase 3: strikethrough pass. Emitted last so the strike overlays // the glyph. emit_strikethroughs( @@ -2216,8 +2318,207 @@ pub fn build_row_fg( thickness, row_sel, row_hints, + preedit, fg_scratch, ); + + // Phase 4: the IME caret, topmost element of the composition. + // On a composition cell it must not be a beam — cursor color on + // the cursor-colored block is invisible — so it renders as a + // thick underline in the text color instead; past the end of the + // composition there is no block, so a beam in the cursor color + // reads correctly there. + if let Some(pre) = preedit { + let (col, style, color) = match pre.line.caret { + PreeditCaret::OnCell(col) => ( + col, + DecorationStyle::ImeCaretUnderline, + normalized_to_u8(renderer.named_colors.background.0), + ), + PreeditCaret::PastEnd(col) => ( + col, + DecorationStyle::ImeCaretBeam, + normalized_to_u8( + term_colors[NamedColor::Cursor as usize] + .unwrap_or(renderer.named_colors.cursor), + ), + ), + }; + if col < cols { + emit_preedit_caret( + col as u16, y, grid, cell_w_u32, cell_h_u32, thickness, style, color, + fg_scratch, + ); + } + } +} + +/// Shape one grapheme cluster as a standalone run and emit its glyphs +/// at `(grid_col, y)` with a forced foreground color. Composition +/// cells always shape in the plain style: composing text shouldn't +/// inherit bold/italic from whatever prompt segment sat under the +/// cursor. +#[allow(clippy::too_many_arguments)] +fn emit_preedit_cluster( + cluster: &str, + grid_col: u16, + y: u16, + rasterizer: &mut GridGlyphRasterizer, + grid: &mut GridRenderer, + font_library: &FontLibrary, + route_id: usize, + size_u16: u16, + size_bucket: u16, + cell_h: f32, + text_fg: [u8; 4], + fg_scratch: &mut Vec, +) { + let Some(base) = cluster.chars().next() else { + return; + }; + let run_style_flags = 0u8; + let (font_id, is_emoji) = + rasterizer.resolve_font(base, run_style_flags, font_library, route_id); + + #[cfg(target_os = "macos")] + { + rasterizer.run_utf16_scratch.clear(); + rasterizer.run_cell_starts.clear(); + rasterizer + .run_cell_starts + .push(rasterizer.run_utf16_scratch.len() as u32); + let mut buf = [0u16; 2]; + for ch in cluster.chars() { + rasterizer + .run_utf16_scratch + .extend_from_slice(ch.encode_utf16(&mut buf)); + } + } + #[cfg(not(target_os = "macos"))] + { + rasterizer.run_str_scratch.clear(); + rasterizer.run_str_scratch.extend(cluster.chars()); + } + + // Composition runs get their own cache namespace (the leading + // sentinel): sharing keys with the grid's per-cell scheme would + // require proving the two hash layouts can never collide across + // every combining/cluster shape, and the composition holds a + // handful of entries at most — disjointness is cheaper than the + // proof. + rasterizer.run_hasher = rapidhash::fast::RapidHasher::default(); + rasterizer.run_hasher.write_u32(0x5052_4545); // "PREE" + for (i, ch) in cluster.chars().enumerate() { + rasterizer.run_hasher.write_u32(ch as u32); + rasterizer.run_hasher.write_u32(i as u32); + } + rasterizer.run_hasher.write_u32(font_id); + rasterizer.run_hasher.write_u16(size_bucket); + let hash = rasterizer.run_hasher.finish(); + + let ascent_px = if run_cache_get(&mut rasterizer.run_cache, hash).is_some() { + rasterizer + .ascent_cache + .get(&(font_id, size_bucket)) + .copied() + .unwrap_or(0) + } else { + #[cfg(target_os = "macos")] + let shaped_opt = + shape_run_ct(rasterizer, font_id, size_u16, size_bucket, font_library); + #[cfg(not(target_os = "macos"))] + let shaped_opt = + shape_run_swash(rasterizer, font_id, size_u16, size_bucket, font_library); + let Some((glyphs, ascent_px)) = shaped_opt else { + return; + }; + run_cache_put(&mut rasterizer.run_cache, RunCacheEntry { hash, glyphs }); + ascent_px + }; + + let (synthetic_bold, synthetic_italic) = + rasterizer.get_synthesis(font_id, font_library); + + let mut glyph_ids: SmallVec<[u16; 4]> = SmallVec::new(); + { + let glyphs = + run_cache_get(&mut rasterizer.run_cache, hash).expect("just inserted"); + for g in glyphs { + glyph_ids.push(g.id); + } + } + + for glyph_id in glyph_ids { + let Some((_, slot, is_color)) = ensure_glyph_by_id( + rasterizer, + grid, + font_id, + glyph_id, + size_bucket, + size_u16, + cell_h, + ascent_px, + is_emoji, + synthetic_italic, + synthetic_bold, + ) else { + continue; + }; + if slot.w == 0 || slot.h == 0 { + continue; + } + let (atlas, color) = if is_color { + (CellText::ATLAS_COLOR, [255, 255, 255, 255]) + } else { + (CellText::ATLAS_GRAYSCALE, text_fg) + }; + fg_scratch.push(CellText { + glyph_pos: [slot.x as u32, slot.y as u32], + glyph_size: [slot.w as u32, slot.h as u32], + bearings: [slot.bearing_x, slot.bearing_y], + grid_pos: [grid_col, y], + color, + atlas, + // We computed the inverse foreground ourselves; the shader + // must not swap it again on the forced-cursor cell. + bools: CellText::BOOL_IS_CURSOR_GLYPH, + page: slot.page, + _pad: 0, + }); + } +} + +/// Emit the IME caret decoration sprite at `(col, y)`. +#[allow(clippy::too_many_arguments)] +fn emit_preedit_caret( + col: u16, + y: u16, + grid: &mut GridRenderer, + cell_w: u32, + cell_h: u32, + thickness: u32, + style: DecorationStyle, + color: [u8; 4], + fg_scratch: &mut Vec, +) { + let Some(slot) = ensure_decoration_slot(grid, style, cell_w, cell_h, thickness) + else { + return; + }; + if slot.w == 0 || slot.h == 0 { + return; + } + fg_scratch.push(CellText { + glyph_pos: [slot.x as u32, slot.y as u32], + glyph_size: [slot.w as u32, slot.h as u32], + bearings: [slot.bearing_x, slot.bearing_y], + grid_pos: [col, y], + color, + atlas: CellText::ATLAS_GRAYSCALE, + bools: CellText::BOOL_IS_CURSOR_GLYPH, + page: slot.page, + _pad: 0, + }); } #[allow(clippy::too_many_arguments)] @@ -2234,9 +2535,15 @@ fn emit_underlines( thickness: u32, row_sel: Option, row_hints: &[RowHint], + preedit: Option<&PreeditRow<'_>>, fg_scratch: &mut Vec, ) { for x in 0..cols { + // Composing text takes no decorations from whatever + // was underneath it. + if preedit.is_some_and(|p| p.cell(x).is_some()) { + continue; + } let sq = row[Column(x)]; let style = resolve_style(style_table, sq); let col = x as u16; @@ -2305,9 +2612,15 @@ fn emit_strikethroughs( thickness: u32, row_sel: Option, row_hints: &[RowHint], + preedit: Option<&PreeditRow<'_>>, fg_scratch: &mut Vec, ) { for x in 0..cols { + // Composing text takes no decorations from whatever + // was underneath it. + if preedit.is_some_and(|p| p.cell(x).is_some()) { + continue; + } let sq = row[Column(x)]; let style = resolve_style(style_table, sq); if !style.flags.contains(StyleFlags::STRIKEOUT) { diff --git a/frontends/rioterm/src/ime.rs b/frontends/rioterm/src/ime.rs index 9dd58b882c..9cf61790ed 100644 --- a/frontends/rioterm/src/ime.rs +++ b/frontends/rioterm/src/ime.rs @@ -1,4 +1,3 @@ -use unicode_width::UnicodeWidthChar; #[derive(Debug, Default)] pub struct Ime { /// Whether the IME is enabled. @@ -45,32 +44,48 @@ pub struct Preedit { /// The preedit text. pub text: String, - /// Byte offset for cursor start into the preedit text. + /// Byte offset of the IME caret into the preedit text. /// - /// `None` means that the cursor is invisible. + /// `None` means the caret is at the end of the text. Offsets that + /// don't land on a char boundary (macOS reports UTF-16 ranges that + /// can split a surrogate pair) are dropped rather than trusted — + /// slicing on one would panic downstream. pub cursor_byte_offset: Option, - - /// The cursor offset from the end of the preedit in char width. - pub cursor_end_offset: Option, } impl Preedit { pub fn new(text: String, cursor_byte_offset: Option) -> Self { - let cursor_end_offset = if let Some(byte_offset) = cursor_byte_offset { - // Convert byte offset into char offset. - let cursor_end_offset = text[byte_offset..] - .chars() - .fold(0, |acc, ch| acc + ch.width().unwrap_or(1)); - - Some(cursor_end_offset) - } else { - None - }; - + let cursor_byte_offset = + cursor_byte_offset.filter(|&offset| text.is_char_boundary(offset)); Self { text, cursor_byte_offset, - cursor_end_offset, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn preedit_new_rejects_invalid_byte_offset() { + // Byte 1 is inside 啊's UTF-8 encoding: not a char boundary. + let preedit = Preedit::new("啊a".to_string(), Some(1)); + assert!(preedit.cursor_byte_offset.is_none()); + // Boundary offsets survive, including one-past-the-end. + let preedit = Preedit::new("啊a".to_string(), Some(3)); + assert_eq!(preedit.cursor_byte_offset, Some(3)); + let preedit = Preedit::new("啊a".to_string(), Some(4)); + assert_eq!(preedit.cursor_byte_offset, Some(4)); + } + + #[test] + fn set_preedit_clears_on_none() { + let mut ime = Ime::new(); + ime.set_preedit(Some(Preedit::new("a".to_string(), Some(1)))); + assert!(ime.preedit().is_some()); + ime.set_preedit(None); + assert!(ime.preedit().is_none()); + } +} diff --git a/frontends/rioterm/src/renderer/mod.rs b/frontends/rioterm/src/renderer/mod.rs index 6bbc5b98d9..c7bcf4a62c 100644 --- a/frontends/rioterm/src/renderer/mod.rs +++ b/frontends/rioterm/src/renderer/mod.rs @@ -4,6 +4,7 @@ pub mod confirm_quit; pub mod custom_cursor; pub mod helpers; pub mod island; +pub mod preedit; pub mod scrollbar; pub mod search; pub mod trail_cursor; @@ -294,21 +295,6 @@ impl Renderer { let panel_rect = grid_context.layout_rect; let context = grid_context.context_mut(); - let mut has_ime = false; - if let Some(preedit) = context.ime.preedit() { - if let Some(content) = preedit.text.chars().next() { - context.renderable_content.cursor.content = content; - context.renderable_content.cursor.is_ime_enabled = true; - has_ime = true; - } - } - - if !has_ime { - context.renderable_content.cursor.is_ime_enabled = false; - context.renderable_content.cursor.content = - context.renderable_content.cursor.content_ref; - } - let force_full_damage = has_active_changed || self.is_game_mode_enabled; let is_dirty = context.renderable_content.pending_update.is_dirty(); diff --git a/frontends/rioterm/src/renderer/preedit.rs b/frontends/rioterm/src/renderer/preedit.rs new file mode 100644 index 0000000000..dc518f3d3a --- /dev/null +++ b/frontends/rioterm/src/renderer/preedit.rs @@ -0,0 +1,293 @@ +//! Inline IME preedit: the composition string drawn as a wezterm-style +//! block at the cursor, on a single row. +//! +//! Layout rules, in priority order: +//! +//! - The composition always occupies exactly one row — the cursor row. +//! When it doesn't fit between the cursor column and the right edge, +//! it slides left (the wezterm/alacritty behavior); when it is wider +//! than the whole row, leading clusters are dropped so the tail — +//! where the IME caret lives — stays visible. It never wraps to the +//! line below and never truncates silently at the right edge. +//! - Text is segmented into grapheme clusters, not chars: a cluster is +//! what the user perceives as one character, so a ZWJ emoji or a +//! decomposed `e` + U+0302 occupies one slot and shapes as one unit. +//! Cluster width is clamped to 1 or 2 cells, the only widths a +//! terminal cell can express. +//! - The caret marks where IME editing (arrow keys between conversion +//! segments) currently is: on a composition cell it renders as a +//! thick underline (visible on the cursor-colored block), past the +//! end of the text as a beam on the following cell (visible on the +//! normal background). Both presentations exist because a beam drawn +//! in the cursor color on a cursor-colored block is invisible. +//! +//! The caller decides visibility: the overlay is only built for the +//! active panel and never while the viewport is scrolled into history +//! (`display_offset != 0`), where the cursor row is off-screen and the +//! anchor would lie. + +use crate::ime::Preedit; +use unicode_segmentation::UnicodeSegmentation; +use unicode_width::UnicodeWidthStr; + +/// One cell of the composition line. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PreeditCell { + /// Leading cell of a grapheme cluster; index into `clusters`. + Start(u16), + /// Trailing cell of a two-cell cluster. No glyph of its own — the + /// leading cell's advance covers it — but it takes the block fill. + Continuation, +} + +/// Where and how the IME caret renders. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PreeditCaret { + /// Caret on a composition cell: thick underline under that cell. + OnCell(usize), + /// Caret one past the composition: beam on that (non-block) cell. + PastEnd(usize), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct PreeditLine { + /// Screen row (viewport-relative) the composition renders on. + pub row: usize, + /// First column of the block. + pub start_col: usize, + /// Dense cells from `start_col`; never extends past the row edge. + cells: Vec, + /// Grapheme clusters, indexed by `PreeditCell::Start`. + clusters: Vec, + pub caret: PreeditCaret, +} + +impl PreeditLine { + /// Lay out `preedit` anchored at the cursor cell. Returns `None` + /// for an empty composition or a degenerate grid. + pub fn new( + preedit: &Preedit, + cursor_row: usize, + cursor_col: usize, + columns: usize, + ) -> Option { + if preedit.text.is_empty() || columns == 0 { + return None; + } + + // Segment into clusters with terminal cell widths. + struct Seg<'a> { + byte_start: usize, + cluster: &'a str, + width: usize, + } + let mut segs: Vec = preedit + .text + .grapheme_indices(true) + .map(|(byte_start, cluster)| Seg { + byte_start, + cluster, + width: match UnicodeWidthStr::width(cluster) { + 0 | 1 => 1, + _ => 2, + }, + }) + .collect(); + if segs.is_empty() { + return None; + } + + // The caret sits before the first cluster starting at or past + // the byte offset (mid-cluster offsets snap to the cluster). + // No offset reported means end-of-text, the common IME idiom. + let mut caret_index = match preedit.cursor_byte_offset { + Some(offset) => segs + .iter() + .position(|seg| seg.byte_start >= offset) + .unwrap_or(segs.len()), + None => segs.len(), + }; + + // Wider than the row: drop leading clusters, keeping the tail + // (and with it the caret, which lives near the end while + // composing). A caret pointing into the dropped region clamps + // to the first visible cluster. + let mut total: usize = segs.iter().map(|seg| seg.width).sum(); + let mut dropped = 0usize; + while total > columns && segs.len() > 1 { + total -= segs.remove(0).width; + dropped += 1; + } + caret_index = caret_index.saturating_sub(dropped); + if total > columns { + // A single cluster wider than the grid; nothing sane to draw. + return None; + } + + // Anchor at the cursor, sliding left so the tail stays on-row. + let start_col = cursor_col.min(columns - total); + + let mut cells = Vec::with_capacity(total); + let mut clusters = Vec::with_capacity(segs.len()); + let mut caret = None; + for (i, seg) in segs.iter().enumerate() { + if i == caret_index { + caret = Some(PreeditCaret::OnCell(start_col + cells.len())); + } + cells.push(PreeditCell::Start(clusters.len() as u16)); + for _ in 1..seg.width { + cells.push(PreeditCell::Continuation); + } + clusters.push(seg.cluster.to_string()); + } + let caret = caret.unwrap_or({ + let past = start_col + cells.len(); + if past < columns { + PreeditCaret::PastEnd(past) + } else { + // Composition touches the right edge: underline the + // last cluster instead of pushing the beam off-row. + let last_start = cells + .iter() + .rposition(|c| matches!(c, PreeditCell::Start(_))) + .unwrap_or(0); + PreeditCaret::OnCell(start_col + last_start) + } + }); + + Some(Self { + row: cursor_row, + start_col, + cells, + clusters, + caret, + }) + } + + /// The composition cell at an absolute column, bounds-checked on + /// both sides. + #[inline] + pub fn cell(&self, col: usize) -> Option { + col.checked_sub(self.start_col) + .and_then(|i| self.cells.get(i)) + .copied() + } + + #[inline] + pub fn cluster(&self, index: u16) -> &str { + self.clusters + .get(index as usize) + .map(String::as_str) + .unwrap_or_default() + } + + /// Column one past the block's last cell. + #[inline] + pub fn end_col(&self) -> usize { + self.start_col + self.cells.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn preedit(text: &str, caret: Option) -> Preedit { + Preedit::new(text.to_string(), caret) + } + + #[test] + fn ascii_at_cursor() { + let line = PreeditLine::new(&preedit("abc", Some(3)), 5, 10, 80).unwrap(); + assert_eq!(line.row, 5); + assert_eq!(line.start_col, 10); + assert_eq!(line.cell(10), Some(PreeditCell::Start(0))); + assert_eq!(line.cell(12), Some(PreeditCell::Start(2))); + assert_eq!(line.cell(13), None); + assert_eq!(line.cell(9), None); + assert_eq!(line.caret, PreeditCaret::PastEnd(13)); + } + + #[test] + fn wide_clusters_take_two_cells() { + // "日本" = two clusters, two cells each. + let line = PreeditLine::new(&preedit("日本", None), 0, 0, 80).unwrap(); + assert_eq!(line.cell(0), Some(PreeditCell::Start(0))); + assert_eq!(line.cell(1), Some(PreeditCell::Continuation)); + assert_eq!(line.cell(2), Some(PreeditCell::Start(1))); + assert_eq!(line.cell(3), Some(PreeditCell::Continuation)); + assert_eq!(line.caret, PreeditCaret::PastEnd(4)); + } + + #[test] + fn grapheme_clusters_stay_whole() { + // Decomposed e + combining circumflex: one cluster, one cell. + let line = PreeditLine::new(&preedit("e\u{302}x", None), 0, 0, 80).unwrap(); + assert_eq!(line.cell(0), Some(PreeditCell::Start(0))); + assert_eq!(line.cluster(0), "e\u{302}"); + assert_eq!(line.cell(1), Some(PreeditCell::Start(1))); + assert_eq!(line.cluster(1), "x"); + + // ZWJ family emoji: one cluster, clamped to two cells. + let family = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}"; + let line = PreeditLine::new(&preedit(family, None), 0, 0, 80).unwrap(); + assert_eq!(line.cell(0), Some(PreeditCell::Start(0))); + assert_eq!(line.cell(1), Some(PreeditCell::Continuation)); + assert_eq!(line.cell(2), None); + assert_eq!(line.cluster(0), family); + } + + #[test] + fn slides_left_at_the_right_edge() { + // Cursor at col 77 of 80; "日本語" needs 6 cells → starts at 74. + let line = PreeditLine::new(&preedit("日本語", None), 24, 77, 80).unwrap(); + assert_eq!(line.start_col, 74); + assert_eq!(line.end_col(), 80); + // Caret would land at col 80 — clamped to an underline on 語. + assert_eq!(line.caret, PreeditCaret::OnCell(78)); + // Bottom row, no wrap: the row is exactly the cursor row. + assert_eq!(line.row, 24); + } + + #[test] + fn longer_than_the_row_keeps_the_tail() { + let text = "あ".repeat(50); // 100 cells wide + let line = PreeditLine::new(&preedit(&text, None), 0, 10, 80).unwrap(); + // 10 clusters dropped: 40 remain (80 cells), flush to col 0. + assert_eq!(line.start_col, 0); + assert_eq!(line.end_col(), 80); + assert_eq!(line.cluster(0), "あ"); + assert_eq!(line.caret, PreeditCaret::OnCell(78)); + } + + #[test] + fn caret_inside_composition_is_an_underline() { + // Caret before 本 (byte offset 3). + let line = PreeditLine::new(&preedit("日本語", Some(3)), 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(2)); + // A non-boundary offset is dropped by `Preedit::new` and the + // caret falls back to end-of-text. + let line = PreeditLine::new(&preedit("日本語", Some(4)), 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::PastEnd(6)); + // A char-boundary offset inside a grapheme cluster (between + // `e` and its combining mark) snaps forward to the next + // cluster boundary. + let line = PreeditLine::new(&preedit("e\u{302}x", Some(1)), 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(1)); + } + + #[test] + fn caret_survives_tail_cropping() { + let text = "あ".repeat(50); + // Caret at the very start, which gets dropped: clamps to the + // first visible cluster. + let line = PreeditLine::new(&preedit(&text, Some(0)), 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(0)); + } + + #[test] + fn empty_and_degenerate_input() { + assert!(PreeditLine::new(&preedit("", None), 0, 0, 80).is_none()); + assert!(PreeditLine::new(&preedit("a", None), 0, 0, 0).is_none()); + } +} diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index f5b442d3f0..9da09ae534 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -73,6 +73,16 @@ pub struct Screen<'screen> { pub renderer: Renderer, pub sugarloaf: Sugarloaf<'screen>, pub context_manager: context::ContextManager, + /// IME state is per window, not per context: the platform IME + /// composes into whichever context is current, and exactly one + /// composition can exist per view. Keeping it here (instead of on + /// `Context`) makes a stale preedit on a background tab or split + /// structurally impossible. + pub ime: crate::ime::Ime, + /// Screen row the preedit rendered on last frame, so the row can + /// be rebuilt when the composition moves or ends even when + /// terminal damage reports nothing. + last_preedit_row: Option, last_ime_cursor_pos: Option<(f32, f32)>, hints_config: Vec>, pub resize_state: Option, @@ -264,7 +274,6 @@ impl Screen<'_> { content: config.cursor.shape.into(), content_ref: config.cursor.shape.into(), state: CursorState::new(config.cursor.shape.into()), - is_ime_enabled: false, }; let context_manager = context::ContextManager::start( @@ -306,6 +315,8 @@ impl Screen<'_> { mouse_bindings: crate::bindings::default_mouse_bindings(), modifiers: Modifiers::default(), context_manager, + ime: crate::ime::Ime::new(), + last_preedit_row: None, sugarloaf, mouse: Mouse::new(config.scroll.multiplier, config.scroll.divider), touchpurpose: TouchPurpose::default(), @@ -692,7 +703,7 @@ impl Screen<'_> { key: &rio_window::event::KeyEvent, clipboard: &mut Clipboard, ) { - if self.context_manager.current().ime.preedit().is_some() { + if self.ime.preedit().is_some() { return; } @@ -3928,6 +3939,12 @@ impl Screen<'_> { )>, hint_labels: Option>, label_style_base: Option, + /// Active IME composition, laid out on the cursor row. + /// Only ever `Some` for the active panel with an + /// unscrolled viewport: the composition belongs to the + /// focused context, and a scrolled viewport has no + /// on-screen cursor row to anchor it to. + preedit_line: Option, } let (active_key, scaled_margin) = { @@ -4034,7 +4051,24 @@ impl Screen<'_> { let cursor_blinking = ctx.renderable_content.has_blinking_enabled; let cursor_blink_visible = !cursor_blinking || ctx.renderable_content.is_blinking_cursor_visible; - let cursor_preedit = ctx.ime.preedit().is_some(); + // IME state is window-level (`self.ime`); it renders + // on the active panel only, and never over scrollback + // (the cursor row is off-viewport there — an anchor + // computed from it would paint on history). + let preedit_line = if is_active && display_offset == 0 { + self.ime.preedit().and_then(|preedit| { + crate::renderer::preedit::PreeditLine::new( + preedit, + (cursor.state.pos.row.0.max(0) as usize) + .min(ctx.renderable_content.screen_lines.max(1) - 1), + cursor.state.pos.col.0, + ctx.renderable_content.columns.max(1), + ) + }) + } else { + None + }; + let cursor_preedit = preedit_line.is_some(); // OSC 12 wins; otherwise fall back to the named-color // theme value. `Renderer::color`'s fallback (the // indexed-color List) is not populated for the Cursor @@ -4072,6 +4106,7 @@ impl Screen<'_> { hovered_hyperlink, hint_labels, label_style_base, + preedit_line, }); } @@ -4199,6 +4234,13 @@ impl Screen<'_> { } _ => row, }; + // Thread the composition only into its own + // row: everything else renders untouched. + let preedit_row = p + .preedit_line + .as_ref() + .filter(|line| line.row == y) + .map(|line| crate::grid_emit::PreeditRow { line }); crate::grid_emit::build_row_bg( row, cols, @@ -4207,6 +4249,7 @@ impl Screen<'_> { &p.term_colors, row_sel, &hint_scratch, + preedit_row.as_ref(), &mut bg_scratch, ); let cursor_col_for_row = if p.cursor_visible @@ -4232,6 +4275,7 @@ impl Screen<'_> { p.cell_h, row_sel, &hint_scratch, + preedit_row.as_ref(), &font_library, p.route_id, cursor_col_for_row, @@ -4273,6 +4317,29 @@ impl Screen<'_> { } } + // The composition is painted into the row's CPU + // cells, so its row must rebuild whenever the overlay + // exists, moved, or just disappeared — even when + // terminal damage says nothing changed (the text under + // it didn't; the overlay did). Cheap: at most two rows. + if p.is_active { + let current = p.preedit_line.as_ref().map(|line| line.row); + for row in [ + self.last_preedit_row + .filter(|_| self.last_preedit_row != current), + current, + ] + .into_iter() + .flatten() + { + if row < p.visible_rows.len() { + rebuild_row(p, row, grid, rasterizer); + p.visible_rows[row].dirty = false; + } + } + self.last_preedit_row = current; + } + // Atlas-full recovery: the backend cleared the atlas // during the rebuild above, so rows written before the // clear reference stale slots. Re-emit everything. From baa2817035618cee926c93c26f84c5665532cad1 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sat, 5 Sep 2026 21:47:23 +0200 Subject: [PATCH 04/14] preedit: honor hidden caret, fix sprite cache, anchor popup --- frontends/rioterm/src/application.rs | 52 +++--- frontends/rioterm/src/grid_emit.rs | 187 +++++++++++++--------- frontends/rioterm/src/ime.rs | 59 ++++--- frontends/rioterm/src/renderer/preedit.rs | 59 +++++-- frontends/rioterm/src/screen/mod.rs | 35 +++- 5 files changed, 260 insertions(+), 132 deletions(-) diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs index e6cf5da241..a53791205f 100644 --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -1,5 +1,5 @@ use crate::event::{ClickState, EventPayload, EventProxy, RioEvent, RioEventType}; -use crate::ime::Preedit; +use crate::ime::{Preedit, PreeditCursor}; use crate::renderer::utils::update_colors_based_on_theme; use crate::router::{routes::RoutePath, Router}; use crate::scheduler::{Scheduler, TimerId, Topic}; @@ -1921,29 +1921,48 @@ impl ApplicationHandler for Application<'_> { } WindowEvent::Ime(ime) => { - if route.window.screen.renderer.assistant.is_active() { - return; - } + // The assistant swallows IME input, but clear events + // (empty Preedit, Disabled) must still pass: dropping + // them leaves the window-level preedit stuck, and the + // preedit gate in `process_key_event` then swallows + // every plain keystroke after the assistant closes. + let assistant_active = route.window.screen.renderer.assistant.is_active(); match ime { Ime::Commit(text) => { + if assistant_active { + return; + } // Don't use bracketed paste for single char input. route.window.screen.paste(&text, text.chars().count() > 1); } Ime::Preedit(text, cursor_offset) => { + if assistant_active && !text.is_empty() { + return; + } let preedit = if text.is_empty() { None } else { - Some(Preedit::new(text, cursor_offset.map(|offset| offset.0))) + // The platform's `None` means the IME asked + // for a hidden caret (candidate paging) — + // NOT end-of-text, which arrives as an + // explicit offset. + let cursor = match cursor_offset { + Some((start, _)) => PreeditCursor::Byte(start), + None => PreeditCursor::Hidden, + }; + Some(Preedit::new(text, cursor)) }; if route.window.screen.ime.preedit() != preedit.as_ref() { route.window.screen.ime.set_preedit(preedit); // The overlay lives in the grid's CPU cell - // buffers: force the current panel to - // repaint so the composition (or its - // removal) is visible this frame, not - // whenever the terminal next damages. + // buffers and its rows are force-rebuilt + // each frame while composing; a plain + // dirty mark is enough to get a frame + // scheduled — full terminal damage would + // re-shape every visible row per + // keystroke for nothing. route .window .screen @@ -1951,9 +1970,7 @@ impl ApplicationHandler for Application<'_> { .current_mut() .renderable_content .pending_update - .set_terminal_damage( - rio_backend::event::TerminalDamage::Full, - ); + .set_dirty(); route.request_redraw(); } } @@ -1962,9 +1979,10 @@ impl ApplicationHandler for Application<'_> { } Ime::Disabled => { // Disabling wipes any live preedit (input - // source switched mid-composition): damage and - // repaint like the Preedit arm, or the block - // ghosts on screen until unrelated damage. + // source switched mid-composition): mark dirty + // and repaint like the Preedit arm, or the + // block ghosts on screen until unrelated + // damage. let had_preedit = route.window.screen.ime.preedit().is_some(); route.window.screen.ime.set_enabled(false); if had_preedit { @@ -1975,9 +1993,7 @@ impl ApplicationHandler for Application<'_> { .current_mut() .renderable_content .pending_update - .set_terminal_damage( - rio_backend::event::TerminalDamage::Full, - ); + .set_dirty(); route.request_redraw(); } } diff --git a/frontends/rioterm/src/grid_emit.rs b/frontends/rioterm/src/grid_emit.rs index 155dc6ac22..d7c4a6faa7 100644 --- a/frontends/rioterm/src/grid_emit.rs +++ b/frontends/rioterm/src/grid_emit.rs @@ -46,6 +46,11 @@ use crate::renderer::Renderer; /// emit passes for the single row the composition lives on. pub struct PreeditRow<'a> { pub line: &'a PreeditLine, + /// The cursor color the frame resolved once (OSC 12 wins, then the + /// theme) — the same value the cursor-block uniforms use, threaded + /// here so the block fill, the PastEnd beam, and the cursor can + /// never diverge. + pub block_bg: [u8; 4], } impl PreeditRow<'_> { @@ -913,13 +918,14 @@ fn rasterize_decoration( (bytes, cell_w, thickness, bearing_y) } DecorationStyle::ImeCaretUnderline => { - // Double-thickness underline at the underline position — - // the ghostty presentation for the caret inside a - // composition. - let h = (thickness * 2).max(2).min(cell_h); - let bytes = vec![0xFFu8; (cell_w * h) as usize]; - let bearing_y = (h + underline_gap_below(cell_h)) as i16; - (bytes, cell_w, h, bearing_y) + // The cursor's underline sprite at doubled thickness: one + // rasterizer for both, so a change to the underline + // position can't leave the IME caret misaligned with the + // underline cursor. + let t2 = (thickness * 2).max(2).min(cell_h); + let (bytes, w, h, _bearing_x, bearing_y) = + rasterize_cursor(CursorSpriteStyle::Underline, cell_w, cell_h, t2); + (bytes, w as u32, h as u32, bearing_y) } DecorationStyle::ImeCaretBeam => { // Full-height beam pinned to the cell's left edge, in the @@ -931,10 +937,13 @@ fn rasterize_decoration( } } -/// Look up or insert a decoration sprite into the grid atlas. Key is -/// (decoration font_id sentinel, cell_w as glyph_id, thickness as -/// size_bucket) — the same cache that backs regular glyphs, so -/// decorations ride the grid's glyph-eviction policy for free. +/// Look up or insert a decoration sprite into the grid atlas. Keyed by +/// (decoration font_id sentinel, cell_w as glyph_id, thickness+cell_h +/// as size_bucket) — the same cache that backs regular glyphs, so +/// decorations ride the grid's glyph-eviction policy for free. Every +/// decoration's bearing (and the IME beam's height) depends on +/// `cell_h`, so it must key the sprite or a line-height-only config +/// reload serves stale-height sprites until eviction. fn ensure_decoration_slot( grid: &mut GridRenderer, style: DecorationStyle, @@ -945,7 +954,7 @@ fn ensure_decoration_slot( let key = GlyphKey { font_id: DECORATION_FONT_ID_BASE + style as u32, glyph_id: cell_w, - size_bucket: thickness as u16, + size_bucket: ((thickness as u16 & 0xF) << 12) | (cell_h.min(0xFFF) as u16), }; if let Some(slot) = grid.lookup_glyph(key) { return Some(slot); @@ -1080,7 +1089,7 @@ pub fn cell_bg( } #[inline] -fn normalized_to_u8(c: [f32; 4]) -> [u8; 4] { +pub(crate) fn normalized_to_u8(c: [f32; 4]) -> [u8; 4] { [ (c[0].clamp(0.0, 1.0) * 255.0) as u8, (c[1].clamp(0.0, 1.0) * 255.0) as u8, @@ -1103,16 +1112,10 @@ pub fn build_row_bg( ) { bg_scratch.clear(); - // Block fill behind every composition cell — the same resolved - // cursor color the cursor-block uniforms use (OSC 12 wins, then - // the theme), so the block and the forced block cursor on the + // Block fill behind every composition cell: the frame's resolved + // cursor color, so the block and the forced block cursor on the // first composition cell can never be two different colors. - let preedit_block_bg = preedit.map(|_| { - normalized_to_u8( - term_colors[NamedColor::Cursor as usize] - .unwrap_or(renderer.named_colors.cursor), - ) - }); + let preedit_block_bg = preedit.map(|p| p.block_bg); // Fast path: row has no selection, no color-changing hints, and no // composition. (HyperlinkHover only contributes an underline, @@ -1668,6 +1671,40 @@ fn shape_run_swash( // Emission +/// Shape the rasterizer's current run scratch, keyed in the run cache +/// by `hash`, and return the ascent for `(font_id, size_bucket)`; on a +/// miss the shaped glyphs are stored under `hash`. `None` means +/// shaping failed (no font handle). The one shaping-cache protocol, +/// shared by the grid run path and the preedit path. +fn shape_cached( + rasterizer: &mut GridGlyphRasterizer, + hash: u64, + font_id: u32, + size_u16: u16, + size_bucket: u16, + font_library: &FontLibrary, +) -> Option { + if run_cache_get(&mut rasterizer.run_cache, hash).is_some() { + // Cache hit — ascent already stored. + return Some( + rasterizer + .ascent_cache + .get(&(font_id, size_bucket)) + .copied() + .unwrap_or(0), + ); + } + #[cfg(target_os = "macos")] + let shaped_opt = + shape_run_ct(rasterizer, font_id, size_u16, size_bucket, font_library); + #[cfg(not(target_os = "macos"))] + let shaped_opt = + shape_run_swash(rasterizer, font_id, size_u16, size_bucket, font_library); + let (glyphs, ascent_px) = shaped_opt?; + run_cache_put(&mut rasterizer.run_cache, RunCacheEntry { hash, glyphs }); + Some(ascent_px) +} + /// Run-level fg emission. Shapes once per run, emits one CellText per /// shaped glyph. Works on both macOS (CoreText) and non-macOS (swash). /// @@ -2107,26 +2144,16 @@ pub fn build_row_fg( let hash = rasterizer.run_hasher.finish(); // Shape (cached) and capture ascent for this (font_id, size). - let ascent_px = if run_cache_get(&mut rasterizer.run_cache, hash).is_some() { - // Cache hit — ascent already stored. - rasterizer - .ascent_cache - .get(&(font_id, size_bucket)) - .copied() - .unwrap_or(0) - } else { - #[cfg(target_os = "macos")] - let shaped_opt = - shape_run_ct(rasterizer, font_id, size_u16, size_bucket, font_library); - #[cfg(not(target_os = "macos"))] - let shaped_opt = - shape_run_swash(rasterizer, font_id, size_u16, size_bucket, font_library); - let Some((glyphs, ascent_px)) = shaped_opt else { - x = end; - continue; - }; - run_cache_put(&mut rasterizer.run_cache, RunCacheEntry { hash, glyphs }); - ascent_px + let Some(ascent_px) = shape_cached( + rasterizer, + hash, + font_id, + size_u16, + size_bucket, + font_library, + ) else { + x = end; + continue; }; let (synthetic_bold, synthetic_italic) = @@ -2329,26 +2356,43 @@ pub fn build_row_fg( // composition there is no block, so a beam in the cursor color // reads correctly there. if let Some(pre) = preedit { - let (col, style, color) = match pre.line.caret { - PreeditCaret::OnCell(col) => ( + let caret = match pre.line.caret { + PreeditCaret::OnCell(col) => Some(( col, DecorationStyle::ImeCaretUnderline, normalized_to_u8(renderer.named_colors.background.0), - ), - PreeditCaret::PastEnd(col) => ( - col, - DecorationStyle::ImeCaretBeam, - normalized_to_u8( - term_colors[NamedColor::Cursor as usize] - .unwrap_or(renderer.named_colors.cursor), - ), - ), + )), + PreeditCaret::PastEnd(col) => { + Some((col, DecorationStyle::ImeCaretBeam, pre.block_bg)) + } + // The IME asked for no caret (candidate paging). + PreeditCaret::Hidden => None, }; - if col < cols { - emit_preedit_caret( - col as u16, y, grid, cell_w_u32, cell_h_u32, thickness, style, color, - fg_scratch, - ); + if let Some((col, style, color)) = caret { + if col < cols { + emit_preedit_caret( + col as u16, y, grid, cell_w_u32, cell_h_u32, thickness, style, color, + fg_scratch, + ); + } + // A caret underline on a wide cluster covers both of its + // cells; one cell would underline half the kanji. + if matches!(style, DecorationStyle::ImeCaretUnderline) + && pre.cell(col + 1) == Some(PreeditCell::Continuation) + && col + 1 < cols + { + emit_preedit_caret( + (col + 1) as u16, + y, + grid, + cell_w_u32, + cell_h_u32, + thickness, + style, + color, + fg_scratch, + ); + } } } } @@ -2416,24 +2460,15 @@ fn emit_preedit_cluster( rasterizer.run_hasher.write_u16(size_bucket); let hash = rasterizer.run_hasher.finish(); - let ascent_px = if run_cache_get(&mut rasterizer.run_cache, hash).is_some() { - rasterizer - .ascent_cache - .get(&(font_id, size_bucket)) - .copied() - .unwrap_or(0) - } else { - #[cfg(target_os = "macos")] - let shaped_opt = - shape_run_ct(rasterizer, font_id, size_u16, size_bucket, font_library); - #[cfg(not(target_os = "macos"))] - let shaped_opt = - shape_run_swash(rasterizer, font_id, size_u16, size_bucket, font_library); - let Some((glyphs, ascent_px)) = shaped_opt else { - return; - }; - run_cache_put(&mut rasterizer.run_cache, RunCacheEntry { hash, glyphs }); - ascent_px + let Some(ascent_px) = shape_cached( + rasterizer, + hash, + font_id, + size_u16, + size_bucket, + font_library, + ) else { + return; }; let (synthetic_bold, synthetic_italic) = diff --git a/frontends/rioterm/src/ime.rs b/frontends/rioterm/src/ime.rs index 9cf61790ed..9c41a6523c 100644 --- a/frontends/rioterm/src/ime.rs +++ b/frontends/rioterm/src/ime.rs @@ -39,28 +39,42 @@ impl Ime { } } -#[derive(Debug, Default, PartialEq, Eq)] +/// Where the IME put its caret, as the platform reported it. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PreeditCursor { + /// The IME asked for no visible caret. Wayland (`cursor_begin = + /// -1`) and Windows deliver this with non-empty text while the + /// user pages through conversion candidates; drawing a caret then + /// would contradict the IME's own UI. + Hidden, + /// Caret before the cluster containing this byte offset; offsets + /// at or past the end of the text mean end-of-text. + Byte(usize), +} + +#[derive(Debug, PartialEq, Eq)] pub struct Preedit { /// The preedit text. pub text: String, - /// Byte offset of the IME caret into the preedit text. + /// The IME caret. /// - /// `None` means the caret is at the end of the text. Offsets that - /// don't land on a char boundary (macOS reports UTF-16 ranges that - /// can split a surrogate pair) are dropped rather than trusted — - /// slicing on one would panic downstream. - pub cursor_byte_offset: Option, + /// Byte offsets that don't land on a char boundary (macOS reports + /// UTF-16 ranges that can split a surrogate pair) snap to + /// end-of-text rather than being trusted — slicing on one would + /// panic downstream. + pub cursor: PreeditCursor, } impl Preedit { - pub fn new(text: String, cursor_byte_offset: Option) -> Self { - let cursor_byte_offset = - cursor_byte_offset.filter(|&offset| text.is_char_boundary(offset)); - Self { - text, - cursor_byte_offset, - } + pub fn new(text: String, cursor: PreeditCursor) -> Self { + let cursor = match cursor { + PreeditCursor::Byte(offset) if !text.is_char_boundary(offset) => { + PreeditCursor::Byte(text.len()) + } + other => other, + }; + Self { text, cursor } } } @@ -71,19 +85,22 @@ mod tests { #[test] fn preedit_new_rejects_invalid_byte_offset() { // Byte 1 is inside 啊's UTF-8 encoding: not a char boundary. - let preedit = Preedit::new("啊a".to_string(), Some(1)); - assert!(preedit.cursor_byte_offset.is_none()); + let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(1)); + assert_eq!(preedit.cursor, PreeditCursor::Byte(4)); // Boundary offsets survive, including one-past-the-end. - let preedit = Preedit::new("啊a".to_string(), Some(3)); - assert_eq!(preedit.cursor_byte_offset, Some(3)); - let preedit = Preedit::new("啊a".to_string(), Some(4)); - assert_eq!(preedit.cursor_byte_offset, Some(4)); + let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(3)); + assert_eq!(preedit.cursor, PreeditCursor::Byte(3)); + let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(4)); + assert_eq!(preedit.cursor, PreeditCursor::Byte(4)); + // A hidden caret stays hidden. + let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Hidden); + assert_eq!(preedit.cursor, PreeditCursor::Hidden); } #[test] fn set_preedit_clears_on_none() { let mut ime = Ime::new(); - ime.set_preedit(Some(Preedit::new("a".to_string(), Some(1)))); + ime.set_preedit(Some(Preedit::new("a".to_string(), PreeditCursor::Byte(1)))); assert!(ime.preedit().is_some()); ime.set_preedit(None); assert!(ime.preedit().is_none()); diff --git a/frontends/rioterm/src/renderer/preedit.rs b/frontends/rioterm/src/renderer/preedit.rs index dc518f3d3a..057fa771b3 100644 --- a/frontends/rioterm/src/renderer/preedit.rs +++ b/frontends/rioterm/src/renderer/preedit.rs @@ -19,14 +19,15 @@ //! thick underline (visible on the cursor-colored block), past the //! end of the text as a beam on the following cell (visible on the //! normal background). Both presentations exist because a beam drawn -//! in the cursor color on a cursor-colored block is invisible. +//! in the cursor color on a cursor-colored block is invisible. When +//! the IME reports a hidden caret (candidate paging), none renders. //! //! The caller decides visibility: the overlay is only built for the //! active panel and never while the viewport is scrolled into history //! (`display_offset != 0`), where the cursor row is off-screen and the //! anchor would lie. -use crate::ime::Preedit; +use crate::ime::{Preedit, PreeditCursor}; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; @@ -47,6 +48,8 @@ pub enum PreeditCaret { OnCell(usize), /// Caret one past the composition: beam on that (non-block) cell. PastEnd(usize), + /// The IME asked for no caret (candidate paging): draw none. + Hidden, } #[derive(Debug, Clone, PartialEq)] @@ -99,13 +102,14 @@ impl PreeditLine { // The caret sits before the first cluster starting at or past // the byte offset (mid-cluster offsets snap to the cluster). - // No offset reported means end-of-text, the common IME idiom. - let mut caret_index = match preedit.cursor_byte_offset { - Some(offset) => segs - .iter() - .position(|seg| seg.byte_start >= offset) - .unwrap_or(segs.len()), - None => segs.len(), + // A hidden caret (candidate paging) stays hidden. + let mut caret_index = match preedit.cursor { + PreeditCursor::Hidden => None, + PreeditCursor::Byte(offset) => Some( + segs.iter() + .position(|seg| seg.byte_start >= offset) + .unwrap_or(segs.len()), + ), }; // Wider than the row: drop leading clusters, keeping the tail @@ -118,7 +122,7 @@ impl PreeditLine { total -= segs.remove(0).width; dropped += 1; } - caret_index = caret_index.saturating_sub(dropped); + caret_index = caret_index.map(|index| index.saturating_sub(dropped)); if total > columns { // A single cluster wider than the grid; nothing sane to draw. return None; @@ -129,9 +133,12 @@ impl PreeditLine { let mut cells = Vec::with_capacity(total); let mut clusters = Vec::with_capacity(segs.len()); - let mut caret = None; + let mut caret = match caret_index { + Some(_) => None, + None => Some(PreeditCaret::Hidden), + }; for (i, seg) in segs.iter().enumerate() { - if i == caret_index { + if caret_index == Some(i) { caret = Some(PreeditCaret::OnCell(start_col + cells.len())); } cells.push(PreeditCell::Start(clusters.len() as u16)); @@ -186,6 +193,16 @@ impl PreeditLine { pub fn end_col(&self) -> usize { self.start_col + self.cells.len() } + + /// Column the OS candidate popup should anchor to: the caret, + /// or the last composition cell when the caret is hidden. + #[inline] + pub fn popup_anchor_col(&self) -> usize { + match self.caret { + PreeditCaret::OnCell(col) | PreeditCaret::PastEnd(col) => col, + PreeditCaret::Hidden => self.end_col().saturating_sub(1), + } + } } #[cfg(test)] @@ -193,7 +210,9 @@ mod tests { use super::*; fn preedit(text: &str, caret: Option) -> Preedit { - Preedit::new(text.to_string(), caret) + // `None` = end-of-text, the common IME idiom while composing. + let cursor = PreeditCursor::Byte(caret.unwrap_or(text.len())); + Preedit::new(text.to_string(), cursor) } #[test] @@ -285,6 +304,20 @@ mod tests { assert_eq!(line.caret, PreeditCaret::OnCell(0)); } + #[test] + fn hidden_caret_draws_no_caret() { + // Wayland/Windows report a hidden caret while the user pages + // through candidates; no caret may render then. + let p = Preedit::new("日本語".to_string(), PreeditCursor::Hidden); + let line = PreeditLine::new(&p, 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::Hidden); + assert_eq!(line.popup_anchor_col(), 5); + // Cropping keeps it hidden too. + let long = Preedit::new("あ".repeat(50), PreeditCursor::Hidden); + let line = PreeditLine::new(&long, 0, 0, 80).unwrap(); + assert_eq!(line.caret, PreeditCaret::Hidden); + } + #[test] fn empty_and_degenerate_input() { assert!(PreeditLine::new(&preedit("", None), 0, 0, 80).is_none()); diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index 9da09ae534..3c79c81b51 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -4240,7 +4240,12 @@ impl Screen<'_> { .preedit_line .as_ref() .filter(|line| line.row == y) - .map(|line| crate::grid_emit::PreeditRow { line }); + .map(|line| crate::grid_emit::PreeditRow { + line, + block_bg: crate::grid_emit::normalized_to_u8( + p.cursor_color, + ), + }); crate::grid_emit::build_row_bg( row, cols, @@ -4578,6 +4583,29 @@ impl Screen<'_> { let layout = current_item.val.dimension; let cursor_pos = current_item.val.renderable_content.cursor.state.pos; + // While composing, the candidate popup follows the IME caret, + // not the terminal cursor: the composition renders inline and + // can slide away from the cursor cell, and a popup opening + // tens of cells from the caret overlaps the freshly drawn + // text. Mirrors the layout the renderer uses (same inputs). + let content = ¤t_item.val.renderable_content; + let (anchor_row, anchor_col) = match self.ime.preedit().filter(|_| { + content.display_offset == 0 && content.columns > 0 && content.screen_lines > 0 + }) { + Some(preedit) => match crate::renderer::preedit::PreeditLine::new( + preedit, + (cursor_pos.row.0.max(0) as usize).min(content.screen_lines - 1), + cursor_pos.col.0, + content.columns, + ) { + Some(line) => { + (line.row, line.popup_anchor_col().min(content.columns - 1)) + } + None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0), + }, + None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0), + }; + // Calculate pixel position of cursor — canonical integer // stride (line_height already baked into cell_height). let cell_width = layout.cell.cell_width as f32; @@ -4600,9 +4628,8 @@ impl Screen<'_> { let origin_y = panel_rect[1] + scaled_margin.top; // Convert grid position to pixel position - let pixel_x = - origin_x + (cursor_pos.col.0 as f32 * cell_width) + (cell_width * 0.5); - let pixel_y = origin_y + (cursor_pos.row.0 as f32 * cell_height); + let pixel_x = origin_x + (anchor_col as f32 * cell_width) + (cell_width * 0.5); + let pixel_y = origin_y + (anchor_row as f32 * cell_height); // Validate final coordinates if pixel_x.is_nan() || pixel_y.is_nan() || pixel_x < 0.0 || pixel_y < 0.0 { From 05187c32f52456c8614d87a25d8e2b4ebc012c70 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sat, 5 Sep 2026 22:49:52 +0200 Subject: [PATCH 05/14] pass no preedit through libsugarloaf, fix clippy --- libsugarloaf/src/capi.rs | 4 ++++ rio-grid/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libsugarloaf/src/capi.rs b/libsugarloaf/src/capi.rs index 9227fc5666..985547105f 100644 --- a/libsugarloaf/src/capi.rs +++ b/libsugarloaf/src/capi.rs @@ -1232,6 +1232,9 @@ pub unsafe extern "C" fn sl_render_surface( &term_colors, row_sel, &hint_scratch, + // IME composition is a frontend concern; the C API + // renders no preedit overlay. + None, &mut bg_scratch, ); // Break shaping runs around the cursor cell so partial @@ -1260,6 +1263,7 @@ pub unsafe extern "C" fn sl_render_surface( cell_h, row_sel, &hint_scratch, + None, &font_library, 0, cursor_col_for_row, diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index cd251579df..89a458348d 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -2532,7 +2532,7 @@ fn emit_preedit_cluster( #[cfg(not(target_os = "macos"))] { rasterizer.run_str_scratch.clear(); - rasterizer.run_str_scratch.extend(cluster.chars()); + rasterizer.run_str_scratch.push_str(cluster); } // Composition runs get their own cache namespace (the leading From b781c242444fff0ea9563d276720632942330409 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sat, 5 Sep 2026 23:27:25 +0200 Subject: [PATCH 06/14] remove accidentally committed local notes --- rio-boo-pane-phase5.patch | 1169 ---- rio-scale-sync-research.md | 156 - rio-ucs-detect-0.5.3.yaml | 11247 ----------------------------------- rio-vt-perf-vs-ghostty.md | 410 -- 4 files changed, 12982 deletions(-) delete mode 100644 rio-boo-pane-phase5.patch delete mode 100644 rio-scale-sync-research.md delete mode 100644 rio-ucs-detect-0.5.3.yaml delete mode 100644 rio-vt-perf-vs-ghostty.md diff --git a/rio-boo-pane-phase5.patch b/rio-boo-pane-phase5.patch deleted file mode 100644 index 8ed244e024..0000000000 --- a/rio-boo-pane-phase5.patch +++ /dev/null @@ -1,1169 +0,0 @@ -diff --git a/Cargo.lock b/Cargo.lock -index 410132b87b..ea5e704141 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -534,6 +534,79 @@ dependencies = [ - "piper", - ] - -+[[package]] -+name = "boo-core" -+version = "0.1.0" -+dependencies = [ -+ "boo-db", -+ "boo-syntax", -+ "ignore", -+ "nucleo", -+ "ropey", -+ "rustc-hash 2.1.3", -+ "screen", -+ "smallvec", -+ "streaming-iterator", -+ "tree-sitter", -+ "tree-sitter-cpp", -+ "tree-sitter-go", -+ "tree-sitter-jam", -+ "tree-sitter-javascript", -+ "tree-sitter-json", -+ "tree-sitter-md", -+ "tree-sitter-python", -+ "tree-sitter-rust", -+ "tree-sitter-toml-ng", -+ "unicode-segmentation", -+ "unicode-width", -+] -+ -+[[package]] -+name = "boo-db" -+version = "0.1.0" -+dependencies = [ -+ "blake3", -+ "boo-hir", -+ "boo-lower", -+ "boo-syntax", -+ "protobuf", -+ "rusqlite", -+ "rustc-hash 2.1.3", -+ "salsa", -+ "scip", -+ "serde", -+ "serde_json", -+ "smol_str", -+] -+ -+[[package]] -+name = "boo-hir" -+version = "0.1.0" -+dependencies = [ -+ "rustc-hash 2.1.3", -+ "serde", -+ "smol_str", -+] -+ -+[[package]] -+name = "boo-lower" -+version = "0.1.0" -+dependencies = [ -+ "boo-hir", -+ "boo-syntax", -+ "smol_str", -+] -+ -+[[package]] -+name = "boo-syntax" -+version = "0.1.0" -+dependencies = [ -+ "boo-hir", -+ "tree-sitter", -+ "tree-sitter-go", -+ "tree-sitter-rust", -+] -+ - [[package]] - name = "borsh" - version = "1.8.0" -@@ -544,6 +617,22 @@ dependencies = [ - "cfg_aliases", - ] - -+[[package]] -+name = "boxcar" -+version = "0.2.14" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" -+ -+[[package]] -+name = "bstr" -+version = "1.13.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" -+dependencies = [ -+ "memchr", -+ "serde_core", -+] -+ - [[package]] - name = "bumpalo" - version = "3.20.3" -@@ -1141,6 +1230,15 @@ dependencies = [ - "crossbeam-utils", - ] - -+[[package]] -+name = "crossbeam-queue" -+version = "0.3.13" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" -+dependencies = [ -+ "crossbeam-utils", -+] -+ - [[package]] - name = "crossbeam-utils" - version = "0.8.22" -@@ -1494,6 +1592,18 @@ dependencies = [ - "pin-project-lite", - ] - -+[[package]] -+name = "fallible-iterator" -+version = "0.3.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" -+ -+[[package]] -+name = "fallible-streaming-iterator" -+version = "0.1.9" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" -+ - [[package]] - name = "fastrand" - version = "2.5.0" -@@ -1895,6 +2005,19 @@ dependencies = [ - "xkeysym", - ] - -+[[package]] -+name = "globset" -+version = "0.4.19" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" -+dependencies = [ -+ "aho-corasick", -+ "bstr", -+ "log 0.4.33", -+ "regex-automata", -+ "regex-syntax", -+] -+ - [[package]] - name = "glow" - version = "0.17.0" -@@ -2035,11 +2158,31 @@ version = "0.17.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - dependencies = [ -+ "allocator-api2", -+ "equivalent", - "foldhash 0.2.0", - "serde", - "serde_core", - ] - -+[[package]] -+name = "hashlink" -+version = "0.10.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -+dependencies = [ -+ "hashbrown 0.15.5", -+] -+ -+[[package]] -+name = "hashlink" -+version = "0.12.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248" -+dependencies = [ -+ "hashbrown 0.17.1", -+] -+ - [[package]] - name = "heck" - version = "0.5.0" -@@ -2161,6 +2304,22 @@ dependencies = [ - "icu_properties", - ] - -+[[package]] -+name = "ignore" -+version = "0.4.32" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "0b17771570a2b94107741a7b033f19132c2eee21d59d21b24d2ced26500bd66e" -+dependencies = [ -+ "crossbeam-deque", -+ "globset", -+ "log 0.4.33", -+ "memchr", -+ "regex-automata", -+ "same-file", -+ "walkdir", -+ "winapi-util", -+] -+ - [[package]] - name = "image" - version = "0.25.10" -@@ -2222,6 +2381,21 @@ dependencies = [ - "libc", - ] - -+[[package]] -+name = "intrusive-collections" -+version = "0.10.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4275b20e6057cd7733fd8df8a5a31701e4fe44497dad0f3fa0e1c4fb971506be" -+ -+[[package]] -+name = "inventory" -+version = "0.3.24" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" -+dependencies = [ -+ "rustversion", -+] -+ - [[package]] - name = "iovec" - version = "0.1.4" -@@ -2582,6 +2756,17 @@ dependencies = [ - "tracing", - ] - -+[[package]] -+name = "libsqlite3-sys" -+version = "0.35.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" -+dependencies = [ -+ "cc", -+ "pkg-config", -+ "vcpkg", -+] -+ - [[package]] - name = "linked-hash-map" - version = "0.5.6" -@@ -2950,6 +3135,27 @@ dependencies = [ - "windows-sys 0.61.2", - ] - -+[[package]] -+name = "nucleo" -+version = "0.5.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5262af4c94921c2646c5ac6ff7900c2af9cbb08dc26a797e18130a7019c039d4" -+dependencies = [ -+ "nucleo-matcher", -+ "parking_lot", -+ "rayon", -+] -+ -+[[package]] -+name = "nucleo-matcher" -+version = "0.3.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "bf33f538733d1a5a3494b836ba913207f14d9d4a1d3cd67030c5061bdd2cac85" -+dependencies = [ -+ "memchr", -+ "unicode-segmentation", -+] -+ - [[package]] - name = "num-derive" - version = "0.4.2" -@@ -3670,6 +3876,26 @@ version = "1.0.18" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" - -+[[package]] -+name = "protobuf" -+version = "3.7.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" -+dependencies = [ -+ "once_cell", -+ "protobuf-support", -+ "thiserror 1.0.69", -+] -+ -+[[package]] -+name = "protobuf-support" -+version = "3.7.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" -+dependencies = [ -+ "thiserror 1.0.69", -+] -+ - [[package]] - name = "pxfm" - version = "0.1.30" -@@ -4110,6 +4336,7 @@ version = "0.5.5" - dependencies = [ - "ahash", - "bitflags 2.13.1", -+ "boo-core", - "clap", - "corcovado", - "cpal", -@@ -4133,6 +4360,7 @@ dependencies = [ - "rio-notifier", - "rio-window", - "rustc-hash 2.1.3", -+ "screen", - "serde", - "smallvec", - "sugarloaf", -@@ -4147,6 +4375,16 @@ dependencies = [ - "windows-sys 0.60.2", - ] - -+[[package]] -+name = "ropey" -+version = "1.6.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" -+dependencies = [ -+ "smallvec", -+ "str_indices", -+] -+ - [[package]] - name = "roxmltree" - version = "0.20.0" -@@ -4163,6 +4401,20 @@ dependencies = [ - "spirv", - ] - -+[[package]] -+name = "rusqlite" -+version = "0.37.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" -+dependencies = [ -+ "bitflags 2.13.1", -+ "fallible-iterator", -+ "fallible-streaming-iterator", -+ "hashlink 0.10.0", -+ "libsqlite3-sys", -+ "smallvec", -+] -+ - [[package]] - name = "rustc-hash" - version = "1.1.0" -@@ -4225,6 +4477,49 @@ dependencies = [ - "bytemuck", - ] - -+[[package]] -+name = "salsa" -+version = "0.28.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "cf0e374215cd2db2b5c75d7b3a99cb0cc052c0595335dfdefc03d4eb08f4aa81" -+dependencies = [ -+ "boxcar", -+ "crossbeam-queue", -+ "crossbeam-utils", -+ "hashbrown 0.17.1", -+ "hashlink 0.12.1", -+ "indexmap", -+ "intrusive-collections", -+ "inventory", -+ "parking_lot", -+ "portable-atomic", -+ "rayon", -+ "rustc-hash 2.1.3", -+ "salsa-macro-rules", -+ "salsa-macros", -+ "smallvec", -+ "thin-vec", -+ "tracing", -+ "typeid", -+] -+ -+[[package]] -+name = "salsa-macro-rules" -+version = "0.28.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "85f4b7d4405540bbd6d4ffa52d4322d983f3781954d3073067ac1bdb028459b3" -+ -+[[package]] -+name = "salsa-macros" -+version = "0.28.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "445be2bfbb2f67cb663225ecd7bc5a25370c0250fca30f9d8cbad9a913650370" -+dependencies = [ -+ "proc-macro2", -+ "quote", -+ "syn 3.0.3", -+] -+ - [[package]] - name = "same-file" - version = "1.0.6" -@@ -4234,6 +4529,15 @@ dependencies = [ - "winapi-util", - ] - -+[[package]] -+name = "scip" -+version = "0.9.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "26a72133c2d6fd45c9a3a343bcb3db2faa30f68f0919bfa3370ca85add5460c3" -+dependencies = [ -+ "protobuf", -+] -+ - [[package]] - name = "scoped-tls" - version = "1.0.1" -@@ -4246,6 +4550,15 @@ version = "1.2.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -+[[package]] -+name = "screen" -+version = "0.0.1" -+dependencies = [ -+ "bitflags 2.13.1", -+ "smallvec", -+ "unicode-width", -+] -+ - [[package]] - name = "sctk-adwaita" - version = "0.10.1" -@@ -4301,6 +4614,7 @@ version = "1.0.151" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" - dependencies = [ -+ "indexmap", - "itoa", - "memchr", - "serde", -@@ -4628,6 +4942,18 @@ version = "1.1.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -+[[package]] -+name = "str_indices" -+version = "0.4.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" -+ -+[[package]] -+name = "streaming-iterator" -+version = "0.1.9" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" -+ - [[package]] - name = "strict-num" - version = "0.1.1" -@@ -4828,6 +5154,12 @@ dependencies = [ - "winapi-util", - ] - -+[[package]] -+name = "thin-vec" -+version = "0.2.19" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "79def32ffcd477db1ff26f76dab9e3a91f0bd42a85ca96577089b24623056f9d" -+ - [[package]] - name = "thiserror" - version = "1.0.69" -@@ -5134,6 +5466,117 @@ dependencies = [ - "web-sys", - ] - -+[[package]] -+name = "tree-sitter" -+version = "0.26.12" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "83c567a8e18ae93f20982c90370b16fd24023aeaf52f6052b96957ab253a0fec" -+dependencies = [ -+ "cc", -+ "regex", -+ "regex-syntax", -+ "serde_json", -+ "streaming-iterator", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-cpp" -+version = "0.23.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "df2196ea9d47b4ab4a31b9297eaa5a5d19a0b121dceb9f118f6790ad0ab94743" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-go" -+version = "0.25.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "c8560a4d2f835cc0d4d2c2e03cbd0dde2f6114b43bc491164238d333e28b16ea" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-jam" -+version = "0.1.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "7d02033ddfde80571e547864e3faee09bb6a6e69570776a80633ca4eebbc5620" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-javascript" -+version = "0.25.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "68204f2abc0627a90bdf06e605f5c470aa26fdcb2081ea553a04bdad756693f5" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-json" -+version = "0.24.8" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4d727acca406c0020cffc6cf35516764f36c8e3dc4408e5ebe2cb35a947ec471" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-language" -+version = "0.1.7" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" -+ -+[[package]] -+name = "tree-sitter-md" -+version = "0.5.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "2efd398be546456c814598ee56c0f51769a77241511b4a58077815d120afa882" -+dependencies = [ -+ "cc", -+ "tree-sitter", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-python" -+version = "0.25.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-rust" -+version = "0.24.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "439e577dbe07423ec2582ac62c7531120dbfccfa6e5f92406f93dd271a120e45" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ -+[[package]] -+name = "tree-sitter-toml-ng" -+version = "0.7.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e9adc2c898ae49730e857d75be403da3f92bb81d8e37a2f918a08dd10de5ebb1" -+dependencies = [ -+ "cc", -+ "tree-sitter-language", -+] -+ - [[package]] - name = "ttf-parser" - version = "0.25.1" -@@ -5146,6 +5589,12 @@ version = "2.1.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9" - -+[[package]] -+name = "typeid" -+version = "1.0.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" -+ - [[package]] - name = "typenum" - version = "1.20.1" -@@ -5235,6 +5684,12 @@ version = "0.1.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -+[[package]] -+name = "vcpkg" -+version = "0.2.15" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -+ - [[package]] - name = "vec_extract_if_polyfill" - version = "0.1.0" -diff --git a/frontends/rioterm/Cargo.toml b/frontends/rioterm/Cargo.toml -index d5ead4aab2..55a6db32f5 100644 ---- a/frontends/rioterm/Cargo.toml -+++ b/frontends/rioterm/Cargo.toml -@@ -17,6 +17,9 @@ name = "rio" - path = "src/main.rs" - - [dependencies] -+# boo editor panes (phase 5 of boo's docs/design/003-libboo.md). -+boo-core = { path = "../../../boo/crates/boo-core" } -+screen = { path = "../../../boo/crates/screen" } - global-hotkey = "0.8.0" - tracing = { workspace = true } - tracing-subscriber = { version = "0.3.23", features = ["env-filter", "registry"] } -diff --git a/frontends/rioterm/src/bindings/mod.rs b/frontends/rioterm/src/bindings/mod.rs -index 27b3d62205..420d4336a1 100644 ---- a/frontends/rioterm/src/bindings/mod.rs -+++ b/frontends/rioterm/src/bindings/mod.rs -@@ -258,6 +258,7 @@ impl From for Action { - "scrolltotop" => Some(Action::ScrollToTop), - "scrolltobottom" => Some(Action::ScrollToBottom), - "splitright" => Some(Action::SplitRight), -+ "splitboo" => Some(Action::SplitBoo), - "splitdown" => Some(Action::SplitDown), - "selectnextsplit" => Some(Action::SelectNextSplit), - "selectprevsplit" => Some(Action::SelectPrevSplit), -@@ -495,6 +496,8 @@ pub enum Action { - - /// Split horizontally - SplitRight, -+ /// Open a boo editor pane (split right). -+ SplitBoo, - - /// Split vertically - SplitDown, -@@ -1179,6 +1182,7 @@ pub fn platform_key_bindings( - key_bindings.extend(bindings!( - KeyBinding; - "d", ModifiersState::SUPER, ~BindingMode::SEARCH, ~BindingMode::VI; Action::SplitRight; -+ "e", ModifiersState::SUPER | ModifiersState::SHIFT, ~BindingMode::SEARCH, ~BindingMode::VI; Action::SplitBoo; - "d", ModifiersState::SUPER | ModifiersState::SHIFT, ~BindingMode::SEARCH, ~BindingMode::VI; Action::SplitDown; - "]", ModifiersState::SUPER, ~BindingMode::SEARCH, ~BindingMode::VI; Action::SelectNextSplit; - "[", ModifiersState::SUPER, ~BindingMode::SEARCH, ~BindingMode::VI; Action::SelectPrevSplit; -diff --git a/frontends/rioterm/src/boo.rs b/frontends/rioterm/src/boo.rs -new file mode 100644 -index 0000000000..0db709e3b6 ---- /dev/null -+++ b/frontends/rioterm/src/boo.rs -@@ -0,0 +1,188 @@ -+//! boo editor panes: the boo editor core embedded as a native Rio pane -+//! (phase 5 of boo's docs/design/003-libboo.md). -+//! -+//! No PTY, no child process, no escape round-trip on input: Rio's -+//! already-decoded key events convert straight into boo's structured -+//! key events and drive `boo_core::surface::Surface` in-process. On the -+//! way out, the surface renders into its own scene grid and the -+//! composed frame (escape text plus the out-of-grid raw queues — kitty -+//! images, Glyph Protocol) feeds the pane's `Crosswords` through the -+//! VT parser, exactly like an rmx virtual buffer: damage tracking, -+//! styles, cursor shape, and graphics all reuse the terminal render -+//! pipeline unchanged. -+ -+use std::path::PathBuf; -+use std::time::Instant; -+ -+use rio_backend::crosswords::Crosswords; -+use rio_backend::event::EventListener; -+use rio_backend::performer::handler::Processor; -+use rio_window::event::{ElementState, KeyEvent as HostKeyEvent}; -+use rio_window::keyboard::{Key as HostKey, ModifiersState, NamedKey}; -+ -+use boo_core::surface::{Effect, Surface}; -+ -+/// One embedded editor. Lives on `Context.boo`; the context's -+/// `Crosswords` is the pane's display, this struct is its brain. -+pub struct BooPane { -+ pub surface: Surface, -+ scene: screen::Screen, -+ parser: Processor, -+ /// Set when editor state changed and the pane must re-present. -+ pub needs_paint: bool, -+} -+ -+/// What a key delivered to the pane asked of the host. -+#[derive(PartialEq)] -+pub enum BooInput { -+ Handled, -+ /// The editor requested quit: close this pane. -+ Quit, -+} -+ -+impl BooPane { -+ /// A pane rooted at `cwd`, opening on the fuzzy finder over the -+ /// workspace files — the same flow as `boo `. -+ pub fn new(cwd: PathBuf, cols: u16, rows: u16) -> Self { -+ let mut editor = boo_core::editor::Editor::new(cwd.clone()); -+ let files = boo_core::finder::collect_files(&cwd); -+ editor -+ .layers -+ .push(boo_core::layer::Layer::Finder(boo_core::finder::Finder::new( -+ files, -+ ))); -+ -+ let mut scene = screen::Screen::new(rows.max(2), cols.max(2)); -+ let _ = scene.set_cursor_style(5); // blinking bar -+ let _ = scene.cursor_visible(true); -+ let _ = scene.set_base(boo_core::ui::theme::FG, boo_core::ui::theme::BG); -+ -+ BooPane { -+ surface: Surface::new(editor), -+ scene, -+ parser: Processor::default(), -+ needs_paint: true, -+ } -+ } -+ -+ /// Push one host key event. Returns `None` when the event doesn't -+ /// map to an editor key (lone modifiers, media keys). -+ pub fn key(&mut self, key: &HostKeyEvent, mods: ModifiersState) -> Option { -+ let boo_key = convert_key(key, mods)?; -+ self.needs_paint = true; -+ if self.surface.key(boo_key).is_break() { -+ return Some(BooInput::Quit); -+ } -+ Some(BooInput::Handled) -+ } -+ -+ /// Apply pasted text as one edit. -+ pub fn paste(&mut self, text: String) { -+ self.surface.paste(text); -+ self.needs_paint = true; -+ } -+ -+ /// Editor-requested host side effects since the last drain. -+ pub fn drain_effects(&mut self) -> Vec { -+ self.surface.drain_effects() -+ } -+ -+ /// True while the kinetic glide needs ~60fps frames. -+ pub fn is_gliding(&self) -> bool { -+ self.surface.editor.kinetic.is_gliding() -+ } -+ -+ pub fn tick(&mut self, now: Instant) { -+ self.surface.tick(now); -+ self.needs_paint = true; -+ } -+ -+ /// Adopt the pane's current cell geometry. A no-op while unchanged. -+ pub fn resize(&mut self, cols: u16, rows: u16) { -+ let (cur_rows, cur_cols) = self.scene.get_size().unwrap_or((0, 0)); -+ if (cur_rows, cur_cols) != (rows, cols) && rows > 1 && cols > 1 { -+ self.scene.set_size(rows, cols); -+ self.needs_paint = true; -+ } -+ } -+ -+ /// Present one frame into the pane's terminal: pre-grid raw bytes, -+ /// the composed grid diff, post-grid raw bytes — the same order -+ /// boo's own tty presenter writes them. -+ pub fn present_into(&mut self, terminal: &mut Crosswords) { -+ self.needs_paint = false; -+ self.surface.prepare(); -+ if self.surface.render(&mut self.scene).is_err() { -+ return; -+ } -+ let mut bytes = self.scene.drain_raw(); -+ if let Ok(frame) = self.scene.compose() { -+ bytes.extend_from_slice(frame.as_bytes()); -+ } -+ bytes.extend(self.scene.drain_raw_after()); -+ self.parser.advance(terminal, &bytes); -+ } -+} -+ -+/// Decode a host key event into boo's kitty-shaped key. Shift is baked -+/// into `Key::Character` codepoints by the platform; functional keys -+/// carry the kitty PUA codes boo's collapse expects. -+fn convert_key(key: &HostKeyEvent, mods: ModifiersState) -> Option { -+ let code = match key.logical_key.as_ref() { -+ HostKey::Named(named) => named_code(&named)?, -+ HostKey::Character(s) => s.chars().next()? as u32, -+ _ => return None, -+ }; -+ -+ let mut modifiers = screen::Modifiers::empty(); -+ if mods.shift_key() { -+ modifiers |= screen::Modifiers::SHIFT; -+ } -+ if mods.alt_key() { -+ modifiers |= screen::Modifiers::ALT; -+ } -+ if mods.control_key() { -+ modifiers |= screen::Modifiers::CTRL; -+ } -+ if mods.super_key() { -+ modifiers |= screen::Modifiers::SUPER; -+ } -+ -+ let event_type = match (key.state, key.repeat) { -+ (ElementState::Pressed, false) => screen::KeyEventType::Press, -+ (ElementState::Pressed, true) => screen::KeyEventType::Repeat, -+ (ElementState::Released, _) => screen::KeyEventType::Release, -+ }; -+ -+ Some(screen::Key::from_event(screen::KeyEvent { -+ code, -+ modifiers, -+ event_type, -+ shifted_key: None, -+ base_key: None, -+ text: None, -+ })) -+} -+ -+/// kitty keyboard protocol PUA assignments (what `boo_key` in the C -+/// ABI takes too). -+fn named_code(named: &NamedKey) -> Option { -+ Some(match named { -+ NamedKey::Space => ' ' as u32, -+ NamedKey::Escape => 57344, -+ NamedKey::Enter => 57345, -+ NamedKey::Tab => 57346, -+ NamedKey::Backspace => 57347, -+ NamedKey::Insert => 57348, -+ NamedKey::Delete => 57349, -+ NamedKey::ArrowLeft => 57350, -+ NamedKey::ArrowRight => 57351, -+ NamedKey::ArrowUp => 57352, -+ NamedKey::ArrowDown => 57353, -+ NamedKey::PageUp => 57354, -+ NamedKey::PageDown => 57355, -+ NamedKey::Home => 57356, -+ NamedKey::End => 57357, -+ _ => return None, -+ }) -+} -diff --git a/frontends/rioterm/src/context/mod.rs b/frontends/rioterm/src/context/mod.rs -index 76129089e5..6205f51548 100644 ---- a/frontends/rioterm/src/context/mod.rs -+++ b/frontends/rioterm/src/context/mod.rs -@@ -62,13 +62,17 @@ pub struct Context { - /// Set when this pane is an rmx virtual buffer: it has no PTY of - /// its own, and its input is framed back to the owning pane. - pub rmx: Option, -+ /// Set when this pane hosts the boo editor: no PTY, input goes to -+ /// the embedded surface, frames arrive through the VT parser. -+ pub boo: Option, - _io_thread: Option, performer::State)>>, - } - - impl Drop for Context { - fn drop(&mut self) { -- // rmx virtual buffers own no PTY and no child process. -- if self.rmx.is_some() { -+ // rmx virtual buffers and boo panes own no PTY and no child -+ // process. -+ if self.rmx.is_some() || self.boo.is_some() { - return; - } - -@@ -203,6 +207,7 @@ pub fn create_dead_context( - title: ContextTitle::default(), - ime: Ime::new(), - rmx: None, -+ boo: None, - _io_thread: None, - } - } -@@ -244,6 +249,53 @@ pub fn create_virtual_context( - title: ContextTitle::default(), - ime: Ime::new(), - rmx: Some(link), -+ boo: None, -+ _io_thread: None, -+ } -+} -+ -+/// Create a pane hosting the boo editor: a grid, no PTY. Input goes to -+/// the embedded `boo_core` surface as structured key events; frames -+/// come back as composed escape text through the VT parser. -+pub fn create_boo_context( -+ event_proxy: T, -+ window_id: WindowId, -+ route_id: usize, -+ rich_text_id: usize, -+ dimension: ContextDimension, -+ working_dir: std::path::PathBuf, -+) -> Context { -+ let terminal = Crosswords::new( -+ dimension, -+ CursorShape::Block, -+ event_proxy, -+ window_id, -+ route_id, -+ 0, -+ ); -+ let terminal: Arc>> = Arc::new(FairMutex::new(terminal)); -+ let (sender, _receiver) = corcovado::channel::channel(); -+ let pane = crate::boo::BooPane::new( -+ working_dir, -+ dimension.columns as u16, -+ dimension.lines as u16, -+ ); -+ -+ Context { -+ route_id, -+ #[cfg(not(target_os = "windows"))] -+ main_fd: Arc::new(-1), -+ #[cfg(not(target_os = "windows"))] -+ shell_pid: 0, -+ messenger: Messenger::new(sender), -+ renderable_content: RenderableContent::new(Cursor::default()), -+ terminal, -+ rich_text_id, -+ dimension, -+ title: ContextTitle::default(), -+ ime: Ime::new(), -+ rmx: None, -+ boo: Some(pane), - _io_thread: None, - } - } -@@ -402,6 +454,7 @@ impl ContextManager { - title: ContextTitle::default(), - ime: Ime::new(), - rmx: None, -+ boo: None, - _io_thread: io_thread, - }) - } -@@ -1117,6 +1170,40 @@ impl ContextManager { - } - } - -+ /// Split right into a boo editor pane rooted at the focused -+ /// pane's working directory (same derivation as `split`). -+ pub fn split_boo(&mut self, rich_text_id: usize, sugarloaf: &mut Sugarloaf) { -+ let mut working_dir = self.config.working_dir.clone(); -+ if self.config.cwd { -+ #[cfg(not(target_os = "windows"))] -+ { -+ let current_context = self.current(); -+ if let Ok(path) = teletypewriter::foreground_process_path( -+ *current_context.main_fd, -+ current_context.shell_pid, -+ ) { -+ working_dir = Some(path.to_string_lossy().to_string()); -+ } -+ } -+ } -+ let cwd = working_dir -+ .map(std::path::PathBuf::from) -+ .or_else(|| std::env::current_dir().ok()) -+ .unwrap_or_else(|| std::path::PathBuf::from("/")); -+ -+ let route_id = next_route_id(); -+ let new_context = create_boo_context( -+ self.event_proxy.clone(), -+ self.window_id, -+ route_id, -+ rich_text_id, -+ self.current().dimension, -+ cwd, -+ ); -+ self.contexts[self.current_index].split_right(new_context, sugarloaf); -+ self.current_route = route_id; -+ } -+ - pub fn split_from_config( - &mut self, - rich_text_id: usize, -diff --git a/frontends/rioterm/src/main.rs b/frontends/rioterm/src/main.rs -index 5646c68f3b..3693a09359 100644 ---- a/frontends/rioterm/src/main.rs -+++ b/frontends/rioterm/src/main.rs -@@ -20,6 +20,7 @@ mod mouse; - mod panic; - mod platform; - mod renderer; -+mod boo; - mod rmx; - mod router; - mod scheduler; -diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs -index 0090635e73..56fef90455 100644 ---- a/frontends/rioterm/src/screen/mod.rs -+++ b/frontends/rioterm/src/screen/mod.rs -@@ -843,6 +843,21 @@ impl Screen<'_> { - let mode = self.get_mode(); - let mods = self.modifiers.state(); - -+ // boo pane: keys go to the embedded editor as structured -+ // events, never to a PTY. Releases feed it directly (they -+ // launch the kinetic glide); presses run the global bindings -+ // first so Cmd+W / splits / tabs keep working. -+ if self.context_manager.current().boo.is_some() { -+ if key.state == ElementState::Released { -+ self.boo_key_input(key, clipboard); -+ return; -+ } -+ if !self.process_key_bindings(key, &mode, mods, clipboard) { -+ self.boo_key_input(key, clipboard); -+ } -+ return; -+ } -+ - if key.state == ElementState::Released { - if !mode.contains(Mode::REPORT_EVENT_TYPES) - || mode.contains(Mode::VI) -@@ -1300,6 +1315,9 @@ impl Screen<'_> { - Act::SplitRight => { - self.split_right(); - } -+ Act::SplitBoo => { -+ self.split_boo(); -+ } - Act::SplitDown => { - self.split_down(); - } -@@ -1665,7 +1683,92 @@ impl Screen<'_> { - /// Send user input for the focused pane. A plain pane writes to - /// its own PTY; an rmx virtual buffer has none, so its input is - /// framed as `i;ev=in` on the owning pane's stream (spec §8). -+ /// One decoded key event for the focused boo pane. -+ fn boo_key_input( -+ &mut self, -+ key: &rio_window::event::KeyEvent, -+ clipboard: &mut Clipboard, -+ ) { -+ let mods = self.modifiers.state(); -+ let current = self.context_manager.current_mut(); -+ let route_id = current.route_id; -+ let Some(pane) = current.boo.as_mut() else { -+ return; -+ }; -+ let quit = pane.key(key, mods) == Some(crate::boo::BooInput::Quit); -+ let effects = pane.drain_effects(); -+ for effect in effects { -+ match effect { -+ boo_core::surface::Effect::ClipboardWrite(text) => { -+ clipboard.set(ClipboardType::Clipboard, text); -+ } -+ // The welcome image lives inside the pane's own grid -+ // here (Rio parses the kitty sequences), so its -+ // cleanup bytes arrive through the composed frame. -+ boo_core::surface::Effect::WelcomeDismissed => {} -+ } -+ } -+ if quit { -+ self.close_split_or_tab(clipboard); -+ return; -+ } -+ self.mark_route_dirty(route_id); -+ self.context_manager.request_render(); -+ } -+ -+ /// Advance boo panes before a frame: adopt resizes, tick the -+ /// kinetic glide, and feed re-rendered frames through the VT -+ /// parser. The pane's `Crosswords` then carries damage/styles/ -+ /// cursor exactly like a terminal-backed pane. -+ fn boo_pump(&mut self) { -+ let mut glide_live = false; -+ for (_, item) in self -+ .context_manager -+ .current_grid_mut() -+ .contexts_mut() -+ .iter_mut() -+ { -+ let ctx = &mut item.val; -+ let Some(pane) = ctx.boo.as_mut() else { -+ continue; -+ }; -+ pane.resize(ctx.dimension.columns as u16, ctx.dimension.lines as u16); -+ if pane.is_gliding() { -+ pane.tick(std::time::Instant::now()); -+ glide_live = true; -+ } -+ if pane.needs_paint { -+ let terminal = ctx.terminal.clone(); -+ let mut terminal = terminal.lock(); -+ pane.present_into(&mut terminal); -+ drop(terminal); -+ ctx.renderable_content.pending_update.set_dirty(); -+ } -+ } -+ if glide_live { -+ // ~60fps frames while the glide coasts, same cadence as -+ // boo's terminal host. -+ self.context_manager.schedule_render_on_route(15); -+ } -+ } -+ - pub fn send_input(&mut self, bytes: Vec) { -+ // boo pane: whatever reaches this point is paste-shaped text -+ // (keys were intercepted upstream, and the pane's grid never -+ // enables bracketed paste, so no markers arrive). -+ { -+ let current = self.context_manager.current_mut(); -+ let route_id = current.route_id; -+ if let Some(pane) = current.boo.as_mut() { -+ if let Ok(text) = String::from_utf8(bytes) { -+ pane.paste(text); -+ } -+ self.mark_route_dirty(route_id); -+ self.context_manager.request_render(); -+ return; -+ } -+ } -+ - let link = self.ctx().current().rmx.clone(); - let Some(link) = link else { - self.ctx_mut().current_mut().messenger.send_write(bytes); -@@ -2042,6 +2145,14 @@ impl Screen<'_> { - self.mark_dirty(); - } - -+ pub fn split_boo(&mut self) { -+ let rich_text_id = next_rich_text_id(); -+ self.context_manager -+ .split_boo(rich_text_id, &mut self.sugarloaf); -+ -+ self.mark_dirty(); -+ } -+ - pub fn split_down(&mut self) { - let rich_text_id = next_rich_text_id(); - self.context_manager -@@ -4293,6 +4404,8 @@ impl Screen<'_> { - } - } - -+ self.boo_pump(); -+ - let (window_update, any_panel_dirty) = self - .renderer - .run(&mut self.sugarloaf, &mut self.context_manager); diff --git a/rio-scale-sync-research.md b/rio-scale-sync-research.md deleted file mode 100644 index cf7876eff1..0000000000 --- a/rio-scale-sync-research.md +++ /dev/null @@ -1,156 +0,0 @@ -# Scale/grid desync after sleep and on hidden tabs — root-cause analysis - -Analysis only, no code changes. Sources: full trace of rio + rio-window + -sugarloaf (2026-08-02), with ghostty and zed read as reference architectures. -Two user-visible symptoms: (a) wake from sleep → font/grid/scale wrong; -(b) switching to a tab that wasn't visible → font/grid/scale wrong. - -## The architectural root - -Rio's scale pipeline has exactly **one** entry point and **one** mutator: -`WindowEvent::ScaleFactorChanged` → `Screen::set_scale`. The live window -scale is read once at window creation and never again. Every downstream copy -(sugarloaf context scale, RootStyle, Text, per-panel ContextDimension, -per-grid scaled_margin) is only written by that mutator. So the system's -correctness rests entirely on two assumptions: - -1. AppKit always delivers `windowDidChangeBackingProperties:` when anything - scale-relevant changes, and -2. one delivered event fully invalidates every scale-derived artifact. - -Both assumptions fail, in different ways, and each failure mode maps to one -of the two symptoms. - -## Symptom (a): sleep/wake — the event never arrives, and the layer decays - -Ranked by likely causal contribution: - -1. **Numeric de-dupe swallows wake "changes"** (rio-window - window_delegate.rs:905-909). The only macOS producer compares the new - `backingScaleFactor` against `previous_scale_factor` and returns if equal. - After sleep/wake AppKit can rebuild the backing store, reset layer state, - or briefly report a different screen and settle back — the *number* ends - up the same, so rio re-runs nothing. There is no second chance: - `previous_scale_factor` is updated before the event is queued, `resumed()` - is empty, no `NSApplicationDidChangeScreenParametersNotification` or - NSWorkspace wake observer exists (one is commented out), and - `windowDidChangeScreen:` deliberately touches only the display link. -2. **`CAMetalLayer.contentsScale` is set once at creation and never - re-applied** (sugarloaf metal context). `rescale()` stores the float; - `resize()` sets only `drawableSize`. If the wake sequence leaves the layer - with a different effective contentsScale than rio believes, nothing ever - re-asserts it — the compositor scales the texture and everything looks - subtly wrong-sized/blurry even though rio's own math is consistent. -3. **Un-occlusion never requests a frame by itself.** `Occluded(false)` only - flips `needs_render_after_occlusion`; that flag is consumed inside - PTY-driven render events. An idle shell after wake produces none, so the - first thing shown is whatever the swapchain held. Compounding it, the - macOS redraw path is display-link-mediated and the link refuses to start - while occluded and idle-stops itself. -4. **Drawable-acquisition failure right after wake silently eats a frame.** - The Metal path returns without discarding/resetting and without re-arming - a redraw — after per-panel damage was already consumed. That content is - lost until new damage arrives. (The wgpu path resets; Metal doesn't.) - This is almost certainly issue #1506's mechanism. -5. **The startup synthesized scale event is dead code** — initialized-equal - then guarded-equal — so a window created on a non-primary-scale monitor - during a weird wake state never gets a correcting event either. - -## Symptom (b): hidden tabs — dimensions update, pixels don't - -The intuitive theory ("background tabs miss the resize fan-out") is wrong: -`Screen::set_scale` and `resize_all_grids` iterate every tab, update every -panel's dimensions, reflow every terminal, and SIGWINCH every PTY. The -staleness is render-side — dimensions are right, the *sprites* are wrong: - -1. **Nothing converts "scale changed" into full terminal damage.** The - rescale path only sets a dirty flag; `Crosswords::resize` early-returns - without damage when cols/lines are unchanged; the per-panel GridRenderer - `resize` early-returns when cols/rows are unchanged and never sets - `needs_full_rebuild`. Damage then merges to Partial/Noop, so resident - glyph quads keep the old `size_bucket` (old font_px) while the uniforms - (cell_size, projection) already use the new metrics. That mismatch *is* - the "font/grid off" frame, and it can hit the active tab too whenever a - DPI change preserves cols×rows. -2. **The tab-switch safety net is broken by a key collision.** The renderer - detects "active pane changed" by comparing taffy `NodeId`s — but each tab - owns its own taffy tree, and identically-shaped trees produce identical - NodeIds. Switching tabs usually compares equal, so the force-full-damage - path never fires for the newly shown tab, which then presents GPU buffers - rasterized under the old scale. -3. **Damage events can't reach background tabs at all**: - `get_by_route_id` searches only the current tab, so - `TerminalDamaged`/`RenderRoute`/`UpdateGraphics` for a background pane - no-op. Whatever a hidden tab missed, it stays missed until its own PTY - speaks again. -4. **Tab switching performs zero reconciliation** — visibility toggling and - a dirty mark on the focused pane only. No dimension check, no scale - comparison, no grid-renderer ensure. -5. Secondary but real: **`ContextGrid.scale` is written once at creation and - never updated** (paddings, gaps, border hit-boxes stay at creation-time - DPI for every tab), and **a new tab inherits panel-sized dimensions and - double-subtracts the margin**, so it's born slightly short until the next - window resize. - -## How ghostty and zed avoid this class of bug - -Two opposite but internally consistent strategies: - -- **Ghostty: per-surface observers + idempotent push.** Every surface (each - tab is a real NSWindow) owns its own `viewDidChangeBackingProperties` and - `didChangeScreen` observers and its own stored scale. Screen change - *deliberately re-fires* the backing-properties path "just in case the - scale differs" (their issue 2731 — the exact de-dupe trap rio has). A - scale change re-derives the framebuffer size itself from the cached - logical size instead of waiting for a layout pass. Becoming visible - triggers an immediate unconditional updateFrame+drawFrame. All entry - points early-return on no-change, which makes redundant re-firing free — - that's the property that lets them re-sync aggressively. -- **Zed: no per-tab pixel state to go stale.** One `Window.scale_factor`, - re-pulled wholesale from the platform on resize, move, screen change, - backing change, *and window activation* (an accidental wake-recovery - path rio lacks). Hidden tabs are simply not laid out; the terminal's - alacritty grid is resized from prepaint on the first frame after it - becomes visible, with a diff in `set_size` making reconciliation free. - Glyph caches key on scale, so a DPI change produces new atlas entries - rather than stale ones. - -Rio sits in the worst quadrant today: ghostty-style long-lived per-context -caches, but zed-style single-point event delivery — cached state everywhere, -with only one fragile trigger allowed to invalidate it. - -## What this implies (direction, not code) - -1. **Stop trusting single delivery.** Re-read the live window scale and - compare against the stored one at cheap, natural checkpoints: window - focus/activation, occlusion clearing, tab switch, and screen-change. If - different, run the existing rescale path. This one habit (zed's - activation re-pull, ghostty's re-fire-on-screen-change) collapses most of - symptom (a) regardless of which AppKit notification got coalesced. -2. **Make staleness impossible to present, not just unlikely.** The renderer - should treat scaled font metrics as part of the damage key: if a panel's - `scaled_font_size`/cell metrics differ from what its GPU buffers were - built with, that's full damage by definition — independent of cols/rows - comparisons or event ordering. Equivalently: key the "active changed" - check on route_id rather than cross-tree NodeIds, and let rescale mark - full terminal damage explicitly. -3. **Re-assert `contentsScale` whenever scale is applied** (and on - screen-change/wake checkpoints), instead of assuming the layer still - holds its creation-time value. -4. **Un-occlusion and drawable-failure must both re-arm a redraw** on their - own, without waiting for PTY traffic; the Metal drawable-failure path - must stop consuming damage it didn't present (#1506). -5. **Fix the delivery layer while at it**: register the screen-parameters - observer, update `previous_scale_factor` only after successful dispatch, - and un-dead the startup synthesized event. -6. Longer term, pick a side of the ghostty/zed dichotomy for per-tab state: - either every scale-derived cache gets an owner that re-validates it on - visibility (ghostty), or per-tab pixel state shrinks until the first - rendered frame after a switch derives everything live (zed). The current - hybrid is what makes these bugs recur (see the eight prior scale-fix - commits in the log). - -Related open issues this likely closes or explains: #1506 (wrong rendering -after sleep), the quake-window mixed-DPI residue (positioned while hidden; -backing-properties may not fire for ordered-out windows), and #1680's HiDPI -blurriness reports where users toggled displays. diff --git a/rio-ucs-detect-0.5.3.yaml b/rio-ucs-detect-0.5.3.yaml deleted file mode 100644 index a678cc3dba..0000000000 --- a/rio-ucs-detect-0.5.3.yaml +++ /dev/null @@ -1,11247 +0,0 @@ -ambiguous_width: 1 -datetime: 2026-07-31 21:06:09 UTC -environment: - COLORTERM: truecolor - TERM: rio - TERM_PROGRAM: rio - TERM_PROGRAM_VERSION: 0.5.3 -height: 60 -preferred_encoding: UTF-8 -python_version: 3.14.6 -seconds_elapsed: 523.191614084004 -session_arguments: - all: true - limit_category_time: 0 - limit_codepoints: 0 - limit_errors: 0 - limit_graphemes: 0 - limit_graphemes_pct: 0 - stream: stderr - timeout_cps: 5.0 -software_name: Rio -software_version: 0.5.3 -system: Darwin -terminal_results: - background_color_hex: '#0f0f0d0d0e0e' - background_color_rgb: - - 3855 - - 3341 - - 3598 - cell_height: 26 - cell_width: 13 - color_scheme: false - colored_underlines: false - decrqcra: false - decrqss: false - decrqss_settings: false - decrqss_truecolor_probe: false - device_attributes: - extensions: - - 4 - - 6 - - 22 - - 52 - raw: "\e[?62;4;6;22;52c" - service_class: 62 - foreground_color_hex: '#ffffffffffff' - foreground_color_rgb: - - 65535 - - 65535 - - 65535 - height: 60 - iterm2_features: - features: {} - supported: false - kitty_clipboard_protocol: false - kitty_graphics: true - kitty_keyboard: - disambiguate: false - report_all_keys: false - report_alternates: false - report_events: false - report_text: false - kitty_notifications: false - kitty_pointer_shapes: false - kitty_query: false - modes: - 1004: - changeable: true - enabled: false - mode_description: Send FocusIn/FocusOut events - mode_name: FOCUS_IN_OUT_EVENTS - supported: true - value: 2 - value_description: RESET - 1006: - changeable: true - enabled: false - mode_description: Enable SGR Mouse Mode - mode_name: MOUSE_EXTENDED_SGR - supported: true - value: 2 - value_description: RESET - 2004: - changeable: true - enabled: false - mode_description: Set bracketed paste mode - mode_name: BRACKETED_PASTE - supported: true - value: 2 - value_description: RESET - 2026: - changeable: true - enabled: false - mode_description: Synchronized Output - mode_name: SYNCHRONIZED_OUTPUT - supported: true - value: 2 - value_description: RESET - 2027: - changeable: false - enabled: false - mode_description: Grapheme Clustering - mode_name: GRAPHEME_CLUSTERING - supported: false - value: 0 - value_description: NOT_RECOGNIZED - 2031: - changeable: false - enabled: false - mode_description: Color palette updates - mode_name: COLOR_PALETTE_UPDATES - supported: false - value: 0 - value_description: NOT_RECOGNIZED - 2048: - changeable: false - enabled: false - mode_description: In-Band Window Resize Notifications - mode_name: IN_BAND_WINDOW_RESIZE - supported: false - value: 0 - value_description: NOT_RECOGNIZED - 5522: - changeable: false - enabled: false - mode_description: Bracketed Paste MIME - mode_name: BRACKETED_PASTE_MIME - supported: false - value: 0 - value_description: NOT_RECOGNIZED - number_of_colors: 16777216 - osc52_clipboard: true - osc52_detection: - da1_extension_52: true - xtgettcap_ms: true - pixels_height: 1560 - pixels_width: 2600 - screen_ratio: '16:10' - screen_ratio_name: WSXGA - sixel: true - software_method: XTVERSION - software_name: Rio - software_version: 0.5.3 - styled_underlines: true - tab_stop_width: 8 - text_sizing: - scale: false - width: false - ttype: rio - width: 200 - xtgettcap: - capabilities: - AX: '' - BD: "\e[?2004l" - BE: "\e[?2004h" - Cr: "\e]112\a" - Cs: "\e]12;%p1%s\a" - E3: "\e[3J" - Ms: "\e]52;%p1%s;%p2%s\a" - PE: "\e[201~" - PS: "\e[200~" - RGB: 8/8/8 - Se: "\e[0 q" - Smulx: "\e[4:%p1%dm" - Ss: "\e[%p1%d q" - Sync: "\eP=%p1%ds\e\\" - TN: rio - XF: '' - XT: '' - acsc: '``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~' - am: '' - bce: '' - bel: "\a" - blink: "\e[5m" - bold: "\e[1m" - ccc: '' - civis: "\e[?25l" - clear: "\e[H\e[2J" - cnorm: "\e[?12l\e[?25h" - colors: '256' - cols: '80' - cr: "\r" - csr: "\e[%i%p1%d;%p2%dr" - cub: "\e[%p1%dD" - cub1: "\b" - cud: "\e[%p1%dB" - cud1: ' - - ' - cuf: "\e[%p1%dC" - cuf1: "\e[C" - cup: "\e[%i%p1%d;%p2%dH" - cuu: "\e[%p1%dA" - cuu1: "\e[A" - cvvis: "\e[?12;25h" - dch: "\e[%p1%dP" - dch1: "\e[P" - dim: "\e[2m" - dl: "\e[%p1%dM" - dl1: "\e[M" - dsl: "\e]2;\a" - ech: "\e[%p1%dX" - ed: "\e[J" - el: "\e[K" - el1: "\e[1K" - flash: "\e[?5h$<100/>\e[?5l" - fsl: "\a" - home: "\e[H" - hpa: "\e[%i%p1%dG" - hs: '' - ht: "\t" - hts: "\eH" - ich: "\e[%p1%d@" - il: "\e[%p1%dL" - il1: "\e[L" - ind: ' - - ' - indn: "\e[%p1%dS" - initc: "\e]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\e\ - \\" - invis: "\e[8m" - is2: "\e[!p\e[?3;4l\e[4l\e>" - it: '8' - kDC: "\e[3;2~" - kDC3: "\e[3;3~" - kDC4: "\e[3;4~" - kDC5: "\e[3;5~" - kDC6: "\e[3;6~" - kDC7: "\e[3;7~" - kDN: "\e[1;2B" - kDN3: "\e[1;3B" - kDN4: "\e[1;4B" - kDN5: "\e[1;5B" - kDN6: "\e[1;6B" - kDN7: "\e[1;7B" - kEND: "\e[1;2F" - kEND3: "\e[1;3F" - kEND4: "\e[1;4F" - kEND5: "\e[1;5F" - kEND6: "\e[1;6F" - kEND7: "\e[1;7F" - kHOM: "\e[1;2H" - kHOM3: "\e[1;3H" - kHOM4: "\e[1;4H" - kHOM5: "\e[1;5H" - kHOM6: "\e[1;6H" - kHOM7: "\e[1;7H" - kIC: "\e[2;2~" - kIC3: "\e[2;3~" - kIC4: "\e[2;4~" - kIC5: "\e[2;5~" - kIC6: "\e[2;6~" - kIC7: "\e[2;7~" - kLFT: "\e[1;2D" - kLFT3: "\e[1;3D" - kLFT4: "\e[1;4D" - kLFT5: "\e[1;5D" - kLFT6: "\e[1;6D" - kLFT7: "\e[1;7D" - kNXT: "\e[6;2~" - kNXT3: "\e[6;3~" - kNXT4: "\e[6;4~" - kNXT5: "\e[6;5~" - kNXT6: "\e[6;6~" - kNXT7: "\e[6;7~" - kPRV: "\e[5;2~" - kPRV3: "\e[5;3~" - kPRV4: "\e[5;4~" - kPRV5: "\e[5;5~" - kPRV6: "\e[5;6~" - kPRV7: "\e[5;7~" - kRIT: "\e[1;2C" - kRIT3: "\e[1;3C" - kRIT4: "\e[1;4C" - kRIT5: "\e[1;5C" - kRIT6: "\e[1;6C" - kRIT7: "\e[1;7C" - kUP: "\e[1;2A" - kUP3: "\e[1;3A" - kUP4: "\e[1;4A" - kUP5: "\e[1;5A" - kUP6: "\e[1;6A" - kUP7: "\e[1;7A" - kb2: "\eOE" - kbs: "\x7F" - kcbt: "\e[Z" - kcub1: "\eOD" - kcud1: "\eOB" - kcuf1: "\eOC" - kcuu1: "\eOA" - kdch1: "\e[3~" - kend: "\eOF" - kent: "\eOM" - kf1: "\eOP" - kf10: "\e[21~" - kf11: "\e[23~" - kf12: "\e[24~" - kf2: "\eOQ" - kf3: "\eOR" - kf4: "\eOS" - kf5: "\e[15~" - kf6: "\e[17~" - kf7: "\e[18~" - kf8: "\e[19~" - kf9: "\e[20~" - khome: "\eOH" - kich1: "\e[2~" - kind: "\e[1;2B" - km: '' - kmous: "\e[M" - knp: "\e[6~" - kpp: "\e[5~" - kri: "\e[1;2A" - kxIN: "\e[I" - kxOUT: "\e[O" - lines: '24' - mc5i: '' - mir: '' - msgr: '' - npc: '' - oc: "\e]104\a" - op: "\e[39;49m" - pairs: '32767' - rc: "\e8" - rep: "%p1%c\e[%p2%{1}%-%db" - rev: "\e[7m" - ri: "\eM" - rin: "\e[%p1%dT" - ritm: "\e[23m" - rmacs: "\e(B" - rmam: "\e[?7l" - rmcup: "\e[?1049l\e[23;0;0t" - rmir: "\e[4l" - rmkx: "\e[?1l\e>" - rmm: "\e[?1034l" - rmso: "\e[27m" - rmul: "\e[24m" - rs1: "\ec\e]104\a" - rs2: "\e[!p\e[?3;4l\e[4l\e>" - sc: "\e7" - setab: "\e[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" - setaf: "\e[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" - sgr: "%?%p9%t\e(0%e\e(B%;\e[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m" - sgr0: "\e(B\e[m" - sitm: "\e[3m" - smacs: "\e(0" - smam: "\e[?7h" - smcup: "\e[?1049h\e[22;0;0t" - smir: "\e[4h" - smkx: "\e[?1h\e=" - smso: "\e[7m" - smul: "\e[4m" - tbc: "\e[3g" - tsl: "\e]2;" - u6: "\e[%i%d;%dR" - u7: "\e[6n" - u8: "\e[?%[;0123456789]c" - u9: "\e[c" - vpa: "\e[%i%p1%dd" - xenl: '' - supported: true - xtgettcap-bad-screenleak: false - xtversion-bad-screenleak: false - xtversion_raw: Rio 0.5.3 -test_results: - emoji_vs15_results: - 9.0.0: - failed_codepoints: - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u23eb\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u23ec\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u23f0\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u26ce\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2705\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u270a\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u270b\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2728\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u274c\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u274e\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2754\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2755\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2795\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2796\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u2797\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u27b0\ufe0e - - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchar: \u27bf\ufe0e - n_errors: 17 - n_total: 158 - pct_success: 89.24050632911393 - emoji_vs16_results: - 9.0.0: - failed_codepoints: [] - n_errors: 0 - n_total: 426 - pct_success: 100.0 - emoji_zwj_results: - '17.0': - failed_codepoints: - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\u2764\ufe0f\u200d\U0001f468 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f466\u200d\U0001f466 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f467 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f467\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f467\u200d\U0001f467 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f468\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f468\u200d\U0001f466\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f468\u200d\U0001f467 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f468\u200d\U0001f467\u200d\U0001f467 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f469\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f469\u200d\U0001f467 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467 - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f430\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f430\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f430\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f430\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001faef\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001faef\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001faef\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001faef\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f430\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f430\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f430\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001faef\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001faef\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001faef\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001faef\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f430\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f430\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f430\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f430\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001faef\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001faef\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001faef\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001faef\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f430\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f430\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f430\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001faef\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001faef\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001faef\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001faef\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f430\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f430\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f430\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f430\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001faef\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001faef\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001faef\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001faef\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2764\ufe0f\u200d\U0001f468 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2764\ufe0f\u200d\U0001f469 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f466\u200d\U0001f466 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f467 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f467\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f467\u200d\U0001f467 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f469\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f469\u200d\U0001f466\u200d\U0001f466 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f469\u200d\U0001f467 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f466 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f469\u200d\U0001f467\u200d\U0001f467 - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f430\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f430\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f430\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f430\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f91d\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001faef\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001faef\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001faef\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001faef\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fc - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f430\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f430\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f91d\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001faef\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001faef\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001faef\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001faef\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f430\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f430\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f430\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f91d\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001faef\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001faef\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001faef\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001faef\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fe - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f430\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f430\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f430\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f91d\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001faef\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001faef\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001faef\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001faef\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f430\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f430\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f430\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f430\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f468\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f91d\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001faef\u200d\U0001f469\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001faef\u200d\U0001f469\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001faef\u200d\U0001f469\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001faef\u200d\U0001f469\U0001f3fe - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f91d\u200d\U0001f9d1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9d1\u200d\U0001f9d2 - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9d1\u200d\U0001f9d2\u200d\U0001f9d2 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9d2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9d2\u200d\U0001f9d2 - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 12 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3fe - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fb\u200d\U0001faf2\U0001f3ff - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fb - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3fe - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fc\u200d\U0001faf2\U0001f3ff - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fb - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3fe - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fd\u200d\U0001faf2\U0001f3ff - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fb - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3fd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3fe\u200d\U0001faf2\U0001f3ff - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fb - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001faf1\U0001f3ff\u200d\U0001faf2\U0001f3fe - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fb\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fe\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3ff\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\u2695\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\u2696\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\u2708\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f33e - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f373 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f37c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f393 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f3a4 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f3a8 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f3eb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f3ed - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f4bb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f4bc - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f527 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f52c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f680 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f692 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9af - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2695\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2696\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\u2708\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f33e - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f373 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f37c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f393 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f3a4 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f3a8 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f3eb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f3ed - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f4bb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f4bc - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f527 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f52c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f680 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f692 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9af - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fb\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fe\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3ff\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fb\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fe\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3ff\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\u2695\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\u2696\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\u2708\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f33e - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f373 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f37c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f384 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f393 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f3a4 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f3a8 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f3eb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f3ed - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f4bb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f4bc - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f527 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f52c - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f680 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f692 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9af - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f384 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f384 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f384 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f384 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9bd - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\u2695\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\u2696\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\u2708\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f33e - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f373 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f37c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f384 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f393 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f3a4 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f3a8 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f3eb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f3ed - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f4bb - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f4bc - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f527 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f52c - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f680 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f692 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9af - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9af\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9bc - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9bc\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9bd - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9bd\u200d\u27a1\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \u26f9\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \u26f9\ufe0f\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \u26f9\ufe0f\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fb\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fb\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fc\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fc\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fd\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fd\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fe\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3fe\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3ff\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f3c3\U0001f3ff\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3c4\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f3ca\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\ufe0f\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3cb\ufe0f\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\ufe0f\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3cc\ufe0f\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f46e\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f46e\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46e\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f46f\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f46f\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f46f\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f470\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f470\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f470\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f471\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f471\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f471\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f473\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f473\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f473\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f477\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f477\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f477\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f481\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f481\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f481\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f482\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f482\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f482\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f486\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f486\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f486\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f487\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f487\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f487\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchar: \U0001f575\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f575\ufe0f\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f575\ufe0f\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f645\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f645\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f645\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f646\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f646\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f646\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f647\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f647\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f647\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64b\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64b\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64b\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64d\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64d\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64d\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64e\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f64e\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f64e\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6a3\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b4\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b5\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fb\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fb\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fc\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fc\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fd\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fd\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fe\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3fe\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3ff\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f6b6\U0001f3ff\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f926\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f926\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f926\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f935\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f935\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f935\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f937\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f937\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f937\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f938\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f938\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f938\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f939\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f939\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f939\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93c\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93c\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93c\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93d\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93d\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93d\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93e\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f93e\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f93e\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b8\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9b9\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cd\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fb\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fb\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fc\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fc\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fd\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fd\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fe\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3fe\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3ff\u200d\u2640\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 8 - measured_by_wcwidth: 2 - wchar: \U0001f9ce\U0001f3ff\u200d\u2642\ufe0f\u200d\u27a1\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9cf\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d4\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d6\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d7\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d8\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d9\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9da\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9da\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9da\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9db\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9db\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9db\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dc\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fb\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fb\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fc\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fc\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fd\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fd\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fe\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3fe\u200d\u2642\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3ff\u200d\u2640\ufe0f - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9dd\U0001f3ff\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9de\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9de\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9df\u200d\u2640\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9df\u200d\u2642\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9b0 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9b1 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9b2 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f468\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fb\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fc\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fd\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3fe\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f468\U0001f3ff\u200d\U0001f9b3 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9b0 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9b1 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9b2 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f469\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fb\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fc\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fd\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3fe\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f469\U0001f3ff\u200d\U0001f9b3 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9b0 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9b1 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9b2 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f9b3 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9b0 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9b1 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9b2 - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f9b3 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \u26d3\ufe0f\u200d\U0001f4a5 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \u2764\ufe0f\u200d\U0001f525 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \u2764\ufe0f\u200d\U0001fa79 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f344\u200d\U0001f7eb - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f34b\u200d\U0001f7e9 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3f3\ufe0f\u200d\u26a7\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3f3\ufe0f\u200d\U0001f308 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f3f4\u200d\u2620\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f408\u200d\u2b1b - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f415\u200d\U0001f9ba - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f426\u200d\u2b1b - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f426\u200d\U0001f525 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f43b\u200d\u2744\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f441\ufe0f\u200d\U0001f5e8\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f62e\u200d\U0001f4a8 - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f635\u200d\U0001f4ab - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f636\u200d\U0001f32b\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f642\u200d\u2194\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f642\u200d\u2195\ufe0f - - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\u200d\U0001fa70 - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f430\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f430\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001f430\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001fa70 - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001faef\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001faef\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001faef\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fb\u200d\U0001faef\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001f430\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001fa70 - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001faef\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001faef\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fc\u200d\U0001faef\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f430\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001f430\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001fa70 - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001faef\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001faef\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001faef\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fd\u200d\U0001faef\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f430\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001f430\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001fa70 - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001faef\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001faef\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3fe\u200d\U0001faef\u200d\U0001f9d1\U0001f3ff - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f430\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f430\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f430\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 10 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001f430\u200d\U0001f9d1\U0001f3fe - - measured_by_terminal: 6 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001fa70 - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001faef\u200d\U0001f9d1\U0001f3fb - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001faef\u200d\U0001f9d1\U0001f3fc - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001faef\u200d\U0001f9d1\U0001f3fd - - measured_by_terminal: 9 - measured_by_wcwidth: 2 - wchar: \U0001f9d1\U0001f3ff\u200d\U0001faef\u200d\U0001f9d1\U0001f3fe - n_errors: 1445 - n_total: 1445 - pct_success: 0.0 - language_results: - (Jinan): - failed: [] - n_errors: 0 - n_total: 501 - pct_success: 100.0 - (Yeonbyeon): - failed: [] - n_errors: 0 - n_total: 307 - pct_success: 100.0 - Aja: - failed: [] - n_errors: 0 - n_total: 79 - pct_success: 100.0 - Amarakaeri: - failed: [] - n_errors: 0 - n_total: 64 - pct_success: 100.0 - Arabic, Standard: - failed: [] - n_errors: 0 - n_total: 60 - pct_success: 100.0 - Assyrian Neo-Aramaic: - failed: [] - n_errors: 0 - n_total: 42 - pct_success: 100.0 - Baatonum: - failed: [] - n_errors: 0 - n_total: 59 - pct_success: 100.0 - Bamun: - failed: [] - n_errors: 0 - n_total: 75 - pct_success: 100.0 - Belanda Viri: - failed: [] - n_errors: 0 - n_total: 85 - pct_success: 100.0 - Bengali: - failed: - - grapheme_id: Bengali-2-04 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09bf\u0982 - - grapheme_id: Bengali-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09a4\u09bf - - grapheme_id: Bengali-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b0\u09c0 - - grapheme_id: Bengali-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b7\u09be - - grapheme_id: Bengali-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b7\u09bf - - grapheme_id: Bengali-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b7\u09c7 - - grapheme_id: Bengali-2-11 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b7\u09cd\u09af - - grapheme_id: Bengali-2-12 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0995\u09cd\u09b7\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-16 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0996\u09cd\u09af\u09be - - grapheme_id: Bengali-2-1c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0997\u09cd\u09b0\u09be - - grapheme_id: Bengali-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0997\u09cd\u09b0\u09c7 - - grapheme_id: Bengali-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0998\u09c7\u09be - - grapheme_id: Bengali-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0999\u09cd\u0996\u09be - - grapheme_id: Bengali-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0999\u09cd\u0997\u09c0 - - grapheme_id: Bengali-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0999\u09cd\u0997\u09c7 - - grapheme_id: Bengali-2-2e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099a\u09cd\u099b\u09be - - grapheme_id: Bengali-2-2f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099a\u09cd\u099b\u09be\u200c - - grapheme_id: Bengali-2-31 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099a\u09cd\u099b\u09c7 - - grapheme_id: Bengali-2-3b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099c\u09cd\u099e\u09be - - grapheme_id: Bengali-2-3e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099e\u09cd\u099a\u09bf - - grapheme_id: Bengali-2-3f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099e\u09cd\u099b\u09bf - - grapheme_id: Bengali-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u099f\u09cd\u09b0\u09c7 - - grapheme_id: Bengali-2-49 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a3\u09cd\u09a1\u09c7 - - grapheme_id: Bengali-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09a4\u09be - - grapheme_id: Bengali-2-56 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09a4\u09bf - - grapheme_id: Bengali-2-59 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09ac\u09c7 - - grapheme_id: Bengali-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09af\u09be - - grapheme_id: Bengali-2-5d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09af\u09bf - - grapheme_id: Bengali-2-5e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-60 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09b0\u09be - - grapheme_id: Bengali-2-61 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u09b0\u09c7 - - grapheme_id: Bengali-2-63 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u200d\u09aa\u09c0 - - grapheme_id: Bengali-2-64 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a4\u09cd\u200d\u09b8\u09be - - grapheme_id: Bengali-2-6f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09a6\u09bf - - grapheme_id: Bengali-2-70 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09a6\u09c7 - - grapheme_id: Bengali-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09a7\u09be - - grapheme_id: Bengali-2-73 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09a7\u09bf - - grapheme_id: Bengali-2-74 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09a7\u09c7 - - grapheme_id: Bengali-2-75 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09ac\u09be - - grapheme_id: Bengali-2-79 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a6\u09cd\u09b0\u09cb - - grapheme_id: Bengali-2-7f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a7\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-89 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a4\u09be - - grapheme_id: Bengali-2-8a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a4\u09bf - - grapheme_id: Bengali-2-8b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a4\u09c7 - - grapheme_id: Bengali-2-8c - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a4\u09cd\u09b0\u09bf - - grapheme_id: Bengali-2-8d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a4\u09cd\u09b0\u09c7 - - grapheme_id: Bengali-2-8e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a5\u09c0 - - grapheme_id: Bengali-2-90 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09a7\u09be - - grapheme_id: Bengali-2-94 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09ae\u09be - - grapheme_id: Bengali-2-97 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09af\u09be - - grapheme_id: Bengali-2-98 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09a8\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-a0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09aa\u09cd\u09a4\u09be - - grapheme_id: Bengali-2-a2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09aa\u09cd\u09b0\u09be - - grapheme_id: Bengali-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09ac\u09cd\u09a7\u09bf - - grapheme_id: Bengali-2-af - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09ac\u09cd\u09af\u09be - - grapheme_id: Bengali-2-b7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09ad\u09cd\u09b0\u09be - - grapheme_id: Bengali-2-be - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09ae\u09cd\u09aa\u09be - - grapheme_id: Bengali-2-c2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09ae\u09cd\u09ae\u09be - - grapheme_id: Bengali-2-cd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09be\u0982 - - grapheme_id: Bengali-2-d4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u099c\u09be - - grapheme_id: Bengali-2-d8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09a5\u09c7 - - grapheme_id: Bengali-2-d9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09a6\u09cb - - grapheme_id: Bengali-2-db - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09a7\u09be - - grapheme_id: Bengali-2-dc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09a8\u09c0 - - grapheme_id: Bengali-2-de - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09ac\u09be - - grapheme_id: Bengali-2-df - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09ac\u09bf - - grapheme_id: Bengali-2-e0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09ac\u09cb - - grapheme_id: Bengali-2-e1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09ac\u09cd\u09af - - grapheme_id: Bengali-2-e4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09ae\u09c7 - - grapheme_id: Bengali-2-e6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b0\u09cd\u09af\u09be - - grapheme_id: Bengali-2-ee - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b2\u09cd\u09af\u09be - - grapheme_id: Bengali-2-ef - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b2\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-f0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b2\u09cd\u09b2\u09c7 - - grapheme_id: Bengali-2-f9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b6\u09cd\u099a\u09bf - - grapheme_id: Bengali-2-fa - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b6\u09cd\u09ac\u09be - - grapheme_id: Bengali-2-fb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b6\u09cd\u09ac\u09c7 - - grapheme_id: Bengali-2-fd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b6\u09cd\u09af\u09c7 - - grapheme_id: Bengali-2-ff - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b6\u09cd\u09b0\u09be - - grapheme_id: Bengali-2-105 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u099f\u09be - - grapheme_id: Bengali-2-106 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u099f\u09bf - - grapheme_id: Bengali-2-107 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u099f\u09cd\u09b0 - - grapheme_id: Bengali-2-108 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u099f\u09cd\u09b0\u09c0 - - grapheme_id: Bengali-2-109 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u099f\u09cd\u09b0\u09c7 - - grapheme_id: Bengali-2-10a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u09a0\u09be - - grapheme_id: Bengali-2-10b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u09a0\u09bf - - grapheme_id: Bengali-2-10c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b7\u09cd\u09a0\u09c0 - - grapheme_id: Bengali-2-111 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09be\u0982 - - grapheme_id: Bengali-2-119 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09a4\u09bf - - grapheme_id: Bengali-2-11a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09a4\u09bf\u200c - - grapheme_id: Bengali-2-11b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09a4\u09cd\u09b0 - - grapheme_id: Bengali-2-11d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09a5\u09be - - grapheme_id: Bengali-2-11e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09a5\u09cd\u09af - - grapheme_id: Bengali-2-120 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09ac\u09be - - grapheme_id: Bengali-2-121 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09ac\u09c0 - - grapheme_id: Bengali-2-123 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09af\u09be - - grapheme_id: Bengali-2-124 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u09b8\u09cd\u09af\u09c7 - n_errors: 96 - n_total: 385 - pct_success: 75.06493506493507 - Bhojpuri: - failed: - - grapheme_id: Bhojpuri-2-06 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0918\u094d\u0918\u093f\u094d - - grapheme_id: Bhojpuri-2-08 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - grapheme_id: Bhojpuri-2-0a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - grapheme_id: Bhojpuri-2-1b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0918\u094d\u0915\u093e - - grapheme_id: Bhojpuri-2-1e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0918\u094d\u0930\u094d\u0935\u094d\u092f - - grapheme_id: Bhojpuri-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0918\u094d\u0932\u093f - - grapheme_id: Bhojpuri-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0918\u094d\u0936\u094d\u092f - - grapheme_id: Bhojpuri-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0918\u094d\u0938\u094d\u091f - - grapheme_id: Bhojpuri-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - grapheme_id: Bhojpuri-2-26 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - grapheme_id: Bhojpuri-2-2d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - grapheme_id: Bhojpuri-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - grapheme_id: Bhojpuri-2-3f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - grapheme_id: Bhojpuri-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e - - grapheme_id: Bhojpuri-2-42 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e\u093e - - grapheme_id: Bhojpuri-2-43 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093f - - grapheme_id: Bhojpuri-2-4a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - grapheme_id: Bhojpuri-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - grapheme_id: Bhojpuri-2-59 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - grapheme_id: Bhojpuri-2-66 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - grapheme_id: Bhojpuri-2-68 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092f\u093e - - grapheme_id: Bhojpuri-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u093f - - grapheme_id: Bhojpuri-2-73 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u0940 - - grapheme_id: Bhojpuri-2-77 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f - - grapheme_id: Bhojpuri-2-78 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - grapheme_id: Bhojpuri-2-79 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u094b - - grapheme_id: Bhojpuri-2-7a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093e - - grapheme_id: Bhojpuri-2-7f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - grapheme_id: Bhojpuri-2-81 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0930\u093e - - grapheme_id: Bhojpuri-2-8b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - grapheme_id: Bhojpuri-2-92 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - grapheme_id: Bhojpuri-2-9a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - grapheme_id: Bhojpuri-2-9f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - grapheme_id: Bhojpuri-2-a0 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0940 - - grapheme_id: Bhojpuri-2-a9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u0940 - - grapheme_id: Bhojpuri-2-ab - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - grapheme_id: Bhojpuri-2-ac - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - grapheme_id: Bhojpuri-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u0940 - - grapheme_id: Bhojpuri-2-ae - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u094d\u092f - - grapheme_id: Bhojpuri-2-b1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - grapheme_id: Bhojpuri-2-b2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0938\u093e - n_errors: 41 - n_total: 313 - pct_success: 86.90095846645367 - Bora: - failed: [] - n_errors: 0 - n_total: 59 - pct_success: 100.0 - Burmese: - failed: - - grapheme_id: Burmese-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1000\u103b\u1031 - - grapheme_id: Burmese-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1000\u103c\u1031 - - grapheme_id: Burmese-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1000\u103c\u103d\u1031 - - grapheme_id: Burmese-2-16 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1001\u103b\u1031 - - grapheme_id: Burmese-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1001\u103c\u1031 - - grapheme_id: Burmese-2-33 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1015\u103b\u1031 - - grapheme_id: Burmese-2-37 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1015\u103c\u1031 - - grapheme_id: Burmese-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1019\u103c\u1031 - - grapheme_id: Burmese-2-52 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u101c\u103b\u1031 - - grapheme_id: Burmese-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u101c\u103b\u103e\u1031 - n_errors: 10 - n_total: 268 - pct_success: 96.26865671641791 - Catalan (2): - failed: [] - n_errors: 0 - n_total: 68 - pct_success: 100.0 - Chakma: - failed: - - grapheme_id: Chakma-2-07 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011107\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011108\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-15 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001110c\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001110e\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011111\U00011133\U00011122\U0001112c - - grapheme_id: Chakma-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011116\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011116\U00011133\U00011122\U0001112c - - grapheme_id: Chakma-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011118\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-2f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001111a\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001111d\U00011133\U00011122\U0001112c - - grapheme_id: Chakma-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001111f\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-46 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011121\U00011133\U00011120\U0001112c - - grapheme_id: Chakma-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011123\U00011133\U00011126\U0001112c - n_errors: 13 - n_total: 267 - pct_success: 95.13108614232209 - Chickasaw: - failed: [] - n_errors: 0 - n_total: 61 - pct_success: 100.0 - Chinantec, Chiltepec: - failed: [] - n_errors: 0 - n_total: 78 - pct_success: 100.0 - Chinese, Gan: - failed: [] - n_errors: 0 - n_total: 503 - pct_success: 100.0 - Chinese, Hakka: - failed: [] - n_errors: 0 - n_total: 498 - pct_success: 100.0 - Chinese, Jinyu: - failed: [] - n_errors: 0 - n_total: 500 - pct_success: 100.0 - Chinese, Mandarin (Beijing): - failed: [] - n_errors: 0 - n_total: 513 - pct_success: 100.0 - Chinese, Mandarin (Guiyang): - failed: [] - n_errors: 0 - n_total: 504 - pct_success: 100.0 - Chinese, Mandarin (Harbin): - failed: [] - n_errors: 0 - n_total: 504 - pct_success: 100.0 - Chinese, Mandarin (Nanjing): - failed: [] - n_errors: 0 - n_total: 501 - pct_success: 100.0 - Chinese, Mandarin (Simplified): - failed: [] - n_errors: 0 - n_total: 531 - pct_success: 100.0 - Chinese, Mandarin (Tianjin): - failed: [] - n_errors: 0 - n_total: 511 - pct_success: 100.0 - Chinese, Mandarin (Traditional): - failed: [] - n_errors: 0 - n_total: 505 - pct_success: 100.0 - Chinese, Min Nan: - failed: [] - n_errors: 0 - n_total: 503 - pct_success: 100.0 - Chinese, Wu: - failed: [] - n_errors: 0 - n_total: 506 - pct_success: 100.0 - Chinese, Xiang: - failed: [] - n_errors: 0 - n_total: 501 - pct_success: 100.0 - Chinese, Yue: - failed: [] - n_errors: 0 - n_total: 503 - pct_success: 100.0 - Colorado: - failed: [] - n_errors: 0 - n_total: 55 - pct_success: 100.0 - Dagaare, Southern: - failed: [] - n_errors: 0 - n_total: 63 - pct_success: 100.0 - Dangme: - failed: [] - n_errors: 0 - n_total: 58 - pct_success: 100.0 - Dari: - failed: [] - n_errors: 0 - n_total: 54 - pct_success: 100.0 - Dendi: - failed: [] - n_errors: 0 - n_total: 73 - pct_success: 100.0 - Dinka, Northeastern: - failed: [] - n_errors: 0 - n_total: 55 - pct_success: 100.0 - Ditammari: - failed: [] - n_errors: 0 - n_total: 60 - pct_success: 100.0 - Dzongkha: - failed: [] - n_errors: 0 - n_total: 200 - pct_success: 100.0 - Evenki: - failed: [] - n_errors: 0 - n_total: 77 - pct_success: 100.0 - Farsi, Western: - failed: [] - n_errors: 0 - n_total: 49 - pct_success: 100.0 - Fon: - failed: [] - n_errors: 0 - n_total: 88 - pct_success: 100.0 - French (Welche): - failed: [] - n_errors: 0 - n_total: 60 - pct_success: 100.0 - Fur: - failed: [] - n_errors: 0 - n_total: 90 - pct_success: 100.0 - Ga: - failed: [] - n_errors: 0 - n_total: 62 - pct_success: 100.0 - Gen: - failed: [] - n_errors: 0 - n_total: 87 - pct_success: 100.0 - Gilyak: - failed: [] - n_errors: 0 - n_total: 84 - pct_success: 100.0 - Gujarati: - failed: - - grapheme_id: Gujarati-2-07 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a95\u0acd\u0a95\u0abe - - grapheme_id: Gujarati-2-08 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a95\u0acd\u0a95\u0acb - - grapheme_id: Gujarati-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a95\u0acd\u0aa4\u0abf - - grapheme_id: Gujarati-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a95\u0acd\u0ab7\u0abe - - grapheme_id: Gujarati-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a9a\u0acd\u0a9a\u0abe - - grapheme_id: Gujarati-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a9a\u0acd\u0a9b\u0abe - - grapheme_id: Gujarati-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a9c\u0acd\u0a9e\u0abe - - grapheme_id: Gujarati-2-2a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0a9c\u0acd\u0aaf\u0acb - - grapheme_id: Gujarati-2-3b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aa4\u0abe - - grapheme_id: Gujarati-2-3c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aa4\u0abf - - grapheme_id: Gujarati-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aa4\u0acd\u0ab5 - - grapheme_id: Gujarati-2-3e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aa4\u0acd\u0ab5\u0abe - - grapheme_id: Gujarati-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aaf\u0abe - - grapheme_id: Gujarati-2-43 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aaf\u0abe\u0a82 - - grapheme_id: Gujarati-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aaf\u0abf - - grapheme_id: Gujarati-2-46 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0aaf\u0acb - - grapheme_id: Gujarati-2-48 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0ab0\u0ac0 - - grapheme_id: Gujarati-2-49 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa4\u0acd\u0ab0\u0acd\u0aaf - - grapheme_id: Gujarati-2-51 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa6\u0acd\u0aa7\u0abe - - grapheme_id: Gujarati-2-52 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa6\u0acd\u0aa7\u0abe\u0a82 - - grapheme_id: Gujarati-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa6\u0acd\u0aa7\u0abf - - grapheme_id: Gujarati-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa6\u0acd\u0ab5\u0abe - - grapheme_id: Gujarati-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa7\u0acd\u0aaf\u0abe - - grapheme_id: Gujarati-2-64 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa8\u0acd\u0aa4\u0acb - - grapheme_id: Gujarati-2-66 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa8\u0acd\u0aae\u0abe - - grapheme_id: Gujarati-2-69 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aa8\u0acd\u0aaf\u0abe - - grapheme_id: Gujarati-2-71 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aaa\u0acd\u0ab0\u0abe - - grapheme_id: Gujarati-2-85 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0aae\u0acd\u0aaf\u0abe\u0a82 - - grapheme_id: Gujarati-2-91 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aa4\u0abe - - grapheme_id: Gujarati-2-93 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aa5\u0abf - - grapheme_id: Gujarati-2-94 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aa6\u0acb - - grapheme_id: Gujarati-2-96 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aae\u0abf - - grapheme_id: Gujarati-2-99 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aaf\u0abe - - grapheme_id: Gujarati-2-9a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab0\u0acd\u0aaf\u0acb - - grapheme_id: Gujarati-2-ac - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab5\u0acd\u0aaf\u0abe - - grapheme_id: Gujarati-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab5\u0acd\u0aaf\u0acb - - grapheme_id: Gujarati-2-bb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab7\u0acd\u0a9f\u0abf - - grapheme_id: Gujarati-2-bc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab7\u0acd\u0a9f\u0acd\u0ab0 - - grapheme_id: Gujarati-2-bd - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0ab7\u0acd\u0a9f\u0acd\u0ab0\u0ac0 - - grapheme_id: Gujarati-2-be - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0ab7\u0acd\u0a9f\u0acd\u0ab0\u0acb - - grapheme_id: Gujarati-2-bf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab7\u0acd\u0aa0\u0abe - - grapheme_id: Gujarati-2-c9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0aa4\u0abf - - grapheme_id: Gujarati-2-ca - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0aa4\u0ac0 - - grapheme_id: Gujarati-2-cb - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0aa4\u0acd\u0ab0\u0ac0 - - grapheme_id: Gujarati-2-cc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0aa5\u0abe - - grapheme_id: Gujarati-2-cd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0aa5\u0abf - - grapheme_id: Gujarati-2-cf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0ab0\u0ac0 - - grapheme_id: Gujarati-2-d1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0ab5\u0abe - - grapheme_id: Gujarati-2-d2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0ab5\u0ac0 - - grapheme_id: Gujarati-2-d3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ab8\u0acd\u0ab8\u0abe - n_errors: 50 - n_total: 343 - pct_success: 85.42274052478133 - Gumuz: - failed: [] - n_errors: 0 - n_total: 54 - pct_success: 100.0 - Hindi: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u094d\u092f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0938\u093e - - grapheme_id: Hindi-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0928\u093f - - grapheme_id: Hindi-2-03 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0930\u093f - - grapheme_id: Hindi-2-04 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093f - - grapheme_id: Hindi-2-05 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u093e - - grapheme_id: Hindi-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u094b\u0902 - - grapheme_id: Hindi-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093f - - grapheme_id: Hindi-2-13 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091f\u094d\u091f\u093f - - grapheme_id: Hindi-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0915\u093e - - grapheme_id: Hindi-2-1c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093e - - grapheme_id: Hindi-2-1e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0928\u094b\u0902 - - grapheme_id: Hindi-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092a\u093e - - grapheme_id: Hindi-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092e\u093e - - grapheme_id: Hindi-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e - - grapheme_id: Hindi-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u0940 - - grapheme_id: Hindi-2-27 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0938\u093e - - grapheme_id: Hindi-2-29 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0918\u093f - - grapheme_id: Hindi-2-2c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e - - grapheme_id: Hindi-2-2d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f - - grapheme_id: Hindi-2-2e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u092d\u093e - - grapheme_id: Hindi-2-2f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0930\u094b - - grapheme_id: Hindi-2-33 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0927\u094d\u092f\u093e - - grapheme_id: Hindi-2-34 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u093f\u0903 - - grapheme_id: Hindi-2-36 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093f - - grapheme_id: Hindi-2-37 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094b\u0902 - - grapheme_id: Hindi-2-38 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930 - - grapheme_id: Hindi-2-39 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u093e - - grapheme_id: Hindi-2-3b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0926\u0940 - - grapheme_id: Hindi-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0927\u093f - - grapheme_id: Hindi-2-3e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0927\u0940 - - grapheme_id: Hindi-2-3f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0927\u094b\u0902 - - grapheme_id: Hindi-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0928\u093f - - grapheme_id: Hindi-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092e\u093e - - grapheme_id: Hindi-2-4a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0924\u093f - - grapheme_id: Hindi-2-4d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0930\u093e\u0902 - - grapheme_id: Hindi-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092d\u093f - - grapheme_id: Hindi-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093f - - grapheme_id: Hindi-2-58 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093e - - grapheme_id: Hindi-2-5b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e - - grapheme_id: Hindi-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u094b\u0902 - - grapheme_id: Hindi-2-5d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u094b - - grapheme_id: Hindi-2-62 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u0932\u093f - - grapheme_id: Hindi-2-65 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u094b\u0902 - - grapheme_id: Hindi-2-67 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u091a\u093f - - grapheme_id: Hindi-2-68 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u092f\u094b\u0902 - - grapheme_id: Hindi-2-6a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0930\u093e - - grapheme_id: Hindi-2-6c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0915\u093e - - grapheme_id: Hindi-2-6d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093f - - grapheme_id: Hindi-2-6e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u094b\u0902 - - grapheme_id: Hindi-2-6f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u094b\u0902 - - grapheme_id: Hindi-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u092f\u094b\u0902 - - grapheme_id: Hindi-2-74 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u093e - - grapheme_id: Hindi-2-75 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930 - - grapheme_id: Hindi-2-76 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u0940 - - grapheme_id: Hindi-2-7a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u094b\u0902 - - grapheme_id: Hindi-2-7b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u0940 - - grapheme_id: Hindi-2-85 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u095e\u094d\u0924\u093e - n_errors: 82 - n_total: 390 - pct_success: 78.97435897435898 - Japanese: - failed: [] - n_errors: 0 - n_total: 505 - pct_success: 100.0 - Japanese (Osaka): - failed: [] - n_errors: 0 - n_total: 497 - pct_success: 100.0 - Japanese (Tokyo): - failed: [] - n_errors: 0 - n_total: 499 - pct_success: 100.0 - Javanese (Javanese): - failed: - - grapheme_id: Javanese (Javanese)-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-07 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-08 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua98f\ua9ba - - grapheme_id: Javanese (Javanese)-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua98f\ua9bc - - grapheme_id: Javanese (Javanese)-2-0a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua994 - - grapheme_id: Javanese (Javanese)-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua994\ua9b8 - - grapheme_id: Javanese (Javanese)-2-0c - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua994\ua9bf - - grapheme_id: Javanese (Javanese)-2-0d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua995 - - grapheme_id: Javanese (Javanese)-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a0\ua9b6 - - grapheme_id: Javanese (Javanese)-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-11 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a0\ua9ba - - grapheme_id: Javanese (Javanese)-2-12 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a2 - - grapheme_id: Javanese (Javanese)-2-13 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a2\ua982 - - grapheme_id: Javanese (Javanese)-2-14 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-15 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a4 - - grapheme_id: Javanese (Javanese)-2-16 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a4\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-17 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a5\ua9b6 - - grapheme_id: Javanese (Javanese)-2-19 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a5\ua9bf\ua9b6 - - grapheme_id: Javanese (Javanese)-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a7 - - grapheme_id: Javanese (Javanese)-2-1b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a7\ua9b6 - - grapheme_id: Javanese (Javanese)-2-1c - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a7\ua9ba - - grapheme_id: Javanese (Javanese)-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-1e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9a9\ua9b8 - - grapheme_id: Javanese (Javanese)-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9aa - - grapheme_id: Javanese (Javanese)-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9ae - - grapheme_id: Javanese (Javanese)-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b1\ua982 - - grapheme_id: Javanese (Javanese)-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b1\ua9b6 - - grapheme_id: Javanese (Javanese)-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b1\ua9b8 - - grapheme_id: Javanese (Javanese)-2-26 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b1\ua9ba - - grapheme_id: Javanese (Javanese)-2-27 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2\ua981 - - grapheme_id: Javanese (Javanese)-2-29 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-2a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-2b - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2\ua9ba - - grapheme_id: Javanese (Javanese)-2-2c - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua98f\ua9c0\ua9b2\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-2e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua992\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-30 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua992\ua9bf\ua9ba - - grapheme_id: Javanese (Javanese)-2-31 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua992\ua9c0\ua992 - - grapheme_id: Javanese (Javanese)-2-34 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-36 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua992\ua9bc - - grapheme_id: Javanese (Javanese)-2-37 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua992\ua9bf\ua9bc\ua981 - - grapheme_id: Javanese (Javanese)-2-38 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua9ad\ua981 - - grapheme_id: Javanese (Javanese)-2-3a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua9ad\ua9b8 - - grapheme_id: Javanese (Javanese)-2-3b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua994\ua9c0\ua9ad\ua9bc - - grapheme_id: Javanese (Javanese)-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua995\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-40 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua997\ua9bf\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua995 - - grapheme_id: Javanese (Javanese)-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua995\ua9b6 - - grapheme_id: Javanese (Javanese)-2-43 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua997 - - grapheme_id: Javanese (Javanese)-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua997\ua9b6 - - grapheme_id: Javanese (Javanese)-2-45 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua997\ua9b8\ua981 - - grapheme_id: Javanese (Javanese)-2-46 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99a\ua9c0\ua997\ua9b8\ua982 - - grapheme_id: Javanese (Javanese)-2-49 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99d\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-4a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua99d\ua9ba\ua9b4\ua982 - - grapheme_id: Javanese (Javanese)-2-4d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9ba\ua983 - - grapheme_id: Javanese (Javanese)-2-4e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-52 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua98f\ua9b8 - - grapheme_id: Javanese (Javanese)-2-54 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua992 - - grapheme_id: Javanese (Javanese)-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua994\ua9b8 - - grapheme_id: Javanese (Javanese)-2-56 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua99a - - grapheme_id: Javanese (Javanese)-2-57 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-58 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a0\ua9b6 - - grapheme_id: Javanese (Javanese)-2-59 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a0\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-5a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-5b - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a0\ua9ba - - grapheme_id: Javanese (Javanese)-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a4 - - grapheme_id: Javanese (Javanese)-2-5d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a4\ua9b6 - - grapheme_id: Javanese (Javanese)-2-5e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-5f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9a9\ua9bc - - grapheme_id: Javanese (Javanese)-2-60 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-61 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9ad\ua9ba - - grapheme_id: Javanese (Javanese)-2-62 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-63 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b1\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-64 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b1\ua9b6\ua983 - - grapheme_id: Javanese (Javanese)-2-65 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b1\ua9be - - grapheme_id: Javanese (Javanese)-2-66 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b1\ua9c0\ua9ae - - grapheme_id: Javanese (Javanese)-2-67 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-68 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b2\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-69 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a0\ua9c0\ua9b2\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-6c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-6f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-70 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-71 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9a0\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9a2 - - grapheme_id: Javanese (Javanese)-2-73 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-74 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9a9\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-75 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a2\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-77 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9ba\ua983 - - grapheme_id: Javanese (Javanese)-2-78 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-79 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-7a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-7b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua98f\ua9b8 - - grapheme_id: Javanese (Javanese)-2-7c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua98f\ua9bc - - grapheme_id: Javanese (Javanese)-2-7d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua992 - - grapheme_id: Javanese (Javanese)-2-7e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994 - - grapheme_id: Javanese (Javanese)-2-7f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua981 - - grapheme_id: Javanese (Javanese)-2-80 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9b6 - - grapheme_id: Javanese (Javanese)-2-81 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9b8 - - grapheme_id: Javanese (Javanese)-2-82 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9b8\ua982 - - grapheme_id: Javanese (Javanese)-2-83 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9ba - - grapheme_id: Javanese (Javanese)-2-84 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9bc - - grapheme_id: Javanese (Javanese)-2-85 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua994\ua9bf\ua9b8 - - grapheme_id: Javanese (Javanese)-2-86 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua997 - - grapheme_id: Javanese (Javanese)-2-87 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua997\ua9b8 - - grapheme_id: Javanese (Javanese)-2-88 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua997\ua9bc - - grapheme_id: Javanese (Javanese)-2-89 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99a\ua9b6 - - grapheme_id: Javanese (Javanese)-2-8a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99b\ua983 - - grapheme_id: Javanese (Javanese)-2-8b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99b\ua9b6 - - grapheme_id: Javanese (Javanese)-2-8c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d - - grapheme_id: Javanese (Javanese)-2-8d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua981 - - grapheme_id: Javanese (Javanese)-2-8e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9b6 - - grapheme_id: Javanese (Javanese)-2-8f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9b8 - - grapheme_id: Javanese (Javanese)-2-90 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9b8\ua981 - - grapheme_id: Javanese (Javanese)-2-91 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9ba\ua981 - - grapheme_id: Javanese (Javanese)-2-92 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9ba\ua9b4\ua981 - - grapheme_id: Javanese (Javanese)-2-93 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua99d\ua9bc - - grapheme_id: Javanese (Javanese)-2-94 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-95 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua982 - - grapheme_id: Javanese (Javanese)-2-96 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua983 - - grapheme_id: Javanese (Javanese)-2-97 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9b6 - - grapheme_id: Javanese (Javanese)-2-98 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-99 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9b8\ua981 - - grapheme_id: Javanese (Javanese)-2-9a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9ba - - grapheme_id: Javanese (Javanese)-2-9b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9bc - - grapheme_id: Javanese (Javanese)-2-9c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9bc\ua982 - - grapheme_id: Javanese (Javanese)-2-9d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a0\ua9bd - - grapheme_id: Javanese (Javanese)-2-9e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2 - - grapheme_id: Javanese (Javanese)-2-9f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua981 - - grapheme_id: Javanese (Javanese)-2-a0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua982 - - grapheme_id: Javanese (Javanese)-2-a1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-a2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-a3 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua9ba - - grapheme_id: Javanese (Javanese)-2-a4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a2\ua9bc - - grapheme_id: Javanese (Javanese)-2-a5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a4 - - grapheme_id: Javanese (Javanese)-2-a6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a4\ua9b6 - - grapheme_id: Javanese (Javanese)-2-a7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a4\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-a8 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a4\ua9ba - - grapheme_id: Javanese (Javanese)-2-a9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a4\ua9bc - - grapheme_id: Javanese (Javanese)-2-aa - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-ab - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua981 - - grapheme_id: Javanese (Javanese)-2-ac - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9b3\ua9ba\ua9b4\ua982 - - grapheme_id: Javanese (Javanese)-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9b6 - - grapheme_id: Javanese (Javanese)-2-ae - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9b8 - - grapheme_id: Javanese (Javanese)-2-af - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9bc - - grapheme_id: Javanese (Javanese)-2-b0 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9bf - - grapheme_id: Javanese (Javanese)-2-b1 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a5\ua9bf\ua9b6 - - grapheme_id: Javanese (Javanese)-2-b2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7 - - grapheme_id: Javanese (Javanese)-2-b3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua981 - - grapheme_id: Javanese (Javanese)-2-b4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua9b6 - - grapheme_id: Javanese (Javanese)-2-b5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua9b8 - - grapheme_id: Javanese (Javanese)-2-b6 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua9ba - - grapheme_id: Javanese (Javanese)-2-b7 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-b8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a7\ua9bc - - grapheme_id: Javanese (Javanese)-2-b9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-ba - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a9\ua982 - - grapheme_id: Javanese (Javanese)-2-bb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a9\ua9b6 - - grapheme_id: Javanese (Javanese)-2-bc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a9\ua9b8\ua982 - - grapheme_id: Javanese (Javanese)-2-bd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9a9\ua9bc - - grapheme_id: Javanese (Javanese)-2-be - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-bf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ad\ua9b6 - - grapheme_id: Javanese (Javanese)-2-c0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ad\ua9b8 - - grapheme_id: Javanese (Javanese)-2-c1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ae - - grapheme_id: Javanese (Javanese)-2-c2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ae\ua982 - - grapheme_id: Javanese (Javanese)-2-c3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ae\ua9b6 - - grapheme_id: Javanese (Javanese)-2-c4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ae\ua9b8 - - grapheme_id: Javanese (Javanese)-2-c5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9ae\ua9bc - - grapheme_id: Javanese (Javanese)-2-c6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-c7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua982 - - grapheme_id: Javanese (Javanese)-2-c8 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua983 - - grapheme_id: Javanese (Javanese)-2-c9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua9b6 - - grapheme_id: Javanese (Javanese)-2-ca - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-cb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua9b8 - - grapheme_id: Javanese (Javanese)-2-cc - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-cd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b1\ua9bc - - grapheme_id: Javanese (Javanese)-2-ce - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-cf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2\ua981 - - grapheme_id: Javanese (Javanese)-2-d0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-d1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-d2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-d3 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a4\ua9c0\ua9b2\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-d6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-d8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9bf\ua983 - - grapheme_id: Javanese (Javanese)-2-da - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9bf\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-db - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-dc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-dd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-de - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a0\ua9b6 - - grapheme_id: Javanese (Javanese)-2-df - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-e0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a4 - - grapheme_id: Javanese (Javanese)-2-e1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a4\ua9b6 - - grapheme_id: Javanese (Javanese)-2-e2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-e3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a5\ua9b6 - - grapheme_id: Javanese (Javanese)-2-e4 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a5\ua9ba - - grapheme_id: Javanese (Javanese)-2-e5 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a7\ua9ba - - grapheme_id: Javanese (Javanese)-2-e6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-e7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9a9\ua982 - - grapheme_id: Javanese (Javanese)-2-e8 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9ab\ua9ba - - grapheme_id: Javanese (Javanese)-2-e9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-ea - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9ad\ua9b8 - - grapheme_id: Javanese (Javanese)-2-eb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-ec - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9b1\ua9b6\ua982 - - grapheme_id: Javanese (Javanese)-2-ed - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9b1\ua9bc - - grapheme_id: Javanese (Javanese)-2-ee - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a5\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-f0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9ba\ua983 - - grapheme_id: Javanese (Javanese)-2-f1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-f3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-f4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-f5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-f6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9a7 - - grapheme_id: Javanese (Javanese)-2-f7 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9a7\ua9ba - - grapheme_id: Javanese (Javanese)-2-f8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-f9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a7\ua9c0\ua9b2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-fc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9ba\ua983 - - grapheme_id: Javanese (Javanese)-2-fd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-ff - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9bf\ua983 - - grapheme_id: Javanese (Javanese)-2-100 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9bf\ua9b6\ua983 - - grapheme_id: Javanese (Javanese)-2-101 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-102 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua994\ua9bc - - grapheme_id: Javanese (Javanese)-2-103 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-104 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a0\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-105 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a4\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-106 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-107 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a5\ua9b6 - - grapheme_id: Javanese (Javanese)-2-108 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a5\ua9b8 - - grapheme_id: Javanese (Javanese)-2-109 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a5\ua9bc - - grapheme_id: Javanese (Javanese)-2-10a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7 - - grapheme_id: Javanese (Javanese)-2-10b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua981 - - grapheme_id: Javanese (Javanese)-2-10c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua982 - - grapheme_id: Javanese (Javanese)-2-10d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua983 - - grapheme_id: Javanese (Javanese)-2-10e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9b6 - - grapheme_id: Javanese (Javanese)-2-10f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9b8 - - grapheme_id: Javanese (Javanese)-2-110 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9b8\ua981 - - grapheme_id: Javanese (Javanese)-2-111 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9ba - - grapheme_id: Javanese (Javanese)-2-112 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9ba\ua982 - - grapheme_id: Javanese (Javanese)-2-113 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9bc\ua982 - - grapheme_id: Javanese (Javanese)-2-114 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a7\ua9be\ua9b8\ua982 - - grapheme_id: Javanese (Javanese)-2-115 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-116 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9a9\ua9b6 - - grapheme_id: Javanese (Javanese)-2-117 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9ab - - grapheme_id: Javanese (Javanese)-2-118 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9ab\ua983 - - grapheme_id: Javanese (Javanese)-2-119 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-11a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9a9\ua9c0\ua9b2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-11d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9aa\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-120 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ab\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-125 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9ba\ua983 - - grapheme_id: Javanese (Javanese)-2-126 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-127 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9ba\ua9b4\ua981 - - grapheme_id: Javanese (Javanese)-2-128 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-129 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua99a\ua9bc\ua981 - - grapheme_id: Javanese (Javanese)-2-12a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9a0\ua9b8 - - grapheme_id: Javanese (Javanese)-2-12b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9a7\ua9b8 - - grapheme_id: Javanese (Javanese)-2-12c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9a9\ua9b8 - - grapheme_id: Javanese (Javanese)-2-12d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-12e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9ad\ua9b6 - - grapheme_id: Javanese (Javanese)-2-12f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9ad\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-130 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9ad\ua9ba - - grapheme_id: Javanese (Javanese)-2-131 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9ae - - grapheme_id: Javanese (Javanese)-2-132 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-133 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b1\ua982 - - grapheme_id: Javanese (Javanese)-2-134 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b1\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-135 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b1\ua9b8 - - grapheme_id: Javanese (Javanese)-2-136 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-137 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b2\ua981 - - grapheme_id: Javanese (Javanese)-2-138 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b2\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-139 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ad\ua9c0\ua9b2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-13e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ae\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-13f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ae\ua9ba\ua9b4\ua981 - - grapheme_id: Javanese (Javanese)-2-140 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9ae\ua9ba\ua9b4\ua983 - - grapheme_id: Javanese (Javanese)-2-141 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9ae\ua9bf\ua9b8\ua983 - - grapheme_id: Javanese (Javanese)-2-144 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-2-147 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9bf\ua983 - - grapheme_id: Javanese (Javanese)-2-148 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua98f - - grapheme_id: Javanese (Javanese)-2-149 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua98f\ua981 - - grapheme_id: Javanese (Javanese)-2-14a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua98f\ua9bf\ua9b6 - - grapheme_id: Javanese (Javanese)-2-14b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua992 - - grapheme_id: Javanese (Javanese)-2-14c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua994\ua9b8 - - grapheme_id: Javanese (Javanese)-2-14d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua994\ua9bf - - grapheme_id: Javanese (Javanese)-2-14e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua99b\ua9b6 - - grapheme_id: Javanese (Javanese)-2-14f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a0 - - grapheme_id: Javanese (Javanese)-2-150 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a0\ua9bf - - grapheme_id: Javanese (Javanese)-2-151 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a2\ua982 - - grapheme_id: Javanese (Javanese)-2-152 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-153 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-154 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a5 - - grapheme_id: Javanese (Javanese)-2-155 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a7 - - grapheme_id: Javanese (Javanese)-2-156 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a7\ua981 - - grapheme_id: Javanese (Javanese)-2-157 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a9 - - grapheme_id: Javanese (Javanese)-2-158 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a9\ua9b6 - - grapheme_id: Javanese (Javanese)-2-159 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a9\ua9bc - - grapheme_id: Javanese (Javanese)-2-15a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9a9\ua9bf - - grapheme_id: Javanese (Javanese)-2-15b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9ad - - grapheme_id: Javanese (Javanese)-2-15c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9ae - - grapheme_id: Javanese (Javanese)-2-15d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1 - - grapheme_id: Javanese (Javanese)-2-15e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1\ua982 - - grapheme_id: Javanese (Javanese)-2-15f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1\ua9b6 - - grapheme_id: Javanese (Javanese)-2-160 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-161 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1\ua9b8 - - grapheme_id: Javanese (Javanese)-2-162 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b1\ua9ba - - grapheme_id: Javanese (Javanese)-2-163 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b2 - - grapheme_id: Javanese (Javanese)-2-164 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b2\ua981 - - grapheme_id: Javanese (Javanese)-2-165 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b2\ua9b6 - - grapheme_id: Javanese (Javanese)-2-166 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b2\ua9b6\ua981 - - grapheme_id: Javanese (Javanese)-2-167 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b1\ua9c0\ua9b2\ua9b8 - - grapheme_id: Javanese (Javanese)-2-169 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \ua9b2\ua9ba\ua9b4 - - grapheme_id: Javanese (Javanese)-1-10 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua98f\ua9c0 - - grapheme_id: Javanese (Javanese)-1-3b - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a0\ua9c0 - - grapheme_id: Javanese (Javanese)-1-42 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a2\ua9c0 - - grapheme_id: Javanese (Javanese)-1-4d - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a4\ua9c0 - - grapheme_id: Javanese (Javanese)-1-51 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a5\ua9b3\ua9c0 - - grapheme_id: Javanese (Javanese)-1-57 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a5\ua9c0 - - grapheme_id: Javanese (Javanese)-1-5e - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a7\ua9c0 - - grapheme_id: Javanese (Javanese)-1-68 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9a9\ua9c0 - - grapheme_id: Javanese (Javanese)-1-7c - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9ad\ua9c0 - - grapheme_id: Javanese (Javanese)-1-8f - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \ua9b1\ua9c0 - n_errors: 321 - n_total: 530 - pct_success: 39.43396226415095 - Kabyle: - failed: [] - n_errors: 0 - n_total: 62 - pct_success: 100.0 - Kannada: - failed: - - grapheme_id: Kannada-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c95\u0cbe\u0c82 - - grapheme_id: Kannada-2-0a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c95\u0cc1\u0c82 - - grapheme_id: Kannada-2-11 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c95\u0cca\u0c82 - - grapheme_id: Kannada-2-12 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c96\u0cbe\u0c82 - - grapheme_id: Kannada-2-14 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c97\u0cbe\u0c82 - - grapheme_id: Kannada-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c97\u0cca\u0c82 - - grapheme_id: Kannada-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c9f\u0cc1\u0c82 - - grapheme_id: Kannada-2-45 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0ca8\u0cbe\u0c82 - - grapheme_id: Kannada-2-65 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0caf\u0cc1\u0c82 - - grapheme_id: Kannada-2-6d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0cb0\u0cbe\u0c82 - - grapheme_id: Kannada-2-84 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0cb5\u0cc1\u0c82 - - grapheme_id: Kannada-2-8c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0cb6\u0cbe\u0c82 - - grapheme_id: Kannada-2-93 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0cb8\u0cbe\u0c82 - - grapheme_id: Kannada-2-9b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0cb9\u0cca\u0c82 - n_errors: 14 - n_total: 287 - pct_success: 95.1219512195122 - Khmer, Central: - failed: - - grapheme_id: Khmer, Central-2-07 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1780\u17d2\u178a\u17c5 - - grapheme_id: Khmer, Central-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1780\u17d2\u179a\u17c4 - - grapheme_id: Khmer, Central-2-0d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1780\u17d2\u179f\u17b6 - - grapheme_id: Khmer, Central-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1781\u17d2\u1787\u17b6 - - grapheme_id: Khmer, Central-2-12 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1781\u17d2\u179b\u17b6 - - grapheme_id: Khmer, Central-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1782\u17d2\u1793\u17b6 - - grapheme_id: Khmer, Central-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1782\u17d2\u1798\u17b6 - - grapheme_id: Khmer, Central-2-1c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1782\u17d2\u179a\u17b6 - - grapheme_id: Khmer, Central-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1782\u17d2\u179a\u17b9\u17c7 - - grapheme_id: Khmer, Central-2-26 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1784\u17d2\u1780\u17be - - grapheme_id: Khmer, Central-2-2c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1784\u17d2\u179a\u17c0 - - grapheme_id: Khmer, Central-2-2d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1784\u17d2\u17a0\u17b6 - - grapheme_id: Khmer, Central-2-35 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1785\u17d2\u1785\u17c1 - - grapheme_id: Khmer, Central-2-36 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1785\u17d2\u1794\u17b6 - - grapheme_id: Khmer, Central-2-38 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1786\u17d2\u1793\u17b6\u17c6 - - grapheme_id: Khmer, Central-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1786\u17d2\u1793\u17c4 - - grapheme_id: Khmer, Central-2-3b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1786\u17d2\u179b\u17be - - grapheme_id: Khmer, Central-2-3f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1787\u17d2\u1787\u17b6 - - grapheme_id: Khmer, Central-2-40 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1787\u17d2\u1789\u17b6 - - grapheme_id: Khmer, Central-2-43 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1787\u17d2\u179a\u17be - - grapheme_id: Khmer, Central-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1787\u17d2\u179a\u17c0 - - grapheme_id: Khmer, Central-2-45 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1787\u17d2\u179a\u17c2 - - grapheme_id: Khmer, Central-2-47 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u1788\u17d2\u1798\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-4a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1789\u17d2\u1787\u17b6 - - grapheme_id: Khmer, Central-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1789\u17d2\u1789\u17b6 - - grapheme_id: Khmer, Central-2-4d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1789\u17d2\u1789\u17c8 - - grapheme_id: Khmer, Central-2-55 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u178a\u17d2\u178b\u17b6 - - grapheme_id: Khmer, Central-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u178e\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-5e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u178e\u17d2\u178a\u17b6 - - grapheme_id: Khmer, Central-2-5f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u178e\u17d2\u178a\u17bb\u17c7 - - grapheme_id: Khmer, Central-2-61 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u178e\u17d2\u178e\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-6b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u178f\u17d2\u179a\u17b6 - - grapheme_id: Khmer, Central-2-71 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1790\u17d2\u1784\u17c3 - - grapheme_id: Khmer, Central-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1790\u17d2\u1793\u17b6 - - grapheme_id: Khmer, Central-2-75 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1790\u17d2\u179b\u17c3 - - grapheme_id: Khmer, Central-2-7c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1791\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-80 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1791\u17d2\u1799\u17b6 - - grapheme_id: Khmer, Central-2-84 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1792\u17d2\u1793\u17b6 - - grapheme_id: Khmer, Central-2-86 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1792\u17d2\u1799\u17b6 - - grapheme_id: Khmer, Central-2-87 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1792\u17d2\u1799\u17c4 - - grapheme_id: Khmer, Central-2-88 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1792\u17d2\u179c\u17be - - grapheme_id: Khmer, Central-2-8c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17c1\u17c7 - - grapheme_id: Khmer, Central-2-8e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-93 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u1790\u17c2 - - grapheme_id: Khmer, Central-2-94 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u1790\u17c4 - - grapheme_id: Khmer, Central-2-95 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u1791\u17b6 - - grapheme_id: Khmer, Central-2-96 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u1791\u17c8 - - grapheme_id: Khmer, Central-2-9a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u1799\u17b6 - - grapheme_id: Khmer, Central-2-9b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1793\u17d2\u179b\u17c2 - - grapheme_id: Khmer, Central-2-a1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1794\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-a6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1794\u17d2\u178a\u17c1 - - grapheme_id: Khmer, Central-2-aa - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1794\u17d2\u179a\u17b6 - - grapheme_id: Khmer, Central-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1794\u17d2\u179a\u17be - - grapheme_id: Khmer, Central-2-ae - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1794\u17d2\u179a\u17c2 - - grapheme_id: Khmer, Central-2-b1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u178a\u17b6 - - grapheme_id: Khmer, Central-2-b2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u1791\u17b6 - - grapheme_id: Khmer, Central-2-b4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u1791\u17c1 - - grapheme_id: Khmer, Central-2-b5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u1793\u17c2 - - grapheme_id: Khmer, Central-2-b6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u179b\u17b6 - - grapheme_id: Khmer, Central-2-b9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u179f\u17b6 - - grapheme_id: Khmer, Central-2-ba - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u179f\u17c1 - - grapheme_id: Khmer, Central-2-bb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1795\u17d2\u179f\u17c3 - - grapheme_id: Khmer, Central-2-bf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1796\u17c4\u17c7 - - grapheme_id: Khmer, Central-2-c1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1796\u17d2\u1799\u17b6 - - grapheme_id: Khmer, Central-2-c4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1796\u17d2\u179a\u17c0 - - grapheme_id: Khmer, Central-2-c5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1796\u17d2\u179a\u17c3 - - grapheme_id: Khmer, Central-2-d0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u1784\u17b6 - - grapheme_id: Khmer, Central-2-d1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u1785\u17b6 - - grapheme_id: Khmer, Central-2-d2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u178a\u17b6 - - grapheme_id: Khmer, Central-2-d3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u178a\u17c2 - - grapheme_id: Khmer, Central-2-d4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u1793\u17b6 - - grapheme_id: Khmer, Central-2-d5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u1793\u17c1 - - grapheme_id: Khmer, Central-2-d9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u1797\u17b6 - - grapheme_id: Khmer, Central-2-dc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u179a\u17b6 - - grapheme_id: Khmer, Central-2-df - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u179a\u17be - - grapheme_id: Khmer, Central-2-e0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u179a\u17c1 - - grapheme_id: Khmer, Central-2-e1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u179b\u17c0 - - grapheme_id: Khmer, Central-2-e2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1798\u17d2\u179b\u17c3 - - grapheme_id: Khmer, Central-2-f6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179b\u17d2\u1794\u17c8 - - grapheme_id: Khmer, Central-2-f7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179b\u17d2\u1798\u17be - - grapheme_id: Khmer, Central-2-ff - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u1782\u17b6 - - grapheme_id: Khmer, Central-2-101 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u178a\u17d2\u179a\u17b8 - - grapheme_id: Khmer, Central-2-102 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u178f\u17d2\u179a - - grapheme_id: Khmer, Central-2-103 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u1790\u17b6 - - grapheme_id: Khmer, Central-2-106 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u1793\u17b6 - - grapheme_id: Khmer, Central-2-107 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u1798\u17b6 - - grapheme_id: Khmer, Central-2-108 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u1798\u17be - - grapheme_id: Khmer, Central-2-10a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u179a\u17b6 - - grapheme_id: Khmer, Central-2-10d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u179b\u17b6 - - grapheme_id: Khmer, Central-2-10e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u179f\u17d2\u179c\u17c2 - n_errors: 90 - n_total: 443 - pct_success: 79.68397291196389 - Khün: - failed: - - grapheme_id: Khün-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1a23\u1a55\u1a6e\u1a60 - - grapheme_id: Khün-2-46 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1a2f\u1a60\u1a45\u1a60\u1a3f\u1a62 - - grapheme_id: Khün-2-47 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1a2f\u1a62\u1a60\u1a45\u1a60\u1a3f - - grapheme_id: Khün-2-e5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1a49\u1a60\u1a3f\u1a62\u1a60\u1a4b - n_errors: 4 - n_total: 396 - pct_success: 98.98989898989899 - Korean: - failed: [] - n_errors: 0 - n_total: 315 - pct_success: 100.0 - Lamnso': - failed: [] - n_errors: 0 - n_total: 74 - pct_success: 100.0 - Lao: - failed: [] - n_errors: 0 - n_total: 277 - pct_success: 100.0 - Lingala (tones): - failed: [] - n_errors: 0 - n_total: 71 - pct_success: 100.0 - Magahi: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0930\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u091f\u094d\u091f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0938\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0927\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u094d\u092f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0938\u093e - - grapheme_id: Magahi-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u094d\u0923 - - grapheme_id: Magahi-2-05 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091f\u094d\u091f\u0940 - - grapheme_id: Magahi-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091f\u094d\u0930\u093e - - grapheme_id: Magahi-2-09 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e\u093f - - grapheme_id: Magahi-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e\u0902 - - grapheme_id: Magahi-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u094d\u0924 - - grapheme_id: Magahi-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u093e - n_errors: 43 - n_total: 314 - pct_success: 86.30573248407643 - Maithili: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0930\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u0940 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0938\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u092d\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0927\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u093f\u0903 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0930\u093e\u0902 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092d\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u0932\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u091a\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0940 - - inherited_from: Magahi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u094d\u092f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u0940 - - grapheme_id: Maithili-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u094d\u092f - - grapheme_id: Maithili-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u094d\u0935 - - grapheme_id: Maithili-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0928\u0940 - - grapheme_id: Maithili-2-0d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092a\u0940 - - grapheme_id: Maithili-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u094d\u0924 - - grapheme_id: Maithili-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0918\u094b - - grapheme_id: Maithili-2-13 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u0903 - - grapheme_id: Maithili-2-14 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093e - - grapheme_id: Maithili-2-15 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u093f - - grapheme_id: Maithili-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0924\u093e - - grapheme_id: Maithili-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092c\u094d\u0927\u093f - - grapheme_id: Maithili-2-1c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092d\u094d\u092f\u093e - - grapheme_id: Maithili-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092a\u094d\u0930 - - grapheme_id: Maithili-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0915\u093f - - grapheme_id: Maithili-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092c\u093e - - grapheme_id: Maithili-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093e - - grapheme_id: Maithili-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u093e - - grapheme_id: Maithili-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092a\u093f - - grapheme_id: Maithili-2-27 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u092f\u093f - n_errors: 71 - n_total: 357 - pct_success: 80.11204481792717 - Malayalam: - failed: - - grapheme_id: Malayalam-2-08 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d3e - - grapheme_id: Malayalam-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d3f - - grapheme_id: Malayalam-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d41\u0d02 - - grapheme_id: Malayalam-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d47 - - grapheme_id: Malayalam-2-0d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d4a - - grapheme_id: Malayalam-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d4b - - grapheme_id: Malayalam-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d24\u0d3f - - grapheme_id: Malayalam-2-12 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-16 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d3e - - grapheme_id: Malayalam-2-17 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d3f - - grapheme_id: Malayalam-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d47 - - grapheme_id: Malayalam-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d16\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d17\u0d4d\u0d26\u0d3e - - grapheme_id: Malayalam-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d17\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d3f - - grapheme_id: Malayalam-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d46 - - grapheme_id: Malayalam-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d47 - - grapheme_id: Malayalam-2-26 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d19\u0d3f - - grapheme_id: Malayalam-2-2d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d3f - - grapheme_id: Malayalam-2-2f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d41\u0d02 - - grapheme_id: Malayalam-2-30 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d46 - - grapheme_id: Malayalam-2-31 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d47 - - grapheme_id: Malayalam-2-36 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1c\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1e\u0d4d\u0d1e\u0d3f - - grapheme_id: Malayalam-2-3a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1e\u0d4d\u0d1e\u0d46 - - grapheme_id: Malayalam-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d3e - - grapheme_id: Malayalam-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d3f - - grapheme_id: Malayalam-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d4b - - grapheme_id: Malayalam-2-4b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d3e - - grapheme_id: Malayalam-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d3f - - grapheme_id: Malayalam-2-4e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d41\u0d02 - - grapheme_id: Malayalam-2-4f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d46 - - grapheme_id: Malayalam-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d3e - - grapheme_id: Malayalam-2-5d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d3f - - grapheme_id: Malayalam-2-5f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d41\u0d02 - - grapheme_id: Malayalam-2-60 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d46 - - grapheme_id: Malayalam-2-61 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d47 - - grapheme_id: Malayalam-2-62 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d4b - - grapheme_id: Malayalam-2-65 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d28\u0d02 - - grapheme_id: Malayalam-2-66 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d28\u0d3f - - grapheme_id: Malayalam-2-68 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-69 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d2f\u0d47 - - grapheme_id: Malayalam-2-6c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d35\u0d02 - - grapheme_id: Malayalam-2-72 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d26\u0d4d\u0d27\u0d3f - - grapheme_id: Malayalam-2-74 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d26\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-81 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d02 - - grapheme_id: Malayalam-2-82 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d46 - - grapheme_id: Malayalam-2-83 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30 - - grapheme_id: Malayalam-2-84 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30\u0d4d\u0d2f - - grapheme_id: Malayalam-2-85 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-89 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d27\u0d3f - - grapheme_id: Malayalam-2-8b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d3e - - grapheme_id: Malayalam-2-8c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d3f - - grapheme_id: Malayalam-2-8d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d40 - - grapheme_id: Malayalam-2-8f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d41\u0d02 - - grapheme_id: Malayalam-2-90 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d46 - - grapheme_id: Malayalam-2-91 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d47 - - grapheme_id: Malayalam-2-92 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d4b - - grapheme_id: Malayalam-2-95 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2e\u0d3e - - grapheme_id: Malayalam-2-99 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-9a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-9b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d47 - - grapheme_id: Malayalam-2-9c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d4b - - grapheme_id: Malayalam-2-9d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d31\u0d46 - - grapheme_id: Malayalam-2-9f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u200d\u0d2a\u0d3f - - grapheme_id: Malayalam-2-a9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d3f - - grapheme_id: Malayalam-2-ab - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d46 - - grapheme_id: Malayalam-2-ac - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d4b - - grapheme_id: Malayalam-2-af - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d30\u0d3e - - grapheme_id: Malayalam-2-b3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2c\u0d4d\u0d32\u0d3f - - grapheme_id: Malayalam-2-b8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2d\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-b9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2d\u0d4d\u0d30\u0d3e - - grapheme_id: Malayalam-2-c3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2a\u0d3e - - grapheme_id: Malayalam-2-c4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2a\u0d4d\u0d30 - - grapheme_id: Malayalam-2-c6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2e\u0d3f - - grapheme_id: Malayalam-2-ce - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d15\u0d4d\u0d15\u0d41 - - grapheme_id: Malayalam-2-cf - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d24\u0d3f - - grapheme_id: Malayalam-2-d2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-d3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d2f\u0d3f - - grapheme_id: Malayalam-2-dd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-de - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d41 - - grapheme_id: Malayalam-2-df - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d41\u0d02 - - grapheme_id: Malayalam-2-e0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d4d\u200c - - grapheme_id: Malayalam-2-e1 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d17\u0d4d\u0d17\u0d02 - - grapheme_id: Malayalam-2-e2 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d1a\u0d4d\u0d1a - - grapheme_id: Malayalam-2-e3 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d23\u0d4d\u0d23 - - grapheme_id: Malayalam-2-e4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d24\u0d4d\u0d24 - - grapheme_id: Malayalam-2-e5 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d24\u0d4d\u0d24\u0d3f - - grapheme_id: Malayalam-2-e6 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d24\u0d4d\u0d24\u0d41 - - grapheme_id: Malayalam-2-e7 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d24\u0d4d\u0d24\u0d47 - - grapheme_id: Malayalam-2-e8 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d26\u0d47 - - grapheme_id: Malayalam-2-e9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d26\u0d4d\u0d26 - - grapheme_id: Malayalam-2-ea - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d2a\u0d4d\u0d2a\u0d46 - - grapheme_id: Malayalam-2-ed - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d2e\u0d4d\u0d2e\u0d3f - - grapheme_id: Malayalam-2-ee - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d30\u0d3e - - grapheme_id: Malayalam-2-ef - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d35\u0d4d\u0d35 - - grapheme_id: Malayalam-2-f1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u200d\u0d39\u0d3f - - grapheme_id: Malayalam-2-f5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d31\u0d4d\u0d31\u0d02 - - grapheme_id: Malayalam-2-f7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d31\u0d4d\u0d31\u0d47 - - grapheme_id: Malayalam-2-100 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-102 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d3e - - grapheme_id: Malayalam-2-103 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d3e\u0d02 - - grapheme_id: Malayalam-2-104 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d46 - - grapheme_id: Malayalam-2-105 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u200d\u0d15\u0d3f - - grapheme_id: Malayalam-2-107 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u200d\u0d15\u0d47 - - grapheme_id: Malayalam-2-109 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u200d\u0d28\u0d3f - - grapheme_id: Malayalam-2-10a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u200d\u0d2a\u0d4d\u0d2a - - grapheme_id: Malayalam-2-113 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u0d33\u0d3e - - grapheme_id: Malayalam-2-114 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u0d33\u0d3f - - grapheme_id: Malayalam-2-116 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4a - - grapheme_id: Malayalam-2-117 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15 - - grapheme_id: Malayalam-2-118 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d3f - - grapheme_id: Malayalam-2-119 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d41 - - grapheme_id: Malayalam-2-11a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d41\u0d02 - - grapheme_id: Malayalam-2-11b - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d46 - - grapheme_id: Malayalam-2-11c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u200d\u0d15\u0d4d\u0d15\u0d4d\u200c - - grapheme_id: Malayalam-2-126 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d35\u0d4d\u0d2f\u0d02 - - grapheme_id: Malayalam-2-127 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d35\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-12e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d36\u0d4d\u0d35\u0d3e - - grapheme_id: Malayalam-2-132 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-133 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d30 - - grapheme_id: Malayalam-2-134 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d30\u0d40 - - grapheme_id: Malayalam-2-137 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d2f\u0d3e - - grapheme_id: Malayalam-2-13a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d3e\u0d02 - - grapheme_id: Malayalam-2-13d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d15\u0d3e - - grapheme_id: Malayalam-2-13f - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d24\u0d4d\u0d30\u0d02 - - grapheme_id: Malayalam-2-140 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d24\u0d4d\u0d30\u0d40 - - grapheme_id: Malayalam-2-142 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d25\u0d3e - - grapheme_id: Malayalam-2-143 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d25\u0d3f - - grapheme_id: Malayalam-2-145 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d4d\u200c - - grapheme_id: Malayalam-2-147 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d35\u0d3e - - grapheme_id: Malayalam-2-148 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d35\u0d40 - - grapheme_id: Malayalam-2-14a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d3e - - grapheme_id: Malayalam-2-14b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d3f - - grapheme_id: Malayalam-2-14c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d41\u0d02 - - grapheme_id: Malayalam-2-14d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d4b - - grapheme_id: Malayalam-2-151 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d39\u0d3f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d4a - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d24\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d37\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d16\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d17\u0d4d\u0d26\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d17\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d15\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d19\u0d4d\u0d19\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1a\u0d4d\u0d1a\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1c\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1e\u0d4d\u0d1e\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1e\u0d4d\u0d1e\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d1f\u0d4d\u0d1f\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d23\u0d4d\u0d1f\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d24\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d28\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d28\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d2f\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d24\u0d4d\u0d35\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d26\u0d4d\u0d27\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d26\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30\u0d4d\u0d2f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d24\u0d4d\u0d30\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d27\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d40 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d28\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2e\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d28\u0d4d\u0d2f\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d2a\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2a\u0d4d\u0d30\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2c\u0d4d\u0d32\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2d\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2d\u0d4d\u0d30\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2a\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2a\u0d4d\u0d30 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2e\u0d4d\u0d2e\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d15\u0d4d\u0d15\u0d41 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d24\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d2f\u0d4d\u0d2f\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d30\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d31\u0d4d\u0d31\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d31\u0d4d\u0d31\u0d47 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d3e\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d32\u0d4d\u0d32\u0d46 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u0d33\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d33\u0d4d\u0d33\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d35\u0d4d\u0d2f\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d35\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d36\u0d4d\u0d35\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d30 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d1f\u0d4d\u0d30\u0d40 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d37\u0d4d\u0d2f\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d3e\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d15\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d24\u0d4d\u0d30\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d24\u0d4d\u0d30\u0d40 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d25\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d25\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d31\u0d4d\u0d31\u0d4d\u200c - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d35\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d35\u0d40 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d3e - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d3f - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d41\u0d02 - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d38\u0d4d\u0d38\u0d4b - - inherited_from: Malayalam - measured_by_wcwidth: 2 - wchars: \u0d39\u0d3f\u0d02 - - grapheme_id: Malayalam-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d15\u0d4d\u0d15\u0d46 - - grapheme_id: Malayalam-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0d17\u0d4d\u0d17\u0d02 - n_errors: 246 - n_total: 845 - pct_success: 70.88757396449704 - Maldivian: - failed: [] - n_errors: 0 - n_total: 226 - pct_success: 100.0 - Maori (2): - failed: [] - n_errors: 0 - n_total: 48 - pct_success: 100.0 - Marathi: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u0940 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0927\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u093f\u0903 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u091a\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0940 - - inherited_from: Magahi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - grapheme_id: Marathi-2-00 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0915\u093e\u0902 - - grapheme_id: Marathi-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u0940 - - grapheme_id: Marathi-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u0940\u0902 - - grapheme_id: Marathi-2-04 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u092f\u093e - - grapheme_id: Marathi-2-08 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u092f\u093e - - grapheme_id: Marathi-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u092f\u093e - - grapheme_id: Marathi-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u092f\u093e\u200c - - grapheme_id: Marathi-2-10 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091f\u094d\u092f\u093e - - grapheme_id: Marathi-2-14 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0923\u094d\u092f\u093e - - grapheme_id: Marathi-2-15 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0923\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-17 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u094d\u0935\u093e - - grapheme_id: Marathi-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u094d\u0935\u0947 - - grapheme_id: Marathi-2-1a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-1b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u094d\u092f - - grapheme_id: Marathi-2-1c - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u094d\u092f\u093e - - grapheme_id: Marathi-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u094d\u092f\u0947 - - grapheme_id: Marathi-2-1e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0935\u093e - - grapheme_id: Marathi-2-1f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0935\u093e\u0902 - - grapheme_id: Marathi-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0926\u093f - - grapheme_id: Marathi-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0926\u0940 - - grapheme_id: Marathi-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f\u0902 - - grapheme_id: Marathi-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u0940 - - grapheme_id: Marathi-2-26 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u0940\u0902 - - grapheme_id: Marathi-2-27 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u092f\u093e - - grapheme_id: Marathi-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093f - - grapheme_id: Marathi-2-30 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0939\u093e - - grapheme_id: Marathi-2-31 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0939\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-38 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091c\u093e - - grapheme_id: Marathi-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u0940 - - grapheme_id: Marathi-2-3a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u0940 - - grapheme_id: Marathi-2-3d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092d\u093e - - grapheme_id: Marathi-2-3f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-40 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u093e\u0902 - - grapheme_id: Marathi-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u200d\u092f\u093e - - grapheme_id: Marathi-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e\u0902 - - grapheme_id: Marathi-2-45 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u0932\u093e - - grapheme_id: Marathi-2-49 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0930\u093e\u0902 - - grapheme_id: Marathi-2-4b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093e\u0902 - - grapheme_id: Marathi-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u0940 - - grapheme_id: Marathi-2-4d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u0940\u0902 - - grapheme_id: Marathi-2-4e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u093e - - grapheme_id: Marathi-2-4f - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u093e\u0902 - - grapheme_id: Marathi-2-51 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u093f - - grapheme_id: Marathi-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u093e - - grapheme_id: Marathi-2-54 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0939\u094d\u092f\u093e - n_errors: 86 - n_total: 391 - pct_success: 78.00511508951406 - Mazahua Central: - failed: [] - n_errors: 0 - n_total: 66 - pct_success: 100.0 - Mirandese: - failed: [] - n_errors: 0 - n_total: 62 - pct_success: 100.0 - Mixtec, Metlatónoc: - failed: [] - n_errors: 0 - n_total: 65 - pct_success: 100.0 - Mon: - failed: - - grapheme_id: Mon-2-0d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1007\u103c\u1031 - - grapheme_id: Mon-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1012\u1039\u1002\u1031 - - grapheme_id: Mon-2-22 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1016\u103b\u1031\u1036 - - grapheme_id: Mon-2-23 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1017\u1039\u1010\u1031 - - grapheme_id: Mon-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1017\u1039\u105c\u1031 - - grapheme_id: Mon-2-26 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u1017\u103c\u1031 - - grapheme_id: Mon-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u101e\u1039\u1002\u1031 - n_errors: 7 - n_total: 332 - pct_success: 97.89156626506023 - Mongolian, Halh (Mongolian): - failed: [] - n_errors: 0 - n_total: 22 - pct_success: 100.0 - Mòoré: - failed: [] - n_errors: 0 - n_total: 63 - pct_success: 100.0 - Nanai: - failed: [] - n_errors: 0 - n_total: 65 - pct_success: 100.0 - Navajo: - failed: [] - n_errors: 0 - n_total: 53 - pct_success: 100.0 - Nepali: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092a\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092a\u0940 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u0940 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0930\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u093f\u0903 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930 - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0924\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u092d\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092d\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u200d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u0932\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0930\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0915\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Magahi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u094d\u092f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - grapheme_id: Nepali-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093e - - grapheme_id: Nepali-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0928\u093e - - grapheme_id: Nepali-2-03 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0938\u093e - - grapheme_id: Nepali-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u0930\u093e - - grapheme_id: Nepali-2-0c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091e\u094d\u091a\u093e - - grapheme_id: Nepali-2-11 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0926\u093e - - grapheme_id: Nepali-2-15 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0915\u093e - - grapheme_id: Nepali-2-16 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u091a\u093f - - grapheme_id: Nepali-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0926\u093e - - grapheme_id: Nepali-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0926\u094b - - grapheme_id: Nepali-2-1d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0928\u093e - - grapheme_id: Nepali-2-1e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092b\u094d\u0928\u094b - - grapheme_id: Nepali-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u0924\u093f - - grapheme_id: Nepali-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092a\u093e - - grapheme_id: Nepali-2-25 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u093e - - grapheme_id: Nepali-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u094d\u0927 - - grapheme_id: Nepali-2-2a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0928\u093f - - grapheme_id: Nepali-2-2f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0938\u093e - - grapheme_id: Nepali-2-33 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u093f - - grapheme_id: Nepali-2-36 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u091f\u094d\u092f\u093e - - grapheme_id: Nepali-2-38 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094b - - grapheme_id: Nepali-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0930\u094b - n_errors: 69 - n_total: 352 - pct_success: 80.39772727272727 - Nuosu: - failed: [] - n_errors: 0 - n_total: 336 - pct_success: 100.0 - Orok: - failed: [] - n_errors: 0 - n_total: 65 - pct_success: 100.0 - Otomi, Mezquital: - failed: [] - n_errors: 0 - n_total: 72 - pct_success: 100.0 - Panjabi, Eastern: - failed: [] - n_errors: 0 - n_total: 302 - pct_success: 100.0 - Panjabi, Western: - failed: [] - n_errors: 0 - n_total: 62 - pct_success: 100.0 - Pashto, Northern: - failed: [] - n_errors: 0 - n_total: 62 - pct_success: 100.0 - Picard: - failed: [] - n_errors: 0 - n_total: 53 - pct_success: 100.0 - Pular (Adlam): - failed: [] - n_errors: 0 - n_total: 93 - pct_success: 100.0 - Saint Lucian Creole French: - failed: [] - n_errors: 0 - n_total: 58 - pct_success: 100.0 - Sanskrit: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0930\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093f - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u094d\u092f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091a\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u091b\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u091e\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u091c\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u093f - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092f\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0930\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u093f\u0902 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u092d\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0927\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u093f\u0903 - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u0903 - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930 - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u093f - - inherited_from: Nepali - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0926\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0930\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u092c\u094d\u0927\u093f - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092a\u094d\u0930 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092e\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f - - inherited_from: Magahi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u094d\u0924 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0925\u093f - - inherited_from: Nepali - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092e\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u092f\u093e - - inherited_from: Maithili - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u093e\u0902 - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0935\u094b - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0935\u094d\u092f\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u091a\u093f - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0915\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093e\u0902 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930 - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u093e - - inherited_from: Nepali - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u093f - - inherited_from: Magahi - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u0920\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u0940 - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093f - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u093e - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0935\u0940 - - grapheme_id: Sanskrit-2-00 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0915\u093e - - grapheme_id: Sanskrit-2-02 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u0947\u0903 - - grapheme_id: Sanskrit-2-03 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u094d\u092f\u094b\u0903 - - grapheme_id: Sanskrit-2-04 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0930\u093e - - grapheme_id: Sanskrit-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e\u0902 - - grapheme_id: Sanskrit-2-07 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u093e\u0903 - - grapheme_id: Sanskrit-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u094b - - grapheme_id: Sanskrit-2-0a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0937\u094d\u092f\u093e - - grapheme_id: Sanskrit-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0916\u094d\u092f\u0903 - - grapheme_id: Sanskrit-2-0e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0917\u094d\u092f\u0948\u0903 - - grapheme_id: Sanskrit-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0917\u094d\u0930\u094d\u092f - - grapheme_id: Sanskrit-2-11 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091e\u094d\u091a\u093f - - grapheme_id: Sanskrit-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0923\u094d\u0921\u0903 - - grapheme_id: Sanskrit-2-19 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0923\u094d\u0921\u093f - - grapheme_id: Sanskrit-2-1b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u093e\u0903 - - grapheme_id: Sanskrit-2-1d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0924\u094d\u0925\u093f - - grapheme_id: Sanskrit-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0938\u094b - - grapheme_id: Sanskrit-2-25 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u0938\u094d\u0935\u093e - - grapheme_id: Sanskrit-2-27 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u0903 - - grapheme_id: Sanskrit-2-28 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0926\u094d\u0927\u0948\u0903 - - grapheme_id: Sanskrit-2-2a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0927\u093e\u0903 - - grapheme_id: Sanskrit-2-2c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u093e\u0903 - - grapheme_id: Sanskrit-2-2e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u091e\u094d\u092f\u093e - - grapheme_id: Sanskrit-2-33 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094b - - grapheme_id: Sanskrit-2-34 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u093e\u0903 - - grapheme_id: Sanskrit-2-35 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u0940 - - grapheme_id: Sanskrit-2-36 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u094d\u092f - - grapheme_id: Sanskrit-2-37 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u094d\u092f\u0902 - - grapheme_id: Sanskrit-2-38 - measured_by_terminal: 5 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u094d\u092f\u093e - - grapheme_id: Sanskrit-2-39 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930\u094d\u092f\u0947 - - grapheme_id: Sanskrit-2-3a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0927\u093e - - grapheme_id: Sanskrit-2-3c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0928\u093e - - grapheme_id: Sanskrit-2-3d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0928\u093e\u0903 - - grapheme_id: Sanskrit-2-41 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u092f\u094b - - grapheme_id: Sanskrit-2-45 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u0924\u094c - - grapheme_id: Sanskrit-2-47 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092a\u094d\u092f\u093e - - grapheme_id: Sanskrit-2-49 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092d\u093f\u0903 - - grapheme_id: Sanskrit-2-4a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092d\u094d\u092f\u0903 - - grapheme_id: Sanskrit-2-4b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092d\u094d\u092f\u093e\u0902 - - grapheme_id: Sanskrit-2-4d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092d\u094d\u092f\u094b - - grapheme_id: Sanskrit-2-4e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u0928\u093f - - grapheme_id: Sanskrit-2-50 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092f\u093e\u0903 - - grapheme_id: Sanskrit-2-52 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092f\u094d\u092f\u094b - - grapheme_id: Sanskrit-2-54 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u093e\u0903 - - grapheme_id: Sanskrit-2-58 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0917\u0940 - - grapheme_id: Sanskrit-2-5a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u091a\u093f - - grapheme_id: Sanskrit-2-5c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0923\u093e\u0902 - - grapheme_id: Sanskrit-2-5d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0923\u0940 - - grapheme_id: Sanskrit-2-5e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u093f\u0903 - - grapheme_id: Sanskrit-2-60 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0924\u094d\u0924\u093f - - grapheme_id: Sanskrit-2-62 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0926\u093f - - grapheme_id: Sanskrit-2-64 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0927\u093f - - grapheme_id: Sanskrit-2-6a - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0939\u093e\u0903 - - grapheme_id: Sanskrit-2-6b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0939\u093f - - grapheme_id: Sanskrit-2-6c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0930\u094d\u0939\u094b - - grapheme_id: Sanskrit-2-6d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u093e\u0903 - - grapheme_id: Sanskrit-2-6e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u0915\u093e - - grapheme_id: Sanskrit-2-70 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0932\u094d\u092f\u093e\u0903 - - grapheme_id: Sanskrit-2-73 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0935\u093e\u0903 - - grapheme_id: Sanskrit-2-75 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u093e\u0903 - - grapheme_id: Sanskrit-2-77 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u092f\u093e - - grapheme_id: Sanskrit-2-79 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0936\u094d\u0935\u093f - - grapheme_id: Sanskrit-2-7a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u093e\u0903 - - grapheme_id: Sanskrit-2-7b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u0903 - - grapheme_id: Sanskrit-2-7c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093e - - grapheme_id: Sanskrit-2-7d - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u093e\u0903 - - grapheme_id: Sanskrit-2-7e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u0947 - - grapheme_id: Sanskrit-2-7f - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u091f\u094d\u0930\u094b - - grapheme_id: Sanskrit-2-80 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u092a\u094d\u0930 - - grapheme_id: Sanskrit-2-81 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0937\u094d\u092f\u0903 - - grapheme_id: Sanskrit-2-83 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0915\u093e - - grapheme_id: Sanskrit-2-84 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0915\u093e\u0903 - - grapheme_id: Sanskrit-2-85 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u093f - - grapheme_id: Sanskrit-2-87 - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0924\u094d\u0930\u093e - - grapheme_id: Sanskrit-2-88 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0925\u093e\u0902 - - grapheme_id: Sanskrit-2-89 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u0927\u0940 - - grapheme_id: Sanskrit-2-8a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092e\u093f - - grapheme_id: Sanskrit-2-8b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092e\u093f\u0902 - - grapheme_id: Sanskrit-2-8d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u093e\u0902 - - grapheme_id: Sanskrit-2-8e - measured_by_terminal: 4 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u093e\u0903 - - grapheme_id: Sanskrit-2-8f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0938\u094d\u092f\u0941\u0903 - n_errors: 145 - n_total: 493 - pct_success: 70.58823529411765 - Sanskrit (Grantha): - failed: - - grapheme_id: Sanskrit (Grantha)-2-03 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011315\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-04 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011315\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-20 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001131f\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-21 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001131f\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-24 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011320\U00011348\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-2a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011323\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-31 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-32 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-34 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U0001133f\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-35 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U0001133f\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-37 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U00011341\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-3a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011324\U00011347\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-40 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011325\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011327\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-4e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011327\U0001133f\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-52 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011327\U00011347\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011327\U00011348\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-57 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011328\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-58 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011328\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-5a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011328\U0001133f\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-5f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011328\U00011348\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-6c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132d\U0001133f\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-73 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132e\U0001133f\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-7d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132f\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-7e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132f\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-81 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132f\U00011341\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-84 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132f\U00011348\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-86 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U0001132f\U0001134b\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-8a - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011330\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-8c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011330\U0001133f\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-94 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011332\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-96 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011332\U0001133f\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-9d - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011335\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-9e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011335\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-a7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011336\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-af - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011337\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-b0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011337\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-2-b9 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011338\U0001133e\U00011302 - - grapheme_id: Sanskrit (Grantha)-2-c1 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \U00011339\U0001133e\U00011303 - - grapheme_id: Sanskrit (Grantha)-1-07 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011315\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-0a - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011317\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-0d - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001131a\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-12 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001131c\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-13 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001131e\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-15 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001131f\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-1a - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011323\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-1d - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011324\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-21 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011326\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-24 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011327\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-27 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011328\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-2a - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001132a\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-2c - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001132c\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-2e - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001132d\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-31 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001132e\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-34 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U0001132f\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-37 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011330\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-39 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011332\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-3c - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011335\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-3f - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011336\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-41 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011337\U0001134d - - grapheme_id: Sanskrit (Grantha)-1-44 - measured_by_terminal: 2 - measured_by_wcwidth: 1 - wchars: \U00011338\U0001134d - n_errors: 61 - n_total: 293 - pct_success: 79.18088737201366 - Secoya: - failed: [] - n_errors: 0 - n_total: 53 - pct_success: 100.0 - Seraiki: - failed: [] - n_errors: 0 - n_total: 64 - pct_success: 100.0 - Shan: - failed: [] - n_errors: 0 - n_total: 181 - pct_success: 100.0 - Shipibo-Conibo: - failed: [] - n_errors: 0 - n_total: 64 - pct_success: 100.0 - Sinhala: - failed: [] - n_errors: 0 - n_total: 258 - pct_success: 100.0 - Siona: - failed: [] - n_errors: 0 - n_total: 67 - pct_success: 100.0 - South Azerbaijani: - failed: [] - n_errors: 0 - n_total: 67 - pct_success: 100.0 - Tagalog (Tagalog): - failed: [] - n_errors: 0 - n_total: 34 - pct_success: 100.0 - Tai Dam: - failed: [] - n_errors: 0 - n_total: 189 - pct_success: 100.0 - Tamang, Eastern: - failed: - - inherited_from: Bhojpuri - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0924\u093f - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0924\u094d\u092e\u093e - - inherited_from: Hindi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0924\u094d\u0930 - - inherited_from: Sanskrit - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0928\u093e - - inherited_from: Marathi - measured_by_wcwidth: 2 - wchars: \u0928\u094d\u0939\u093e - - grapheme_id: Tamang, Eastern-2-00 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0915\u094d\u0915\u094b - - grapheme_id: Tamang, Eastern-2-01 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0917\u094d\u092f\u093e - - grapheme_id: Tamang, Eastern-2-03 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u091a\u094d\u092f\u094b - - grapheme_id: Tamang, Eastern-2-07 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u0939\u0940 - - grapheme_id: Tamang, Eastern-2-08 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u092e\u094d\u0939\u094b - - grapheme_id: Tamang, Eastern-2-09 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0939\u094d\u0930\u093e - n_errors: 11 - n_total: 70 - pct_success: 84.28571428571429 - Tamazight, Central Atlas: - failed: [] - n_errors: 0 - n_total: 70 - pct_success: 100.0 - Tamil: - failed: [] - n_errors: 0 - n_total: 175 - pct_success: 100.0 - Tamil (Sri Lanka): - failed: [] - n_errors: 0 - n_total: 175 - pct_success: 100.0 - Telugu: - failed: - - grapheme_id: Telugu-2-06 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c41\u0c02 - - grapheme_id: Telugu-2-0b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c15\u0c41 - - grapheme_id: Telugu-2-0f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c24\u0c41 - - grapheme_id: Telugu-2-12 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c30\u0c3e\u0c02 - - grapheme_id: Telugu-2-14 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c30\u0c42 - - grapheme_id: Telugu-2-18 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c37\u0c3f\u0c02 - - grapheme_id: Telugu-2-1b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c15\u0c4d\u0c38\u0c4d\u0c35\u0c3e - - grapheme_id: Telugu-2-29 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c1a\u0c4d\u0c1a\u0c41 - - grapheme_id: Telugu-2-34 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c1f\u0c41\u0c02 - - grapheme_id: Telugu-2-36 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c1f\u0c4d\u0c32\u0c41 - - grapheme_id: Telugu-2-39 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c21\u0c4d\u0c30\u0c41 - - grapheme_id: Telugu-2-42 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c24\u0c4d\u0c24\u0c41 - - grapheme_id: Telugu-2-44 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c24\u0c4d\u0c28\u0c3f\u0c02 - - grapheme_id: Telugu-2-48 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c24\u0c4d\u0c2f\u0c02 - - grapheme_id: Telugu-2-4b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c24\u0c4d\u0c30\u0c41 - - grapheme_id: Telugu-2-4c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c24\u0c4d\u0c30\u0c4d\u0c2f - - grapheme_id: Telugu-2-53 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c26\u0c41\u0c02 - - grapheme_id: Telugu-2-57 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c26\u0c4d\u0c26\u0c41 - - grapheme_id: Telugu-2-5b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c26\u0c4d\u0c27\u0c3f\u0c02 - - grapheme_id: Telugu-2-5f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c26\u0c4d\u0c30\u0c4d\u0c2f - - grapheme_id: Telugu-2-69 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c28\u0c41\u0c02 - - grapheme_id: Telugu-2-6f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c28\u0c4d\u0c28\u0c41 - - grapheme_id: Telugu-2-83 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c2d\u0c4d\u0c2f\u0c41 - - grapheme_id: Telugu-2-8e - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c2f\u0c41\u0c02 - - grapheme_id: Telugu-2-95 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c1a\u0c41 - - grapheme_id: Telugu-2-97 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c23\u0c3e\u0c02 - - grapheme_id: Telugu-2-98 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c23\u0c41 - - grapheme_id: Telugu-2-9b - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c24\u0c3f\u0c02 - - grapheme_id: Telugu-2-9c - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c24\u0c43 - - grapheme_id: Telugu-2-9f - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c25\u0c3f\u0c02 - - grapheme_id: Telugu-2-a4 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c2c\u0c02 - - grapheme_id: Telugu-2-ad - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c39\u0c3e\u0c02 - - grapheme_id: Telugu-2-ae - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c30\u0c4d\u0c39\u0c41 - - grapheme_id: Telugu-2-c5 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c36\u0c4d\u0c30\u0c3e\u0c02 - - grapheme_id: Telugu-2-cb - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c37\u0c4d\u0c1f\u0c41 - - grapheme_id: Telugu-2-cc - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c37\u0c4d\u0c1f\u0c4d\u0c30 - - grapheme_id: Telugu-2-cd - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c37\u0c4d\u0c1f\u0c4d\u0c30\u0c40 - - grapheme_id: Telugu-2-d0 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c37\u0c4d\u0c20\u0c41 - - grapheme_id: Telugu-2-d7 - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c38\u0c4d\u0c15\u0c43 - - grapheme_id: Telugu-2-da - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c38\u0c4d\u0c24\u0c4d\u0c30 - - grapheme_id: Telugu-2-db - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c38\u0c4d\u0c24\u0c4d\u0c30\u0c40 - - grapheme_id: Telugu-2-df - measured_by_terminal: 3 - measured_by_wcwidth: 2 - wchars: \u0c38\u0c4d\u0c25\u0c4d\u0c2f - n_errors: 42 - n_total: 384 - pct_success: 89.0625 - Tem: - failed: [] - n_errors: 0 - n_total: 67 - pct_success: 100.0 - Thai: - failed: [] - n_errors: 0 - n_total: 262 - pct_success: 100.0 - Thai (2): - failed: [] - n_errors: 0 - n_total: 252 - pct_success: 100.0 - Tibetan, Central: - failed: [] - n_errors: 0 - n_total: 214 - pct_success: 100.0 - Ticuna: - failed: [] - n_errors: 0 - n_total: 88 - pct_success: 100.0 - Uduk: - failed: [] - n_errors: 0 - n_total: 57 - pct_success: 100.0 - Urdu: - failed: - - grapheme_id: Urdu-2-00 - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchars: \u0601\u06f1 - n_errors: 1 - n_total: 110 - pct_success: 99.0909090909091 - Urdu (2): - failed: - - inherited_from: Urdu - measured_by_wcwidth: 2 - wchars: \u0601\u06f1 - n_errors: 1 - n_total: 82 - pct_success: 98.78048780487805 - Veps: - failed: [] - n_errors: 0 - n_total: 59 - pct_success: 100.0 - Vietnamese: - failed: [] - n_errors: 0 - n_total: 117 - pct_success: 100.0 - Vietnamese (Han nom): - failed: [] - n_errors: 0 - n_total: 559 - pct_success: 100.0 - Waama: - failed: [] - n_errors: 0 - n_total: 56 - pct_success: 100.0 - Yaneshaʼ: - failed: [] - n_errors: 0 - n_total: 75 - pct_success: 100.0 - Yiddish, Eastern: - failed: [] - n_errors: 0 - n_total: 58 - pct_success: 100.0 - Yoruba: - failed: [] - n_errors: 0 - n_total: 73 - pct_success: 100.0 - Éwé: - failed: [] - n_errors: 0 - n_total: 79 - pct_success: 100.0 - narrow_results: - 17.0.0: - failed_codepoints: - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0600 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0601 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0602 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0603 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0604 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0605 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u06dd - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u070f - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0890 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u0891 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \u08e2 - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \U000110bd - - measured_by_terminal: 0 - measured_by_wcwidth: 1 - wchar: \U000110cd - n_errors: 13 - n_total: 36254 - pct_success: 99.9641418877917 - ri_results: - '17.0': - failed_codepoints: [] - n_errors: 0 - n_total: 262 - pct_success: 100.0 - sfz_results: - 17.0.0: - failed_codepoints: [] - n_errors: 0 - n_total: 5 - pct_success: 100.0 - sri_results: - 17.0.0: - failed_codepoints: - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e6 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e7 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e9 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ea - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1eb - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ec - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ed - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ee - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ef - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f0 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f1 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f2 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f3 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f4 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f5 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f6 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f7 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f9 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fa - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fb - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fc - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fd - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fe - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ff - n_errors: 26 - n_total: 26 - pct_success: 0.0 - unicode_wide_results: - 17.0.0: - failed_codepoints: - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e6 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e7 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1e9 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ea - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1eb - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ec - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ed - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ee - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ef - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f0 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f1 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f2 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f3 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f4 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f5 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f6 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f7 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1f9 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fa - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fb - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fc - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fd - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1fe - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f1ff - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001f6d8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001fa8a - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001fa8e - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001fac8 - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001facd - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001faea - - measured_by_terminal: 1 - measured_by_wcwidth: 2 - wchar: \U0001faef - n_errors: 33 - n_total: 43592 - pct_success: 99.92429803633695 -wcwidth_version: 0.8.2 -width: 200 diff --git a/rio-vt-perf-vs-ghostty.md b/rio-vt-perf-vs-ghostty.md deleted file mode 100644 index 981735229a..0000000000 --- a/rio-vt-perf-vs-ghostty.md +++ /dev/null @@ -1,410 +0,0 @@ -# rio-vt vs ghostty: closing the benchmark gaps - -Deep analysis of every workload where rio-vt loses in ../rio-vt-benchmark, with -profile evidence and ranked plans. Sources analyzed: local rio-vt 0.5.2 and -../ghostty (b14d92383, close to the libghostty-rs pin). Profiles taken with -macOS `sample`/samply on Apple Silicon against the exact benchmark corpora. - -## The gaps (criterion medians, 2026-07-31 run) - -| workload | rio-vt | ghostty | others | gap | -|---|---|---|---|---| -| unicode_wide | 245 MiB/s | 597 | alacritty 338 | 2.4x | -| ascii_plain | 829 MiB/s | 1538 | | 1.9x | -| scroll_storm | 267 MiB/s | 477 | | 1.8x | -| mixed (process) | 287 MiB/s | 342 | | 1.2x | -| sgr_churn | 230 MiB/s | 233 | alacritty 331, vt100 325 | 1.4x vs alacritty | -| resize_reflow | 1006 µs | 415 µs | vt100 108 (clips, no reflow) | 2.4x | - -rio-vt wins: alt_screen_redraw (565 vs 529), serialize (4.4 µs vs 12.5), plain -resize (5.1 µs vs 70 — ghostty always rewrites pages cell-by-cell; rio's -no-reflow fast paths skip that entirely). - -## Root causes (cross-cutting) - -Five patterns explain all six gaps. Ghostty's core wins not on the inner loops -(its per-cell fill is the same packed-u64 OR-store rio already does) but on -driving every per-line and per-run *fixed* cost to near zero. - -1. **O(rows) bookkeeping on every scroll.** Each linefeed marks 24 rows dirty - through per-line ring-index math, runs a second 24-iteration damage loop, - resets 80 cells ignoring `occ`, and moves an `Option` even when - `None`. Ghostty: one memset, one 8-byte-row-header rotate, `page.dirty = - true`. This taxes ascii_plain (~30% of its time) and unicode_wide too, not - just scroll_storm (~55%). -2. **Early-exit scalar scans that never vectorize.** Two hot loops carry - comments claiming LLVM auto-vectorizes them; it does not (early-exit - iterator loops can't). Ghostty hand-vectorizes both of its equivalents and - says so in comments. ~46% of ascii_plain time. -3. **Heap allocation on hot paths.** A `Vec>` per SGR sequence - (~10 ns, the bigger half of the sgr gap); `split_off`/`append`/`resize_with` - realloc churn per row during reflow (~20-30k heap calls per resize - iteration vs ghostty's ~50 pooled page ops). -4. **Batching exists for ASCII but not for wide chars or non-ASCII decode.** - `input_str` has a good bulk path; `input_codepoints` writes wide chars one - codepoint at a time (~4 cursor resolutions, 2 template builds, 2 atlas - checks per char), and the parser splits multi-byte runs at every ASCII - byte, so simdutf gets 9-24-byte FFI calls (4 per line) instead of 4KB - chunks. ~80% of the unicode gap. -5. **Per-event tracing checks and eager style interning.** `debug!`/`trace!` - atomics run per CR and per LF; every SGR attribute does a full - FxHashMap hash+probe re-intern even when the style is unchanged. Ghostty - has the same interning weakness (hence its identical 233 on sgr_churn) and - a TODO acknowledging the fix rio should also make. - -## Validation status - -Two of the plans were already validated end-to-end on scratch copies of -rio-vt (repo untouched, all 401 unit tests pass on the patched copies): - -- ascii/scroll patch set: ascii_plain **884 → 1720 MiB/s** (past ghostty's - 1538), pure-scroll 62 → 110 MiB/s, mixed 385 → 433 (loop-mode harness). -- scroll patch set (independent agent, same three scroll fixes plus - occ-bounded reset): scroll_storm **273 → ~530 MiB/s** (past ghostty's 477), - LF cost 27.6 → 8.0 ns. - -The sgr plan is validated by microbenchmark (per-fix ns costs measured); the -unicode and reflow plans are measured decompositions with projected gains. - ---- - -## Phase 1 — validated quick wins (ascii_plain, scroll_storm, mixed, + partial sgr) - -**Status: IMPLEMENTED (2026-07-31)** in rio-vt. Criterion medians after -(vs the same-day 4-engine run): ascii_plain 829 → 2090 MiB/s, scroll_storm -267 → 743, mixed 287 → 417, alt_screen_redraw 565 → 841, sgr_churn 230 → 315 -— every parse workload except unicode_wide now beats ghostty; serialize and -resize unchanged. 402/402 rio-vt tests, rio-backend/librio suites, and the -render-parity binary all pass. Item 6 (`is_ascii` pass) was skipped: it needs -a new entry on the now-public Handler trait for ~3%. - -Implementation notes vs the plan below: the `Row::grow` occ mitigation -regressed resize_reflow 2x (the added branch stops `resize_with` from -lowering to a memset-like extend in the reflow loop) and was replaced by a -two-ended tail probe in `Row::reset` — grow's mixed tail is always -old-fill-then-defaults, so probing `inner[occ]` and `inner[len-1]` is exact. - -Everything here is small, local, and already proven on a scratch copy. - -1. **Branchless wide-cleanup scan** — `crosswords/mod.rs:3203`. Replace - `cells.iter().any(|c| c.needs_wide_cleanup())` with a `fold(false, |a, c| - a | c.needs_wide_cleanup())` (or 8-cell OR blocks against the wide mask). - Measured: ascii +30% alone. One line, no semantic change. -2. **Vectorize the printable-run end scan** — `performer/parser/mod.rs:800-804`. - SWAR u64 scan (stop byte = `<0x20 | ==0x7F | >=0x80` via wrapping-sub - masks, 8 B/iter) or NEON/SSE2. Fix the false "LLVM auto-vectorizes" - comment. Measured: print path +27%, compounds with #1. -3. **Kill per-scroll O(rows) bookkeeping** (both agents converged here; - measured ascii +27%, crlf-only +45%, LF 27.6 → 8.0 ns): - - Full-screen scroll region → `mark_fully_damaged()` once instead of the - 24x `damage_line` loop (`crosswords/mod.rs:1226-1229`); - `scroll_down_relative` already does this (mod.rs:1187), and - `peek_damage_event` prefers full damage, so semantics are unchanged. - - Skip the per-row `dirty = true` loop (`grid/mod.rs:426-431`) when full - damage is marked — `snapshot_visible`'s `needs_full` branch copies every - row regardless. For partial regions (DECSTBM) add - `Storage::mark_lines_dirty(start, end)`: resolve the ring offset once, - mark at most two contiguous slices, instead of 24x `compute_index`. - Contract to document/debug_assert: full-screen scroll implies full - damage (verify librio/canario snapshot consumers go through - `snapshot_visible`). - - `Row::reset` (`grid/row.rs:189-208`): occ-bounded reset — probe the last - cell; if resetting it is a no-op, only reset `inner[..occ]` (occ==0 for - recycled blank rows → free). Also build the proto cell once and use - `fill` so the full-reset path lowers to a vector fill. Watch the occ - invariant soft spots: `Row::grow` (row.rs:153-159) appends defaults - without touching occ, and `resize.rs:190` pushes to `inner` directly; - either fix `grow` or gate the fast path on the default style id. -4. **Selection guard + tracing gates** (measured ascii +18%, crlf 90 → 110): - - `mod.rs:1208-1211`: only `take()` the selection when `is_some()` — every - scroll currently moves a large `Option` out and back. - - Feature-gate the per-CR/per-LF `debug!`/`trace!` callsites - (mod.rs:1198, mod.rs:3665, handler.rs:931) — e.g. tracing - `release_max_level_info` for benches/release. Worth ~5% on mixed even - after the other patches. -5. **Kill the per-SGR-sequence Vec** — `performer/handler.rs:1516-1602` - (caller at 1335). Dispatch attrs directly to the handler like vte 0.15 - does, or use `ArrayVec, 32>` (params cap at 32). Microbench: - 9.7 ns/seq, i.e. ~+20-25% on sgr_churn alone. Near-zero risk. -6. **Drop the redundant `is_ascii` pass** — `mod.rs:3146`. The parser only - calls `print_str` with proven printable-ASCII bytes; add an internal - `input_ascii_run` entry with a debug_assert. ~3%. - -Projected phase-1 results: ascii_plain ~1.7 GiB/s (beats ghostty), scroll_storm -~530 MiB/s (beats ghostty), mixed +10-15% (parity or better), sgr_churn ~275. - -## Phase 2 — unicode_wide batching + sgr parity - -**Status: IMPLEMENTED (2026-07-31).** unicode_wide 243 → 723 MiB/s (3.0x), -past ghostty's 685; every parse workload now beats the field. Landed as: -run-classified `input_codepoints` with bulk narrow and wide-pair writers -(`write_narrow_run` / `write_wide_run`, scalar `write_codepoint_cell` -fallback for placeholders/cleanup/edges), width table extended over plane 1 -with a hoistable reference (`width_table()` + `width_in`), and chunked -whole-run decode: `ground_dispatch` feeds simdutf up to 4KB per call -(never splitting a sequence) and controls are split out in codepoint space -by `dispatch_codepoints`. Two deliberate semantic notes: a non-ASCII -charset now falls back to the scalar path in `input_codepoints` (ASCII can -ride in decode runs and must charset-map), and a standalone C1 byte inside -a decode run now executes (previously U+FFFD mid-run, execute at run -lead — unified to execute everywhere). unicode_wide render-parity vs vt100 -passes. The two resize parity-harness failures predate all of this (alpha.4 -fails them too: rio reflows, vt100 clips). - -unicode_wide (measured decomposition: ~185 ns/line in per-codepoint wide -writes, ~58 ns in fragmented simdutf FFI, ~30 ns scroll — phase 1 already -takes the scroll share): - -1. **Batch wide-char writes in `input_codepoints`** (`mod.rs:3057-3138`). - Mirror the ASCII bulk path: hoist template/charset/atlas/columns; classify - maximal same-width runs; precompute wide and spacer u64s once; store pairs - into the row slice after one branchless cleanup scan; cursor advance + - `damage_line` once per run. `Square` is `repr(transparent)` u64 — this is - exactly ghostty's `printSliceFill` shape (Terminal.zig:727-731). Fallbacks: - end-of-row wide wrap (LeadingSpacer), first cell needing wide cleanup, - INSERT mode (already falls back). Projected: 220 → 380-450 MiB/s alone. -2. **Decode whole ground runs in one simdutf call** — `parser/mod.rs:793-832`. - Extend the decode run to the next C0 control instead of the next ASCII - byte (printable ASCII decodes fine as UTF-8); route cp<0x20 to `execute` - in codepoint space (`dispatch_codepoints`, parser/mod.rs:894). Keeps the - pure-ASCII `print_str` path untouched. Kills the 4-tiny-FFI-calls-per-line - shape (58 → ~20-38 ns/line) and the per-space `input_str` preamble. - Careful: bare C1 run-lead execute semantics (parser/mod.rs:812-819). -3. **Full-plane static width LUT** — replace `codepoint_width.rs`'s - OnceLock+BMP table with a build-time 3-stage table covering all planes - (ghostty's `unicode/lut.zig` model). Removes the atomic load per lookup - and the scalar `UnicodeWidthChar` fallback for emoji. Enabler for #1's - run classifier. - -sgr_churn to parity: - -4. **256-slot direct-mapped style memo** in front of `StyleSet::intern` - (`style.rs:146-161`): fold the style to an index, 16-byte compare, fall - through to FxHashMap on miss. Safe (style ids are never invalidated; no - compaction exists). Measured 0.7 vs 5.0 ns. +10-13%. -5. **Unpacked cursor style + skip no-op re-interns** (`grid/mod.rs:656-660`): - keep a shadow `Style` beside the template id; early-return when an SGR - doesn't change it. Sync at set_template_style, cursor save/restore, grid - swap. Small here, bigger on real repeated-SGR traffic. - -Projected: unicode_wide ~450-550 MiB/s (near ghostty's 597), sgr_churn ~330 -(parity with alacritty/vt100). - -## Phase 3 — reflow streaming rewrite (resize_reflow) - -Profile: resize time is almost entirely `RawVec::reserve`→realloc churn + -`Row::shrink`'s per-row alloc+memmove + two O(n) ring rotates and two -`reverse()` passes. Cells move 2-3x; ~20-30k heap calls per iteration vs -ghostty's ~50 pooled page ops (its ReflowCursor writes each cell once into -pool-backed pages, trims trailing blanks, materializes blank rows lazily). - -1. **Streaming `shrink_columns`** (`grid/resize.rs:293-507`): walk wrapped - runs oldest-first, `extend_from_slice` occupied slices into output rows - pre-allocated at exact width; recycle consumed source-row Vecs as output - buffers (shrink: old cap ≥ new width → steady-state alloc-free, one move - per cell). Kills `split_off` allocs (row.rs:170), `append_front` full-row - memcpy (row.rs:278), `resize_with` tails. Hairiest part of the file: wide - spacers (resize.rs:404-433), cursor reflow (474-495), display_offset, - reflow_remap trackers must be preserved. Est. 40-60% off the shrink half. -2. **Streaming `grow_columns`** (resize.rs:95-282): same shape; push freed - source Vecs into the storage recycle pool so the next shrink is free. - Eliminates the per-merge realloc, `drain` memmove, and the third-copy - `Row::grow` pass. Est. 30-50% off the grow half. -3. **Open the recycle pool to column reflow** (`storage.rs:62`, cap - `MAX_CACHE_SIZE=1000` at storage.rs:14): byte-budgeted cap; a resize - out-and-back then cycles the same buffers with zero malloc. Trade-off: - retained memory — add idle shrink or byte cap. -4. **Copy only occupied cells** (`row.occ`) and replace `is_clear()` full - scans with occ==0 checks; blank tails become memset. -5. **Reflow only wrapped runs**: rows with `occ <= columns` that don't - continue a wrap move as 40-byte Row structs (zero cell movement). Neutral - on this all-wrapped benchmark; collapses realistic resizes toward the - existing 5 µs fast path. -6. **Kill the O(n) shuffles**: iterate the ring via the zero offset and emit - in storage order (no `rotate_left` rezero at storage.rs:360, no - `reverse()`); keep a persistent scratch Vec on Grid. Also grow cols before - rows like ghostty so new rows aren't created at the old width. - -Projected: at or below ghostty's 415 µs (rio has no per-cell style re-intern -to pay during reflow, unlike ghostty's style-set re-adds), while keeping the -no-scrollback fast path that already beats ghostty 14x. - -## Phase 4 — structural (optional, larger blast radius) - -- **Damage as a bitset**: `LineDamage {line, damaged}` is 16 bytes with a - redundant field (mod.rs:213-219); a u64 bitmask makes partial-region scroll - damage one OR. Longer term, a scroll-delta-aware damage model lets the - renderer blit instead of full-copy. -- **O(1) scroll**: move `dirty` out of `Row` (per-grid bitmask), pooled - pre-cleared rows so the recycled-row reset disappears (post-phase-1 residue - is ~8 ns/scroll vs ghostty's counter bump; crlf-only 110 MiB/s shows the - remaining headroom). -- **Bulk CSI param consumption** à la ghostty's `consumeCsiParams` - (stream.zig:661-752): consume digit runs with parser state in locals - instead of per-byte `change_state`→`advance_csi_param` (~6-8% of mixed). -- **Deferred style interning** (intern in `cell_template()` behind a dirty - flag): collapses multi-attr sequences (4 interns → 1); every reader of - `cursor.template.style_id()` needs a flush — do after phase 2's memo. - Ghostty has a TODO for exactly this. - -## Not worth touching (measured) - -The cell fill itself (~3%), the memchr ESC scan (~1%), `Square::from_template` -(already one OR-store), `Storage::swap` (5-qword copy), ring rotation -(already an offset bump, cheaper than ghostty's header memmove). - -## Follow-up optimizations (2026-07-31, same day) - -After the phase-1 review, three more changes landed in rio-vt: - -1. `Handler::input_ascii_str` — a provided trait method carrying the - parser's printable-ASCII guarantee across the Handler boundary, so - `Crosswords` skips the redundant `is_ascii` re-scan (`print_str` was - the second-hottest line in the print path). ascii +13%. -2. A direct-mapped 1024-slot memo of candidate ids in front of - `StyleSet::intern` (candidates verified against `styles[id]`, so stale - slots miss, never lie). Break-even on the adversarial 256-color-cycling - corpus, a win for real repeated-style traffic. -3. `advance_csi_param_run` — a CsiParam run consumer in the parser that - accumulates digit runs into a local instead of paying state dispatch and - `self.param` load/store per byte. sgr_churn +21%, putting it past - alacritty (349 vs 331 MiB/s). - -Also found: **rio-vt and libghostty-vt cannot be linked into one binary** — -both bundle simdutf (different versions, different compilers), and the -linker mixes the two C++ builds' objects, crashing with SIGBUS inside -`convert_utf8_to_utf32_with_errors` (this had silently killed the -benchmark's unicode_wide group all along). The benchmark repo now isolates -ghostty benches in separate binaries behind a `ghostty` feature. If librio -ever links against something bundling simdutf, the same applies; a real fix -needs symbol hiding or prefixing in the simdutf crate's build. - -## Reproducing - -- Benchmarks: `cargo bench` in ../rio-vt-benchmark (now includes ghostty via - libghostty-vt; needs zig 0.16). -- Profiles: harness at the session scratchpad `rio-prof/` (modes: ascii, - unicode, scroll, sgr, reflow), profiled with `sample 10` or samply. -- Validated patched scratch copies of rio-vt (with the phase-1 changes) were - left in the session scratchpad (`rio-copy/`, `riocopy/`); they pass the - full rio-vt test suite. - ---- - -## Phase 3 — concretized plan (2026-08-01, from ghostty + rio source dossiers) - -Baseline today: resize_reflow **763 µs** (tree includes the WIP shrink tail-buffer -pool in resize.rs/row.rs), ghostty 415 µs, alacritty 2.92 ms. Target ≤415 µs. - -### Why rio can beat 415 µs, not just match it - -Ghostty's reflow (~1,785 lines) is one streaming pass writing each cell once — -but ~60% of that implementation is capacity management for per-page interned -side tables: every styled cell pays a hash+probe re-intern into the destination -page's RefCountedSet (addWithId same-id fast path only holds while pages map -1:1), every grapheme/hyperlink does trial alloc/free probes, and style-set -overflow triggers increaseCapacity = full re-clone of the destination page -(PageList.zig:1770-1810, 1909-1932; their own TODO at page.zig:994-997 -questions addWithId). Rio's global style table means style_id survives reflow -untouched — none of that cost exists. Ghostty also sweeps O(tracked_pins) per -written cell (PageList.zig:1374-1386); rio's remap is positional and selection -is dropped on column resize. The only thing ghostty does better is the shape: -stream once, write once, recycle in lockstep. - -### Existing assets - -- **Dangling streaming core**: commit 6c5aec06d3 (chain: 94d946a1, 8ced3237, - 1cd2c875, 6c5aec06) has `grid/reflow.rs` — 256 lines, reflow-cursor model, - oldest-first streaming, trailing-blank trim, free-list recycling, 6 parity - tests. Cell-level only: cursor/remap/display_offset not wired. Reachable via - stash@{0}'s parent; recover with `git checkout 6c5aec06d3 -- rio-vt/src/crosswords/grid/reflow.rs`. -- **WIP in tree**: shrink tail-buffer pool (Row::shrink_into + - append_front_from + pool in shrink_columns). Superseded by the rewrite but - commit it first as a fallback increment. - -### Steps - -1. ~~WIP pool~~ (dropped — superseded by the rewrite; the uncommitted - resize.rs/row.rs changes stay uncommitted and get replaced). -2. **Resurrect reflow.rs** and adopt the three ghostty semantics it lacks: - direction-agnostic single pass (grow and shrink through one code path), - deferred blank-row counter (never materialize trailing blank rows), and - wide-char-at-last-column = emit LeadingSpacer, wrap, retry same cell. - Trailing-blank trim stays gated on !wrapline with overrides for - semantic-prompt rows and blanks left of the cursor (ghostty - PageList.zig:1288-1340 is the reference). -3. **Wire the bookkeeping** the core deliberately skipped, porting the exact - contracts from resize.rs: cursor mapping incl. should_wrap unwind/rewind at - entry/exit for BOTH cursors (ghostty forgets the live cursor's - pending_wrap — Screen.zig:1957 fixes only the saved one; don't copy that), - display_offset (+1 per row inserted above, −1 per row removed above, clamp - to history at end), reflow_remap trackers with the consumed = columns − - displaced accounting (tests.rs:374 and :407 are the guards), occ - maintenance (fix Row::grow's occ soft spot while there), and cap - truncation advancing total_lines_scrolled. -4. **Fix the metadata loss as part of the rewrite**: current Row::from_vec - hardcodes semantic_prompt=None / kitty_virtual_placeholder=false / - has_extras=true, so shrink-created rows lose OSC 133 marks and placeholder - flags today. The streaming core should copy row metadata from the source - row it's consuming (ghostty's copyRowMetadata carries semantic_prompt; it - re-derives the kitty flag per cell). Add mod.rs tests for both across a - reflow. -5. **Recycle in lockstep**: consumed source-row Vecs feed new destination rows - (shrink is naturally self-sufficient: old cap ≥ new width); grow's freed - buffers and cap-truncated rows push into Storage::free (byte-budgeted cap - replacing the count cap); Row::new filler and refill_to pull from it. - Steady state: allocation count ≈ wrap-count delta, peak ≈ max(old,new). -6. **Kill the O(rows) shuffles**: emit in ring order via the zero offset (no - reverse(), no rotate_left rezero in take_all), persistent scratch Vec on - Grid. Invert resize order ghostty-style: grow = cols then rows, shrink = - rows then cols (dispatcher rationale at PageList.zig:1001-1024) so filler - rows are born at the new width. -7. **Keep both fast paths** (5.1 µs plain resize beats ghostty 14x) and add - the doc-item-5 degeneration: unwrapped rows with occ ≤ columns move as Row - structs, zero cell movement — realistic resizes collapse toward the fast - path even when a few wrapped runs exist. - -### Test plan - -Existing: 8 unit tests in grid/tests.rs (incl. the two remap guards), storage -pool tests, parity harness (the two resize scenarios fail by design vs vt100 — -not a signal). Add: metadata propagation (step 4), occ invariant checks, -grow len==1 spacer-only merge, both-cursor pending-wrap across resize, blank- -row laziness (trailing blank rows never materialize), and a proptest: -random grid → reflow W1→W2→W1 → equal modulo trailing blanks, occ bounds -hold, wrapline/spacer pairing valid. Port ghostty's edge-case list (71 resize -tests in PageList.zig) as a checklist, not verbatim. - -### Gates - -resize_reflow ≤415 µs (stretch: ≤350 on the styled corpus, where ghostty pays -re-interns and rio doesn't); plain resize ≤5.5 µs unchanged; all parse -workloads unchanged; 402+ rio-vt tests green; librio/canario suites green. - -### Risks - -- remap trackers are the tightest coupling (settlement order vs push order). -- Row/Storage::swap 5-qword debug_assert — don't add Row fields. -- Behavior deltas from step 4 are visible (prompt-nav after resize) — note in - changelog as a fix. -- Blank-row laziness changes total_lines/history counts in edge cases — vt100 - parity harness will show diffs; adjudicate deliberately. - -### Phase 3 status (2026-08-01, paused) - -Streaming rewrite DONE on branch `reflow-streaming` (worktree ../rio-reflow, -commit a19670604e): grow+shrink unified in grid/reflow.rs (~350 lines), legacy -slow paths deleted, anchors replace incremental cursor/viewport/remap -bookkeeping, Storage::free is a persistent buffer pool, row metadata -(semantic_prompt/kitty flag) now survives reflow. 413/413 tests, clippy clean. - -Numbers: plain resize 5.1 → 4.0 µs; reflow warm (repeated drags, pool cycling) -467 µs/cycle; criterion cold 1006 → 763 µs vs ghostty 415. Profile: remaining -cold gap is ~7-10k per-row Vec regrowths on first widen (~300 µs, structural) -+ ~18 ns/row span-loop overhead. The structural fix is the contiguous-page -experiment in stash@{1} (grid/page.rs, 326 lines, validated in isolation) — -flat Vec per page, row-major, would put cold reflow under ghostty. - -stash@{0} = superseded shrink tail-buffer pool (resize.rs/row.rs WIP). -rio-vt-benchmark/Cargo.toml restored to `=0.5.3` (was temporarily pointed at -the worktree while iterating). From 64fcc80fbb0f7653b463a1872bb214a6305083bf Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sat, 5 Sep 2026 23:28:56 +0200 Subject: [PATCH 07/14] ime popup area spans caret to composition end --- frontends/rioterm/src/screen/mod.rs | 68 ++++++++++++++++++----------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index 5764095086..477c669e26 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -83,7 +83,7 @@ pub struct Screen<'screen> { /// be rebuilt when the composition moves or ends even when /// terminal damage reports nothing. last_preedit_row: Option, - last_ime_cursor_pos: Option<(f32, f32)>, + last_ime_cursor_pos: Option<(f32, f32, f32)>, hints_config: Vec>, /// Hint regexes compiled on first use, keyed by pattern. Hover /// hit-testing runs on every mouse move; recompiling the URL @@ -4666,25 +4666,34 @@ impl Screen<'_> { // not the terminal cursor: the composition renders inline and // can slide away from the cursor cell, and a popup opening // tens of cells from the caret overlaps the freshly drawn - // text. Mirrors the layout the renderer uses (same inputs). + // text. The reported area's width spans from the caret to the + // composition's end, so the OS also knows how much freshly + // drawn text to avoid covering. + // Mirrors the layout the renderer uses (same inputs). let content = ¤t_item.val.renderable_content; - let (anchor_row, anchor_col) = match self.ime.preedit().filter(|_| { - content.display_offset == 0 && content.columns > 0 && content.screen_lines > 0 - }) { - Some(preedit) => match rio_grid::preedit::PreeditLine::new( - &preedit.text, - preedit.cursor, - (cursor_pos.row.0.max(0) as usize).min(content.screen_lines - 1), - cursor_pos.col.0, - content.columns, - ) { - Some(line) => { - (line.row, line.popup_anchor_col().min(content.columns - 1)) - } - None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0), - }, - None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0), - }; + let (anchor_row, anchor_col, anchor_cells) = + match self.ime.preedit().filter(|_| { + content.display_offset == 0 + && content.columns > 0 + && content.screen_lines > 0 + }) { + Some(preedit) => match rio_grid::preedit::PreeditLine::new( + &preedit.text, + preedit.cursor, + (cursor_pos.row.0.max(0) as usize).min(content.screen_lines - 1), + cursor_pos.col.0, + content.columns, + ) { + Some(line) => { + let col = line.popup_anchor_col().min(content.columns - 1); + let cells = + line.end_col().min(content.columns).max(col + 1) - col; + (line.row, col, cells) + } + None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0, 1), + }, + None => (cursor_pos.row.0.max(0) as usize, cursor_pos.col.0, 1), + }; // Calculate pixel position of cursor — canonical integer // stride (line_height already baked into cell_height). @@ -4717,20 +4726,27 @@ impl Screen<'_> { return; } - // Check if position has changed significantly to avoid unnecessary updates - if let Some((last_x, last_y)) = self.last_ime_cursor_pos { - if (pixel_x - last_x).abs() < 1.0 && (pixel_y - last_y).abs() < 1.0 { - return; // Position hasn't changed significantly + // A PastEnd caret sits one cell past the composition, where + // `end_col - col` is 0; the area is always at least one cell. + let area_width = anchor_cells as f32 * cell_width; + + // Check if the area changed significantly to avoid unnecessary updates + if let Some((last_x, last_y, last_w)) = self.last_ime_cursor_pos { + if (pixel_x - last_x).abs() < 1.0 + && (pixel_y - last_y).abs() < 1.0 + && (area_width - last_w).abs() < 1.0 + { + return; // Area hasn't changed significantly } } - // Update last position - self.last_ime_cursor_pos = Some((pixel_x, pixel_y)); + // Update last area + self.last_ime_cursor_pos = Some((pixel_x, pixel_y, area_width)); // Set IME cursor area window.set_ime_cursor_area( rio_window::dpi::PhysicalPosition::new(pixel_x as f64, pixel_y as f64), - rio_window::dpi::PhysicalSize::new(cell_width as f64, cell_height as f64), + rio_window::dpi::PhysicalSize::new(area_width as f64, cell_height as f64), ); } From ef3a6e161fd444b2e870bfe12451a02c958d770a Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 08:58:06 +0200 Subject: [PATCH 08/14] preedit: overlay gating, wide-bleed guard, containing-cluster caret --- frontends/rioterm/src/application.rs | 56 +++++++++++---------- frontends/rioterm/src/context/mod.rs | 3 +- frontends/rioterm/src/context/renderable.rs | 7 ++- frontends/rioterm/src/screen/mod.rs | 1 - rio-grid/src/lib.rs | 29 ++++++++++- rio-grid/src/preedit.rs | 23 ++++++--- 6 files changed, 78 insertions(+), 41 deletions(-) diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs index 6d8d1cc7f4..ab1377cc71 100644 --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -2016,26 +2016,27 @@ impl ApplicationHandler for Application<'_> { } WindowEvent::Ime(ime) => { - // The assistant swallows IME input, but clear events - // (empty Preedit, Disabled) must still pass: dropping - // them leaves the window-level preedit stuck, and the - // preedit gate in `process_key_event` then swallows - // every plain keystroke after the assistant closes. - let assistant_active = route.window.screen.renderer.assistant.is_active(); + // Modal overlays (assistant, command palette) take + // their input through key events and never see IME + // ones: while one is up, swallow composition input but + // still CLEAR any stored preedit — a live composition + // would keep painting into the grid behind the overlay + // and its key gate would swallow every plain keystroke + // after the overlay closes. (Search stays open to IME: + // commits route into the search input via `paste`.) + let overlay_active = route.window.screen.renderer.assistant.is_active() + || route.window.screen.renderer.command_palette.is_enabled(); match ime { Ime::Commit(text) => { - if assistant_active { + if overlay_active { return; } // Don't use bracketed paste for single char input. route.window.screen.paste(&text, text.chars().count() > 1); } Ime::Preedit(text, cursor_offset) => { - if assistant_active && !text.is_empty() { - return; - } - let preedit = if text.is_empty() { + let preedit = if text.is_empty() || overlay_active { None } else { // The platform's `None` means the IME asked @@ -2050,7 +2051,22 @@ impl ApplicationHandler for Application<'_> { }; if route.window.screen.ime.preedit() != preedit.as_ref() { + let composing = preedit.is_some(); route.window.screen.ime.set_preedit(preedit); + if composing { + // The composition anchors at the cursor + // row: snap out of scrollback so it can + // never be live but invisible while the + // preedit key gate swallows input, and + // drop the selection like typing does — + // the composition is the active signal + // now. + route + .window + .screen + .scroll_bottom_when_cursor_not_visible(); + route.window.screen.clear_selection(); + } // The overlay lives in the grid's CPU cell // buffers and its rows are force-rebuilt // each frame while composing; a plain @@ -2058,14 +2074,7 @@ impl ApplicationHandler for Application<'_> { // scheduled — full terminal damage would // re-shape every visible row per // keystroke for nothing. - route - .window - .screen - .context_manager - .current_mut() - .renderable_content - .pending_update - .set_dirty(); + route.window.screen.mark_dirty(); route.request_redraw(); } } @@ -2081,14 +2090,7 @@ impl ApplicationHandler for Application<'_> { let had_preedit = route.window.screen.ime.preedit().is_some(); route.window.screen.ime.set_enabled(false); if had_preedit { - route - .window - .screen - .context_manager - .current_mut() - .renderable_content - .pending_update - .set_dirty(); + route.window.screen.mark_dirty(); route.request_redraw(); } } diff --git a/frontends/rioterm/src/context/mod.rs b/frontends/rioterm/src/context/mod.rs index f360577bdc..18b717f20b 100644 --- a/frontends/rioterm/src/context/mod.rs +++ b/frontends/rioterm/src/context/mod.rs @@ -93,8 +93,7 @@ impl Context { pub fn cursor_from_ref(&self) -> Cursor { Cursor { state: self.renderable_content.cursor.state.new_from_self(), - content: self.renderable_content.cursor.content_ref, - content_ref: self.renderable_content.cursor.content_ref, + content: self.renderable_content.cursor.content, } } } diff --git a/frontends/rioterm/src/context/renderable.rs b/frontends/rioterm/src/context/renderable.rs index 727a0777ce..69e0623066 100644 --- a/frontends/rioterm/src/context/renderable.rs +++ b/frontends/rioterm/src/context/renderable.rs @@ -22,11 +22,15 @@ pub enum WindowUpdate { Background(BackgroundState), } +/// `content` is the configured cursor shape as a char, read once to +/// seed the terminal's cursor shape. The glyph actually drawn each +/// frame comes from `state`; the old `content`/`content_ref` pair +/// (drawn vs configured) collapsed when the IME preview stopped +/// swapping the drawn char. #[derive(Default, Clone, Debug)] pub struct Cursor { pub state: CursorState, pub content: char, - pub content_ref: char, } #[derive(Clone, Copy, Debug)] @@ -143,7 +147,6 @@ impl RenderableContent { pub fn from_cursor_config(config_cursor: &CursorConfig) -> Self { let cursor = Cursor { content: config_cursor.shape.into(), - content_ref: config_cursor.shape.into(), state: CursorState::new(config_cursor.shape.into()), }; Self::new(cursor) diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index 477c669e26..a9e21b09f3 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -298,7 +298,6 @@ impl Screen<'_> { let cursor = Cursor { content: config.cursor.shape.into(), - content_ref: config.cursor.shape.into(), state: CursorState::new(config.cursor.shape.into()), }; diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index 89a458348d..5d7206655e 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -32,7 +32,7 @@ use rio_backend::config::colors::{AnsiColor, NamedColor}; use rio_backend::crosswords::grid::row::Row; use rio_backend::crosswords::pos::{Column, Line, Pos}; use rio_backend::crosswords::search::Match; -use rio_backend::crosswords::square::{ContentTag, Extras, Square}; +use rio_backend::crosswords::square::{ContentTag, Extras, Square, Wide}; use rio_backend::crosswords::style::{Style, StyleFlags, UnderlineKind}; use rio_backend::selection::SelectionRange; use rustc_hash::FxHashMap; @@ -1896,6 +1896,14 @@ pub fn build_row_fg( x += 1; continue; } + // A wide glyph whose spacer lands on a composition cell would + // bleed its right half into the block: drop it while the + // composition covers it (the same rule the grid applies when + // half of a wide char is overwritten). + if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.cell(x + 1).is_some()) { + x += 1; + continue; + } if is_run_breaker(sq) { x += 1; continue; @@ -2118,6 +2126,14 @@ pub fn build_row_fg( break; } let sq2 = row[Column(end)]; + // Stop before a wide glyph whose spacer lands on a + // composition cell too: shaping it would bleed its right + // half into the block (see the run-start guard). + if sq2.wide() == Wide::Wide + && preedit.is_some_and(|p| p.cell(end + 1).is_some()) + { + break; + } if is_run_breaker(sq2) { break; } @@ -2511,6 +2527,17 @@ fn emit_preedit_cluster( let Some(base) = cluster.chars().next() else { return; }; + // Layout clamps every cluster to at most 2 cells (the widths a + // terminal expresses), but shaping draws natural-width ink: a + // >=3-cell cluster (a Devanagari conjunct, say) would paint + // bg-colored ink past the block onto normal background and over + // neighboring glyphs. Degrade those rare clusters to their base + // char, which fits the reserved cells. + let cluster = if rio_unicode::UnicodeWidthStr::width(cluster) > 2 { + &cluster[..base.len_utf8()] + } else { + cluster + }; let run_style_flags = 0u8; let (font_id, is_emoji) = rasterizer.resolve_font(base, run_style_flags, font_library, route_id); diff --git a/rio-grid/src/preedit.rs b/rio-grid/src/preedit.rs index a197fb8a0b..3b2bab7a7d 100644 --- a/rio-grid/src/preedit.rs +++ b/rio-grid/src/preedit.rs @@ -114,15 +114,18 @@ impl PreeditLine { return None; } - // The caret sits before the first cluster starting at or past - // the byte offset (mid-cluster offsets snap to the cluster). - // A hidden caret (candidate paging) stays hidden. + // The caret sits on the cluster CONTAINING the byte offset, so + // an intra-cluster offset (jamo-level Korean IMEs report these) + // marks the cluster being edited, not its neighbor; offsets at + // or past the end mean end-of-text. A hidden caret (candidate + // paging) stays hidden. let mut caret_index = match cursor { PreeditCursor::Hidden => None, + PreeditCursor::Byte(offset) if offset >= text.len() => Some(segs.len()), PreeditCursor::Byte(offset) => Some( segs.iter() - .position(|seg| seg.byte_start >= offset) - .unwrap_or(segs.len()), + .rposition(|seg| seg.byte_start <= offset) + .unwrap_or(0), ), }; @@ -308,10 +311,14 @@ mod tests { let line = layout("日本語", Some(3)).unwrap(); assert_eq!(line.caret, PreeditCaret::OnCell(2)); // A char-boundary offset inside a grapheme cluster (between - // `e` and its combining mark) snaps forward to the next - // cluster boundary. + // `e` and its combining mark) marks the cluster being edited: + // the one CONTAINING the offset, as jamo-level Korean IMEs + // expect. let line = layout("e\u{302}x", Some(1)).unwrap(); - assert_eq!(line.caret, PreeditCaret::OnCell(1)); + assert_eq!(line.caret, PreeditCaret::OnCell(0)); + // An intra-cluster offset in a wide cluster stays on it. + let line = layout("日本語", Some(4)).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(2)); } #[test] From a0e1955d4bcaaeed8740544d2dbc78b2ca54d178 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 09:12:28 +0200 Subject: [PATCH 09/14] drop redundant local Wide import --- rio-grid/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index 5d7206655e..40e424e60c 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -1595,7 +1595,6 @@ fn is_run_breaker(sq: Square) -> bool { /// run text + hash + cluster mapping. #[inline(always)] fn is_skipped_spacer(sq: Square) -> bool { - use rio_backend::crosswords::square::Wide; matches!(sq.wide(), Wide::Spacer | Wide::LeadingSpacer) } From e4094e0b009018570f64e4d64acf17ee12f3a05c Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 09:46:05 +0200 Subject: [PATCH 10/14] ime: modal roster gate, shaped-advance clamp, unified ink suppression --- frontends/rioterm/src/application.rs | 76 +++++------ frontends/rioterm/src/ime.rs | 28 ++-- frontends/rioterm/src/router/mod.rs | 18 +++ frontends/rioterm/src/screen/mod.rs | 46 +++++++ rio-grid/src/lib.rs | 193 +++++++++++++++++++-------- rio-grid/src/preedit.rs | 23 +++- 6 files changed, 271 insertions(+), 113 deletions(-) diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs index ab1377cc71..479bcc07ee 100644 --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -2016,31 +2016,50 @@ impl ApplicationHandler for Application<'_> { } WindowEvent::Ime(ime) => { - // Modal overlays (assistant, command palette) take - // their input through key events and never see IME - // ones: while one is up, swallow composition input but - // still CLEAR any stored preedit — a live composition - // would keep painting into the grid behind the overlay - // and its key gate would swallow every plain keystroke - // after the overlay closes. (Search stays open to IME: - // commits route into the search input via `paste`.) - let overlay_active = route.window.screen.renderer.assistant.is_active() - || route.window.screen.renderer.command_palette.is_enabled(); - + // Modal overlays own keyboard input (`modal_owns_input` + // walks the `has_key_wait` roster): while one is up, + // composition input must not reach the terminal, but + // any stored preedit must still CLEAR: a live + // composition would keep painting into the grid behind + // the overlay and its key gate would swallow every + // plain keystroke after the overlay closes. Search + // stays open to IME (commits route into the search + // input via `paste`). match ime { Ime::Commit(text) => { - if overlay_active { + // Composed characters (dead keys, CJK) arrive + // only as commits, never as key text: route + // them into the palette query the way plain + // key text reaches it in `has_key_wait`. + if route.window.screen.renderer.command_palette.is_enabled() { + if !text.is_empty() && text.chars().all(|c| !c.is_control()) { + let query = format!( + "{}{}", + route.window.screen.renderer.command_palette.query, + text + ); + route + .window + .screen + .renderer + .command_palette + .set_query(query); + route.request_overlay_redraw(); + } + return; + } + if route.modal_owns_input() { return; } // Don't use bracketed paste for single char input. route.window.screen.paste(&text, text.chars().count() > 1); } Ime::Preedit(text, cursor_offset) => { - let preedit = if text.is_empty() || overlay_active { + let preedit = if text.is_empty() || route.modal_owns_input() { None } else { // The platform's `None` means the IME asked - // for a hidden caret (candidate paging) — + // for a hidden caret (candidate paging), // NOT end-of-text, which arrives as an // explicit offset. let cursor = match cursor_offset { @@ -2050,31 +2069,10 @@ impl ApplicationHandler for Application<'_> { Some(Preedit::new(text, cursor)) }; - if route.window.screen.ime.preedit() != preedit.as_ref() { - let composing = preedit.is_some(); - route.window.screen.ime.set_preedit(preedit); - if composing { - // The composition anchors at the cursor - // row: snap out of scrollback so it can - // never be live but invisible while the - // preedit key gate swallows input, and - // drop the selection like typing does — - // the composition is the active signal - // now. - route - .window - .screen - .scroll_bottom_when_cursor_not_visible(); - route.window.screen.clear_selection(); - } - // The overlay lives in the grid's CPU cell - // buffers and its rows are force-rebuilt - // each frame while composing; a plain - // dirty mark is enough to get a frame - // scheduled — full terminal damage would - // re-shape every visible row per - // keystroke for nothing. - route.window.screen.mark_dirty(); + // `set_ime_preedit` owns the composing side + // effects (scroll snap, selection, dirty mark) + // and their search-mode exceptions. + if route.window.screen.set_ime_preedit(preedit) { route.request_redraw(); } } diff --git a/frontends/rioterm/src/ime.rs b/frontends/rioterm/src/ime.rs index 18fd8b578c..4fd735697e 100644 --- a/frontends/rioterm/src/ime.rs +++ b/frontends/rioterm/src/ime.rs @@ -48,20 +48,21 @@ pub struct Preedit { /// The IME caret. /// - /// Byte offsets that don't land on a char boundary (macOS reports - /// UTF-16 ranges that can split a surrogate pair) snap to - /// end-of-text rather than being trusted — slicing on one would - /// panic downstream. + /// Byte offsets are clamped to the text length but otherwise kept + /// verbatim, including intra-cluster and non-char-boundary values + /// (macOS reports UTF-16 ranges that can split a surrogate pair; + /// jamo-level Korean IMEs report offsets inside an NFC syllable). + /// Nothing downstream slices on the offset: the layout only + /// compares it against cluster starts, snapping the caret to the + /// cluster CONTAINING it. pub cursor: PreeditCursor, } impl Preedit { pub fn new(text: String, cursor: PreeditCursor) -> Self { let cursor = match cursor { - PreeditCursor::Byte(offset) if !text.is_char_boundary(offset) => { - PreeditCursor::Byte(text.len()) - } - other => other, + PreeditCursor::Byte(offset) => PreeditCursor::Byte(offset.min(text.len())), + PreeditCursor::Hidden => PreeditCursor::Hidden, }; Self { text, cursor } } @@ -72,14 +73,15 @@ mod tests { use super::*; #[test] - fn preedit_new_rejects_invalid_byte_offset() { - // Byte 1 is inside 啊's UTF-8 encoding: not a char boundary. + fn preedit_new_clamps_byte_offset() { + // Intra-char offsets pass through verbatim: the layout snaps + // them to the containing cluster and never slices on them. let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(1)); - assert_eq!(preedit.cursor, PreeditCursor::Byte(4)); - // Boundary offsets survive, including one-past-the-end. + assert_eq!(preedit.cursor, PreeditCursor::Byte(1)); let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(3)); assert_eq!(preedit.cursor, PreeditCursor::Byte(3)); - let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(4)); + // Past-the-end offsets clamp to the length. + let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Byte(9)); assert_eq!(preedit.cursor, PreeditCursor::Byte(4)); // A hidden caret stays hidden. let preedit = Preedit::new("啊a".to_string(), PreeditCursor::Hidden); diff --git a/frontends/rioterm/src/router/mod.rs b/frontends/rioterm/src/router/mod.rs index 77059c8995..9544399207 100644 --- a/frontends/rioterm/src/router/mod.rs +++ b/frontends/rioterm/src/router/mod.rs @@ -136,6 +136,24 @@ impl Route<'_> { self.request_overlay_redraw(); } + /// Whether a modal overlay currently owns keyboard input: the same + /// roster `has_key_wait` walks (island rename input, command + /// palette, quit confirmation, and any non-terminal route such as + /// the assistant or welcome screens). IME composition must not + /// reach the terminal behind any of them. + #[inline] + pub fn modal_owns_input(&self) -> bool { + self.window + .screen + .renderer + .island + .as_ref() + .is_some_and(|island| island.is_color_picker_open()) + || self.window.screen.renderer.command_palette.is_enabled() + || self.window.screen.renderer.confirm_quit.is_active() + || self.path != RoutePath::Terminal + } + #[inline] pub fn quit(&mut self) { std::process::exit(0); diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index a9e21b09f3..b4df0a41e9 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -394,6 +394,52 @@ impl Screen<'_> { .set_dirty(); } + /// Window-level IME preedit update, with the side effects composing + /// implies. Returns whether a repaint is needed. + /// + /// Composing mirrors what the equivalent plain typing does: + /// - outside search, snap out of scrollback (a live composition + /// must never be invisible while the preedit key gate swallows + /// input) and drop the selection, like `send_bytes`; + /// - during search, never snap and keep a vi-mode selection, like + /// `search_input`. + /// + /// The snap runs on every composition event, not only on changes: + /// candidate paging re-reports identical text, and that event must + /// still restore visibility after a mid-composition scroll. + pub fn set_ime_preedit(&mut self, preedit: Option) -> bool { + let composing = preedit.is_some(); + let changed = self.ime.preedit() != preedit.as_ref(); + if changed { + self.ime.set_preedit(preedit); + } + + let mut needs_render = changed; + if composing { + if self.search_active() { + if changed && !self.get_mode().contains(Mode::VI) { + // Clear selection so we do not obstruct any matches. + self.context_manager.current_mut().set_selection(None); + } + } else { + let mut terminal = self.ctx_mut().current_mut().terminal.lock(); + if terminal.display_offset() != 0 { + terminal.scroll_display(Scroll::Bottom); + needs_render = true; + } + drop(terminal); + if changed { + self.clear_selection(); + } + } + } + + if changed { + self.mark_dirty(); + } + needs_render + } + #[inline] pub fn set_modifiers(&mut self, modifiers: Modifiers) { self.modifiers = modifiers; diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index 40e424e60c..c0784eff56 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -62,6 +62,16 @@ impl PreeditRow<'_> { fn cell(&self, col: usize) -> Option { self.line.cell(col) } + + /// Whether ink drawn at `col` spanning `span` cells would land on + /// any composition cell. The one suppression predicate for every + /// fg emitter: glyphs (and their decorations) that would paint + /// into the block are dropped for the composition's duration, the + /// way the grid blanks a half-overwritten wide char. + #[inline] + fn covers_ink(&self, col: usize, span: usize) -> bool { + (col..col.saturating_add(span)).any(|c| self.cell(c).is_some()) + } } /// Color/palette operations the emit code needs from its host @@ -1899,7 +1909,7 @@ pub fn build_row_fg( // bleed its right half into the block: drop it while the // composition covers it (the same rule the grid applies when // half of a wide char is overwritten). - if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.cell(x + 1).is_some()) { + if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { x += 1; continue; } @@ -1968,6 +1978,13 @@ pub fn build_row_fg( if let Some((_, slot, is_color, span)) = ensure_custom_glyph_by_codepoint( grid, registry, ch as u32, cell_w_u32, cell_h, color, ) { + // A registered glyph's render span can overflow into + // the next cell: like a wide glyph, drop it while the + // composition block covers any cell its ink spans. + if preedit.is_some_and(|p| p.covers_ink(x, span as usize)) { + x += 1; + continue; + } if slot.w != 0 && slot.h != 0 { // Center the rasterised glyph in its render-span box // (`span × cell_w` wide, `cell_h` tall). The raster @@ -2128,9 +2145,7 @@ pub fn build_row_fg( // Stop before a wide glyph whose spacer lands on a // composition cell too: shaping it would bleed its right // half into the block (see the run-start guard). - if sq2.wide() == Wide::Wide - && preedit.is_some_and(|p| p.cell(end + 1).is_some()) - { + if sq2.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(end, 2)) { break; } if is_run_breaker(sq2) { @@ -2419,16 +2434,23 @@ pub fn build_row_fg( let Some(PreeditCell::Start(cluster)) = pre.cell(col) else { continue; }; + let reserved_cells = if pre.cell(col + 1) == Some(PreeditCell::Continuation) { + 2 + } else { + 1 + }; emit_preedit_cluster( pre.line.cluster(cluster), col as u16, y, + reserved_cells, rasterizer, grid, font_library, route_id, size_u16, size_bucket, + cell_w, cell_h, text_fg, fg_scratch, @@ -2513,12 +2535,14 @@ fn emit_preedit_cluster( cluster: &str, grid_col: u16, y: u16, + reserved_cells: usize, rasterizer: &mut GridGlyphRasterizer, grid: &mut GridRenderer, font_library: &FontLibrary, route_id: usize, size_u16: u16, size_bucket: u16, + cell_w: f32, cell_h: f32, text_fg: [u8; 4], fg_scratch: &mut Vec, @@ -2526,66 +2550,79 @@ fn emit_preedit_cluster( let Some(base) = cluster.chars().next() else { return; }; - // Layout clamps every cluster to at most 2 cells (the widths a - // terminal expresses), but shaping draws natural-width ink: a - // >=3-cell cluster (a Devanagari conjunct, say) would paint - // bg-colored ink past the block onto normal background and over - // neighboring glyphs. Degrade those rare clusters to their base - // char, which fits the reserved cells. - let cluster = if rio_unicode::UnicodeWidthStr::width(cluster) > 2 { - &cluster[..base.len_utf8()] - } else { - cluster - }; let run_style_flags = 0u8; let (font_id, is_emoji) = rasterizer.resolve_font(base, run_style_flags, font_library, route_id); - #[cfg(target_os = "macos")] - { - rasterizer.run_utf16_scratch.clear(); - rasterizer.run_cell_starts.clear(); - rasterizer - .run_cell_starts - .push(rasterizer.run_utf16_scratch.len() as u32); - let mut buf = [0u16; 2]; - for ch in cluster.chars() { + // Layout reserves 1 or 2 cells per cluster; shaping draws natural + // width. Per-char width sums misjudge both directions (a ZWJ emoji + // sums to 6 cells yet shapes to ~2, a conjunct sums to 2 yet can + // ink 3), so the overflow decision uses the SHAPED advance: when + // the full cluster's advance overflows its reserved cells, retry + // with the base char, which fits. The half-cell slack absorbs + // color-font advance quirks. + let max_advance = reserved_cells as f32 * cell_w + cell_w * 0.5; + let mut text = cluster; + let (hash, ascent_px) = loop { + #[cfg(target_os = "macos")] + { + rasterizer.run_utf16_scratch.clear(); + rasterizer.run_cell_starts.clear(); rasterizer - .run_utf16_scratch - .extend_from_slice(ch.encode_utf16(&mut buf)); + .run_cell_starts + .push(rasterizer.run_utf16_scratch.len() as u32); + let mut buf = [0u16; 2]; + for ch in text.chars() { + rasterizer + .run_utf16_scratch + .extend_from_slice(ch.encode_utf16(&mut buf)); + } } - } - #[cfg(not(target_os = "macos"))] - { - rasterizer.run_str_scratch.clear(); - rasterizer.run_str_scratch.push_str(cluster); - } - - // Composition runs get their own cache namespace (the leading - // sentinel): sharing keys with the grid's per-cell scheme would - // require proving the two hash layouts can never collide across - // every combining/cluster shape, and the composition holds a - // handful of entries at most — disjointness is cheaper than the - // proof. - rasterizer.run_hasher = rapidhash::fast::RapidHasher::default(); - rasterizer.run_hasher.write_u32(0x5052_4545); // "PREE" - for (i, ch) in cluster.chars().enumerate() { - rasterizer.run_hasher.write_u32(ch as u32); - rasterizer.run_hasher.write_u32(i as u32); - } - rasterizer.run_hasher.write_u32(font_id); - rasterizer.run_hasher.write_u16(size_bucket); - let hash = rasterizer.run_hasher.finish(); - - let Some(ascent_px) = shape_cached( - rasterizer, - hash, - font_id, - size_u16, - size_bucket, - font_library, - ) else { - return; + #[cfg(not(target_os = "macos"))] + { + rasterizer.run_str_scratch.clear(); + rasterizer.run_str_scratch.push_str(text); + } + + // Composition runs get their own cache namespace (the leading + // sentinel): sharing keys with the grid's per-cell scheme would + // require proving the two hash layouts can never collide across + // every combining/cluster shape, and the composition holds a + // handful of entries at most, so disjointness is cheaper than + // the proof. + rasterizer.run_hasher = rapidhash::fast::RapidHasher::default(); + rasterizer.run_hasher.write_u32(0x5052_4545); // "PREE" + for (i, ch) in text.chars().enumerate() { + rasterizer.run_hasher.write_u32(ch as u32); + rasterizer.run_hasher.write_u32(i as u32); + } + rasterizer.run_hasher.write_u32(font_id); + rasterizer.run_hasher.write_u16(size_bucket); + let hash = rasterizer.run_hasher.finish(); + + let Some(ascent_px) = shape_cached( + rasterizer, + hash, + font_id, + size_u16, + size_bucket, + font_library, + ) else { + return; + }; + + if text.len() > base.len_utf8() { + let advance: f32 = run_cache_get(&mut rasterizer.run_cache, hash) + .expect("just inserted") + .iter() + .map(|g| g.advance) + .sum(); + if advance > max_advance { + text = &cluster[..base.len_utf8()]; + continue; + } + } + break (hash, ascent_px); }; let (synthetic_bold, synthetic_italic) = @@ -2697,6 +2734,12 @@ fn emit_underlines( continue; } let sq = row[Column(x)]; + // The fg pass drops a wide glyph whose spacer lands on the + // block; drop its decorations too, or they float under an + // empty cell next to the composition. + if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { + continue; + } let style = resolve_style(row_styles, x); let col = x as u16; // SGR underline (UNDER, double, curly, …) wins over the @@ -2774,6 +2817,12 @@ fn emit_strikethroughs( continue; } let sq = row[Column(x)]; + // The fg pass drops a wide glyph whose spacer lands on the + // block; drop its decorations too, or they float under an + // empty cell next to the composition. + if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { + continue; + } let style = resolve_style(row_styles, x); if !style.flags.contains(StyleFlags::STRIKEOUT) { continue; @@ -3201,6 +3250,34 @@ mod hint_label_tests { } } +#[cfg(test)] +mod preedit_suppression_tests { + use super::*; + use preedit::{PreeditCursor, PreeditLine}; + + /// `covers_ink` is the one predicate every fg emitter consults to + /// drop ink that would land on the composition block (wide glyphs + /// whose spacer sits under it, custom glyphs whose render span + /// reaches into it). + #[test] + fn covers_ink_spans() { + // Block occupies columns 4..8 ("日本" at cursor col 4). + let line = PreeditLine::new("日本", PreeditCursor::Byte(6), 0, 4, 80).unwrap(); + let pre = PreeditRow { + line: &line, + block_bg: [0; 4], + }; + // One-cell ink left of the block never triggers. + assert!(!pre.covers_ink(3, 1)); + // Two-cell ink at column 3 reaches the block's first cell. + assert!(pre.covers_ink(3, 2)); + // Inside the block. + assert!(pre.covers_ink(5, 1)); + // Ink starting at the block's end is clear of it. + assert!(!pre.covers_ink(8, 2)); + } +} + #[cfg(test)] mod cluster_text_tests { use super::*; diff --git a/rio-grid/src/preedit.rs b/rio-grid/src/preedit.rs index 3b2bab7a7d..c3bccb77a6 100644 --- a/rio-grid/src/preedit.rs +++ b/rio-grid/src/preedit.rs @@ -80,9 +80,9 @@ pub struct PreeditLine { impl PreeditLine { /// Lay out the composition `text` anchored at the cursor cell. /// Returns `None` for an empty composition or a degenerate grid. - /// `cursor` byte offsets never slice `text`, so a non-boundary - /// offset is safe here (the frontend snaps those to end-of-text - /// before this is reached). + /// `cursor` byte offsets never slice `text`, only compare against + /// cluster starts, so intra-cluster and non-char-boundary offsets + /// are safe and mark the cluster containing them. pub fn new( text: &str, cursor: PreeditCursor, @@ -321,6 +321,23 @@ mod tests { assert_eq!(line.caret, PreeditCaret::OnCell(2)); } + #[test] + fn korean_intra_syllable_caret_marks_the_syllable() { + // Jamo-level Korean IMEs report byte offsets inside an NFC + // syllable while it is being edited; the caret must sit on + // that syllable, not at the end of the composition. + let line = layout("한글", Some(1)).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(0)); + let line = layout("한글", Some(4)).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(2)); + // The syllable boundary itself belongs to the next syllable. + let line = layout("한글", Some(3)).unwrap(); + assert_eq!(line.caret, PreeditCaret::OnCell(2)); + // End-of-text stays a PastEnd beam. + let line = layout("한글", Some(6)).unwrap(); + assert_eq!(line.caret, PreeditCaret::PastEnd(4)); + } + #[test] fn caret_survives_tail_cropping() { let text = "あ".repeat(50); From ef9cd081138084edd596a8e7753cfc954653c899 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 10:04:19 +0200 Subject: [PATCH 11/14] test: zwj emoji shaped advance fits reserved cells --- rio-grid/src/lib.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index c0784eff56..5f3882b2c0 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -3255,6 +3255,73 @@ mod preedit_suppression_tests { use super::*; use preedit::{PreeditCursor, PreeditLine}; + /// Shape `text` as one run with the real default font library and + /// return the summed advance, exactly the quantity the composed- + /// cluster overflow fallback in `emit_preedit_cluster` keys on. + fn shaped_advance( + r: &mut GridGlyphRasterizer, + lib: &FontLibrary, + text: &str, + size: u16, + ) -> Option { + let base = text.chars().next()?; + let (font_id, _) = r.resolve_font(base, 0, lib, 0); + #[cfg(target_os = "macos")] + let glyphs = { + r.run_utf16_scratch.clear(); + r.run_cell_starts.clear(); + r.run_cell_starts.push(0); + let mut buf = [0u16; 2]; + for ch in text.chars() { + r.run_utf16_scratch + .extend_from_slice(ch.encode_utf16(&mut buf)); + } + shape_run_ct(r, font_id, size, size, lib)?.0 + }; + #[cfg(not(target_os = "macos"))] + let glyphs = { + r.run_str_scratch.clear(); + r.run_str_scratch.push_str(text); + shape_run_swash(r, font_id, size, size, lib)?.0 + }; + Some(glyphs.iter().map(|g| g.advance).sum()) + } + + /// A ZWJ emoji must fit its 2 reserved cells under the shaped- + /// advance overflow rule, or IME candidate selection would degrade + /// it to the base person glyph. Per-char width sums say 6 cells; + /// the shaped advance is the truth this pins. + #[test] + fn zwj_emoji_shaped_advance_fits_reserved_cells() { + let font_library = FontLibrary::default(); + let mut r = GridGlyphRasterizer::new(); + let size: u16 = 28; + let family = "\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}"; + let Some(family_advance) = shaped_advance(&mut r, &font_library, family, size) + else { + // No shaping handle for this font in the environment: + // nothing to measure. + return; + }; + assert!(family_advance > 0.0); + + // In a monospace font every narrow advance IS the cell width. + let cell_w = shaped_advance(&mut r, &font_library, "m", size).unwrap(); + // Reserved 2 cells + the fallback's half-cell slack. + let max_advance = 2.0 * cell_w + cell_w * 0.5; + // Strict only where CI ships a real emoji font (Apple Color + // Emoji); a fontless Linux container may shape to notdef with + // arbitrary metrics. + #[cfg(target_os = "macos")] + assert!( + family_advance <= max_advance, + "family emoji advance {family_advance} exceeds {max_advance}: \ + the IME composition would degrade it to the base glyph" + ); + #[cfg(not(target_os = "macos"))] + let _ = max_advance; + } + /// `covers_ink` is the one predicate every fg emitter consults to /// drop ink that would land on the composition block (wide glyphs /// whose spacer sits under it, custom glyphs whose render span From d4d5ba17a624b3841ba292273f1a53451440ae16 Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 10:33:29 +0200 Subject: [PATCH 12/14] ime: full modal roster, overlay text sink, suppresses predicate --- frontends/rioterm/src/application.rs | 24 +- .../rioterm/src/renderer/command_palette.rs | 13 + frontends/rioterm/src/renderer/island.rs | 21 +- frontends/rioterm/src/router/mod.rs | 65 +++-- frontends/rioterm/src/screen/mod.rs | 50 ++-- rio-grid/src/lib.rs | 250 +++++++++++------- 6 files changed, 258 insertions(+), 165 deletions(-) diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs index 479bcc07ee..c98db1e230 100644 --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -2027,25 +2027,11 @@ impl ApplicationHandler for Application<'_> { // input via `paste`). match ime { Ime::Commit(text) => { - // Composed characters (dead keys, CJK) arrive - // only as commits, never as key text: route - // them into the palette query the way plain - // key text reaches it in `has_key_wait`. - if route.window.screen.renderer.command_palette.is_enabled() { - if !text.is_empty() && text.chars().all(|c| !c.is_control()) { - let query = format!( - "{}{}", - route.window.screen.renderer.command_palette.query, - text - ); - route - .window - .screen - .renderer - .command_palette - .set_query(query); - route.request_overlay_redraw(); - } + // Text-input overlays (island rename, palette) + // consume commits first, in `has_key_wait`'s + // order; other modals swallow them; only a bare + // terminal receives the text. + if route.overlay_commit_text(&text) { return; } if route.modal_owns_input() { diff --git a/frontends/rioterm/src/renderer/command_palette.rs b/frontends/rioterm/src/renderer/command_palette.rs index 999f7074e0..1304f23924 100644 --- a/frontends/rioterm/src/renderer/command_palette.rs +++ b/frontends/rioterm/src/renderer/command_palette.rs @@ -506,6 +506,19 @@ impl CommandPalette { self.last_scroll_time = None; } + /// Append committed or typed text to the query, with the palette's + /// input policy (non-empty, no control chars) applied in ONE place + /// for both the key path and the IME commit path. Returns whether + /// anything was appended. + pub fn append_query(&mut self, text: &str) -> bool { + if text.is_empty() || text.chars().any(|c| c.is_control()) { + return false; + } + let query = format!("{}{}", self.query, text); + self.set_query(query); + true + } + pub fn set_query(&mut self, query: String) { self.query = query; self.selected_index = 0; diff --git a/frontends/rioterm/src/renderer/island.rs b/frontends/rioterm/src/renderer/island.rs index f2d38d3ca8..8d3974308a 100644 --- a/frontends/rioterm/src/renderer/island.rs +++ b/frontends/rioterm/src/renderer/island.rs @@ -1118,17 +1118,28 @@ impl Island { } _ => { if let Some(text) = key_event.text.as_ref() { - let s = text.as_str(); - if !s.is_empty() && s.chars().all(|c| !c.is_control()) { - self.rename_input.push_str(s); - self.rename_caret_time = Instant::now(); - } + self.append_rename_text(text.as_str()); } } } true } + /// Append committed or typed text to the rename input, with the + /// field's input policy (non-empty, no control chars) applied in + /// ONE place for both the key path and the IME commit path. + /// Returns whether the field is open and consumed the text. + pub fn append_rename_text(&mut self, text: &str) -> bool { + if self.color_picker_tab.is_none() { + return false; + } + if !text.is_empty() && text.chars().all(|c| !c.is_control()) { + self.rename_input.push_str(text); + self.rename_caret_time = Instant::now(); + } + true + } + /// Check if a click hits a color swatch in the picker. /// Returns true if the click was consumed. pub fn handle_color_picker_click( diff --git a/frontends/rioterm/src/router/mod.rs b/frontends/rioterm/src/router/mod.rs index 9544399207..dcb15605cb 100644 --- a/frontends/rioterm/src/router/mod.rs +++ b/frontends/rioterm/src/router/mod.rs @@ -136,11 +136,43 @@ impl Route<'_> { self.request_overlay_redraw(); } - /// Whether a modal overlay currently owns keyboard input: the same - /// roster `has_key_wait` walks (island rename input, command - /// palette, quit confirmation, and any non-terminal route such as - /// the assistant or welcome screens). IME composition must not - /// reach the terminal behind any of them. + /// Route committed IME text (dead keys, CJK) into whichever overlay + /// currently owns text input, walking the same order `has_key_wait` + /// walks for plain key text: the island rename input first, then + /// the command palette. Composed characters arrive ONLY as commits, + /// never as key text, so without this the overlays would be + /// ASCII-only. Returns whether an overlay consumed the text. + pub fn overlay_commit_text(&mut self, text: &str) -> bool { + if let Some(ref mut island) = self.window.screen.renderer.island { + if island.append_rename_text(text) { + self.request_overlay_redraw(); + return true; + } + } + if self.window.screen.renderer.command_palette.is_enabled() { + if self + .window + .screen + .renderer + .command_palette + .append_query(text) + { + self.request_overlay_redraw(); + } + return true; + } + false + } + + /// Whether a modal overlay currently owns keyboard input, so IME + /// composition must not reach the terminal behind it: the island + /// rename input, the command palette, the quit confirmation, the + /// assistant (which `report_error` can activate WITHOUT leaving + /// `RoutePath::Terminal`), and any non-terminal route. One + /// deliberate asymmetry with `has_key_wait`: the welcome screen + /// lets plain keys fall through to the hidden terminal, but IME + /// commits are blocked here; feeding composed text to a shell the + /// user cannot see helps nobody. #[inline] pub fn modal_owns_input(&self) -> bool { self.window @@ -151,6 +183,7 @@ impl Route<'_> { .is_some_and(|island| island.is_color_picker_open()) || self.window.screen.renderer.command_palette.is_enabled() || self.window.screen.renderer.confirm_quit.is_active() + || self.window.screen.renderer.assistant.is_active() || self.path != RoutePath::Terminal } @@ -307,23 +340,13 @@ impl Route<'_> { } _ => { if let Some(text) = key_event.text.as_ref() { - // Filter out control characters - let text_str = text.as_str(); - if !text_str.is_empty() - && text_str.chars().all(|c| !c.is_control()) + if self + .window + .screen + .renderer + .command_palette + .append_query(text.as_str()) { - let current_query = self - .window - .screen - .renderer - .command_palette - .query - .clone(); - self.window - .screen - .renderer - .command_palette - .set_query(format!("{}{}", current_query, text_str)); self.request_overlay_redraw(); } } diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index b4df0a41e9..e3cd9814c3 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -397,16 +397,21 @@ impl Screen<'_> { /// Window-level IME preedit update, with the side effects composing /// implies. Returns whether a repaint is needed. /// - /// Composing mirrors what the equivalent plain typing does: - /// - outside search, snap out of scrollback (a live composition - /// must never be invisible while the preedit key gate swallows - /// input) and drop the selection, like `send_bytes`; - /// - during search, never snap and keep a vi-mode selection, like - /// `search_input`. + /// Composing always snaps out of scrollback: the overlay renders + /// only at `display_offset == 0` while the preedit key gate + /// swallows input, so a scrolled viewport would mean a live but + /// invisible composition and a terminal that looks frozen. This + /// holds during search too; the snap composes with the relative + /// `Scroll::Delta` restore in `search_reset_state` exactly like a + /// manual mid-search scroll does, and committing the query re-runs + /// `goto_match`, which scrolls back to the focused match. The snap + /// runs on every composition event, not only on changes: candidate + /// paging re-reports identical text, and that event must still + /// restore visibility after a mid-composition scroll. /// - /// The snap runs on every composition event, not only on changes: - /// candidate paging re-reports identical text, and that event must - /// still restore visibility after a mid-composition scroll. + /// Selection follows what the equivalent plain typing does: plain + /// input drops it (`send_bytes`), search typing drops it only + /// outside vi mode (`search_input` keeps a vi visual selection). pub fn set_ime_preedit(&mut self, preedit: Option) -> bool { let composing = preedit.is_some(); let changed = self.ime.preedit() != preedit.as_ref(); @@ -416,19 +421,20 @@ impl Screen<'_> { let mut needs_render = changed; if composing { - if self.search_active() { - if changed && !self.get_mode().contains(Mode::VI) { - // Clear selection so we do not obstruct any matches. - self.context_manager.current_mut().set_selection(None); - } - } else { - let mut terminal = self.ctx_mut().current_mut().terminal.lock(); - if terminal.display_offset() != 0 { - terminal.scroll_display(Scroll::Bottom); - needs_render = true; - } - drop(terminal); - if changed { + let mut terminal = self.ctx_mut().current_mut().terminal.lock(); + if terminal.display_offset() != 0 { + terminal.scroll_display(Scroll::Bottom); + needs_render = true; + } + drop(terminal); + + if changed { + if self.search_active() { + if !self.get_mode().contains(Mode::VI) { + // Clear selection so we do not obstruct any matches. + self.context_manager.current_mut().set_selection(None); + } + } else { self.clear_selection(); } } diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index 5f3882b2c0..48ea655015 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -64,14 +64,31 @@ impl PreeditRow<'_> { } /// Whether ink drawn at `col` spanning `span` cells would land on - /// any composition cell. The one suppression predicate for every - /// fg emitter: glyphs (and their decorations) that would paint - /// into the block are dropped for the composition's duration, the - /// way the grid blanks a half-overwritten wide char. + /// any composition cell. Used where the span is dynamic (a custom + /// glyph's render span); per-cell emitters use [`Self::suppresses`]. #[inline] fn covers_ink(&self, col: usize, span: usize) -> bool { (col..col.saturating_add(span)).any(|c| self.cell(c).is_some()) } + + /// THE suppression policy for per-cell fg emitters (glyphs and + /// their decorations) while composing: drop the cell when it is + /// under the block, when it is a wide base whose spacer is, or + /// when it is a spacer whose base is. Half-covered wide glyphs + /// vanish whole (the rule the grid applies when half of a wide + /// char is overwritten), and neither half may leave a floating + /// decoration behind. + #[inline] + fn suppresses(&self, sq: Square, col: usize) -> bool { + if self.cell(col).is_some() { + return true; + } + match sq.wide() { + Wide::Wide => self.cell(col + 1).is_some(), + Wide::Spacer => col > 0 && self.cell(col - 1).is_some(), + _ => false, + } + } } /// Color/palette operations the emit code needs from its host @@ -1899,17 +1916,10 @@ pub fn build_row_fg( let mut x: usize = 0; while x < max { let sq = row[Column(x)]; - // Composition cells emit in their own pass below, shaped per - // grapheme cluster so they can't ligate with terminal text. - if preedit.is_some_and(|p| p.cell(x).is_some()) { - x += 1; - continue; - } - // A wide glyph whose spacer lands on a composition cell would - // bleed its right half into the block: drop it while the - // composition covers it (the same rule the grid applies when - // half of a wide char is overwritten). - if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { + // Composition cells emit in their own pass below (shaped per + // grapheme cluster so they can't ligate with terminal text), + // and a wide glyph half-covered by the block is dropped whole. + if preedit.is_some_and(|p| p.suppresses(sq, x)) { x += 1; continue; } @@ -2136,16 +2146,12 @@ pub fn build_row_fg( // Extend the run while (font_id, style_flags) match. let mut end = x + 1; while end < cols { - // Stop before stepping onto composition cells — those are - // taken over by the preedit pass. - if preedit.is_some_and(|p| p.cell(end).is_some()) { - break; - } let sq2 = row[Column(end)]; - // Stop before a wide glyph whose spacer lands on a - // composition cell too: shaping it would bleed its right - // half into the block (see the run-start guard). - if sq2.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(end, 2)) { + // Stop before composition cells (taken over by the preedit + // pass) and before a wide glyph the block half-covers, + // whose shaping would bleed into it (see the run-start + // guard). + if preedit.is_some_and(|p| p.suppresses(sq2, end)) { break; } if is_run_breaker(sq2) { @@ -2525,6 +2531,64 @@ pub fn build_row_fg( } } +/// Fill the run scratch with `text`, hash it into the composition +/// cache namespace (the leading "PREE" sentinel keeps it disjoint from +/// the grid's per-cell keys), shape it (cached), and return +/// `(hash, ascent, summed advance)`. `None` means no shaping handle. +fn shape_preedit_text( + rasterizer: &mut GridGlyphRasterizer, + text: &str, + font_id: u32, + size_u16: u16, + size_bucket: u16, + font_library: &FontLibrary, +) -> Option<(u64, i16, f32)> { + #[cfg(target_os = "macos")] + { + rasterizer.run_utf16_scratch.clear(); + rasterizer.run_cell_starts.clear(); + rasterizer + .run_cell_starts + .push(rasterizer.run_utf16_scratch.len() as u32); + let mut buf = [0u16; 2]; + for ch in text.chars() { + rasterizer + .run_utf16_scratch + .extend_from_slice(ch.encode_utf16(&mut buf)); + } + } + #[cfg(not(target_os = "macos"))] + { + rasterizer.run_str_scratch.clear(); + rasterizer.run_str_scratch.push_str(text); + } + + rasterizer.run_hasher = rapidhash::fast::RapidHasher::default(); + rasterizer.run_hasher.write_u32(0x5052_4545); // "PREE" + for (i, ch) in text.chars().enumerate() { + rasterizer.run_hasher.write_u32(ch as u32); + rasterizer.run_hasher.write_u32(i as u32); + } + rasterizer.run_hasher.write_u32(font_id); + rasterizer.run_hasher.write_u16(size_bucket); + let hash = rasterizer.run_hasher.finish(); + + let ascent_px = shape_cached( + rasterizer, + hash, + font_id, + size_u16, + size_bucket, + font_library, + )?; + let advance: f32 = run_cache_get(&mut rasterizer.run_cache, hash) + .expect("just inserted") + .iter() + .map(|g| g.advance) + .sum(); + Some((hash, ascent_px, advance)) +} + /// Shape one grapheme cluster as a standalone run and emit its glyphs /// at `(grid_col, y)` with a forced foreground color. Composition /// cells always shape in the plain style: composing text shouldn't @@ -2557,52 +2621,31 @@ fn emit_preedit_cluster( // Layout reserves 1 or 2 cells per cluster; shaping draws natural // width. Per-char width sums misjudge both directions (a ZWJ emoji // sums to 6 cells yet shapes to ~2, a conjunct sums to 2 yet can - // ink 3), so the overflow decision uses the SHAPED advance: when - // the full cluster's advance overflows its reserved cells, retry - // with the base char, which fits. The half-cell slack absorbs - // color-font advance quirks. + // ink 3), so the overflow decision uses the SHAPED advance: a full + // cluster overflowing its reserved cells retries as the base char, + // and a base char that still overflows draws nothing rather than + // spill background-colored ink over the neighboring cell. The + // half-cell slack absorbs color-font advance quirks. let max_advance = reserved_cells as f32 * cell_w + cell_w * 0.5; - let mut text = cluster; - let (hash, ascent_px) = loop { - #[cfg(target_os = "macos")] - { - rasterizer.run_utf16_scratch.clear(); - rasterizer.run_cell_starts.clear(); - rasterizer - .run_cell_starts - .push(rasterizer.run_utf16_scratch.len() as u32); - let mut buf = [0u16; 2]; - for ch in text.chars() { - rasterizer - .run_utf16_scratch - .extend_from_slice(ch.encode_utf16(&mut buf)); - } - } - #[cfg(not(target_os = "macos"))] - { - rasterizer.run_str_scratch.clear(); - rasterizer.run_str_scratch.push_str(text); - } - - // Composition runs get their own cache namespace (the leading - // sentinel): sharing keys with the grid's per-cell scheme would - // require proving the two hash layouts can never collide across - // every combining/cluster shape, and the composition holds a - // handful of entries at most, so disjointness is cheaper than - // the proof. - rasterizer.run_hasher = rapidhash::fast::RapidHasher::default(); - rasterizer.run_hasher.write_u32(0x5052_4545); // "PREE" - for (i, ch) in text.chars().enumerate() { - rasterizer.run_hasher.write_u32(ch as u32); - rasterizer.run_hasher.write_u32(i as u32); - } - rasterizer.run_hasher.write_u32(font_id); - rasterizer.run_hasher.write_u16(size_bucket); - let hash = rasterizer.run_hasher.finish(); - - let Some(ascent_px) = shape_cached( + let base_str = &cluster[..base.len_utf8()]; + let Some((hash, ascent_px, advance)) = shape_preedit_text( + rasterizer, + cluster, + font_id, + size_u16, + size_bucket, + font_library, + ) else { + return; + }; + let (hash, ascent_px) = if advance <= max_advance { + (hash, ascent_px) + } else if base_str.len() == cluster.len() { + return; + } else { + let Some((hash, ascent_px, advance)) = shape_preedit_text( rasterizer, - hash, + base_str, font_id, size_u16, size_bucket, @@ -2610,19 +2653,10 @@ fn emit_preedit_cluster( ) else { return; }; - - if text.len() > base.len_utf8() { - let advance: f32 = run_cache_get(&mut rasterizer.run_cache, hash) - .expect("just inserted") - .iter() - .map(|g| g.advance) - .sum(); - if advance > max_advance { - text = &cluster[..base.len_utf8()]; - continue; - } + if advance > max_advance { + return; } - break (hash, ascent_px); + (hash, ascent_px) }; let (synthetic_bold, synthetic_italic) = @@ -2728,16 +2762,11 @@ fn emit_underlines( fg_scratch: &mut Vec, ) { for x in 0..cols { - // Composing text takes no decorations from whatever - // was underneath it. - if preedit.is_some_and(|p| p.cell(x).is_some()) { - continue; - } let sq = row[Column(x)]; - // The fg pass drops a wide glyph whose spacer lands on the - // block; drop its decorations too, or they float under an - // empty cell next to the composition. - if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { + // Composing text takes no decorations from whatever sat under + // it, and either half of a wide glyph the fg pass dropped must + // not leave a floating decoration next to the block. + if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } let style = resolve_style(row_styles, x); @@ -2811,16 +2840,11 @@ fn emit_strikethroughs( fg_scratch: &mut Vec, ) { for x in 0..cols { - // Composing text takes no decorations from whatever - // was underneath it. - if preedit.is_some_and(|p| p.cell(x).is_some()) { - continue; - } let sq = row[Column(x)]; - // The fg pass drops a wide glyph whose spacer lands on the - // block; drop its decorations too, or they float under an - // empty cell next to the composition. - if sq.wide() == Wide::Wide && preedit.is_some_and(|p| p.covers_ink(x, 2)) { + // Composing text takes no decorations from whatever sat under + // it, and either half of a wide glyph the fg pass dropped must + // not leave a floating decoration next to the block. + if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } let style = resolve_style(row_styles, x); @@ -3343,6 +3367,36 @@ mod preedit_suppression_tests { // Ink starting at the block's end is clear of it. assert!(!pre.covers_ink(8, 2)); } + + /// A wide pair half-covered by the block suppresses BOTH halves, + /// whichever half the block touches: glyphs and decorations vanish + /// together, never a floating underline under an empty half-cell. + #[test] + fn suppresses_covers_half_covered_wide_pairs() { + // Block occupies columns 4..8 ("日本" at cursor col 4). + let line = PreeditLine::new("日本", PreeditCursor::Byte(6), 0, 4, 80).unwrap(); + let pre = PreeditRow { + line: &line, + block_bg: [0; 4], + }; + let narrow = Square::default(); + let mut wide = Square::default(); + wide.set_wide(Wide::Wide); + let mut spacer = Square::default(); + spacer.set_wide(Wide::Spacer); + + // Plain cells: only covered columns suppress. + assert!(!pre.suppresses(narrow, 3)); + assert!(pre.suppresses(narrow, 4)); + // Wide base at 3: its spacer at 4 is under the block. + assert!(pre.suppresses(wide, 3)); + assert!(!pre.suppresses(wide, 1)); + // Spacer at 8: its base at 7 is under the block. + assert!(pre.suppresses(spacer, 8)); + assert!(!pre.suppresses(spacer, 9)); + // Column 0 spacer never underflows. + assert!(!pre.suppresses(spacer, 0)); + } } #[cfg(test)] From 421950fc43eb2ea753fa03cf47f9aadf1054e3bf Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 14:09:33 +0200 Subject: [PATCH 13/14] ime: one modal roster, draw overflowing base chars, cache advance --- .../rioterm/src/renderer/command_palette.rs | 8 +- frontends/rioterm/src/renderer/island.rs | 17 +- frontends/rioterm/src/renderer/mod.rs | 8 + frontends/rioterm/src/router/mod.rs | 438 ++++++++++-------- frontends/rioterm/src/screen/mod.rs | 12 +- rio-grid/src/lib.rs | 141 +++--- 6 files changed, 356 insertions(+), 268 deletions(-) diff --git a/frontends/rioterm/src/renderer/command_palette.rs b/frontends/rioterm/src/renderer/command_palette.rs index 1304f23924..59101b2af7 100644 --- a/frontends/rioterm/src/renderer/command_palette.rs +++ b/frontends/rioterm/src/renderer/command_palette.rs @@ -506,12 +506,12 @@ impl CommandPalette { self.last_scroll_time = None; } - /// Append committed or typed text to the query, with the palette's - /// input policy (non-empty, no control chars) applied in ONE place - /// for both the key path and the IME commit path. Returns whether + /// Append committed or typed text to the query, applying the + /// shared overlay input policy (`is_printable_text`) so the key + /// path and the IME commit path can never drift. Returns whether /// anything was appended. pub fn append_query(&mut self, text: &str) -> bool { - if text.is_empty() || text.chars().any(|c| c.is_control()) { + if !crate::renderer::is_printable_text(text) { return false; } let query = format!("{}{}", self.query, text); diff --git a/frontends/rioterm/src/renderer/island.rs b/frontends/rioterm/src/renderer/island.rs index 8d3974308a..31401b9161 100644 --- a/frontends/rioterm/src/renderer/island.rs +++ b/frontends/rioterm/src/renderer/island.rs @@ -1125,18 +1125,17 @@ impl Island { true } - /// Append committed or typed text to the rename input, with the - /// field's input policy (non-empty, no control chars) applied in - /// ONE place for both the key path and the IME commit path. - /// Returns whether the field is open and consumed the text. + /// Append committed or typed text to the rename input, applying + /// the shared overlay input policy (`is_printable_text`) so the + /// key path and the IME commit path can never drift. Returns + /// whether text was actually appended (same contract as + /// `CommandPalette::append_query`). pub fn append_rename_text(&mut self, text: &str) -> bool { - if self.color_picker_tab.is_none() { + if self.color_picker_tab.is_none() || !crate::renderer::is_printable_text(text) { return false; } - if !text.is_empty() && text.chars().all(|c| !c.is_control()) { - self.rename_input.push_str(text); - self.rename_caret_time = Instant::now(); - } + self.rename_input.push_str(text); + self.rename_caret_time = Instant::now(); true } diff --git a/frontends/rioterm/src/renderer/mod.rs b/frontends/rioterm/src/renderer/mod.rs index 89cd830a79..62f7c11145 100644 --- a/frontends/rioterm/src/renderer/mod.rs +++ b/frontends/rioterm/src/renderer/mod.rs @@ -1,3 +1,11 @@ +/// The input policy every overlay text sink applies, in ONE place so +/// the key path and the IME commit path can never drift: non-empty and +/// free of control characters. +#[inline] +pub(crate) fn is_printable_text(text: &str) -> bool { + !text.is_empty() && text.chars().all(|c| !c.is_control()) +} + pub mod assistant; pub mod command_palette; pub mod confirm_quit; diff --git a/frontends/rioterm/src/router/mod.rs b/frontends/rioterm/src/router/mod.rs index dcb15605cb..1e7dd65b59 100644 --- a/frontends/rioterm/src/router/mod.rs +++ b/frontends/rioterm/src/router/mod.rs @@ -32,6 +32,23 @@ use std::time::{Duration, Instant}; // #[cfg(not(any(target_os = "macos", target_os = "windows")))] const RIO_TITLE: &str = "▲"; +/// The modal overlays that can own keyboard/IME input, in dispatch +/// priority order (see [`Route::active_modal`]). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Modal { + /// The island tab-rename input (a text sink). + IslandRename, + /// The command palette (a text sink). + CommandPalette, + /// The quit confirmation dialog. + ConfirmQuit, + /// The error assistant; `report_error` can activate it WITHOUT + /// leaving `RoutePath::Terminal`. + Assistant, + /// A non-terminal route (the welcome / config screens). + Route, +} + pub struct Route<'a> { pub assistant: assistant::Assistant, pub path: RoutePath, @@ -136,55 +153,77 @@ impl Route<'_> { self.request_overlay_redraw(); } - /// Route committed IME text (dead keys, CJK) into whichever overlay - /// currently owns text input, walking the same order `has_key_wait` - /// walks for plain key text: the island rename input first, then - /// the command palette. Composed characters arrive ONLY as commits, - /// never as key text, so without this the overlays would be - /// ASCII-only. Returns whether an overlay consumed the text. - pub fn overlay_commit_text(&mut self, text: &str) -> bool { - if let Some(ref mut island) = self.window.screen.renderer.island { - if island.append_rename_text(text) { - self.request_overlay_redraw(); - return true; - } + /// The modal overlay currently owning keyboard/IME input, in + /// dispatch priority order. THE roster: `has_key_wait`, + /// `modal_owns_input`, and `overlay_commit_text` all derive from + /// this, so a new overlay added here is gated everywhere at once. + pub fn active_modal(&self) -> Option { + if self + .window + .screen + .renderer + .island + .as_ref() + .is_some_and(|island| island.is_color_picker_open()) + { + return Some(Modal::IslandRename); } if self.window.screen.renderer.command_palette.is_enabled() { - if self - .window - .screen - .renderer - .command_palette - .append_query(text) - { - self.request_overlay_redraw(); + return Some(Modal::CommandPalette); + } + if self.window.screen.renderer.confirm_quit.is_active() { + return Some(Modal::ConfirmQuit); + } + if self.window.screen.renderer.assistant.is_active() { + return Some(Modal::Assistant); + } + if self.path != RoutePath::Terminal { + return Some(Modal::Route); + } + None + } + + /// Route committed IME text (dead keys, CJK) into the overlay that + /// owns input, when it is a text sink. Composed characters arrive + /// ONLY as commits, never as key text, so without this the + /// overlays would be ASCII-only. Returns whether the text was + /// consumed (an open text sink swallows even rejected text, the + /// way `has_key_wait` blocks all keys for it). + pub fn overlay_commit_text(&mut self, text: &str) -> bool { + match self.active_modal() { + Some(Modal::IslandRename) => { + if let Some(ref mut island) = self.window.screen.renderer.island { + if island.append_rename_text(text) { + self.request_overlay_redraw(); + } + } + true + } + Some(Modal::CommandPalette) => { + if self + .window + .screen + .renderer + .command_palette + .append_query(text) + { + self.request_overlay_redraw(); + } + true } - return true; + _ => false, } - false } /// Whether a modal overlay currently owns keyboard input, so IME - /// composition must not reach the terminal behind it: the island - /// rename input, the command palette, the quit confirmation, the - /// assistant (which `report_error` can activate WITHOUT leaving - /// `RoutePath::Terminal`), and any non-terminal route. One + /// composition must not reach the terminal behind it. One /// deliberate asymmetry with `has_key_wait`: the welcome screen /// lets plain keys fall through to the hidden terminal, but IME /// commits are blocked here; feeding composed text to a shell the /// user cannot see helps nobody. #[inline] pub fn modal_owns_input(&self) -> bool { - self.window - .screen - .renderer - .island - .as_ref() - .is_some_and(|island| island.is_color_picker_open()) - || self.window.screen.renderer.command_palette.is_enabled() - || self.window.screen.renderer.confirm_quit.is_active() - || self.window.screen.renderer.assistant.is_active() - || self.path != RoutePath::Terminal + self.active_modal().is_some() } #[inline] @@ -200,205 +239,210 @@ impl Route<'_> { ) -> bool { use rio_window::event::ElementState; - // Handle island color picker / rename input - if let Some(ref mut island) = self.window.screen.renderer.island { - if island.is_color_picker_open() { - let consumed = island.handle_rename_input( - key_event, - &mut self.window.screen.context_manager, - ); - if consumed { - self.request_overlay_redraw(); - return true; + // One dispatch on THE modal roster; each arm keeps its + // existing handling. `active_modal` already checked each + // overlay's open state. + let Some(modal) = self.active_modal() else { + return false; + }; + match modal { + Modal::IslandRename => { + if let Some(ref mut island) = self.window.screen.renderer.island { + island.handle_rename_input( + key_event, + &mut self.window.screen.context_manager, + ); } + self.request_overlay_redraw(); + true } - } - // Handle command palette input first (works in all routes) - if self.window.screen.renderer.command_palette.is_enabled() { - if key_event.state == ElementState::Pressed { - match &key_event.logical_key { - Key::Named(NamedKey::Escape) => { - self.window - .screen - .renderer - .command_palette - .set_enabled(false); - self.request_overlay_redraw(); - } - Key::Named(NamedKey::ArrowUp) => { - self.window - .screen - .renderer - .command_palette - .move_selection_up(); - self.request_overlay_redraw(); - } - Key::Named(NamedKey::ArrowDown) => { - self.window - .screen - .renderer - .command_palette - .move_selection_down(); - self.request_overlay_redraw(); - } - Key::Named(NamedKey::Tab) => { - self.window - .screen - .renderer - .command_palette - .move_selection_down(); - self.request_overlay_redraw(); - } - Key::Named(NamedKey::Enter) => { - // Snapshot what the palette wants to do FIRST, - // before taking a mut-borrow on it, so we can - // freely call other `self.window.screen.*` - // methods in the match arms without tripping - // the borrow checker on nested disjoint borrows. - let selected_font = self - .window - .screen - .renderer - .command_palette - .get_selected_font(); - let selected_action = self - .window - .screen - .renderer - .command_palette - .get_selected_action(); - use crate::renderer::command_palette::PaletteAction; - - // Fonts-mode Enter: copy the family name to - // the system clipboard and close. The copy - // icon on each row advertises this. - if let Some(font) = selected_font { - clipboard.set( - rio_backend::clipboard::ClipboardType::Clipboard, - font, - ); + Modal::CommandPalette => { + if key_event.state == ElementState::Pressed { + match &key_event.logical_key { + Key::Named(NamedKey::Escape) => { self.window .screen .renderer .command_palette .set_enabled(false); self.request_overlay_redraw(); - return true; } - - match selected_action { - // `ListFonts` stays inside the palette — - // swap the palette's contents from the - // command list to the registered font - // family names and keep it open. - Some(PaletteAction::ListFonts) => { - let fonts = - self.window.screen.sugarloaf.font_family_names(); + Key::Named(NamedKey::ArrowUp) => { + self.window + .screen + .renderer + .command_palette + .move_selection_up(); + self.request_overlay_redraw(); + } + Key::Named(NamedKey::ArrowDown) => { + self.window + .screen + .renderer + .command_palette + .move_selection_down(); + self.request_overlay_redraw(); + } + Key::Named(NamedKey::Tab) => { + self.window + .screen + .renderer + .command_palette + .move_selection_down(); + self.request_overlay_redraw(); + } + Key::Named(NamedKey::Enter) => { + // Snapshot what the palette wants to do FIRST, + // before taking a mut-borrow on it, so we can + // freely call other `self.window.screen.*` + // methods in the match arms without tripping + // the borrow checker on nested disjoint borrows. + let selected_font = self + .window + .screen + .renderer + .command_palette + .get_selected_font(); + let selected_action = self + .window + .screen + .renderer + .command_palette + .get_selected_action(); + use crate::renderer::command_palette::PaletteAction; + + // Fonts-mode Enter: copy the family name to + // the system clipboard and close. The copy + // icon on each row advertises this. + if let Some(font) = selected_font { + clipboard.set( + rio_backend::clipboard::ClipboardType::Clipboard, + font, + ); self.window .screen .renderer .command_palette - .enter_fonts_mode(fonts); + .set_enabled(false); + self.request_overlay_redraw(); + return true; + } + + match selected_action { + // `ListFonts` stays inside the palette — + // swap the palette's contents from the + // command list to the registered font + // family names and keep it open. + Some(PaletteAction::ListFonts) => { + let fonts = + self.window.screen.sugarloaf.font_family_names(); + self.window + .screen + .renderer + .command_palette + .enter_fonts_mode(fonts); + } + // Any other command is a one-shot: close + // the palette first, then dispatch. + Some(action) => { + self.window + .screen + .renderer + .command_palette + .set_enabled(false); + self.window + .screen + .execute_palette_action(action, clipboard); + } + // No match at all — Enter just closes. + None => { + self.window + .screen + .renderer + .command_palette + .set_enabled(false); + } } - // Any other command is a one-shot: close - // the palette first, then dispatch. - Some(action) => { + self.request_overlay_redraw(); + } + Key::Named(NamedKey::Backspace) => { + let current_query = + self.window.screen.renderer.command_palette.query.clone(); + if !current_query.is_empty() { + let mut chars = current_query.chars().collect::>(); + chars.pop(); self.window .screen .renderer .command_palette - .set_enabled(false); - self.window - .screen - .execute_palette_action(action, clipboard); + .set_query(chars.into_iter().collect()); + self.request_overlay_redraw(); } - // No match at all — Enter just closes. - None => { - self.window + } + _ => { + if let Some(text) = key_event.text.as_ref() { + if self + .window .screen .renderer .command_palette - .set_enabled(false); + .append_query(text.as_str()) + { + self.request_overlay_redraw(); + } } } - self.request_overlay_redraw(); } - Key::Named(NamedKey::Backspace) => { - let current_query = - self.window.screen.renderer.command_palette.query.clone(); - if !current_query.is_empty() { - let mut chars = current_query.chars().collect::>(); - chars.pop(); - self.window - .screen - .renderer - .command_palette - .set_query(chars.into_iter().collect()); + } + true // Block all input when command palette is active + } + + Modal::ConfirmQuit => { + if key_event.state == rio_window::event::ElementState::Pressed { + match &key_event.logical_key { + Key::Character(c) if c.as_str() == "n" || c.as_str() == "N" => { + self.window.screen.renderer.confirm_quit.set_active(false); self.request_overlay_redraw(); } - } - _ => { - if let Some(text) = key_event.text.as_ref() { - if self - .window - .screen - .renderer - .command_palette - .append_query(text.as_str()) - { - self.request_overlay_redraw(); - } + Key::Named(NamedKey::Escape) => { + self.window.screen.renderer.confirm_quit.set_active(false); + self.request_overlay_redraw(); + } + Key::Character(c) if c.as_str() == "y" || c.as_str() == "Y" => { + self.quit(); + return true; } + _ => {} } } + true } - return true; // Block all input when command palette is active - } - if self.window.screen.renderer.confirm_quit.is_active() { - if key_event.state == rio_window::event::ElementState::Pressed { - match &key_event.logical_key { - Key::Character(c) if c.as_str() == "n" || c.as_str() == "N" => { - self.window.screen.renderer.confirm_quit.set_active(false); - self.request_overlay_redraw(); - } - Key::Named(NamedKey::Escape) => { - self.window.screen.renderer.confirm_quit.set_active(false); - self.request_overlay_redraw(); - } - Key::Character(c) if c.as_str() == "y" || c.as_str() == "Y" => { - self.quit(); - return true; - } - _ => {} + // Path-independent, so a `report_error` toast raised at + // `RoutePath::Terminal` blocks keys symmetrically with the + // IME gate and Enter can dismiss it (previously only a mouse + // click could, while plain keys leaked to the shell). + Modal::Assistant => { + if key_event.logical_key == Key::Named(NamedKey::Enter) { + self.assistant.clear(); + self.window.screen.renderer.assistant.clear(); + self.request_overlay_redraw(); } + true } - return true; - } - - if self.path == RoutePath::Terminal { - return false; - } - let is_enter = key_event.logical_key == Key::Named(NamedKey::Enter); - - // Handle assistant overlay dismiss - if self.window.screen.renderer.assistant.is_active() { - if is_enter { - self.assistant.clear(); - self.window.screen.renderer.assistant.clear(); - self.request_overlay_redraw(); + Modal::Route => { + let is_enter = key_event.logical_key == Key::Named(NamedKey::Enter); + if self.path == RoutePath::Welcome && is_enter { + rio_backend::config::create_config_file(None); + self.path = RoutePath::Terminal; + } + // The welcome screen lets keys fall through (pre-existing + // leak to the hidden terminal, see `modal_owns_input`). + false } - return true; } - - if self.path == RoutePath::Welcome && is_enter { - rio_backend::config::create_config_file(None); - self.path = RoutePath::Terminal; - } - - false } } diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index e3cd9814c3..666a1d908f 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -422,11 +422,21 @@ impl Screen<'_> { let mut needs_render = changed; if composing { let mut terminal = self.ctx_mut().current_mut().terminal.lock(); - if terminal.display_offset() != 0 { + let snapped_offset = terminal.display_offset(); + if snapped_offset != 0 { terminal.scroll_display(Scroll::Bottom); needs_render = true; } drop(terminal); + if snapped_offset != 0 && self.search_active() { + // Keep the vi-origin restore honest: `search_reset_state` + // applies a relative `Scroll::Delta`, so the snap's + // displacement must be recorded the way `goto_match` + // records its own scrolls, or Esc after composing lands + // the viewport clamped at the bottom instead of at the + // vi origin. + self.search_state.display_offset_delta += snapped_offset as i32; + } if changed { if self.search_active() { diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index 48ea655015..a6fa3287c2 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -1292,6 +1292,9 @@ struct ShapedGlyph { } struct RunCacheEntry { + /// Summed glyph advance, computed once at insert so per-frame + /// consumers (the preedit overflow check) never re-walk glyphs. + advance: f32, /// 64-bit rapidhash of (font_id, size_bucket, style_flags, run bytes). /// We key on the hash alone — no stored run string, no equality /// check on lookup. `CellCacheTable` pattern @@ -1632,7 +1635,7 @@ fn is_skipped_spacer(sq: Square) -> bool { fn run_cache_get( buckets: &mut [Vec], hash: u64, -) -> Option<&[ShapedGlyph]> { +) -> Option<&RunCacheEntry> { let idx = (hash as usize) & (RUN_BUCKET_COUNT - 1); let bucket = &mut buckets[idx]; let last = bucket.len().checked_sub(1)?; @@ -1641,7 +1644,7 @@ fn run_cache_get( if i != last { bucket[i..=last].rotate_left(1); } - return Some(&bucket[last].glyphs); + return Some(&bucket[last]); } } None @@ -1790,10 +1793,11 @@ fn shape_run_swash( // Emission /// Shape the rasterizer's current run scratch, keyed in the run cache -/// by `hash`, and return the ascent for `(font_id, size_bucket)`; on a -/// miss the shaped glyphs are stored under `hash`. `None` means -/// shaping failed (no font handle). The one shaping-cache protocol, -/// shared by the grid run path and the preedit path. +/// by `hash`, and return `(ascent, summed advance)` for +/// `(font_id, size_bucket)`; on a miss the shaped glyphs are stored +/// under `hash` with their advance. `None` means shaping failed (no +/// font handle). The one shaping-cache protocol, shared by the grid +/// run path and the preedit path. fn shape_cached( rasterizer: &mut GridGlyphRasterizer, hash: u64, @@ -1801,16 +1805,18 @@ fn shape_cached( size_u16: u16, size_bucket: u16, font_library: &FontLibrary, -) -> Option { - if run_cache_get(&mut rasterizer.run_cache, hash).is_some() { - // Cache hit — ascent already stored. - return Some( +) -> Option<(i16, f32)> { + if let Some(entry) = run_cache_get(&mut rasterizer.run_cache, hash) { + // Cache hit — advance stored, ascent in its own cache. + let advance = entry.advance; + return Some(( rasterizer .ascent_cache .get(&(font_id, size_bucket)) .copied() .unwrap_or(0), - ); + advance, + )); } #[cfg(target_os = "macos")] let shaped_opt = @@ -1819,8 +1825,16 @@ fn shape_cached( let shaped_opt = shape_run_swash(rasterizer, font_id, size_u16, size_bucket, font_library); let (glyphs, ascent_px) = shaped_opt?; - run_cache_put(&mut rasterizer.run_cache, RunCacheEntry { hash, glyphs }); - Some(ascent_px) + let advance: f32 = glyphs.iter().map(|g| g.advance).sum(); + run_cache_put( + &mut rasterizer.run_cache, + RunCacheEntry { + hash, + glyphs, + advance, + }, + ); + Some((ascent_px, advance)) } /// Run-level fg emission. Shapes once per run, emits one CellText per @@ -1901,6 +1915,7 @@ pub fn build_row_fg( row_sel, row_hints, preedit, + glyph_registry.as_ref(), fg_scratch, ); @@ -2284,7 +2299,7 @@ pub fn build_row_fg( let hash = rasterizer.run_hasher.finish(); // Shape (cached) and capture ascent for this (font_id, size). - let Some(ascent_px) = shape_cached( + let Some((ascent_px, _)) = shape_cached( rasterizer, hash, font_id, @@ -2315,8 +2330,9 @@ pub fn build_row_fg( // shaped emoji runs that outgrow 64 slots spill to heap once. let mut glyph_emits: SmallVec<[(u16, u16); 64]> = SmallVec::new(); { - let glyphs = - run_cache_get(&mut rasterizer.run_cache, hash).expect("just inserted"); + let glyphs = &run_cache_get(&mut rasterizer.run_cache, hash) + .expect("just inserted") + .glyphs; let mut cell_idx_in_run: u16 = 0; // Both platforms record explicit per-cell starts into the // shaping buffer (UTF-16 units on macOS, UTF-8 bytes on @@ -2480,6 +2496,7 @@ pub fn build_row_fg( row_sel, row_hints, preedit, + glyph_registry.as_ref(), fg_scratch, ); @@ -2573,7 +2590,7 @@ fn shape_preedit_text( rasterizer.run_hasher.write_u16(size_bucket); let hash = rasterizer.run_hasher.finish(); - let ascent_px = shape_cached( + let (ascent_px, advance) = shape_cached( rasterizer, hash, font_id, @@ -2581,11 +2598,6 @@ fn shape_preedit_text( size_bucket, font_library, )?; - let advance: f32 = run_cache_get(&mut rasterizer.run_cache, hash) - .expect("just inserted") - .iter() - .map(|g| g.advance) - .sum(); Some((hash, ascent_px, advance)) } @@ -2622,9 +2634,10 @@ fn emit_preedit_cluster( // width. Per-char width sums misjudge both directions (a ZWJ emoji // sums to 6 cells yet shapes to ~2, a conjunct sums to 2 yet can // ink 3), so the overflow decision uses the SHAPED advance: a full - // cluster overflowing its reserved cells retries as the base char, - // and a base char that still overflows draws nothing rather than - // spill background-colored ink over the neighboring cell. The + // cluster overflowing its reserved cells retries as the base char. + // A base (or single) char that still overflows draws anyway: + // hiding the character the user is actively composing is a worse + // artifact than its transient spill next to the block. The // half-cell slack absorbs color-font advance quirks. let max_advance = reserved_cells as f32 * cell_w + cell_w * 0.5; let base_str = &cluster[..base.len_utf8()]; @@ -2638,12 +2651,10 @@ fn emit_preedit_cluster( ) else { return; }; - let (hash, ascent_px) = if advance <= max_advance { + let (hash, ascent_px) = if advance <= max_advance || base_str.len() == cluster.len() { (hash, ascent_px) - } else if base_str.len() == cluster.len() { - return; } else { - let Some((hash, ascent_px, advance)) = shape_preedit_text( + let Some((hash, ascent_px, _)) = shape_preedit_text( rasterizer, base_str, font_id, @@ -2653,9 +2664,6 @@ fn emit_preedit_cluster( ) else { return; }; - if advance > max_advance { - return; - } (hash, ascent_px) }; @@ -2664,8 +2672,9 @@ fn emit_preedit_cluster( let mut glyph_ids: SmallVec<[u16; 4]> = SmallVec::new(); { - let glyphs = - run_cache_get(&mut rasterizer.run_cache, hash).expect("just inserted"); + let glyphs = &run_cache_get(&mut rasterizer.run_cache, hash) + .expect("just inserted") + .glyphs; for g in glyphs { glyph_ids.push(g.id); } @@ -2759,6 +2768,7 @@ fn emit_underlines( row_sel: Option, row_hints: &[RowHint], preedit: Option<&PreeditRow<'_>>, + glyph_registry: Option<&rio_backend::sugarloaf::font::glyph_registry::GlyphRegistry>, fg_scratch: &mut Vec, ) { for x in 0..cols { @@ -2769,6 +2779,20 @@ fn emit_underlines( if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } + // A registered custom glyph's render span can reach into the + // block from one logical column; the fg pass drops it for + // that, so drop its decoration too. `covers_ink(x, 2)` first: + // the registry (an RwLock) is only consulted next to the + // block. + if let (Some(pre), Some(registry)) = (preedit, glyph_registry) { + if pre.covers_ink(x, 2) + && registry.get(sq.c() as u32).is_some_and(|entry| { + pre.covers_ink(x, (entry.width as u16).clamp(1, 2) as usize) + }) + { + continue; + } + } let style = resolve_style(row_styles, x); let col = x as u16; // SGR underline (UNDER, double, curly, …) wins over the @@ -2837,6 +2861,7 @@ fn emit_strikethroughs( row_sel: Option, row_hints: &[RowHint], preedit: Option<&PreeditRow<'_>>, + glyph_registry: Option<&rio_backend::sugarloaf::font::glyph_registry::GlyphRegistry>, fg_scratch: &mut Vec, ) { for x in 0..cols { @@ -2847,6 +2872,20 @@ fn emit_strikethroughs( if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } + // A registered custom glyph's render span can reach into the + // block from one logical column; the fg pass drops it for + // that, so drop its decoration too. `covers_ink(x, 2)` first: + // the registry (an RwLock) is only consulted next to the + // block. + if let (Some(pre), Some(registry)) = (preedit, glyph_registry) { + if pre.covers_ink(x, 2) + && registry.get(sq.c() as u32).is_some_and(|entry| { + pre.covers_ink(x, (entry.width as u16).clamp(1, 2) as usize) + }) + { + continue; + } + } let style = resolve_style(row_styles, x); if !style.flags.contains(StyleFlags::STRIKEOUT) { continue; @@ -3279,9 +3318,10 @@ mod preedit_suppression_tests { use super::*; use preedit::{PreeditCursor, PreeditLine}; - /// Shape `text` as one run with the real default font library and - /// return the summed advance, exactly the quantity the composed- - /// cluster overflow fallback in `emit_preedit_cluster` keys on. + /// Shape `text` through the REAL production path + /// (`shape_preedit_text`, the fn `emit_preedit_cluster` calls) and + /// return the summed advance: exactly the quantity the composed- + /// cluster overflow fallback keys on, from the same code. fn shaped_advance( r: &mut GridGlyphRasterizer, lib: &FontLibrary, @@ -3290,25 +3330,8 @@ mod preedit_suppression_tests { ) -> Option { let base = text.chars().next()?; let (font_id, _) = r.resolve_font(base, 0, lib, 0); - #[cfg(target_os = "macos")] - let glyphs = { - r.run_utf16_scratch.clear(); - r.run_cell_starts.clear(); - r.run_cell_starts.push(0); - let mut buf = [0u16; 2]; - for ch in text.chars() { - r.run_utf16_scratch - .extend_from_slice(ch.encode_utf16(&mut buf)); - } - shape_run_ct(r, font_id, size, size, lib)?.0 - }; - #[cfg(not(target_os = "macos"))] - let glyphs = { - r.run_str_scratch.clear(); - r.run_str_scratch.push_str(text); - shape_run_swash(r, font_id, size, size, lib)?.0 - }; - Some(glyphs.iter().map(|g| g.advance).sum()) + shape_preedit_text(r, text, font_id, size, size, lib) + .map(|(_, _, advance)| advance) } /// A ZWJ emoji must fit its 2 reserved cells under the shaped- @@ -3330,7 +3353,11 @@ mod preedit_suppression_tests { assert!(family_advance > 0.0); // In a monospace font every narrow advance IS the cell width. - let cell_w = shaped_advance(&mut r, &font_library, "m", size).unwrap(); + // Skip like the emoji guard above when the primary font has no + // shaping handle in this environment. + let Some(cell_w) = shaped_advance(&mut r, &font_library, "m", size) else { + return; + }; // Reserved 2 cells + the fallback's half-cell slack. let max_advance = 2.0 * cell_w + cell_w * 0.5; // Strict only where CI ships a real emoji font (Apple Color From fb73483bb60568c94260d9be1c8da22574417ddc Mon Sep 17 00:00:00 2001 From: Raphael Amorim Date: Sun, 6 Sep 2026 15:08:23 +0200 Subject: [PATCH 14/14] assistant modal only on errors, block welcome keys, dedup span rule --- frontends/rioterm/src/renderer/assistant.rs | 10 ++++ frontends/rioterm/src/renderer/island.rs | 13 +++-- frontends/rioterm/src/router/mod.rs | 47 ++++++++++------ rio-grid/src/lib.rs | 59 +++++++++++---------- 4 files changed, 76 insertions(+), 53 deletions(-) diff --git a/frontends/rioterm/src/renderer/assistant.rs b/frontends/rioterm/src/renderer/assistant.rs index 8ad725b51b..a24041ded9 100644 --- a/frontends/rioterm/src/renderer/assistant.rs +++ b/frontends/rioterm/src/renderer/assistant.rs @@ -88,6 +88,16 @@ impl AssistantOverlay { self.error.is_some() } + /// Whether the active toast is a hard error. Errors are modal + /// (keys and IME blocked, Enter dismisses); warnings render but + /// must never block input over a working terminal. + #[inline] + pub fn is_error(&self) -> bool { + self.error + .as_ref() + .is_some_and(|error| error.level == RioErrorLevel::Error) + } + #[inline] pub fn set_error(&mut self, error: RioError) { self.error = Some(error); diff --git a/frontends/rioterm/src/renderer/island.rs b/frontends/rioterm/src/renderer/island.rs index 31401b9161..3e3353b1f5 100644 --- a/frontends/rioterm/src/renderer/island.rs +++ b/frontends/rioterm/src/renderer/island.rs @@ -1086,20 +1086,20 @@ impl Island { /// Handle keyboard input while the color picker (with rename field) is open. /// Returns true if input was consumed. + /// Handle one key event for the rename input. The caller + /// (`has_key_wait`'s `Modal::IslandRename` arm) already verified + /// the picker is open via `active_modal` and consumes the event + /// unconditionally, so there is nothing to return. pub fn handle_rename_input( &mut self, key_event: &rio_window::event::KeyEvent, context_manager: &mut ContextManager, - ) -> bool { + ) { use rio_window::event::ElementState; use rio_window::keyboard::{Key, NamedKey}; - if self.color_picker_tab.is_none() { - return false; - } - if key_event.state != ElementState::Pressed { - return true; // consume release events too + return; // consume release events too } match &key_event.logical_key { @@ -1122,7 +1122,6 @@ impl Island { } } } - true } /// Append committed or typed text to the rename input, applying diff --git a/frontends/rioterm/src/router/mod.rs b/frontends/rioterm/src/router/mod.rs index 1e7dd65b59..2e081dd2bd 100644 --- a/frontends/rioterm/src/router/mod.rs +++ b/frontends/rioterm/src/router/mod.rs @@ -154,9 +154,12 @@ impl Route<'_> { } /// The modal overlay currently owning keyboard/IME input, in - /// dispatch priority order. THE roster: `has_key_wait`, - /// `modal_owns_input`, and `overlay_commit_text` all derive from - /// this, so a new overlay added here is gated everywhere at once. + /// dispatch priority order. THE roster for KEYBOARD AND IME + /// dispatch: `has_key_wait`, `modal_owns_input`, and + /// `overlay_commit_text` all derive from this, so a new overlay + /// added here is key/IME-gated at once. Mouse paths (hint + /// hover/click, the application.rs pointer handlers) still walk + /// their own overlay checks and need separate wiring. pub fn active_modal(&self) -> Option { if self .window @@ -174,7 +177,10 @@ impl Route<'_> { if self.window.screen.renderer.confirm_quit.is_active() { return Some(Modal::ConfirmQuit); } - if self.window.screen.renderer.assistant.is_active() { + // Only hard errors are modal: a warning toast (font not + // found on live reload, say) renders over a WORKING terminal + // and must never swallow typing or Ctrl+C. + if self.window.screen.renderer.assistant.is_error() { return Some(Modal::Assistant); } if self.path != RoutePath::Terminal { @@ -216,11 +222,10 @@ impl Route<'_> { } /// Whether a modal overlay currently owns keyboard input, so IME - /// composition must not reach the terminal behind it. One - /// deliberate asymmetry with `has_key_wait`: the welcome screen - /// lets plain keys fall through to the hidden terminal, but IME - /// commits are blocked here; feeding composed text to a shell the - /// user cannot see helps nobody. + /// composition must not reach the terminal behind it. Derived from + /// the same roster as `has_key_wait`, so keys and IME are gated + /// identically (the welcome screen blocks both: its PTY is live + /// but invisible). #[inline] pub fn modal_owns_input(&self) -> bool { self.active_modal().is_some() @@ -330,7 +335,7 @@ impl Route<'_> { } match selected_action { - // `ListFonts` stays inside the palette — + // `ListFonts` stays inside the palette: // swap the palette's contents from the // command list to the registered font // family names and keep it open. @@ -355,7 +360,7 @@ impl Route<'_> { .screen .execute_palette_action(action, clipboard); } - // No match at all — Enter just closes. + // No match at all: Enter just closes. None => { self.window .screen @@ -422,9 +427,15 @@ impl Route<'_> { // Path-independent, so a `report_error` toast raised at // `RoutePath::Terminal` blocks keys symmetrically with the // IME gate and Enter can dismiss it (previously only a mouse - // click could, while plain keys leaked to the shell). + // click could, while plain keys leaked to the shell). Only + // the press dismisses: acting on the release would let a + // toast appearing mid-keystroke vanish unseen, and would + // send the orphaned release to the PTY under kitty's + // report-event-types mode. Modal::Assistant => { - if key_event.logical_key == Key::Named(NamedKey::Enter) { + if key_event.state == ElementState::Pressed + && key_event.logical_key == Key::Named(NamedKey::Enter) + { self.assistant.clear(); self.window.screen.renderer.assistant.clear(); self.request_overlay_redraw(); @@ -433,14 +444,16 @@ impl Route<'_> { } Modal::Route => { - let is_enter = key_event.logical_key == Key::Named(NamedKey::Enter); + let is_enter = key_event.state == ElementState::Pressed + && key_event.logical_key == Key::Named(NamedKey::Enter); if self.path == RoutePath::Welcome && is_enter { rio_backend::config::create_config_file(None); self.path = RoutePath::Terminal; } - // The welcome screen lets keys fall through (pre-existing - // leak to the hidden terminal, see `modal_owns_input`). - false + // Block everything else: the PTY behind the welcome + // screen is live, and keys reaching it would execute + // invisibly once the terminal appears. + true } } } diff --git a/rio-grid/src/lib.rs b/rio-grid/src/lib.rs index a6fa3287c2..713b7f0326 100644 --- a/rio-grid/src/lib.rs +++ b/rio-grid/src/lib.rs @@ -1807,7 +1807,7 @@ fn shape_cached( font_library: &FontLibrary, ) -> Option<(i16, f32)> { if let Some(entry) = run_cache_get(&mut rasterizer.run_cache, hash) { - // Cache hit — advance stored, ascent in its own cache. + // Cache hit: advance stored, ascent in its own cache. let advance = entry.advance; return Some(( rasterizer @@ -2753,6 +2753,29 @@ fn emit_preedit_caret( }); } +/// Whether a registered custom glyph's render span reaches the +/// composition block from `col`: the fg pass drops such a glyph, so +/// its decorations must vanish with it. One predicate for both +/// decoration emitters; the span rule mirrors +/// `ensure_custom_glyph_by_codepoint`'s clamp to the protocol's 1..=2. +/// The `covers_ink(col, 2)` prefilter keeps the registry (an RwLock) +/// out of cells that are not next to the block, and callers run this +/// only after the cell is known to carry a decoration. +fn custom_glyph_ink_covered( + pre: &PreeditRow<'_>, + registry: Option<&rio_backend::sugarloaf::font::glyph_registry::GlyphRegistry>, + sq: Square, + col: usize, +) -> bool { + let Some(registry) = registry else { + return false; + }; + pre.covers_ink(col, 2) + && registry.get(sq.c() as u32).is_some_and(|entry| { + pre.covers_ink(col, (entry.width as u16).clamp(1, 2) as usize) + }) +} + #[allow(clippy::too_many_arguments)] fn emit_underlines( row: &Row, @@ -2779,20 +2802,6 @@ fn emit_underlines( if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } - // A registered custom glyph's render span can reach into the - // block from one logical column; the fg pass drops it for - // that, so drop its decoration too. `covers_ink(x, 2)` first: - // the registry (an RwLock) is only consulted next to the - // block. - if let (Some(pre), Some(registry)) = (preedit, glyph_registry) { - if pre.covers_ink(x, 2) - && registry.get(sq.c() as u32).is_some_and(|entry| { - pre.covers_ink(x, (entry.width as u16).clamp(1, 2) as usize) - }) - { - continue; - } - } let style = resolve_style(row_styles, x); let col = x as u16; // SGR underline (UNDER, double, curly, …) wins over the @@ -2807,6 +2816,9 @@ fn emit_underlines( } None => continue, }; + if preedit.is_some_and(|p| custom_glyph_ink_covered(p, glyph_registry, sq, x)) { + continue; + } let Some(slot) = ensure_decoration_slot(grid, deco, cell_w, cell_h, thickness) else { continue; @@ -2872,24 +2884,13 @@ fn emit_strikethroughs( if preedit.is_some_and(|p| p.suppresses(sq, x)) { continue; } - // A registered custom glyph's render span can reach into the - // block from one logical column; the fg pass drops it for - // that, so drop its decoration too. `covers_ink(x, 2)` first: - // the registry (an RwLock) is only consulted next to the - // block. - if let (Some(pre), Some(registry)) = (preedit, glyph_registry) { - if pre.covers_ink(x, 2) - && registry.get(sq.c() as u32).is_some_and(|entry| { - pre.covers_ink(x, (entry.width as u16).clamp(1, 2) as usize) - }) - { - continue; - } - } let style = resolve_style(row_styles, x); if !style.flags.contains(StyleFlags::STRIKEOUT) { continue; } + if preedit.is_some_and(|p| custom_glyph_ink_covered(p, glyph_registry, sq, x)) { + continue; + } let Some(slot) = ensure_decoration_slot( grid, DecorationStyle::Strikethrough,