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