Add docs for buffer.rs

Co-authored-by: Antonio <antonio@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-01-17 10:08:42 -08:00
parent ebe2c3658c
commit 6457ccf9ec
6 changed files with 194 additions and 39 deletions

View File

@ -60,12 +60,15 @@ pub use {tree_sitter_rust, tree_sitter_typescript};
pub use lsp::DiagnosticSeverity;
lazy_static! {
pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new();
}
/// Indicate whether a [Buffer] has permissions to edit.
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum Capability {
/// The buffer is a mutable replica.
ReadWrite,
/// The buffer is a read-only replica.
ReadOnly,
}
@ -107,11 +110,11 @@ pub struct Buffer {
capability: Capability,
}
/// An immutable, cheaply cloneable representation of a certain
/// An immutable, cheaply cloneable representation of a fixed
/// state of a buffer.
pub struct BufferSnapshot {
text: text::BufferSnapshot,
pub git_diff: git::diff::BufferDiff,
git_diff: git::diff::BufferDiff,
pub(crate) syntax: SyntaxSnapshot,
file: Option<Arc<dyn File>>,
diagnostics: SmallVec<[(LanguageServerId, DiagnosticSet); 2]>,
@ -128,25 +131,33 @@ pub struct BufferSnapshot {
/// assumes that indentation is all the same character.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct IndentSize {
/// The number of bytes that comprise the indentation.
pub len: u32,
/// The kind of whitespace used for indentation.
pub kind: IndentKind,
}
/// A whitespace character that's used for indentation.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum IndentKind {
/// An ASCII space character.
#[default]
Space,
/// An ASCII tab chracter.
Tab,
}
/// The shape of a selection cursor.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub enum CursorShape {
/// A vertical bar
#[default]
Bar,
/// A block that surrounds the following character
Block,
/// An underline that runs along the following character
Underscore,
/// A box drawn around the following character
Hollow,
}
@ -158,26 +169,40 @@ struct SelectionSet {
lamport_timestamp: clock::Lamport,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GroupId {
source: Arc<str>,
id: usize,
}
/// A diagnostic associated with a certain range of a buffer.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
/// The name of the service that produced this diagnostic.
pub source: Option<String>,
/// A machine-readable code that identifies this diagnostic.
pub code: Option<String>,
/// Whether this diagnostic is a hint, warning, or error.
pub severity: DiagnosticSeverity,
/// The human-readable message associated with this diagnostic.
pub message: String,
/// An id that identifies the group to which this diagnostic belongs.
///
/// When a language server produces a diagnostic with
/// one or more associated diagnostics, those diagnostics are all
/// assigned a single group id.
pub group_id: usize,
pub is_valid: bool,
/// Whether this diagnostic is the primary diagnostic for its group.
///
/// In a given group, the primary diagnostic is the top-level diagnostic
/// returned by the language server. The non-primary diagnostics are the
/// associated diagnostics.
pub is_primary: bool,
/// Whether this diagnostic is considered to originate from an analysis of
/// files on disk, as opposed to any unsaved buffer contents. This is a
/// property of a given diagnostic source, and is configured for a given
/// language server via the [LspAdapter::disk_based_diagnostic_sources] method
/// for the language server.
pub is_disk_based: bool,
/// Whether this diagnostic marks unnecessary code.
pub is_unnecessary: bool,
}
/// TODO - move this into the `project` crate and make it private.
pub async fn prepare_completion_documentation(
documentation: &lsp::Documentation,
language_registry: &Arc<LanguageRegistry>,
@ -209,77 +234,125 @@ pub async fn prepare_completion_documentation(
}
}
/// Documentation associated with a [Completion].
#[derive(Clone, Debug)]
pub enum Documentation {
/// There is no documentation for this completion.
Undocumented,
/// A single line of documentation.
SingleLine(String),
/// Multiple lines of plain text documentation.
MultiLinePlainText(String),
/// Markdown documentation.
MultiLineMarkdown(ParsedMarkdown),
}
/// A completion provided by a language server
#[derive(Clone, Debug)]
pub struct Completion {
/// The range of the buffer that will be replaced.
pub old_range: Range<Anchor>,
/// The new text that will be inserted.
pub new_text: String,
/// A label for this completion that is shown in the menu.
pub label: CodeLabel,
/// The id of the language server that produced this completion.
pub server_id: LanguageServerId,
/// The documentation for this completion.
pub documentation: Option<Documentation>,
/// The raw completion provided by the language server.
pub lsp_completion: lsp::CompletionItem,
}
/// A code action provided by a language server.
#[derive(Clone, Debug)]
pub struct CodeAction {
/// The id of the language server that produced this code action.
pub server_id: LanguageServerId,
/// The range of the buffer where this code action is applicable.
pub range: Range<Anchor>,
/// The raw code action provided by the language server.
pub lsp_action: lsp::CodeAction,
}
/// An operation used to synchronize this buffer with its other replicas.
#[derive(Clone, Debug, PartialEq)]
pub enum Operation {
/// A text operation.
Buffer(text::Operation),
/// An update to the buffer's diagnostics.
UpdateDiagnostics {
/// The id of the language server that produced the new diagnostics.
server_id: LanguageServerId,
/// The diagnostics.
diagnostics: Arc<[DiagnosticEntry<Anchor>]>,
/// The buffer's lamport timestamp.
lamport_timestamp: clock::Lamport,
},
/// An update to the most recent selections in this buffer.
UpdateSelections {
/// The selections.
selections: Arc<[Selection<Anchor>]>,
/// The buffer's lamport timestamp.
lamport_timestamp: clock::Lamport,
/// Whether the selections are in 'line mode'.
line_mode: bool,
/// The [CursorShape] associated with these selections.
cursor_shape: CursorShape,
},
/// An update to the characters that should trigger autocompletion
/// for this buffer.
UpdateCompletionTriggers {
/// The characters that trigger autocompletion.
triggers: Vec<String>,
/// The buffer's lamport timestamp.
lamport_timestamp: clock::Lamport,
},
}
/// An event that occurs in a buffer.
#[derive(Clone, Debug, PartialEq)]
pub enum Event {
/// The buffer was changed in a way that must be
/// propagated to its other replicas.
Operation(Operation),
/// The buffer was edited.
Edited,
/// The buffer's `dirty` bit changed.
DirtyChanged,
/// The buffer was saved.
Saved,
/// The buffer's file was changed on disk.
FileHandleChanged,
/// The buffer was reloaded.
Reloaded,
/// The buffer's diff_base changed.
DiffBaseChanged,
/// The buffer's language was changed.
LanguageChanged,
/// The buffer's syntax trees were updated.
Reparsed,
/// The buffer's diagnostics were updated.
DiagnosticsUpdated,
/// The buffer was explicitly requested to close.
Closed,
}
/// The file associated with a buffer.
pub trait File: Send + Sync {
/// Returns the [LocalFile] associated with this file, if the
/// file is local.
fn as_local(&self) -> Option<&dyn LocalFile>;
/// Returns whether this file is local.
fn is_local(&self) -> bool {
self.as_local().is_some()
}
/// Returns the file's mtime.
fn mtime(&self) -> SystemTime;
/// Returns the path of this file relative to the worktree's root directory.
@ -298,10 +371,13 @@ pub trait File: Send + Sync {
/// This is needed for looking up project-specific settings.
fn worktree_id(&self) -> usize;
/// Returns whether the file has been deleted.
fn is_deleted(&self) -> bool;
/// Converts this file into an [Any] trait object.
fn as_any(&self) -> &dyn Any;
/// Converts this file into a protobuf message.
fn to_proto(&self) -> rpc::proto::File;
}
@ -310,8 +386,10 @@ pub trait LocalFile: File {
/// Returns the absolute path of this file.
fn abs_path(&self, cx: &AppContext) -> PathBuf;
/// Loads the file's contents from disk.
fn load(&self, cx: &AppContext) -> Task<Result<String>>;
/// Called when the buffer is reloaded from disk.
fn buffer_reloaded(
&self,
buffer_id: u64,
@ -392,11 +470,18 @@ pub struct BufferChunks<'a> {
/// diagnostic status.
#[derive(Clone, Copy, Debug, Default)]
pub struct Chunk<'a> {
/// The text of the chunk.
pub text: &'a str,
/// The syntax highlighting style of the chunk.
pub syntax_highlight_id: Option<HighlightId>,
/// The highlight style that has been applied to this chunk in
/// the editor.
pub highlight_style: Option<HighlightStyle>,
/// The severity of diagnostic associated with this chunk, if any.
pub diagnostic_severity: Option<DiagnosticSeverity>,
/// Whether this chunk of text is marked as unnecessary.
pub is_unnecessary: bool,
/// Whether this chunk of text was originally a tab character.
pub is_tab: bool,
}
@ -418,21 +503,14 @@ pub(crate) struct DiagnosticEndpoint {
/// A class of characters, used for characterizing a run of text.
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum CharKind {
/// Whitespace.
Whitespace,
/// Punctuation.
Punctuation,
/// Word.
Word,
}
impl CharKind {
pub fn coerce_punctuation(self, treat_punctuation_as_word: bool) -> Self {
if treat_punctuation_as_word && self == CharKind::Punctuation {
CharKind::Word
} else {
self
}
}
}
impl Buffer {
/// Create a new buffer with the given base text.
pub fn new<T: Into<String>>(replica_id: ReplicaId, id: u64, base_text: T) -> Self {
@ -554,14 +632,17 @@ impl Buffer {
self
}
/// Returns the [Capability] of this buffer.
pub fn capability(&self) -> Capability {
self.capability
}
/// Whether this buffer can only be read.
pub fn read_only(&self) -> bool {
self.capability == Capability::ReadOnly
}
/// Builds a [Buffer] with the given underlying [TextBuffer], diff base, [File] and [Capability].
pub fn build(
buffer: TextBuffer,
diff_base: Option<String>,
@ -675,6 +756,7 @@ impl Buffer {
.set_language_registry(language_registry);
}
/// This method is called to signal that the buffer has been saved.
pub fn did_save(
&mut self,
version: clock::Global,
@ -689,6 +771,7 @@ impl Buffer {
cx.notify();
}
/// Reloads the contents of the buffer from disk.
pub fn reload(
&mut self,
cx: &mut ModelContext<Self>,
@ -737,6 +820,7 @@ impl Buffer {
rx
}
/// This method is called to signal that the buffer has been reloaded.
pub fn did_reload(
&mut self,
version: clock::Global,
@ -763,6 +847,8 @@ impl Buffer {
cx.notify();
}
/// Updates the [File] backing this buffer. This should be called when
/// the file has changed or has been deleted.
pub fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut ModelContext<Self>) {
let mut file_changed = false;
@ -800,16 +886,20 @@ impl Buffer {
}
}
/// Returns the current diff base, see [Buffer::set_diff_base].
pub fn diff_base(&self) -> Option<&str> {
self.diff_base.as_deref()
}
/// Sets the text that will be used to compute a Git diff
/// against the buffer text.
pub fn set_diff_base(&mut self, diff_base: Option<String>, cx: &mut ModelContext<Self>) {
self.diff_base = diff_base;
self.git_diff_recalc(cx);
cx.emit(Event::DiffBaseChanged);
}
/// Recomputes the Git diff status.
pub fn git_diff_recalc(&mut self, cx: &mut ModelContext<Self>) -> Option<Task<()>> {
let diff_base = self.diff_base.clone()?; // TODO: Make this an Arc
let snapshot = self.snapshot();
@ -830,14 +920,12 @@ impl Buffer {
}))
}
pub fn close(&mut self, cx: &mut ModelContext<Self>) {
cx.emit(Event::Closed);
}
/// Returns the primary [Language] assigned to this [Buffer].
pub fn language(&self) -> Option<&Arc<Language>> {
self.language.as_ref()
}
/// Returns the [Language] at the given location.
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<Arc<Language>> {
let offset = position.to_offset(self);
self.syntax_map
@ -848,26 +936,32 @@ impl Buffer {
.or_else(|| self.language.clone())
}
/// The number of times the buffer was parsed.
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
/// The number of times diagnostics were updated.
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
/// Whether the buffer is being parsed in the background.
#[cfg(any(test, feature = "test-support"))]
pub fn is_parsing(&self) -> bool {
self.parsing_in_background
@ -2377,7 +2471,7 @@ impl BufferSnapshot {
self.syntax.layers_for_range(0..self.len(), &self.text)
}
pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer> {
fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer> {
let offset = position.to_offset(self);
self.syntax
.layers_for_range(offset..offset, &self.text)
@ -2385,12 +2479,14 @@ impl BufferSnapshot {
.last()
}
/// Returns the [Language] at the given location.
pub fn language_at<D: ToOffset>(&self, position: D) -> Option<&Arc<Language>> {
self.syntax_layer_at(position)
.map(|info| info.language)
.or(self.language.as_ref())
}
/// Returns the settings for the language at the given location.
pub fn settings_at<'a, D: ToOffset>(
&self,
position: D,
@ -2399,6 +2495,7 @@ impl BufferSnapshot {
language_settings(self.language_at(position), self.file.as_ref(), cx)
}
/// Returns the [LanguageScope] at the given location.
pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
let offset = position.to_offset(self);
let mut scope = None;
@ -2443,6 +2540,8 @@ impl BufferSnapshot {
})
}
/// Returns a tuple of the range and character kind of the word
/// surrounding the given position.
pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
let mut start = start.to_offset(self);
let mut end = start;
@ -2475,6 +2574,7 @@ impl BufferSnapshot {
(start..end, word_kind)
}
/// Returns the range for the closes syntax node enclosing the given range.
pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
let range = range.start.to_offset(self)..range.end.to_offset(self);
let mut result: Option<Range<usize>> = None;
@ -2543,11 +2643,19 @@ impl BufferSnapshot {
result
}
/// Returns the outline for the buffer.
///
/// This method allows passing an optional [SyntaxTheme] to
/// syntax-highlight the returned symbols.
pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
self.outline_items_containing(0..self.len(), true, theme)
.map(Outline::new)
}
/// Returns all the symbols that contain the given position.
///
/// This method allows passing an optional [SyntaxTheme] to
/// syntax-highlight the returned symbols.
pub fn symbols_containing<T: ToOffset>(
&self,
position: T,
@ -2699,6 +2807,8 @@ impl BufferSnapshot {
Some(items)
}
/// For each grammar in the language, runs the provided
/// [tree_sitter::Query] against the given range.
pub fn matches(
&self,
range: Range<usize>,
@ -2755,6 +2865,7 @@ impl BufferSnapshot {
})
}
/// Returns selections for remote peers intersecting the given range.
#[allow(clippy::type_complexity)]
pub fn remote_selections_in_range(
&self,
@ -2793,6 +2904,13 @@ impl BufferSnapshot {
})
}
/// Whether the buffer contains any git changes.
pub fn has_git_diff(&self) -> bool {
!self.git_diff.is_empty()
}
/// Returns all the Git diff hunks intersecting the given
/// row range.
pub fn git_diff_hunks_in_row_range<'a>(
&'a self,
range: Range<u32>,
@ -2800,6 +2918,8 @@ impl BufferSnapshot {
self.git_diff.hunks_in_row_range(range, self)
}
/// Returns all the Git diff hunks intersecting the given
/// range.
pub fn git_diff_hunks_intersecting_range<'a>(
&'a self,
range: Range<Anchor>,
@ -2807,6 +2927,8 @@ impl BufferSnapshot {
self.git_diff.hunks_intersecting_range(range, self)
}
/// Returns all the Git diff hunks intersecting the given
/// range, in reverse order.
pub fn git_diff_hunks_intersecting_range_rev<'a>(
&'a self,
range: Range<Anchor>,
@ -2814,6 +2936,7 @@ impl BufferSnapshot {
self.git_diff.hunks_intersecting_range_rev(range, self)
}
/// Returns all the diagnostics intersecting the given range.
pub fn diagnostics_in_range<'a, T, O>(
&'a self,
search_range: Range<T>,
@ -2843,6 +2966,9 @@ impl BufferSnapshot {
})
}
/// Returns all the diagnostic groups associated with the given
/// language server id. If no language server id is provided,
/// all diagnostics groups are returned.
pub fn diagnostic_groups(
&self,
language_server_id: Option<LanguageServerId>,
@ -2873,6 +2999,7 @@ impl BufferSnapshot {
groups
}
/// Returns an iterator over the diagnostics for the given group.
pub fn diagnostic_group<'a, O>(
&'a self,
group_id: usize,
@ -2885,22 +3012,27 @@ impl BufferSnapshot {
.flat_map(move |(_, set)| set.group(group_id, self))
}
/// The number of times diagnostics were updated.
pub fn diagnostics_update_count(&self) -> usize {
self.diagnostics_update_count
}
/// The number of times the buffer was parsed.
pub fn parse_count(&self) -> usize {
self.parse_count
}
/// The number of times selections were updated.
pub fn selections_update_count(&self) -> usize {
self.selections_update_count
}
/// Returns a snapshot of underlying file.
pub fn file(&self) -> Option<&Arc<dyn File>> {
self.file.as_ref()
}
/// Resolves the file path (relative to the worktree root) associated with the underlying file.
pub fn resolve_file_path(&self, cx: &AppContext, include_root: bool) -> Option<PathBuf> {
if let Some(file) = self.file() {
if file.path().file_name().is_none() || include_root {
@ -2913,10 +3045,12 @@ impl BufferSnapshot {
}
}
/// The number of times the underlying file was updated.
pub fn file_update_count(&self) -> usize {
self.file_update_count
}
/// The number of times the git diff status was updated.
pub fn git_diff_update_count(&self) -> usize {
self.git_diff_update_count
}
@ -2926,7 +3060,7 @@ fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize {
indent_size_for_text(text.chars_at(Point::new(row, 0)))
}
pub fn indent_size_for_text(text: impl Iterator<Item = char>) -> IndentSize {
fn indent_size_for_text(text: impl Iterator<Item = char>) -> IndentSize {
let mut result = IndentSize::spaces(0);
for c in text {
let kind = match c {
@ -3004,6 +3138,7 @@ impl<'a> BufferChunks<'a> {
}
}
/// Seeks to the given byte offset in the buffer.
pub fn seek(&mut self, offset: usize) {
self.range.start = offset;
self.chunks.seek(self.range.start);
@ -3027,6 +3162,7 @@ impl<'a> BufferChunks<'a> {
}
}
/// The current byte offset in the buffer.
pub fn offset(&self) -> usize {
self.range.start
}
@ -3179,7 +3315,6 @@ impl Default for Diagnostic {
message: Default::default(),
group_id: 0,
is_primary: false,
is_valid: true,
is_disk_based: false,
is_unnecessary: false,
}
@ -3187,6 +3322,7 @@ impl Default for Diagnostic {
}
impl IndentSize {
/// Returns an [IndentSize] representing the given spaces.
pub fn spaces(len: u32) -> Self {
Self {
len,
@ -3194,6 +3330,7 @@ impl IndentSize {
}
}
/// Returns an [IndentSize] representing a tab.
pub fn tab() -> Self {
Self {
len: 1,
@ -3201,10 +3338,12 @@ impl IndentSize {
}
}
/// An iterator over the characters represented by this [IndentSize].
pub fn chars(&self) -> impl Iterator<Item = char> {
iter::repeat(self.char()).take(self.len as usize)
}
/// The character representation of this [IndentSize].
pub fn char(&self) -> char {
match self.kind {
IndentKind::Space => ' ',
@ -3212,6 +3351,8 @@ impl IndentSize {
}
}
/// Consumes the current [IndentSize] and returns a new one that has
/// been shrunk or enlarged by the given size along the given direction.
pub fn with_delta(mut self, direction: Ordering, size: IndentSize) -> Self {
match direction {
Ordering::Less => {
@ -3233,6 +3374,8 @@ impl IndentSize {
}
impl Completion {
/// A key that can be used to sort completions when displaying
/// them to the user.
pub fn sort_key(&self) -> (usize, &str) {
let kind_key = match self.lsp_completion.kind {
Some(lsp::CompletionItemKind::VARIABLE) => 0,
@ -3241,12 +3384,13 @@ impl Completion {
(kind_key, &self.label.text[self.label.filter_range.clone()])
}
/// Whether this completion is a snippet.
pub fn is_snippet(&self) -> bool {
self.lsp_completion.insert_text_format == Some(lsp::InsertTextFormat::SNIPPET)
}
}
pub fn contiguous_ranges(
pub(crate) fn contiguous_ranges(
values: impl Iterator<Item = u32>,
max_len: usize,
) -> impl Iterator<Item = Range<u32>> {
@ -3272,6 +3416,9 @@ pub fn contiguous_ranges(
})
}
/// Returns the [CharKind] for the given character. When a scope is provided,
/// the function checks if the character is considered a word character
/// based on the language scope's word character settings.
pub fn char_kind(scope: &Option<LanguageScope>, c: char) -> CharKind {
if c.is_whitespace() {
return CharKind::Whitespace;

View File

@ -223,7 +223,7 @@ pub fn serialize_diagnostics<'a>(
} as i32,
group_id: entry.diagnostic.group_id as u64,
is_primary: entry.diagnostic.is_primary,
is_valid: entry.diagnostic.is_valid,
is_valid: true,
code: entry.diagnostic.code.clone(),
is_disk_based: entry.diagnostic.is_disk_based,
is_unnecessary: entry.diagnostic.is_unnecessary,
@ -409,7 +409,6 @@ pub fn deserialize_diagnostics(
message: diagnostic.message,
group_id: diagnostic.group_id as usize,
code: diagnostic.code,
is_valid: diagnostic.is_valid,
is_primary: diagnostic.is_primary,
is_disk_based: diagnostic.is_disk_based,
is_unnecessary: diagnostic.is_unnecessary,

View File

@ -3023,7 +3023,7 @@ impl MultiBufferSnapshot {
pub fn has_git_diffs(&self) -> bool {
for excerpt in self.excerpts.iter() {
if !excerpt.buffer.git_diff.is_empty() {
if excerpt.buffer.has_git_diff() {
return true;
}
}

View File

@ -3912,7 +3912,6 @@ impl Project {
message: diagnostic.message.clone(),
group_id,
is_primary: true,
is_valid: true,
is_disk_based,
is_unnecessary,
},
@ -3930,7 +3929,6 @@ impl Project {
message: info.message.clone(),
group_id,
is_primary: false,
is_valid: true,
is_disk_based,
is_unnecessary: false,
},

View File

@ -1471,7 +1471,10 @@ message Diagnostic {
optional string code = 6;
uint64 group_id = 7;
bool is_primary = 8;
// TODO: remove this field
bool is_valid = 9;
bool is_disk_based = 10;
bool is_unnecessary = 11;

View File

@ -681,8 +681,8 @@ pub(crate) fn next_word_start(
for _ in 0..times {
let mut crossed_newline = false;
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
let at_newline = right == '\n';
let found = (left_kind != right_kind && right_kind != CharKind::Whitespace)
@ -711,8 +711,8 @@ fn next_word_end(
*point.column_mut() = 0;
}
point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = ccoerce_punctuation(har_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});
@ -744,8 +744,8 @@ fn previous_word_start(
// cursor because the newline is checked only once.
point =
movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| {
let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation);
let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation);
let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation);
let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
(left_kind != right_kind && !right.is_whitespace()) || left == '\n'
});
@ -952,6 +952,14 @@ pub(crate) fn next_line_end(
end_of_line(map, false, point)
}
fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> Self {
if treat_punctuation_as_word && kind == CharKind::Punctuation {
CharKind::Word
} else {
kind
}
}
#[cfg(test)]
mod test {