Cargo.lock 🔗
@@ -3717,6 +3717,7 @@ dependencies = [
"tree-sitter-rust",
"tree-sitter-typescript",
"ui",
+ "unicode-segmentation",
"unindent",
"url",
"util",
Conrad Irwin created
Release Notes:
- Fixed block cursor rendering only first char of multii-char graphemes.
Cargo.lock | 1
crates/editor/Cargo.toml | 1
crates/editor/src/display_map.rs | 37 ++++++++++++++++++++-------------
crates/editor/src/element.rs | 18 +++++-----------
4 files changed, 30 insertions(+), 27 deletions(-)
@@ -3717,6 +3717,7 @@ dependencies = [
"tree-sitter-rust",
"tree-sitter-typescript",
"ui",
+ "unicode-segmentation",
"unindent",
"url",
"util",
@@ -76,6 +76,7 @@ theme.workspace = true
tree-sitter-html = { workspace = true, optional = true }
tree-sitter-rust = { workspace = true, optional = true }
tree-sitter-typescript = { workspace = true, optional = true }
+unicode-segmentation.workspace = true
unindent = { workspace = true, optional = true }
ui.workspace = true
url.workspace = true
@@ -66,7 +66,8 @@ use std::{
use sum_tree::{Bias, TreeMap};
use tab_map::{TabMap, TabSnapshot};
use text::LineIndent;
-use ui::{div, px, IntoElement, ParentElement, Styled, WindowContext};
+use ui::{div, px, IntoElement, ParentElement, SharedString, Styled, WindowContext};
+use unicode_segmentation::UnicodeSegmentation;
use wrap_map::{WrapMap, WrapSnapshot};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -880,12 +881,10 @@ impl DisplaySnapshot {
layout_line.closest_index_for_x(x) as u32
}
- pub fn display_chars_at(
- &self,
- mut point: DisplayPoint,
- ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
+ pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<SharedString> {
point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
- self.text_chunks(point.row())
+ let chars = self
+ .text_chunks(point.row())
.flat_map(str::chars)
.skip_while({
let mut column = 0;
@@ -895,16 +894,24 @@ impl DisplaySnapshot {
!at_point
}
})
- .map(move |ch| {
- let result = (ch, point);
- if ch == '\n' {
- *point.row_mut() += 1;
- *point.column_mut() = 0;
- } else {
- *point.column_mut() += ch.len_utf8() as u32;
+ .take_while({
+ let mut prev = false;
+ move |char| {
+ let now = char.is_ascii();
+ let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
+ prev = now;
+ !end
}
- result
- })
+ });
+ chars.collect::<String>().graphemes(true).next().map(|s| {
+ if let Some(invisible) = s.chars().next().filter(|&c| is_invisible(c)) {
+ replacement(invisible).unwrap_or(s).to_owned().into()
+ } else if s == "\n" {
+ " ".into()
+ } else {
+ s.to_owned().into()
+ }
+ })
}
pub fn buffer_chars_at(&self, mut offset: usize) -> impl Iterator<Item = (char, usize)> + '_ {
@@ -68,6 +68,7 @@ use sum_tree::Bias;
use theme::{ActiveTheme, Appearance, PlayerColor};
use ui::prelude::*;
use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
+use unicode_segmentation::UnicodeSegmentation;
use util::RangeExt;
use util::ResultExt;
use workspace::{item::Item, Workspace};
@@ -1027,24 +1028,17 @@ impl EditorElement {
}
let block_text = if let CursorShape::Block = selection.cursor_shape {
snapshot
- .display_chars_at(cursor_position)
- .next()
+ .grapheme_at(cursor_position)
.or_else(|| {
if cursor_column == 0 {
- snapshot
- .placeholder_text()
- .and_then(|s| s.chars().next())
- .map(|c| (c, cursor_position))
+ snapshot.placeholder_text().and_then(|s| {
+ s.graphemes(true).next().map(|s| s.to_string().into())
+ })
} else {
None
}
})
- .and_then(|(character, _)| {
- let text = if character == '\n' {
- SharedString::from(" ")
- } else {
- SharedString::from(character.to_string())
- };
+ .and_then(|text| {
let len = text.len();
let font = cursor_row_layout