Fix issues:

- fix the operators' display in the status bar
- fill the subword object placeholder
- fix some spelling mistakes
- fix a bug where the first and last words in a buffer couldn't be selected
This commit is contained in:
fantacell 2025-07-21 21:57:19 +02:00
parent 9b44bb6706
commit bd3b5c6829
4 changed files with 50 additions and 27 deletions

View file

@ -4,7 +4,7 @@
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint}; use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{DisplayRow, EditorStyle, ToOffset, ToPoint, scroll::ScrollAnchor}; use crate::{DisplayRow, EditorStyle, ToOffset, ToPoint, scroll::ScrollAnchor};
use gpui::{Pixels, WindowTextSystem}; use gpui::{Pixels, WindowTextSystem};
use language::Point; use language::{CharClassifier, Point};
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot}; use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
use serde::Deserialize; use serde::Deserialize;
use workspace::searchable::Direction; use workspace::searchable::Direction;
@ -303,13 +303,16 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis
let classifier = map.buffer_snapshot.char_classifier_at(raw_point); let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| { find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| {
let is_word_start = is_subword_start(left, right, &classifier) || left == '\n'
classifier.kind(left) != classifier.kind(right) && !right.is_whitespace(); })
}
pub fn is_subword_start(left: char, right: char, classifier: &CharClassifier) -> bool {
let is_word_start = classifier.kind(left) != classifier.kind(right) && !right.is_whitespace();
let is_subword_start = classifier.is_word('-') && left == '-' && right != '-' let is_subword_start = classifier.is_word('-') && left == '-' && right != '-'
|| left == '_' && right != '_' || left == '_' && right != '_'
|| left.is_lowercase() && right.is_uppercase(); || left.is_lowercase() && right.is_uppercase();
is_word_start || is_subword_start || left == '\n' is_word_start || is_subword_start
})
} }
/// Returns a position of the next word boundary, where a word character is defined as either /// Returns a position of the next word boundary, where a word character is defined as either
@ -361,13 +364,17 @@ pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPo
let classifier = map.buffer_snapshot.char_classifier_at(raw_point); let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_boundary(map, point, FindRange::MultiLine, |left, right| { find_boundary(map, point, FindRange::MultiLine, |left, right| {
is_subword_end(left, right, &classifier) || right == '\n'
})
}
pub fn is_subword_end(left: char, right: char, classifier: &CharClassifier) -> bool {
let is_word_end = let is_word_end =
(classifier.kind(left) != classifier.kind(right)) && !classifier.is_whitespace(left); (classifier.kind(left) != classifier.kind(right)) && !classifier.is_whitespace(left);
let is_subword_end = classifier.is_word('-') && left != '-' && right == '-' let is_subword_end = classifier.is_word('-') && left != '-' && right == '-'
|| left != '_' && right == '_' || left != '_' && right == '_'
|| left.is_lowercase() && right.is_uppercase(); || left.is_lowercase() && right.is_uppercase();
is_word_end || is_subword_end || right == '\n' is_word_end || is_subword_end
})
} }
/// Returns a position of the start of the current paragraph, where a paragraph /// Returns a position of the start of the current paragraph, where a paragraph

View file

