Add docs for buffer.rs

Max Brunsfeld and Antonio created

Co-authored-by: Antonio <antonio@zed.dev>

Change summary

crates/language/src/buffer.rs           | 203 +++++++++++++++++++++++---
crates/language/src/proto.rs            |   3 
crates/multi_buffer/src/multi_buffer.rs |   2 
crates/project/src/project.rs           |   2 
crates/rpc/proto/zed.proto              |   3 
crates/vim/src/motion.rs                |  20 +
6 files changed, 194 insertions(+), 39 deletions(-)

Detailed changes

crates/language/src/buffer.rs 🔗

@@ -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;

crates/language/src/proto.rs 🔗

@@ -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,

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -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;
             }
         }

crates/project/src/project.rs 🔗

@@ -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,
                                 },

crates/rpc/proto/zed.proto 🔗

@@ -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;
 

crates/vim/src/motion.rs 🔗

@@ -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 {