input: Refactor start_of_line, end_of_line by use Rope. (#1232)
- Removed `Cursor` struct, now use `usize` for selection offset.
This commit is contained in:
parent
844574befc
commit
43c848893c
7 changed files with 170 additions and 330 deletions
|
|
@ -312,7 +312,7 @@ impl Render for Example {
|
|||
Button::new("line-column")
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.label(format!("{} ({} c)", loc, cursor.offset()))
|
||||
.label(format!("{} ({} c)", loc, cursor))
|
||||
.on_click(cx.listener(Self::go_to_line))
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ impl Render for Example {
|
|||
Button::new("line-column")
|
||||
.ghost()
|
||||
.xsmall()
|
||||
.label(format!("{} ({} c)", loc, cursor.offset()))
|
||||
.label(format!("{} ({} c)", loc, cursor))
|
||||
.on_click(cx.listener(Self::go_to_line))
|
||||
}),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,159 +1,39 @@
|
|||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
ops::{Add, Deref, Range, Sub},
|
||||
};
|
||||
|
||||
/// Cursor of the text.
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub struct Cursor {
|
||||
/// The byte offset in the text (zero-based).
|
||||
pub(super) offset: usize,
|
||||
}
|
||||
|
||||
impl Cursor {
|
||||
pub fn new(offset: usize) -> Self {
|
||||
Self { offset }
|
||||
}
|
||||
|
||||
/// Returns the byte offset in the text (zero-based).
|
||||
pub fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Cursor {}
|
||||
impl PartialEq for Cursor {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.offset == other.offset
|
||||
}
|
||||
}
|
||||
impl PartialEq<usize> for Cursor {
|
||||
fn eq(&self, other: &usize) -> bool {
|
||||
self.offset == *other
|
||||
}
|
||||
}
|
||||
impl PartialEq<Cursor> for usize {
|
||||
fn eq(&self, other: &Cursor) -> bool {
|
||||
*self == other.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Cursor {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.offset.partial_cmp(&other.offset)
|
||||
}
|
||||
}
|
||||
impl PartialOrd<usize> for Cursor {
|
||||
fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
|
||||
self.offset.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
impl PartialOrd<Cursor> for usize {
|
||||
fn partial_cmp(&self, other: &Cursor) -> Option<Ordering> {
|
||||
self.partial_cmp(&other.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Cursor {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, other: Self) -> Self {
|
||||
self.offset += other.offset;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Add<usize> for Cursor {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, other: usize) -> Self {
|
||||
self.offset += other;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Add<Cursor> for usize {
|
||||
type Output = Cursor;
|
||||
|
||||
fn add(self, other: Cursor) -> Cursor {
|
||||
Cursor::new(self + other.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Cursor {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(mut self, other: Self) -> Self {
|
||||
self.offset -= other.offset;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Sub<usize> for Cursor {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(mut self, other: usize) -> Self {
|
||||
self.offset -= other;
|
||||
self
|
||||
}
|
||||
}
|
||||
impl Sub<Cursor> for usize {
|
||||
type Output = Cursor;
|
||||
|
||||
fn sub(self, other: Cursor) -> Cursor {
|
||||
Cursor::new(self - other.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Cursor {
|
||||
type Target = usize;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.offset
|
||||
}
|
||||
}
|
||||
use std::{fmt, ops::Range};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||
pub struct Selection {
|
||||
pub start: Cursor,
|
||||
pub end: Cursor,
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
impl Selection {
|
||||
pub fn new(start: Cursor, end: Cursor) -> Self {
|
||||
pub fn new(start: usize, end: usize) -> Self {
|
||||
Self { start, end }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.end.offset.saturating_sub(self.start.offset)
|
||||
self.end.saturating_sub(self.start)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.start == self.end
|
||||
}
|
||||
|
||||
/// Clears the selection, setting start and end to 0.
|
||||
pub fn clear(&mut self) {
|
||||
self.start = 0;
|
||||
self.end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Cursor>> for Selection {
|
||||
fn from(value: Range<Cursor>) -> Self {
|
||||
Self::new(value.start, value.end)
|
||||
}
|
||||
}
|
||||
impl From<Selection> for Range<Cursor> {
|
||||
fn from(value: Selection) -> Self {
|
||||
value.start..value.end
|
||||
}
|
||||
}
|
||||
impl From<Range<usize>> for Selection {
|
||||
fn from(value: Range<usize>) -> Self {
|
||||
Self::new(Cursor::new(value.start), Cursor::new(value.end))
|
||||
Self::new(value.start, value.end)
|
||||
}
|
||||
}
|
||||
impl From<Selection> for Range<usize> {
|
||||
fn from(value: Selection) -> Self {
|
||||
value.start.offset..value.end.offset
|
||||
}
|
||||
}
|
||||
impl From<&Selection> for Range<usize> {
|
||||
fn from(value: &Selection) -> Self {
|
||||
value.start.offset..value.end.offset
|
||||
value.start..value.end
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +46,12 @@ pub struct LineColumn {
|
|||
pub column: usize,
|
||||
}
|
||||
|
||||
impl LineColumn {
|
||||
pub fn new(line: usize, column: usize) -> Self {
|
||||
(line, column).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for LineColumn {
|
||||
fn from(value: (usize, usize)) -> Self {
|
||||
Self {
|
||||
|
|
@ -175,6 +61,15 @@ impl From<(usize, usize)> for LineColumn {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<rope::Point> for LineColumn {
|
||||
fn from(value: rope::Point) -> Self {
|
||||
Self {
|
||||
line: value.row as usize + 1,
|
||||
column: value.column as usize + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LineColumn> for tree_sitter::Point {
|
||||
fn from(value: LineColumn) -> Self {
|
||||
Self {
|
||||
|
|
@ -194,6 +89,33 @@ impl fmt::Display for LineColumn {
|
|||
mod tests {
|
||||
use crate::input::LineColumn;
|
||||
|
||||
#[test]
|
||||
fn test_line_column_from_to() {
|
||||
assert_eq!(LineColumn::new(1, 2), LineColumn { line: 1, column: 2 });
|
||||
|
||||
assert_eq!(LineColumn::from((1, 2)), LineColumn { line: 1, column: 2 });
|
||||
assert_eq!(
|
||||
LineColumn::from((10, 10)),
|
||||
LineColumn {
|
||||
line: 10,
|
||||
column: 10
|
||||
}
|
||||
);
|
||||
assert_eq!(LineColumn::from((0, 0)), LineColumn { line: 1, column: 1 });
|
||||
|
||||
assert_eq!(
|
||||
LineColumn::from(rope::Point::new(0, 1)),
|
||||
LineColumn { line: 1, column: 2 }
|
||||
);
|
||||
assert_eq!(
|
||||
LineColumn::from(rope::Point::new(10, 9)),
|
||||
LineColumn {
|
||||
line: 11,
|
||||
column: 10
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_column_display() {
|
||||
assert_eq!(LineColumn::from((1, 2)).to_string(), "1:2");
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ impl TextElement {
|
|||
{
|
||||
// If in visible range lines
|
||||
if cursor_pos.is_none() {
|
||||
let offset = cursor.offset.saturating_sub(prev_lines_offset);
|
||||
let offset = cursor.saturating_sub(prev_lines_offset);
|
||||
if let Some(pos) = line.position_for_index(offset, line_height) {
|
||||
current_row = Some(row);
|
||||
cursor_pos = Some(line_origin + pos);
|
||||
|
|
@ -142,7 +142,7 @@ impl TextElement {
|
|||
|
||||
// Just increase the offset_y and prev_lines_offset.
|
||||
// This will let the scroll_offset to track the cursor position correctly.
|
||||
if prev_lines_offset >= cursor.offset && cursor_pos.is_none() {
|
||||
if prev_lines_offset >= cursor && cursor_pos.is_none() {
|
||||
current_row = Some(row);
|
||||
cursor_pos = Some(line_origin);
|
||||
}
|
||||
|
|
@ -679,16 +679,16 @@ impl Element for TextElement {
|
|||
// IME marked text
|
||||
vec![
|
||||
TextRun {
|
||||
len: marked_range.start.offset,
|
||||
len: marked_range.start,
|
||||
..run.clone()
|
||||
},
|
||||
TextRun {
|
||||
len: marked_range.end.offset - marked_range.start.offset,
|
||||
len: marked_range.end - marked_range.start,
|
||||
underline: marked_run.underline,
|
||||
..run.clone()
|
||||
},
|
||||
TextRun {
|
||||
len: display_text.len() - marked_range.end.offset,
|
||||
len: display_text.len() - marked_range.end,
|
||||
..run.clone()
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use super::{
|
|||
use crate::input::hover_popover::DiagnosticPopover;
|
||||
use crate::input::marker::Marker;
|
||||
use crate::input::text_wrapper::LineWrap;
|
||||
use crate::input::{Cursor, LineColumn, RopeExt as _, Selection};
|
||||
use crate::input::{LineColumn, RopeExt as _, Selection};
|
||||
use crate::{history::History, scroll::ScrollbarState, Root};
|
||||
|
||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||
|
|
@ -252,7 +252,7 @@ pub struct InputState {
|
|||
/// The marked range is the temporary insert text on IME typing.
|
||||
pub(super) marked_range: Option<Selection>,
|
||||
pub(super) last_layout: Option<LastLayout>,
|
||||
pub(super) last_cursor: Option<Cursor>,
|
||||
pub(super) last_cursor: Option<usize>,
|
||||
/// The input container bounds
|
||||
pub(super) input_bounds: Bounds<Pixels>,
|
||||
/// The text bounds
|
||||
|
|
@ -323,7 +323,7 @@ impl InputState {
|
|||
),
|
||||
blink_cursor,
|
||||
history,
|
||||
selected_range: (Cursor::new(0)..Cursor::new(0)).into(),
|
||||
selected_range: Selection::default(),
|
||||
selected_word_range: None,
|
||||
selection_reversed: false,
|
||||
marked_range: None,
|
||||
|
|
@ -514,7 +514,7 @@ impl InputState {
|
|||
|
||||
/// Called after moving the cursor. Updates preferred_column if we know where the cursor now is.
|
||||
fn update_preferred_column(&mut self) {
|
||||
let column_ix = self.text.offset_to_point(self.cursor().offset).column;
|
||||
let column_ix = self.text.offset_to_point(self.cursor()).column;
|
||||
self.preferred_column = Some(column_ix as usize);
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +558,7 @@ impl InputState {
|
|||
return;
|
||||
}
|
||||
|
||||
let offset = self.cursor().offset;
|
||||
let offset = self.cursor();
|
||||
let was_preferred_column = self.preferred_column;
|
||||
|
||||
let row = self.text.offset_to_point(offset).row;
|
||||
|
|
@ -570,7 +570,7 @@ impl InputState {
|
|||
let new_offset = line_start_offset + new_column;
|
||||
|
||||
self.pause_blink_cursor(cx);
|
||||
self.move_to(Cursor::new(new_offset), window, cx);
|
||||
self.move_to(new_offset, window, cx);
|
||||
// Set back the preferred_x_offset
|
||||
self.preferred_column = was_preferred_column;
|
||||
cx.notify();
|
||||
|
|
@ -592,10 +592,9 @@ impl InputState {
|
|||
self.history.ignore = false;
|
||||
// Ensure cursor to start when set text
|
||||
if self.mode.is_single_line() {
|
||||
self.selected_range =
|
||||
(Cursor::new(self.text.len())..Cursor::new(self.text.len())).into();
|
||||
self.selected_range = (self.text.len()..self.text.len()).into();
|
||||
} else {
|
||||
self.selected_range = (Cursor::new(0)..Cursor::new(0)).into();
|
||||
self.selected_range.clear();
|
||||
}
|
||||
// Move scroll to top
|
||||
self.scroll_handle.set_offset(point(px(0.), px(0.)));
|
||||
|
|
@ -613,7 +612,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let text: SharedString = text.into();
|
||||
let range_utf16 = self.range_to_utf16(&(self.cursor().offset..self.cursor().offset));
|
||||
let range_utf16 = self.range_to_utf16(&(self.cursor()..self.cursor()));
|
||||
self.replace_text_in_range(Some(range_utf16), &text, window, cx);
|
||||
self.selected_range = (self.selected_range.end..self.selected_range.end).into();
|
||||
}
|
||||
|
|
@ -742,7 +741,7 @@ impl InputState {
|
|||
|
||||
/// Return the (1-based) line and column of the cursor.
|
||||
pub fn line_column(&self) -> LineColumn {
|
||||
let offset = self.cursor().offset;
|
||||
let offset = self.cursor();
|
||||
let point = self.text.offset_to_point(offset);
|
||||
|
||||
LineColumn {
|
||||
|
|
@ -781,7 +780,7 @@ impl InputState {
|
|||
|
||||
// TODO: Scroll to make the row in center of viewport.
|
||||
|
||||
self.move_to(Cursor::new(offset), window, cx);
|
||||
self.move_to(offset, window, cx);
|
||||
self.update_preferred_column();
|
||||
self.focus(window, cx);
|
||||
}
|
||||
|
|
@ -794,11 +793,7 @@ impl InputState {
|
|||
pub(super) fn left(&mut self, _: &MoveLeft, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_blink_cursor(cx);
|
||||
if self.selected_range.is_empty() {
|
||||
self.move_to(
|
||||
Cursor::new(self.previous_boundary(self.cursor().offset)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.move_to(self.previous_boundary(self.cursor()), window, cx);
|
||||
} else {
|
||||
self.move_to(self.selected_range.start, window, cx)
|
||||
}
|
||||
|
|
@ -807,11 +802,7 @@ impl InputState {
|
|||
pub(super) fn right(&mut self, _: &MoveRight, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_blink_cursor(cx);
|
||||
if self.selected_range.is_empty() {
|
||||
self.move_to(
|
||||
Cursor::new(self.next_boundary(self.selected_range.end.offset)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.move_to(self.next_boundary(self.selected_range.end), window, cx);
|
||||
} else {
|
||||
self.move_to(self.selected_range.end, window, cx)
|
||||
}
|
||||
|
|
@ -824,7 +815,7 @@ impl InputState {
|
|||
|
||||
if !self.selected_range.is_empty() {
|
||||
self.move_to(
|
||||
Cursor::new(self.previous_boundary(self.selected_range.start.saturating_sub(1))),
|
||||
self.previous_boundary(self.selected_range.start.saturating_sub(1)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
|
@ -840,7 +831,7 @@ impl InputState {
|
|||
|
||||
if !self.selected_range.is_empty() {
|
||||
self.move_to(
|
||||
Cursor::new(self.next_boundary(self.selected_range.end.offset.saturating_sub(1))),
|
||||
self.next_boundary(self.selected_range.end.saturating_sub(1)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
|
|
@ -887,11 +878,7 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.select_to(
|
||||
Cursor::new(self.previous_boundary(self.cursor().offset)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.select_to(self.previous_boundary(self.cursor()), window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_right(
|
||||
|
|
@ -900,19 +887,15 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.select_to(
|
||||
Cursor::new(self.next_boundary(self.cursor().offset)),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
self.select_to(self.next_boundary(self.cursor()), window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_up(&mut self, _: &SelectUp, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.mode.is_single_line() {
|
||||
return;
|
||||
}
|
||||
let offset = self.start_of_line(window, cx).saturating_sub(1);
|
||||
self.select_to(Cursor::new(self.previous_boundary(offset)), window, cx);
|
||||
let offset = self.start_of_line().saturating_sub(1);
|
||||
self.select_to(self.previous_boundary(offset), window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_down(
|
||||
|
|
@ -924,8 +907,8 @@ impl InputState {
|
|||
if self.mode.is_single_line() {
|
||||
return;
|
||||
}
|
||||
let offset = (self.end_of_line(window, cx) + 1).min(self.text.len());
|
||||
self.select_to(Cursor::new(self.next_boundary(offset)), window, cx);
|
||||
let offset = (self.end_of_line() + 1).min(self.text.len());
|
||||
self.select_to(self.next_boundary(offset), window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_all(
|
||||
|
|
@ -934,20 +917,20 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.move_to(Cursor::new(0), window, cx);
|
||||
self.select_to(Cursor::new(self.text.len()), window, cx)
|
||||
self.move_to(0, window, cx);
|
||||
self.select_to(self.text.len(), window, cx)
|
||||
}
|
||||
|
||||
pub(super) fn home(&mut self, _: &MoveHome, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_blink_cursor(cx);
|
||||
let offset = self.start_of_line(window, cx);
|
||||
self.move_to(Cursor::new(offset), window, cx);
|
||||
let offset = self.start_of_line();
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn end(&mut self, _: &MoveEnd, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.pause_blink_cursor(cx);
|
||||
let offset = self.end_of_line(window, cx);
|
||||
self.move_to(Cursor::new(offset), window, cx);
|
||||
let offset = self.end_of_line();
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn move_to_start(
|
||||
|
|
@ -956,7 +939,7 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.move_to(Cursor::new(0), window, cx);
|
||||
self.move_to(0, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn move_to_end(
|
||||
|
|
@ -965,8 +948,7 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let end = self.text.len();
|
||||
self.move_to(Cursor::new(end), window, cx);
|
||||
self.move_to(self.text.len(), window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn move_to_previous_word(
|
||||
|
|
@ -976,7 +958,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.move_to(Cursor::new(offset), window, cx);
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn move_to_next_word(
|
||||
|
|
@ -986,7 +968,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.move_to(Cursor::new(offset), window, cx);
|
||||
self.move_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_start(
|
||||
|
|
@ -995,7 +977,7 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.select_to(Cursor::new(0), window, cx);
|
||||
self.select_to(0, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_end(
|
||||
|
|
@ -1005,7 +987,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let end = self.text.len();
|
||||
self.select_to(Cursor::new(end), window, cx);
|
||||
self.select_to(end, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_start_of_line(
|
||||
|
|
@ -1014,8 +996,8 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.start_of_line(window, cx);
|
||||
self.select_to(Cursor::new(self.previous_boundary(offset)), window, cx);
|
||||
let offset = self.start_of_line();
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_end_of_line(
|
||||
|
|
@ -1024,8 +1006,8 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.end_of_line(window, cx);
|
||||
self.select_to(Cursor::new(self.next_boundary(offset)), window, cx);
|
||||
let offset = self.end_of_line();
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_previous_word(
|
||||
|
|
@ -1035,7 +1017,7 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.select_to(Cursor::new(offset), window, cx);
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
pub(super) fn select_to_next_word(
|
||||
|
|
@ -1045,12 +1027,12 @@ impl InputState {
|
|||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.select_to(Cursor::new(offset), window, cx);
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
/// Return the start offset of the previous word.
|
||||
fn previous_start_of_word(&mut self) -> usize {
|
||||
let offset = self.selected_range.start.offset;
|
||||
let offset = self.selected_range.start;
|
||||
// FIXME: Avoid to_string
|
||||
let left_part = self.text.slice(0..offset).to_string();
|
||||
|
||||
|
|
@ -1063,7 +1045,7 @@ impl InputState {
|
|||
|
||||
/// Return the next end offset of the next word.
|
||||
fn next_end_of_word(&mut self) -> usize {
|
||||
let offset = self.cursor().offset;
|
||||
let offset = self.cursor();
|
||||
let right_part = self.text.slice(offset..self.text.len()).to_string();
|
||||
|
||||
UnicodeSegmentation::split_word_bound_indices(right_part.as_str())
|
||||
|
|
@ -1072,20 +1054,24 @@ impl InputState {
|
|||
.unwrap_or(self.text.len())
|
||||
}
|
||||
|
||||
/// Get start of line
|
||||
fn start_of_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> usize {
|
||||
/// Get start of line byte offset of cursor
|
||||
fn start_of_line(&self) -> usize {
|
||||
if self.mode.is_single_line() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let offset = self.previous_boundary(self.cursor().offset);
|
||||
let line = self
|
||||
.text_for_range(self.range_to_utf16(&(0..offset + 1)), &mut None, window, cx)
|
||||
.unwrap_or_default()
|
||||
.rfind('\n')
|
||||
.map(|i| i + 1)
|
||||
.unwrap_or(0);
|
||||
line
|
||||
let row = self.text.offset_to_point(self.cursor()).row;
|
||||
self.text.line_start_offset(row as usize)
|
||||
}
|
||||
|
||||
/// Get end of line byte offset of cursor
|
||||
fn end_of_line(&self) -> usize {
|
||||
if self.mode.is_single_line() {
|
||||
return self.text.len();
|
||||
}
|
||||
|
||||
let row = self.text.offset_to_point(self.cursor()).row;
|
||||
self.text.line_end_offset(row as usize)
|
||||
}
|
||||
|
||||
/// Get start line of selection start or end (The min value).
|
||||
|
|
@ -1096,11 +1082,8 @@ impl InputState {
|
|||
return 0;
|
||||
}
|
||||
|
||||
let mut offset = self.previous_boundary(
|
||||
self.selected_range
|
||||
.start
|
||||
.min(self.selected_range.end.offset),
|
||||
);
|
||||
let mut offset =
|
||||
self.previous_boundary(self.selected_range.start.min(self.selected_range.end));
|
||||
if self.text.char_at(offset) == Some('\r') {
|
||||
offset += 1;
|
||||
}
|
||||
|
|
@ -1114,60 +1097,18 @@ impl InputState {
|
|||
line
|
||||
}
|
||||
|
||||
/// Get end of line
|
||||
fn end_of_line(&mut self, window: &mut Window, cx: &mut Context<Self>) -> usize {
|
||||
if self.mode.is_single_line() {
|
||||
return self.text.len();
|
||||
}
|
||||
|
||||
// let line = self.text.byte_to_line(self.cursor().offset);
|
||||
// let offset = self.text.line_to_byte(line) + self.text.line(line).len();
|
||||
|
||||
let offset = self.next_boundary(self.cursor().offset);
|
||||
// ignore if offset is "\n"
|
||||
if self
|
||||
.text_for_range(
|
||||
self.range_to_utf16(&(offset.saturating_sub(1)..offset)),
|
||||
&mut None,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
.eq("\n")
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
let line = self
|
||||
.text_for_range(
|
||||
self.range_to_utf16(&(offset..self.text.len())),
|
||||
&mut None,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
.find('\n')
|
||||
.map(|i| i + offset)
|
||||
.unwrap_or(self.text.len());
|
||||
line
|
||||
}
|
||||
|
||||
/// Get indent string of next line.
|
||||
///
|
||||
/// To get current and next line indent, to return more depth one.
|
||||
pub(super) fn indent_of_next_line(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> String {
|
||||
pub(super) fn indent_of_next_line(&mut self) -> String {
|
||||
if self.mode.is_single_line() {
|
||||
return "".into();
|
||||
}
|
||||
|
||||
let mut current_indent = String::new();
|
||||
let mut next_indent = String::new();
|
||||
let current_line_start_pos = self.start_of_line(window, cx);
|
||||
let next_line_start_pos = self.end_of_line(window, cx);
|
||||
let current_line_start_pos = self.start_of_line();
|
||||
let next_line_start_pos = self.end_of_line();
|
||||
for c in self.text.chars().skip(current_line_start_pos) {
|
||||
if !c.is_whitespace() {
|
||||
break;
|
||||
|
|
@ -1197,11 +1138,7 @@ impl InputState {
|
|||
|
||||
pub(super) fn backspace(&mut self, _: &Backspace, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.selected_range.is_empty() {
|
||||
self.select_to(
|
||||
Cursor::new(self.previous_boundary(self.cursor().offset)),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
self.select_to(self.previous_boundary(self.cursor()), window, cx)
|
||||
}
|
||||
self.replace_text_in_range(None, "", window, cx);
|
||||
self.pause_blink_cursor(cx);
|
||||
|
|
@ -1209,11 +1146,7 @@ impl InputState {
|
|||
|
||||
pub(super) fn delete(&mut self, _: &Delete, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.selected_range.is_empty() {
|
||||
self.select_to(
|
||||
Cursor::new(self.next_boundary(self.cursor().offset)),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
self.select_to(self.next_boundary(self.cursor()), window, cx)
|
||||
}
|
||||
self.replace_text_in_range(None, "", window, cx);
|
||||
self.pause_blink_cursor(cx);
|
||||
|
|
@ -1225,12 +1158,12 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut offset = self.start_of_line(window, cx);
|
||||
if offset == self.cursor().offset {
|
||||
let mut offset = self.start_of_line();
|
||||
if offset == self.cursor() {
|
||||
offset = offset.saturating_sub(1);
|
||||
}
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(offset..self.cursor().offset))),
|
||||
Some(self.range_to_utf16(&(offset..self.cursor()))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1245,12 +1178,12 @@ impl InputState {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let mut offset = self.end_of_line(window, cx);
|
||||
if offset == self.cursor().offset {
|
||||
let mut offset = self.end_of_line();
|
||||
if offset == self.cursor() {
|
||||
offset = (offset + 1).clamp(0, self.text.len());
|
||||
}
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(self.cursor().offset..offset))),
|
||||
Some(self.range_to_utf16(&(self.cursor()..offset))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1266,7 +1199,7 @@ impl InputState {
|
|||
) {
|
||||
let offset = self.previous_start_of_word();
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(offset..self.cursor().offset))),
|
||||
Some(self.range_to_utf16(&(offset..self.cursor()))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1282,7 +1215,7 @@ impl InputState {
|
|||
) {
|
||||
let offset = self.next_end_of_word();
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(self.cursor().offset..offset))),
|
||||
Some(self.range_to_utf16(&(self.cursor()..offset))),
|
||||
"",
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1294,7 +1227,7 @@ impl InputState {
|
|||
if self.mode.is_multi_line() {
|
||||
// Get current line indent
|
||||
let indent = if self.mode.is_code_editor() {
|
||||
self.indent_of_next_line(window, cx)
|
||||
self.indent_of_next_line()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
|
@ -1359,7 +1292,7 @@ impl InputState {
|
|||
|
||||
let selected_text = self
|
||||
.text_for_range(
|
||||
self.range_to_utf16(&(offset..selected_range.end.offset)),
|
||||
self.range_to_utf16(&(offset..selected_range.end)),
|
||||
&mut None,
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1379,15 +1312,14 @@ impl InputState {
|
|||
}
|
||||
|
||||
if is_selected {
|
||||
self.selected_range =
|
||||
(Cursor::new(start_offset)..selected_range.end + added_len).into();
|
||||
self.selected_range = (start_offset..selected_range.end + added_len).into();
|
||||
} else {
|
||||
self.selected_range =
|
||||
(selected_range.start + added_len..selected_range.end + added_len).into();
|
||||
}
|
||||
} else {
|
||||
// Selected none
|
||||
let offset = self.selected_range.start.offset;
|
||||
let offset = self.selected_range.start;
|
||||
self.replace_text_in_range(
|
||||
Some(self.range_to_utf16(&(offset..offset))),
|
||||
&tab_indent,
|
||||
|
|
@ -1417,7 +1349,7 @@ impl InputState {
|
|||
|
||||
let selected_text = self
|
||||
.text_for_range(
|
||||
self.range_to_utf16(&(offset..selected_range.end.offset)),
|
||||
self.range_to_utf16(&(offset..selected_range.end)),
|
||||
&mut None,
|
||||
window,
|
||||
cx,
|
||||
|
|
@ -1442,14 +1374,12 @@ impl InputState {
|
|||
}
|
||||
|
||||
if is_selected {
|
||||
self.selected_range = (Cursor::new(start_offset)
|
||||
..Cursor::new(selected_range.end.saturating_sub(removed_len)))
|
||||
.into();
|
||||
} else {
|
||||
self.selected_range =
|
||||
(Cursor::new(selected_range.start.saturating_sub(removed_len))
|
||||
..Cursor::new(selected_range.end.saturating_sub(removed_len)))
|
||||
.into();
|
||||
(start_offset..selected_range.end.saturating_sub(removed_len)).into();
|
||||
} else {
|
||||
self.selected_range = (selected_range.start.saturating_sub(removed_len)
|
||||
..selected_range.end.saturating_sub(removed_len))
|
||||
.into();
|
||||
}
|
||||
} else {
|
||||
// Selected none
|
||||
|
|
@ -1470,7 +1400,7 @@ impl InputState {
|
|||
);
|
||||
removed_len = tab_indent.len();
|
||||
let new_offset = start_offset.saturating_sub(removed_len);
|
||||
self.selected_range = (Cursor::new(new_offset)..Cursor::new(new_offset)).into();
|
||||
self.selected_range = (new_offset..new_offset).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1514,9 +1444,9 @@ impl InputState {
|
|||
}
|
||||
|
||||
if event.modifiers.shift {
|
||||
self.select_to(Cursor::new(offset), window, cx);
|
||||
self.select_to(offset, window, cx);
|
||||
} else {
|
||||
self.move_to(Cursor::new(offset), window, cx)
|
||||
self.move_to(offset, window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1671,18 +1601,18 @@ impl InputState {
|
|||
/// The offset is the UTF-8 offset.
|
||||
///
|
||||
/// Ensure the offset use self.next_boundary or self.previous_boundary to get the correct offset.
|
||||
fn move_to(&mut self, cursor: Cursor, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let cursor = Cursor::new(cursor.offset.clamp(0, self.text.len()));
|
||||
self.selected_range = (cursor..cursor).into();
|
||||
fn move_to(&mut self, offset: usize, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let offset = offset.clamp(0, self.text.len());
|
||||
self.selected_range = (offset..offset).into();
|
||||
self.pause_blink_cursor(cx);
|
||||
self.update_preferred_column();
|
||||
cx.notify()
|
||||
}
|
||||
|
||||
/// Get the cursor position.
|
||||
/// Get byte offset of the cursor.
|
||||
///
|
||||
/// The offset is the UTF-8 offset.
|
||||
pub fn cursor(&self) -> Cursor {
|
||||
pub fn cursor(&self) -> usize {
|
||||
if let Some(marked_range) = &self.marked_range {
|
||||
return marked_range.end;
|
||||
}
|
||||
|
|
@ -1814,12 +1744,12 @@ impl InputState {
|
|||
/// The offset is the UTF-8 offset.
|
||||
///
|
||||
/// Ensure the offset use self.next_boundary or self.previous_boundary to get the correct offset.
|
||||
fn select_to(&mut self, offset: Cursor, _: &mut Window, cx: &mut Context<Self>) {
|
||||
fn select_to(&mut self, offset: usize, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let offset = offset.clamp(0, self.text.len());
|
||||
if self.selection_reversed {
|
||||
self.selected_range.start = Cursor::new(offset)
|
||||
self.selected_range.start = offset
|
||||
} else {
|
||||
self.selected_range.end = Cursor::new(offset)
|
||||
self.selected_range.end = offset
|
||||
};
|
||||
|
||||
if self.selected_range.end < self.selected_range.start {
|
||||
|
|
@ -1900,7 +1830,7 @@ impl InputState {
|
|||
|
||||
/// Unselects the currently selected text.
|
||||
pub fn unselect(&mut self, _: &mut Window, cx: &mut Context<Self>) {
|
||||
let offset = self.cursor().offset;
|
||||
let offset = self.cursor();
|
||||
self.selected_range = (offset..offset).into();
|
||||
cx.notify()
|
||||
}
|
||||
|
|
@ -2022,7 +1952,7 @@ impl InputState {
|
|||
}
|
||||
|
||||
let offset = self.index_for_mouse_position(event.position, window, cx);
|
||||
self.select_to(Cursor::new(offset), window, cx);
|
||||
self.select_to(offset, window, cx);
|
||||
}
|
||||
|
||||
fn is_valid_input(&self, new_text: &str, cx: &mut Context<Self>) -> bool {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,7 @@ use gpui::{
|
|||
Window,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
global_state::GlobalState,
|
||||
input::{Cursor, Selection},
|
||||
text::node::LinkMark,
|
||||
ActiveTheme,
|
||||
};
|
||||
use crate::{global_state::GlobalState, input::Selection, text::node::LinkMark, ActiveTheme};
|
||||
|
||||
/// A inline element used to render a inline text and support selectable.
|
||||
///
|
||||
|
|
@ -95,7 +90,7 @@ impl Inline {
|
|||
text_layout: &TextLayout,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> (bool, bool, Option<(usize, usize)>) {
|
||||
) -> (bool, bool, Option<Selection>) {
|
||||
let Some(text_view_state) = GlobalState::global(cx).text_view_state() else {
|
||||
return (false, false, None);
|
||||
};
|
||||
|
|
@ -112,7 +107,7 @@ impl Inline {
|
|||
// Use for debug selection bounds
|
||||
// self.paint_selected_bounds(selection_bounds, window, cx);
|
||||
|
||||
let mut selection = None;
|
||||
let mut selection: Option<Selection> = None;
|
||||
let mut offset = 0;
|
||||
let mut chars = self.text.chars().peekable();
|
||||
while let Some(c) = chars.next() {
|
||||
|
|
@ -130,11 +125,11 @@ impl Inline {
|
|||
|
||||
if point_in_text_selection(pos, char_width, &selection_bounds, line_height) {
|
||||
if selection.is_none() {
|
||||
selection = Some((offset, offset));
|
||||
selection = Some((offset..offset).into());
|
||||
}
|
||||
|
||||
let next_offset = offset + c.len_utf8();
|
||||
selection.as_mut().unwrap().1 = next_offset;
|
||||
selection.as_mut().unwrap().end = next_offset;
|
||||
}
|
||||
|
||||
offset += c.len_utf8();
|
||||
|
|
@ -152,15 +147,15 @@ impl Inline {
|
|||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
let mut start_offset = selection.start.offset();
|
||||
let mut end_offset = selection.end.offset();
|
||||
if end_offset < start_offset {
|
||||
std::mem::swap(&mut start_offset, &mut end_offset);
|
||||
let mut start = selection.start;
|
||||
let mut end = selection.end;
|
||||
if end < start {
|
||||
std::mem::swap(&mut start, &mut end);
|
||||
}
|
||||
let Some(start_position) = text_layout.position_for_index(start_offset) else {
|
||||
let Some(start_position) = text_layout.position_for_index(start) else {
|
||||
return;
|
||||
};
|
||||
let Some(end_position) = text_layout.position_for_index(end_offset) else {
|
||||
let Some(end_position) = text_layout.position_for_index(end) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
@ -307,14 +302,7 @@ impl Element for Inline {
|
|||
let (is_selectable, is_selection, selection) =
|
||||
self.layout_selections(&text_layout, window, cx);
|
||||
|
||||
*state.selection.borrow_mut() = if let Some(selection) = selection {
|
||||
Some(Selection {
|
||||
start: Cursor::new(selection.0),
|
||||
end: Cursor::new(selection.1),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
*state.selection.borrow_mut() = selection;
|
||||
|
||||
if is_selection || is_selectable {
|
||||
window.set_cursor_style(CursorStyle::IBeam, &hitbox);
|
||||
|
|
|
|||
|
|
@ -166,12 +166,12 @@ impl Paragraph {
|
|||
for c in self.children.iter() {
|
||||
if let Some(selection) = c.state.selection.borrow().as_ref() {
|
||||
let part_text = c.state.text.borrow().clone();
|
||||
text.push_str(&part_text[selection.start.offset()..selection.end.offset()]);
|
||||
text.push_str(&part_text[selection.start..selection.end]);
|
||||
}
|
||||
}
|
||||
if let Some(selection) = self.state.selection.borrow().as_ref() {
|
||||
let all_text = self.state.text.borrow().clone();
|
||||
text.push_str(&all_text[selection.start.offset()..selection.end.offset()]);
|
||||
text.push_str(&all_text[selection.start..selection.end]);
|
||||
}
|
||||
|
||||
text
|
||||
|
|
@ -314,7 +314,7 @@ impl CodeBlock {
|
|||
let mut text = String::new();
|
||||
if let Some(selection) = self.state.selection.borrow().as_ref() {
|
||||
let part_text = self.state.text.borrow().clone();
|
||||
text.push_str(&part_text[selection.start.offset()..selection.end.offset()]);
|
||||
text.push_str(&part_text[selection.start..selection.end]);
|
||||
}
|
||||
text
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue