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

View File

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

View File

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

View File

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

View File

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

View File

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