Fix - being a word character for selections (#17171)

Co-Authored-By: Mikayla <mikayla@zed.dev>
Co-Authored-By: Nate <nate@zed.dev>

Closes #15606
Closes #13515

Release Notes:

- Fixes `-` being considered a word character for selections in some
languages

Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Nate <nate@zed.dev>
This commit is contained in:
Conrad Irwin 2024-08-30 12:34:23 -06:00 committed by GitHub
parent c0731bfa28
commit ee6ec50b15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 239 additions and 143 deletions

View file

@ -89,13 +89,12 @@ pub use inline_completion_provider::*;
pub use items::MAX_TAB_TITLE_LEN;
use itertools::Itertools;
use language::{
char_kind,
language_settings::{self, all_language_settings, InlayHintSettings},
markdown, point_from_lsp, AutoindentMode, BracketPair, Buffer, Capability, CharKind, CodeLabel,
CursorShape, Diagnostic, Documentation, IndentKind, IndentSize, Language, OffsetRangeExt,
Point, Selection, SelectionGoal, TransactionId,
};
use language::{point_to_lsp, BufferRow, Runnable, RunnableRange};
use language::{point_to_lsp, BufferRow, CharClassifier, Runnable, RunnableRange};
use linked_editing_ranges::refresh_linked_ranges;
use task::{ResolvedTask, TaskTemplate, TaskVariables};
@ -2443,7 +2442,8 @@ impl Editor {
if let Some(completion_menu) = completion_menu {
let cursor_position = new_cursor_position.to_offset(buffer);
let (word_range, kind) = buffer.surrounding_word(completion_menu.initial_position);
let (word_range, kind) =
buffer.surrounding_word(completion_menu.initial_position, true);
if kind == Some(CharKind::Word)
&& word_range.to_inclusive().contains(&cursor_position)
{
@ -3289,10 +3289,8 @@ impl Editor {
let start_anchor = snapshot.anchor_before(selection.start);
let is_word_char = text.chars().next().map_or(true, |char| {
let scope = snapshot.language_scope_at(start_anchor.to_offset(&snapshot));
let kind = char_kind(&scope, char);
kind == CharKind::Word
let classifier = snapshot.char_classifier_at(start_anchor.to_offset(&snapshot));
classifier.is_word(char)
});
if is_word_char {
@ -3923,7 +3921,7 @@ impl Editor {
fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
let offset = position.to_offset(buffer);
let (word_range, kind) = buffer.surrounding_word(offset);
let (word_range, kind) = buffer.surrounding_word(offset, true);
if offset > word_range.start && kind == Some(CharKind::Word) {
Some(
buffer
@ -12302,10 +12300,11 @@ fn snippet_completions(
};
let scope = language.map(|language| language.default_scope());
let classifier = CharClassifier::new(scope).for_completion(true);
let mut last_word = line_at
.chars()
.rev()
.take_while(|c| char_kind(&scope, *c) == CharKind::Word)
.take_while(|c| classifier.is_word(*c))
.collect::<String>();
last_word = last_word.chars().rev().collect();
let as_offset = text::ToOffset::to_offset(&buffer_position, &snapshot);
@ -12436,8 +12435,11 @@ impl CompletionProvider for Model<Project> {
}
let buffer = buffer.read(cx);
let scope = buffer.snapshot().language_scope_at(position);
if trigger_in_words && char_kind(&scope, char) == CharKind::Word {
let classifier = buffer
.snapshot()
.char_classifier_at(position)
.for_completion(true);
if trigger_in_words && classifier.is_word(char) {
return true;
}

View file

@ -613,7 +613,7 @@ pub fn show_link_definition(
TriggerPoint::Text(trigger_anchor) => {
// If no symbol range returned from language server, use the surrounding word.
let (offset_range, _) =
snapshot.surrounding_word(*trigger_anchor);
snapshot.surrounding_word(*trigger_anchor, false);
RangeInEditor::Text(
snapshot.anchor_before(offset_range.start)
..snapshot.anchor_after(offset_range.end),

View file

@ -1225,7 +1225,7 @@ impl SearchableItem for Editor {
}
SeedQuerySetting::Selection => String::new(),
SeedQuerySetting::Always => {
let (range, kind) = snapshot.surrounding_word(selection.start);
let (range, kind) = snapshot.surrounding_word(selection.start, true);
if kind == Some(CharKind::Word) {
let text: String = snapshot.text_for_range(range).collect();
if !text.trim().is_empty() {

View file

@ -2,9 +2,7 @@
//! in editor given a given motion (e.g. it handles converting a "move left" command into coordinates in editor). It is exposed mostly for use by vim crate.
use super::{Bias, DisplayPoint, DisplaySnapshot, SelectionGoal, ToDisplayPoint};
use crate::{
char_kind, scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToOffset, ToPoint,
};
use crate::{scroll::ScrollAnchor, CharKind, DisplayRow, EditorStyle, RowExt, ToOffset, ToPoint};
use gpui::{px, Pixels, WindowTextSystem};
use language::Point;
use multi_buffer::{MultiBufferRow, MultiBufferSnapshot};
@ -264,10 +262,10 @@ pub fn line_end(
/// uppercase letter, lowercase letter, '_' character or language-specific word character (like '-' in CSS).
pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| {
(char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace())
(classifier.kind(left) != classifier.kind(right) && !classifier.is_whitespace(right))
|| left == '\n'
})
}
@ -277,13 +275,14 @@ pub fn previous_word_start(map: &DisplaySnapshot, point: DisplayPoint) -> Displa
/// lowerspace characters and uppercase characters.
pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_preceding_boundary_display_point(map, point, FindRange::MultiLine, |left, right| {
let is_word_start =
char_kind(&scope, left) != char_kind(&scope, right) && !right.is_whitespace();
let is_subword_start =
left == '_' && right != '_' || left.is_lowercase() && right.is_uppercase();
classifier.kind(left) != classifier.kind(right) && !right.is_whitespace();
let is_subword_start = classifier.is_word('-') && left == '-' && right != '-'
|| left == '_' && right != '_'
|| left.is_lowercase() && right.is_uppercase();
is_word_start || is_subword_start || left == '\n'
})
}
@ -292,10 +291,10 @@ pub fn previous_subword_start(map: &DisplaySnapshot, point: DisplayPoint) -> Dis
/// uppercase letter, lowercase letter, '_' character or language-specific word character (like '-' in CSS).
pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_boundary(map, point, FindRange::MultiLine, |left, right| {
(char_kind(&scope, left) != char_kind(&scope, right) && !left.is_whitespace())
(classifier.kind(left) != classifier.kind(right) && !classifier.is_whitespace(left))
|| right == '\n'
})
}
@ -305,13 +304,14 @@ pub fn next_word_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint
/// lowerspace characters and uppercase characters.
pub fn next_subword_end(map: &DisplaySnapshot, point: DisplayPoint) -> DisplayPoint {
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
find_boundary(map, point, FindRange::MultiLine, |left, right| {
let is_word_end =
(char_kind(&scope, left) != char_kind(&scope, right)) && !left.is_whitespace();
let is_subword_end =
left != '_' && right == '_' || left.is_lowercase() && right.is_uppercase();
(classifier.kind(left) != classifier.kind(right)) && !classifier.is_whitespace(left);
let is_subword_end = classifier.is_word('-') && left != '-' && right == '-'
|| left != '_' && right == '_'
|| left.is_lowercase() && right.is_uppercase();
is_word_end || is_subword_end || right == '\n'
})
}
@ -509,14 +509,14 @@ pub fn chars_before(
pub(crate) fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
let raw_point = point.to_point(map);
let scope = map.buffer_snapshot.language_scope_at(raw_point);
let classifier = map.buffer_snapshot.char_classifier_at(raw_point);
let ix = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left);
let text = &map.buffer_snapshot;
let next_char_kind = text.chars_at(ix).next().map(|c| char_kind(&scope, c));
let next_char_kind = text.chars_at(ix).next().map(|c| classifier.kind(c));
let prev_char_kind = text
.reversed_chars_at(ix)
.next()
.map(|c| char_kind(&scope, c));
.map(|c| classifier.kind(c));
prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
}
@ -527,7 +527,7 @@ pub(crate) fn surrounding_word(
let position = map
.clip_point(position, Bias::Left)
.to_offset(map, Bias::Left);
let (range, _) = map.buffer_snapshot.surrounding_word(position);
let (range, _) = map.buffer_snapshot.surrounding_word(position, false);
let start = range
.start
.to_point(&map.buffer_snapshot)

View file

@ -226,6 +226,7 @@ impl EditorLspTestContext {
..Default::default()
},
block_comment: Some(("<!-- ".into(), " -->".into())),
word_characters: ['-'].into_iter().collect(),
..Default::default()
},
Some(tree_sitter_html::language()),