mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-19 02:17:35 +03:00
Show correct number of characters selected (#16420)
This commit is contained in:
parent
8841d6faad
commit
5e6e465294
@ -12,11 +12,11 @@ use ui::{
|
|||||||
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
use util::paths::FILE_ROW_COLUMN_DELIMITER;
|
||||||
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
use workspace::{item::ItemHandle, StatusItemView, Workspace};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
|
||||||
struct SelectionStats {
|
pub(crate) struct SelectionStats {
|
||||||
lines: usize,
|
pub lines: usize,
|
||||||
characters: usize,
|
pub characters: usize,
|
||||||
selections: usize,
|
pub selections: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CursorPosition {
|
pub struct CursorPosition {
|
||||||
@ -44,7 +44,10 @@ impl CursorPosition {
|
|||||||
self.selected_count.selections = editor.selections.count();
|
self.selected_count.selections = editor.selections.count();
|
||||||
let mut last_selection: Option<Selection<usize>> = None;
|
let mut last_selection: Option<Selection<usize>> = None;
|
||||||
for selection in editor.selections.all::<usize>(cx) {
|
for selection in editor.selections.all::<usize>(cx) {
|
||||||
self.selected_count.characters += selection.end - selection.start;
|
self.selected_count.characters += buffer
|
||||||
|
.text_for_range(selection.start..selection.end)
|
||||||
|
.map(|t| t.chars().count())
|
||||||
|
.sum::<usize>();
|
||||||
if last_selection
|
if last_selection
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |last_selection| selection.id > last_selection.id)
|
.map_or(true, |last_selection| selection.id > last_selection.id)
|
||||||
@ -106,6 +109,11 @@ impl CursorPosition {
|
|||||||
}
|
}
|
||||||
text.push(')');
|
text.push(')');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn selection_stats(&self) -> &SelectionStats {
|
||||||
|
&self.selected_count
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for CursorPosition {
|
impl Render for CursorPosition {
|
||||||
|
@ -221,6 +221,8 @@ impl Render for GoToLine {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use cursor_position::{CursorPosition, SelectionStats};
|
||||||
|
use editor::actions::SelectAll;
|
||||||
use gpui::{TestAppContext, VisualTestContext};
|
use gpui::{TestAppContext, VisualTestContext};
|
||||||
use indoc::indoc;
|
use indoc::indoc;
|
||||||
use project::{FakeFs, Project};
|
use project::{FakeFs, Project};
|
||||||
@ -335,6 +337,83 @@ mod tests {
|
|||||||
assert_single_caret_at_row(&editor, expected_highlighted_row, cx);
|
assert_single_caret_at_row(&editor, expected_highlighted_row, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_unicode_characters_selection(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
fs.insert_tree(
|
||||||
|
"/dir",
|
||||||
|
json!({
|
||||||
|
"a.rs": "ēlo"
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||||
|
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
let cursor_position = cx.new_view(|_| CursorPosition::new(workspace));
|
||||||
|
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||||
|
status_bar.add_right_item(cursor_position, cx);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let worktree_id = workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let _buffer = project
|
||||||
|
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let editor = workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
workspace.open_path((worktree_id, "a.rs"), None, true, cx)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.downcast::<Editor>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
assert_eq!(
|
||||||
|
&SelectionStats {
|
||||||
|
lines: 0,
|
||||||
|
characters: 0,
|
||||||
|
selections: 1,
|
||||||
|
},
|
||||||
|
workspace
|
||||||
|
.status_bar()
|
||||||
|
.read(cx)
|
||||||
|
.item_of_type::<CursorPosition>()
|
||||||
|
.expect("missing cursor position item")
|
||||||
|
.read(cx)
|
||||||
|
.selection_stats(),
|
||||||
|
"No selections should be initially"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
editor.update(cx, |editor, cx| editor.select_all(&SelectAll, cx));
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
assert_eq!(
|
||||||
|
&SelectionStats {
|
||||||
|
lines: 1,
|
||||||
|
characters: 3,
|
||||||
|
selections: 1,
|
||||||
|
},
|
||||||
|
workspace
|
||||||
|
.status_bar()
|
||||||
|
.read(cx)
|
||||||
|
.item_of_type::<CursorPosition>()
|
||||||
|
.expect("missing cursor position item")
|
||||||
|
.read(cx)
|
||||||
|
.selection_stats(),
|
||||||
|
"After selecting a text with multibyte unicode characters, the character count should be correct"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn open_go_to_line_view(
|
fn open_go_to_line_view(
|
||||||
workspace: &View<Workspace>,
|
workspace: &View<Workspace>,
|
||||||
cx: &mut VisualTestContext,
|
cx: &mut VisualTestContext,
|
||||||
|
Loading…
Reference in New Issue
Block a user