@ -3,6 +3,7 @@ use std::{error::Error, fmt::Display};
use editor::{ use editor::{
DisplayPoint, DisplayPoint,
display_map::{DisplaySnapshot, ToDisplayPoint}, display_map::{DisplaySnapshot, ToDisplayPoint},
movement,
}; };
use language::{CharClassifier, CharKind}; use language::{CharClassifier, CharKind};
use text::Bias; use text::Bias;
@ -19,7 +20,8 @@ impl Display for UnboundedErr {
impl Error for UnboundedErr {} impl Error for UnboundedErr {}
impl Object { impl Object {
/// Returns the beginning of the inside of the closest object after the cursor if it can easily be found. Follows helix convention; /// Returns the beginning of the inside of the closest object after the cursor if it can easily be found.
/// Follows helix convention.
pub fn helix_next_start( pub fn helix_next_start(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -32,7 +34,8 @@ impl Object {
self.helix_is_start(right, left, classifier) self.helix_is_start(right, left, classifier)
}) })
} }
/// Returns the end of the inside of the closest object after the cursor if it can easily be found. Follows helix convention; /// Returns the end of the inside of the closest object after the cursor if it can easily be found.
/// Follows helix convention.
pub fn helix_next_end( pub fn helix_next_end(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -45,7 +48,8 @@ impl Object {
self.helix_is_end(right, left, classifier) self.helix_is_end(right, left, classifier)
}) })
} }
/// Returns the beginning of the inside of the closest object before the cursor if it can easily be found. Follows helix convention; /// Returns the beginning of the inside of the closest object before the cursor if it can easily be found.
/// Follows helix convention.
pub fn helix_previous_start( pub fn helix_previous_start(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -58,7 +62,8 @@ impl Object {
self.helix_is_start(right, left, classifier) self.helix_is_start(right, left, classifier)
}) })
} }
/// Returns the end of the inside of the closest object before the cursor if it can easily be found. Follows helix convention; /// Returns the end of the inside of the closest object before the cursor if it can easily be found.
/// Follows helix convention.
pub fn helix_previous_end( pub fn helix_previous_end(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -81,10 +86,11 @@ impl Object {
match self { match self {
Self::Word { ignore_punctuation } => { Self::Word { ignore_punctuation } => {
let classifier = classifier.ignore_punctuation(ignore_punctuation); let classifier = classifier.ignore_punctuation(ignore_punctuation);
Ok(is_word_start(left, right, classifier)) Ok(is_word_start(left, right, classifier) || is_buffer_start(left))
} }
Self::Subword { ignore_punctuation } => { Self::Subword { ignore_punctuation } => {
todo!() let classifier = classifier.ignore_punctuation(ignore_punctuation);
Ok(movement::is_subword_start(left, right, &classifier) || is_buffer_start(left))
} }
Self::AngleBrackets => Ok(left == '<'), Self::AngleBrackets => Ok(left == '<'),
Self::BackQuotes => Ok(left == '`'), Self::BackQuotes => Ok(left == '`'),
@ -106,10 +112,11 @@ impl Object {
match self { match self {
Self::Word { ignore_punctuation } => { Self::Word { ignore_punctuation } => {
let classifier = classifier.ignore_punctuation(ignore_punctuation); let classifier = classifier.ignore_punctuation(ignore_punctuation);
Ok(is_word_end(left, right, classifier)) Ok(is_word_end(left, right, classifier) || is_buffer_end(right))
} }
Self::Subword { ignore_punctuation } => { Self::Subword { ignore_punctuation } => {
todo!() let classifier = classifier.ignore_punctuation(ignore_punctuation);
Ok(movement::is_subword_end(left, right, &classifier) || is_buffer_end(right))
} }
Self::AngleBrackets => Ok(right == '>'), Self::AngleBrackets => Ok(right == '>'),
Self::BackQuotes => Ok(right == '`'), Self::BackQuotes => Ok(right == '`'),
@ -136,7 +143,7 @@ fn try_find_boundary(
.next() .next()
.unwrap_or('\0'); .unwrap_or('\0');
for ch in map.buffer_snapshot.chars_at(offset) { for ch in map.buffer_snapshot.chars_at(offset).chain(['\0']) {
if is_boundary(prev_ch, ch)? { if is_boundary(prev_ch, ch)? {
return Ok(Some( return Ok(Some(
map.clip_point(offset.to_display_point(map), Bias::Right), map.clip_point(offset.to_display_point(map), Bias::Right),
@ -157,13 +164,13 @@ fn try_find_preceding_boundary(
let mut offset = from.to_offset(map, Bias::Right); let mut offset = from.to_offset(map, Bias::Right);
let mut prev_ch = map.buffer_snapshot.chars_at(offset).next().unwrap_or('\0'); let mut prev_ch = map.buffer_snapshot.chars_at(offset).next().unwrap_or('\0');
for ch in map.buffer_snapshot.reversed_chars_at(offset) { for ch in map.buffer_snapshot.reversed_chars_at(offset).chain(['\0']) {
if is_boundary(ch, prev_ch)? { if is_boundary(ch, prev_ch)? {
return Ok(Some( return Ok(Some(
map.clip_point(offset.to_display_point(map), Bias::Right), map.clip_point(offset.to_display_point(map), Bias::Right),
)); ));
} }
offset -= ch.len_utf8(); offset = offset.saturating_sub(ch.len_utf8());
prev_ch = ch; prev_ch = ch;
} }
@ -174,6 +181,10 @@ fn is_buffer_start(left: char) -> bool {
left == '\0' left == '\0'
} }
fn is_buffer_end(right: char) -> bool {
right == '\0'
}
fn is_word_start(left: char, right: char, classifier: CharClassifier) -> bool { fn is_word_start(left: char, right: char, classifier: CharClassifier) -> bool {
classifier.kind(left) != classifier.kind(right) classifier.kind(left) != classifier.kind(right)
&& classifier.kind(right) != CharKind::Whitespace && classifier.kind(right) != CharKind::Whitespace

View file

@ -84,7 +84,8 @@ impl Object {
} }
} }
/// Returns the range of the object the cursor is over if it can be found with simple boundary checking. Potentially none. Follows helix convention. /// Returns the range of the object the cursor is over if it can be found with simple boundary checking.
/// Potentially none. Follows helix convention.
fn current_bounded_object( fn current_bounded_object(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -116,7 +117,8 @@ impl Object {
Ok(Some(prev_start..next_end)) Ok(Some(prev_start..next_end))
} }
/// Returns the range of the next object the cursor is not over if it can be found with simple boundary checking. Potentially none. Follows helix convention. /// Returns the range of the next object the cursor is not over if it can be found with simple boundary checking.
/// Potentially none. Follows helix convention.
fn next_bounded_object( fn next_bounded_object(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,
@ -138,7 +140,8 @@ impl Object {
Ok(Some(next_start..end)) Ok(Some(next_start..end))
} }
/// Returns the previous range of the object the cursor not is over if it can be found with simple boundary checking. Potentially none. Follows helix convention. /// Returns the previous range of the object the cursor not is over if it can be found with simple boundary checking.
/// Potentially none. Follows helix convention.
fn previous_bounded_object( fn previous_bounded_object(
self, self,
map: &DisplaySnapshot, map: &DisplaySnapshot,

View file

@ -1044,6 +1044,8 @@ impl Operator {
Operator::AutoIndent => "=".to_string(), Operator::AutoIndent => "=".to_string(),
Operator::ShellCommand => "=".to_string(), Operator::ShellCommand => "=".to_string(),
Operator::HelixMatch => "m".to_string(), Operator::HelixMatch => "m".to_string(),
Operator::SelectNext => "]".to_string(),
Operator::SelectPrevious => "[".to_string(),
_ => self.id().to_string(), _ => self.id().to_string(),
} }
} }