Fix block cursor on graphemes (#19867)
Release Notes: - Fixed block cursor rendering only first char of multii-char graphemes.
This commit is contained in:
parent
1b84fee708
commit
719a7f7890
4 changed files with 30 additions and 27 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3717,6 +3717,7 @@ dependencies = [
|
||||||
"tree-sitter-rust",
|
"tree-sitter-rust",
|
||||||
"tree-sitter-typescript",
|
"tree-sitter-typescript",
|
||||||
"ui",
|
"ui",
|
||||||
|
"unicode-segmentation",
|
||||||
"unindent",
|
"unindent",
|
||||||
"url",
|
"url",
|
||||||
"util",
|
"util",
|
||||||
|
|
|
@ -76,6 +76,7 @@ theme.workspace = true
|
||||||
tree-sitter-html = { workspace = true, optional = true }
|
tree-sitter-html = { workspace = true, optional = true }
|
||||||
tree-sitter-rust = { workspace = true, optional = true }
|
tree-sitter-rust = { workspace = true, optional = true }
|
||||||
tree-sitter-typescript = { workspace = true, optional = true }
|
tree-sitter-typescript = { workspace = true, optional = true }
|
||||||
|
unicode-segmentation.workspace = true
|
||||||
unindent = { workspace = true, optional = true }
|
unindent = { workspace = true, optional = true }
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
|
|
@ -66,7 +66,8 @@ use std::{
|
||||||
use sum_tree::{Bias, TreeMap};
|
use sum_tree::{Bias, TreeMap};
|
||||||
use tab_map::{TabMap, TabSnapshot};
|
use tab_map::{TabMap, TabSnapshot};
|
||||||
use text::LineIndent;
|
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};
|
use wrap_map::{WrapMap, WrapSnapshot};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -880,12 +881,10 @@ impl DisplaySnapshot {
|
||||||
layout_line.closest_index_for_x(x) as u32
|
layout_line.closest_index_for_x(x) as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn display_chars_at(
|
pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<SharedString> {
|
||||||
&self,
|
|
||||||
mut point: DisplayPoint,
|
|
||||||
) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
|
|
||||||
point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
|
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)
|
.flat_map(str::chars)
|
||||||
.skip_while({
|
.skip_while({
|
||||||
let mut column = 0;
|
let mut column = 0;
|
||||||
|
@ -895,16 +894,24 @@ impl DisplaySnapshot {
|
||||||
!at_point
|
!at_point
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map(move |ch| {
|
.take_while({
|
||||||
let result = (ch, point);
|
let mut prev = false;
|
||||||
if ch == '\n' {
|
move |char| {
|
||||||
*point.row_mut() += 1;
|
let now = char.is_ascii();
|
||||||
*point.column_mut() = 0;
|
let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
|
||||||
} else {
|
prev = now;
|
||||||
*point.column_mut() += ch.len_utf8() as u32;
|
!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)> + '_ {
|
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 theme::{ActiveTheme, Appearance, PlayerColor};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
|
use ui::{h_flex, ButtonLike, ButtonStyle, ContextMenu, Tooltip};
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use util::RangeExt;
|
use util::RangeExt;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::{item::Item, Workspace};
|
use workspace::{item::Item, Workspace};
|
||||||
|
@ -1027,24 +1028,17 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
let block_text = if let CursorShape::Block = selection.cursor_shape {
|
let block_text = if let CursorShape::Block = selection.cursor_shape {
|
||||||
snapshot
|
snapshot
|
||||||
.display_chars_at(cursor_position)
|
.grapheme_at(cursor_position)
|
||||||
.next()
|
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
if cursor_column == 0 {
|
if cursor_column == 0 {
|
||||||
snapshot
|
snapshot.placeholder_text().and_then(|s| {
|
||||||
.placeholder_text()
|
s.graphemes(true).next().map(|s| s.to_string().into())
|
||||||
.and_then(|s| s.chars().next())
|
})
|
||||||
.map(|c| (c, cursor_position))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.and_then(|(character, _)| {
|
.and_then(|text| {
|
||||||
let text = if character == '\n' {
|
|
||||||
SharedString::from(" ")
|
|
||||||
} else {
|
|
||||||
SharedString::from(character.to_string())
|
|
||||||
};
|
|
||||||
let len = text.len();
|
let len = text.len();
|
||||||
|
|
||||||
let font = cursor_row_layout
|
let font = cursor_row_layout
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue