Detailed changes
@@ -10,7 +10,7 @@ use crate::{
markdown::parse_markdown,
outline::OutlineItem,
syntax_map::{
- SyntaxLayerInfo, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
+ SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatches,
SyntaxSnapshot, ToTreeSitterPoint,
},
CodeLabel, LanguageScope, Outline,
@@ -42,7 +42,13 @@ use std::{
};
use sum_tree::TreeMap;
use text::operation_queue::OperationQueue;
-pub use text::{Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, *};
+use text::*;
+pub use text::{
+ Anchor, Bias, Buffer as TextBuffer, BufferSnapshot as TextBufferSnapshot, Edit, OffsetRangeExt,
+ OffsetUtf16, Patch, Point, PointUtf16, Rope, RopeFingerprint, Selection, SelectionGoal,
+ Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, ToPointUtf16,
+ Transaction, TransactionId, Unclipped,
+};
use theme::SyntaxTheme;
#[cfg(any(test, feature = "test-support"))]
use util::RandomCharIter;
@@ -54,15 +60,22 @@ pub use {tree_sitter_rust, tree_sitter_typescript};
pub use lsp::DiagnosticSeverity;
lazy_static! {
+ /// A label for the background task spawned by the buffer to compute
+ /// a diff against the contents of its file.
pub 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,
}
+/// An in-memory representation of a source code file, including its text,
+/// syntax trees, git status, and diagnostics.
pub struct Buffer {
text: TextBuffer,
diff_base: Option<String>,
@@ -99,9 +112,11 @@ pub struct Buffer {
capability: Capability,
}
+/// 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]>,
@@ -114,25 +129,37 @@ pub struct BufferSnapshot {
parse_count: usize,
}
+/// The kind and amount of indentation in a particular line. For now,
+/// 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 character.
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,
}
@@ -144,25 +171,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>,
@@ -194,77 +236,127 @@ 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 gained or lost editing capabilities.
CapabilityChanged,
+ /// 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.
@@ -283,19 +375,25 @@ 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;
}
+/// The file associated with a buffer, in the case where the file is on the local disk.
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,
@@ -307,6 +405,10 @@ pub trait LocalFile: File {
);
}
+/// The auto-indent behavior associated with an editing operation.
+/// For some editing operations, each affected line of text has its
+/// indentation recomputed. For other operations, the entire block
+/// of edited text is adjusted uniformly.
#[derive(Clone, Debug)]
pub enum AutoindentMode {
/// Indent each line of inserted text.
@@ -354,6 +456,8 @@ struct BufferChunkHighlights<'a> {
highlight_maps: Vec<HighlightMap>,
}
+/// An iterator that yields chunks of a buffer's text, along with their
+/// syntax highlights and diagnostic status.
pub struct BufferChunks<'a> {
range: Range<usize>,
chunks: text::Chunks<'a>,
@@ -366,16 +470,26 @@ pub struct BufferChunks<'a> {
highlights: Option<BufferChunkHighlights<'a>>,
}
+/// A chunk of a buffer's text, along with its syntax highlight and
+/// 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,
}
+/// A set of edits to a given version of a buffer, computed asynchronously.
pub struct Diff {
pub(crate) base_version: clock::Global,
line_ending: LineEnding,
@@ -390,24 +504,19 @@ pub(crate) struct DiagnosticEndpoint {
is_unnecessary: bool,
}
+/// 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 {
Self::build(
TextBuffer::new(replica_id, id, base_text.into()),
@@ -417,6 +526,7 @@ impl Buffer {
)
}
+ /// Create a new buffer that is a replica of a remote buffer.
pub fn remote(
remote_id: u64,
replica_id: ReplicaId,
@@ -431,6 +541,8 @@ impl Buffer {
)
}
+ /// Create a new buffer that is a replica of a remote buffer, populating its
+ /// state from the given protobuf message.
pub fn from_proto(
replica_id: ReplicaId,
capability: Capability,
@@ -457,6 +569,7 @@ impl Buffer {
Ok(this)
}
+ /// Serialize the buffer's state to a protobuf message.
pub fn to_proto(&self) -> proto::BufferState {
proto::BufferState {
id: self.remote_id(),
@@ -470,6 +583,7 @@ impl Buffer {
}
}
+ /// Serialize as protobufs all of the changes to the buffer since the given version.
pub fn serialize_ops(
&self,
since: Option<clock::Global>,
@@ -516,19 +630,23 @@ impl Buffer {
})
}
+ /// Assign a language to the buffer, returning the buffer.
pub fn with_language(mut self, language: Arc<Language>, cx: &mut ModelContext<Self>) -> Self {
self.set_language(Some(language), cx);
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>,
@@ -573,6 +691,8 @@ impl Buffer {
}
}
+ /// Retrieve a snapshot of the buffer's current state. This is computationally
+ /// cheap, and allows reading from the buffer on a background thread.
pub fn snapshot(&self) -> BufferSnapshot {
let text = self.text.snapshot();
let mut syntax_map = self.syntax_map.lock();
@@ -595,30 +715,38 @@ impl Buffer {
}
}
- pub fn as_text_snapshot(&self) -> &text::BufferSnapshot {
+ #[cfg(test)]
+ pub(crate) fn as_text_snapshot(&self) -> &text::BufferSnapshot {
&self.text
}
+ /// Retrieve a snapshot of the buffer's raw text, without any
+ /// language-related state like the syntax tree or diagnostics.
pub fn text_snapshot(&self) -> text::BufferSnapshot {
self.text.snapshot()
}
+ /// The file associated with the buffer, if any.
pub fn file(&self) -> Option<&Arc<dyn File>> {
self.file.as_ref()
}
+ /// The version of the buffer that was last saved or reloaded from disk.
pub fn saved_version(&self) -> &clock::Global {
&self.saved_version
}
+ /// The fingerprint of the buffer's text when the buffer was last saved or reloaded from disk.
pub fn saved_version_fingerprint(&self) -> RopeFingerprint {
self.file_fingerprint
}
+ /// The mtime of the buffer's file when the buffer was last saved or reloaded from disk.
pub fn saved_mtime(&self) -> SystemTime {
self.saved_mtime
}
+ /// Assign a language to the buffer.
pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut ModelContext<Self>) {
self.syntax_map.lock().clear();
self.language = language;
@@ -626,17 +754,21 @@ impl Buffer {
cx.emit(Event::LanguageChanged);
}
+ /// Assign a language registry to the buffer. This allows the buffer to retrieve
+ /// other languages if parts of the buffer are written in different languages.
pub fn set_language_registry(&mut self, language_registry: Arc<LanguageRegistry>) {
self.syntax_map
.lock()
.set_language_registry(language_registry);
}
+ /// Assign the buffer a new [Capability].
pub fn set_capability(&mut self, capability: Capability, cx: &mut ModelContext<Self>) {
self.capability = capability;
cx.emit(Event::CapabilityChanged)
}
+ /// This method is called to signal that the buffer has been saved.
pub fn did_save(
&mut self,
version: clock::Global,
@@ -651,6 +783,7 @@ impl Buffer {
cx.notify();
}
+ /// Reloads the contents of the buffer from disk.
pub fn reload(
&mut self,
cx: &mut ModelContext<Self>,
@@ -699,6 +832,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,
@@ -725,6 +859,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;
@@ -762,16 +898,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();
@@ -792,14 +932,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
@@ -810,31 +948,39 @@ 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
}
+ /// Indicates whether the buffer contains any regions that may be
+ /// written in a language that hasn't been loaded yet.
pub fn contains_unknown_injections(&self) -> bool {
self.syntax_map.lock().contains_unknown_injections()
}
@@ -941,6 +1087,7 @@ impl Buffer {
cx.notify();
}
+ /// Assign to the buffer a set of diagnostics created by a given language server.
pub fn update_diagnostics(
&mut self,
server_id: LanguageServerId,
@@ -1170,9 +1317,9 @@ impl Buffer {
self.edit(edits, None, cx);
}
- // Create a minimal edit that will cause the the given row to be indented
- // with the given size. After applying this edit, the length of the line
- // will always be at least `new_size.len`.
+ /// Create a minimal edit that will cause the the given row to be indented
+ /// with the given size. After applying this edit, the length of the line
+ /// will always be at least `new_size.len`.
pub fn edit_for_indent_size_adjustment(
row: u32,
current_size: IndentSize,
@@ -1207,6 +1354,8 @@ impl Buffer {
}
}
+ /// Spawns a background task that asynchronously computes a `Diff` between the buffer's text
+ /// and the given new text.
pub fn diff(&self, mut new_text: String, cx: &AppContext) -> Task<Diff> {
let old_text = self.as_rope().clone();
let base_version = self.version();
@@ -1278,7 +1427,7 @@ impl Buffer {
})
}
- /// Spawn a background task that searches the buffer for any whitespace
+ /// Spawns a background task that searches the buffer for any whitespace
/// at the ends of a lines, and returns a `Diff` that removes that whitespace.
pub fn remove_trailing_whitespace(&self, cx: &AppContext) -> Task<Diff> {
let old_text = self.as_rope().clone();
@@ -1298,7 +1447,7 @@ impl Buffer {
})
}
- /// Ensure that the buffer ends with a single newline character, and
+ /// Ensures that the buffer ends with a single newline character, and
/// no other whitespace.
pub fn ensure_final_newline(&mut self, cx: &mut ModelContext<Self>) {
let len = self.len();
@@ -1319,7 +1468,7 @@ impl Buffer {
self.edit([(offset..len, "\n")], None, cx);
}
- /// Apply a diff to the buffer. If the buffer has changed since the given diff was
+ /// Applies a diff to the buffer. If the buffer has changed since the given diff was
/// calculated, then adjust the diff to account for those changes, and discard any
/// parts of the diff that conflict with those changes.
pub fn apply_diff(&mut self, diff: Diff, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
@@ -1358,11 +1507,14 @@ impl Buffer {
self.end_transaction(cx)
}
+ /// Checks if the buffer has unsaved changes.
pub fn is_dirty(&self) -> bool {
self.file_fingerprint != self.as_rope().fingerprint()
|| self.file.as_ref().map_or(false, |file| file.is_deleted())
}
+ /// Checks if the buffer and its file have both changed since the buffer
+ /// was last saved or reloaded.
pub fn has_conflict(&self) -> bool {
self.file_fingerprint != self.as_rope().fingerprint()
&& self
@@ -1371,14 +1523,23 @@ impl Buffer {
.map_or(false, |file| file.mtime() > self.saved_mtime)
}
+ /// Gets a [`Subscription`] that tracks all of the changes to the buffer's text.
pub fn subscribe(&mut self) -> Subscription {
self.text.subscribe()
}
+ /// Starts a transaction, if one is not already in-progress. When undoing or
+ /// redoing edits, all of the edits performed within a transaction are undone
+ /// or redone together.
pub fn start_transaction(&mut self) -> Option<TransactionId> {
self.start_transaction_at(Instant::now())
}
+ /// Starts a transaction, providing the current time. Subsequent transactions
+ /// that occur within a short period of time will be grouped together. This
+ /// is controlled by the buffer's undo grouping duration.
+ ///
+ /// See [`Buffer::set_group_interval`].
pub fn start_transaction_at(&mut self, now: Instant) -> Option<TransactionId> {
self.transaction_depth += 1;
if self.was_dirty_before_starting_transaction.is_none() {
@@ -1387,10 +1548,16 @@ impl Buffer {
self.text.start_transaction_at(now)
}
+ /// Terminates the current transaction, if this is the outermost transaction.
pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
self.end_transaction_at(Instant::now(), cx)
}
+ /// Terminates the current transaction, providing the current time. Subsequent transactions
+ /// that occur within a short period of time will be grouped together. This
+ /// is controlled by the buffer's undo grouping duration.
+ ///
+ /// See [`Buffer::set_group_interval`].
pub fn end_transaction_at(
&mut self,
now: Instant,
@@ -1411,26 +1578,33 @@ impl Buffer {
}
}
+ /// Manually add a transaction to the buffer's undo history.
pub fn push_transaction(&mut self, transaction: Transaction, now: Instant) {
self.text.push_transaction(transaction, now);
}
+ /// Prevent the last transaction from being grouped with any subsequent transactions,
+ /// even if they occur with the buffer's undo grouping duration.
pub fn finalize_last_transaction(&mut self) -> Option<&Transaction> {
self.text.finalize_last_transaction()
}
+ /// Manually group all changes since a given transaction.
pub fn group_until_transaction(&mut self, transaction_id: TransactionId) {
self.text.group_until_transaction(transaction_id);
}
+ /// Manually remove a transaction from the buffer's undo history
pub fn forget_transaction(&mut self, transaction_id: TransactionId) {
self.text.forget_transaction(transaction_id);
}
+ /// Manually merge two adjacent transactions in the buffer's undo history.
pub fn merge_transactions(&mut self, transaction: TransactionId, destination: TransactionId) {
self.text.merge_transactions(transaction, destination);
}
+ /// Waits for the buffer to receive operations with the given timestamps.
pub fn wait_for_edits(
&mut self,
edit_ids: impl IntoIterator<Item = clock::Lamport>,
@@ -1438,6 +1612,7 @@ impl Buffer {
self.text.wait_for_edits(edit_ids)
}
+ /// Waits for the buffer to receive the operations necessary for resolving the given anchors.
pub fn wait_for_anchors(
&mut self,
anchors: impl IntoIterator<Item = Anchor>,
@@ -1445,14 +1620,18 @@ impl Buffer {
self.text.wait_for_anchors(anchors)
}
+ /// Waits for the buffer to receive operations up to the given version.
pub fn wait_for_version(&mut self, version: clock::Global) -> impl Future<Output = Result<()>> {
self.text.wait_for_version(version)
}
+ /// Forces all futures returned by [`Buffer::wait_for_version`], [`Buffer::wait_for_edits`], or
+ /// [`Buffer::wait_for_version`] to resolve with an error.
pub fn give_up_waiting(&mut self) {
self.text.give_up_waiting();
}
+ /// Stores a set of selections that should be broadcasted to all of the buffer's replicas.
pub fn set_active_selections(
&mut self,
selections: Arc<[Selection<Anchor>]>,
@@ -1481,6 +1660,8 @@ impl Buffer {
);
}
+ /// Clears the selections, so that other replicas of the buffer do not see any selections for
+ /// this replica.
pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
if self
.remote_selections
@@ -1491,6 +1672,7 @@ impl Buffer {
}
}
+ /// Replaces the buffer's entire text.
pub fn set_text<T>(&mut self, text: T, cx: &mut ModelContext<Self>) -> Option<clock::Lamport>
where
T: Into<Arc<str>>,
@@ -1499,6 +1681,15 @@ impl Buffer {
self.edit([(0..self.len(), text)], None, cx)
}
+ /// Applies the given edits to the buffer. Each edit is specified as a range of text to
+ /// delete, and a string of text to insert at that location.
+ ///
+ /// If an [`AutoindentMode`] is provided, then the buffer will enqueue an auto-indent
+ /// request for the edited ranges, which will be processed when the buffer finishes
+ /// parsing.
+ ///
+ /// Parsing takes place at the end of a transaction, and may compute synchronously
+ /// or asynchronously, depending on the changes.
pub fn edit<I, S, T>(
&mut self,
edits_iter: I,
@@ -1632,6 +1823,7 @@ impl Buffer {
cx.notify();
}
+ /// Applies the given remote operations to the buffer.
pub fn apply_ops<I: IntoIterator<Item = Operation>>(
&mut self,
ops: I,
@@ -1779,11 +1971,13 @@ impl Buffer {
cx.emit(Event::Operation(operation));
}
+ /// Removes the selections for a given peer.
pub fn remove_peer(&mut self, replica_id: ReplicaId, cx: &mut ModelContext<Self>) {
self.remote_selections.remove(&replica_id);
cx.notify();
}
+ /// Undoes the most recent transaction.
pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
let was_dirty = self.is_dirty();
let old_version = self.version.clone();
@@ -1797,6 +1991,7 @@ impl Buffer {
}
}
+ /// Manually undoes a specific transaction in the buffer's undo history.
pub fn undo_transaction(
&mut self,
transaction_id: TransactionId,
@@ -1813,6 +2008,7 @@ impl Buffer {
}
}
+ /// Manually undoes all changes after a given transaction in the buffer's undo history.
pub fn undo_to_transaction(
&mut self,
transaction_id: TransactionId,
@@ -1832,6 +2028,7 @@ impl Buffer {
undone
}
+ /// Manually redoes a specific transaction in the buffer's redo history.
pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
let was_dirty = self.is_dirty();
let old_version = self.version.clone();
@@ -1845,6 +2042,7 @@ impl Buffer {
}
}
+ /// Manually undoes all changes until a given transaction in the buffer's redo history.
pub fn redo_to_transaction(
&mut self,
transaction_id: TransactionId,
@@ -1864,6 +2062,7 @@ impl Buffer {
redone
}
+ /// Override current completion triggers with the user-provided completion triggers.
pub fn set_completion_triggers(&mut self, triggers: Vec<String>, cx: &mut ModelContext<Self>) {
self.completion_triggers = triggers.clone();
self.completion_triggers_timestamp = self.text.lamport_clock.tick();
@@ -1877,11 +2076,14 @@ impl Buffer {
cx.notify();
}
+ /// Returns a list of strings which trigger a completion menu for this language.
+ /// Usually this is driven by LSP server which returns a list of trigger characters for completions.
pub fn completion_triggers(&self) -> &[String] {
&self.completion_triggers
}
}
+#[doc(hidden)]
#[cfg(any(test, feature = "test-support"))]
impl Buffer {
pub fn edit_via_marked_text(
@@ -1954,10 +2156,12 @@ impl Deref for Buffer {
}
impl BufferSnapshot {
+ /// Returns [`IndentSize`] for a given line that respects user settings and /// language preferences.
pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
indent_size_for_line(self, row)
}
-
+ /// Returns [`IndentSize`] for a given position that respects user settings
+ /// and language preferences.
pub fn language_indent_size_at<T: ToOffset>(&self, position: T, cx: &AppContext) -> IndentSize {
let settings = language_settings(self.language_at(position), self.file(), cx);
if settings.hard_tabs {
@@ -1967,6 +2171,8 @@ impl BufferSnapshot {
}
}
+ /// Retrieve the suggested indent size for all of the given rows. The unit of indentation
+ /// is passed in as `single_indent_size`.
pub fn suggested_indents(
&self,
rows: impl Iterator<Item = u32>,
@@ -2213,6 +2419,10 @@ impl BufferSnapshot {
None
}
+ /// Iterates over chunks of text in the given range of the buffer. Text is chunked
+ /// in an arbitrary way due to being stored in a [`rope::Rope`]. The text is also
+ /// returned in chunks where each chunk has a single syntax highlighting style and
+ /// diagnostic status.
pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) -> BufferChunks {
let range = range.start.to_offset(self)..range.end.to_offset(self);
@@ -2249,7 +2459,9 @@ impl BufferSnapshot {
BufferChunks::new(self.text.as_rope(), range, syntax, diagnostic_endpoints)
}
- pub fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
+ /// Invokes the given callback for each line of text in the given range of the buffer.
+ /// Uses callback to avoid allocating a string for each line.
+ fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
let mut line = String::new();
let mut row = range.start.row;
for chunk in self
@@ -2268,11 +2480,12 @@ impl BufferSnapshot {
}
}
- pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayerInfo> + '_ {
+ /// Iterates over every [`SyntaxLayer`] in the buffer.
+ pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayer> + '_ {
self.syntax.layers_for_range(0..self.len(), &self.text)
}
- pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayerInfo> {
+ 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)
@@ -2280,12 +2493,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,
@@ -2294,6 +2509,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;
@@ -2338,6 +2554,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;
@@ -2370,6 +2588,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;
@@ -2438,11 +2657,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,
@@ -9,20 +9,36 @@ use std::{
use sum_tree::{self, Bias, SumTree};
use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
+/// A set of diagnostics associated with a given buffer, provided
+/// by a single language server.
+///
+/// The diagnostics are stored in a [SumTree], which allows this struct
+/// to be cheaply copied, and allows for efficient retrieval of the
+/// diagnostics that intersect a given range of the buffer.
#[derive(Clone, Debug, Default)]
pub struct DiagnosticSet {
diagnostics: SumTree<DiagnosticEntry<Anchor>>,
}
+/// A single diagnostic in a set. Generic over its range type, because
+/// the diagnostics are stored internally as [Anchor]s, but can be
+/// resolved to different coordinates types like [usize] byte offsets or
+/// [Point]s.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DiagnosticEntry<T> {
+ /// The range of the buffer where the diagnostic applies.
pub range: Range<T>,
+ /// The information about the diagnostic.
pub diagnostic: Diagnostic,
}
+/// A group of related diagnostics, ordered by their start position
+/// in the buffer.
#[derive(Debug)]
pub struct DiagnosticGroup<T> {
+ /// The diagnostics.
pub entries: Vec<DiagnosticEntry<T>>,
+ /// The index into `entries` where the primary diagnostic is stored.
pub primary_ix: usize,
}
@@ -36,7 +52,8 @@ pub struct Summary {
}
impl<T> DiagnosticEntry<T> {
- // Used to provide diagnostic context to lsp codeAction request
+ /// Returns a raw LSP diagnostic ssed to provide diagnostic context to lsp
+ /// codeAction request
pub fn to_lsp_diagnostic_stub(&self) -> lsp::Diagnostic {
let code = self
.diagnostic
@@ -53,6 +70,8 @@ impl<T> DiagnosticEntry<T> {
}
impl DiagnosticSet {
+ /// Constructs a [DiagnosticSet] from a sequence of entries, ordered by
+ /// their position in the buffer.
pub fn from_sorted_entries<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
where
I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
@@ -62,6 +81,7 @@ impl DiagnosticSet {
}
}
+ /// Constructs a [DiagnosticSet] from a sequence of entries in an arbitrary order.
pub fn new<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
where
I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
@@ -80,14 +100,18 @@ impl DiagnosticSet {
}
}
+ /// Returns the number of diagnostics in the set.
pub fn len(&self) -> usize {
self.diagnostics.summary().count
}
+ /// Returns an iterator over the diagnostic entries in the set.
pub fn iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
self.diagnostics.iter()
}
+ /// Returns an iterator over the diagnostic entries that intersect the
+ /// given range of the buffer.
pub fn range<'a, T, O>(
&'a self,
range: Range<T>,
@@ -134,6 +158,7 @@ impl DiagnosticSet {
})
}
+ /// Adds all of this set's diagnostic groups to the given output vector.
pub fn groups(
&self,
language_server_id: LanguageServerId,
@@ -173,6 +198,8 @@ impl DiagnosticSet {
});
}
+ /// Returns all of the diagnostics in a particular diagnostic group,
+ /// in order of their position in the buffer.
pub fn group<'a, O: FromAnchor>(
&'a self,
group_id: usize,
@@ -183,6 +210,7 @@ impl DiagnosticSet {
.map(|entry| entry.resolve(buffer))
}
}
+
impl sum_tree::Item for DiagnosticEntry<Anchor> {
type Summary = Summary;
@@ -198,6 +226,7 @@ impl sum_tree::Item for DiagnosticEntry<Anchor> {
}
impl DiagnosticEntry<Anchor> {
+ /// Converts the [DiagnosticEntry] to a different buffer coordinate type.
pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
DiagnosticEntry {
range: O::from_anchor(&self.range.start, buffer)
@@ -11,7 +11,7 @@ pub struct HighlightId(pub u32);
const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
impl HighlightMap {
- pub fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
+ pub(crate) fn new(capture_names: &[&str], theme: &SyntaxTheme) -> Self {
// For each capture name in the highlight query, find the longest
// key in the theme's syntax styles that matches all of the
// dot-separated components of the capture name.
@@ -51,7 +51,7 @@ impl HighlightMap {
}
impl HighlightId {
- pub fn is_default(&self) -> bool {
+ pub(crate) fn is_default(&self) -> bool {
*self == DEFAULT_SYNTAX_HIGHLIGHT_ID
}
@@ -1,3 +1,11 @@
+//! The `language` crate provides a large chunk of Zed's language-related
+//! features (the other big contributors being project and lsp crates that revolve around LSP features).
+//! Namely, this crate:
+//! - Provides [`Language`], [`Grammar`] and [`LanguageRegistry`] types that
+//! use Tree-sitter to provide syntax highlighting to the editor; note though that `language` doesn't perform the highlighting by itself. It only maps ranges in a buffer to colors. Treesitter is also used for buffer outlines (lists of symbols in a buffer)
+//! - Exposes [`LanguageConfig`] that describes how constructs (like brackets or line comments) should be handled by the editor for a source file of a particular language.
+//!
+//! Notably we do *not* assign a single language to a single file; in real world a single file can consist of multiple programming languages - HTML is a good example of that - and `language` crate tends to reflect that status quo in it's API.
mod buffer;
mod diagnostic_set;
mod highlight_map;
@@ -54,10 +62,13 @@ pub use buffer::*;
pub use diagnostic_set::DiagnosticEntry;
pub use lsp::LanguageServerId;
pub use outline::{Outline, OutlineItem};
-pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo};
+pub use syntax_map::{OwnedSyntaxLayer, SyntaxLayer};
pub use text::LineEnding;
pub use tree_sitter::{Parser, Tree};
+/// Initializes the `language` crate.
+///
+/// This should be called before making use of items from the create.
pub fn init(cx: &mut AppContext) {
language_settings::init(cx);
}
@@ -90,7 +101,9 @@ thread_local! {
}
lazy_static! {
- pub static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
+ pub(crate) static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default();
+ /// A shared grammar for plain text, exposed for reuse by downstream crates.
+ #[doc(hidden)]
pub static ref PLAIN_TEXT: Arc<Language> = Arc::new(Language::new(
LanguageConfig {
name: "Plain Text".into(),
@@ -100,10 +113,14 @@ lazy_static! {
));
}
+/// Types that represent a position in a buffer, and can be converted into
+/// an LSP position, to send to a language server.
pub trait ToLspPosition {
+ /// Converts the value into an LSP position.
fn to_lsp_position(self) -> lsp::Position;
}
+/// A name of a language server.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct LanguageServerName(pub Arc<str>);
@@ -239,6 +256,8 @@ impl CachedLspAdapter {
}
}
+/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
+// e.g. to display a notification or fetch data from the web.
pub trait LspAdapterDelegate: Send + Sync {
fn show_notification(&self, message: &str, cx: &mut AppContext);
fn http_client(&self) -> Arc<dyn HttpClient>;
@@ -284,6 +303,10 @@ pub trait LspAdapter: 'static + Send + Sync {
delegate: &dyn LspAdapterDelegate,
) -> Option<LanguageServerBinary>;
+ /// Returns true if a language server can be reinstalled.
+ /// If language server initialization fails, a reinstallation will be attempted unless the value returned from this method is false.
+ /// Implementations that rely on software already installed on user's system
+ /// should have [`can_be_reinstalled`] return false.
fn can_be_reinstalled(&self) -> bool {
true
}
@@ -295,6 +318,9 @@ pub trait LspAdapter: 'static + Send + Sync {
fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {}
+ /// A callback called for each [`lsp_types::CompletionItem`] obtained from LSP server.
+ /// Some LspAdapter implementations might want to modify the obtained item to
+ /// change how it's displayed.
async fn process_completion(&self, _: &mut lsp::CompletionItem) {}
async fn label_for_completion(
@@ -314,6 +340,7 @@ pub trait LspAdapter: 'static + Send + Sync {
None
}
+ /// Returns initialization options that are going to be sent to a LSP server as a part of [`lsp_types::InitializeParams`]
async fn initialization_options(&self) -> Option<Value> {
None
}
@@ -322,6 +349,7 @@ pub trait LspAdapter: 'static + Send + Sync {
futures::future::ready(serde_json::json!({})).boxed()
}
+ /// Returns a list of code actions supported by a given LspAdapter
fn code_action_kinds(&self) -> Option<Vec<CodeActionKind>> {
Some(vec![
CodeActionKind::EMPTY,
@@ -358,36 +386,59 @@ pub struct CodeLabel {
#[derive(Clone, Deserialize)]
pub struct LanguageConfig {
+ /// Human-readable name of the language.
pub name: Arc<str>,
+ // The name of the grammar in a WASM bundle (experimental).
pub grammar_name: Option<Arc<str>>,
+ /// Given a list of `LanguageConfig`'s, the language of a file can be determined based on the path extension matching any of the `path_suffixes`.
pub path_suffixes: Vec<String>,
+ /// List of bracket types in a language.
pub brackets: BracketPairConfig,
+ /// A regex pattern that determines whether the language should be assigned to a file or not.
#[serde(default, deserialize_with = "deserialize_regex")]
pub first_line_pattern: Option<Regex>,
+ /// If set to true, auto indentation uses last non empty line to determine
+ /// the indentation level for a new line.
#[serde(default = "auto_indent_using_last_non_empty_line_default")]
pub auto_indent_using_last_non_empty_line: bool,
+ /// A regex that is used to determine whether the indentation level should be
+ /// increased in the following line.
#[serde(default, deserialize_with = "deserialize_regex")]
pub increase_indent_pattern: Option<Regex>,
+ /// A regex that is used to determine whether the indentation level should be
+ /// decreased in the following line.
#[serde(default, deserialize_with = "deserialize_regex")]
pub decrease_indent_pattern: Option<Regex>,
+ /// A list of characters that trigger the automatic insertion of a closing
+ /// bracket when they immediately precede the point where an opening
+ /// bracket is inserted.
#[serde(default)]
pub autoclose_before: String,
- #[serde(default)]
- pub line_comment: Option<Arc<str>>,
+ /// A placeholder used internally by Semantic Index.
#[serde(default)]
pub collapsed_placeholder: String,
+ /// A line comment string that is inserted in e.g. `toggle comments` action.
+ #[serde(default)]
+ pub line_comment: Option<Arc<str>>,
+ /// Starting and closing characters of a block comment.
#[serde(default)]
pub block_comment: Option<(Arc<str>, Arc<str>)>,
+ /// A list of language servers that are allowed to run on subranges of a given language.
#[serde(default)]
pub scope_opt_in_language_servers: Vec<String>,
#[serde(default)]
pub overrides: HashMap<String, LanguageConfigOverride>,
+ /// A list of characters that Zed should treat as word characters for the
+ /// purpose of features that operate on word boundaries, like 'move to next word end'
+ /// or a whole-word search in buffer search.
#[serde(default)]
pub word_characters: HashSet<char>,
+ /// The name of a Prettier parser that should be used for this language.
#[serde(default)]
pub prettier_parser_name: Option<String>,
}
+/// Tree-sitter language queries for a given language.
#[derive(Debug, Default)]
pub struct LanguageQueries {
pub highlights: Option<Cow<'static, str>>,
@@ -399,6 +450,9 @@ pub struct LanguageQueries {
pub overrides: Option<Cow<'static, str>>,
}
+/// Represents a language for the given range. Some languages (e.g. HTML)
+/// interleave several languages together, thus a single buffer might actually contain
+/// several nested scopes.
#[derive(Clone, Debug)]
pub struct LanguageScope {
language: Arc<Language>,
@@ -458,9 +512,9 @@ impl Default for LanguageConfig {
block_comment: Default::default(),
scope_opt_in_language_servers: Default::default(),
overrides: Default::default(),
- collapsed_placeholder: Default::default(),
word_characters: Default::default(),
prettier_parser_name: None,
+ collapsed_placeholder: Default::default(),
}
}
}
@@ -478,6 +532,7 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result<Option<Regex>, D
}
}
+#[doc(hidden)]
#[cfg(any(test, feature = "test-support"))]
pub struct FakeLspAdapter {
pub name: &'static str,
@@ -489,9 +544,16 @@ pub struct FakeLspAdapter {
pub prettier_plugins: Vec<&'static str>,
}
+/// Configuration of handling bracket pairs for a given language.
+///
+/// This struct includes settings for defining which pairs of characters are considered brackets and
+/// also specifies any language-specific scopes where these pairs should be ignored for bracket matching purposes.
#[derive(Clone, Debug, Default)]
pub struct BracketPairConfig {
+ /// A list of character pairs that should be treated as brackets in the context of a given language.
pub pairs: Vec<BracketPair>,
+ /// A list of tree-sitter scopes for which a given bracket should not be active.
+ /// N-th entry in `[Self::disabled_scopes_by_bracket_ix]` contains a list of disabled scopes for an n-th entry in `[Self::pairs]`
pub disabled_scopes_by_bracket_ix: Vec<Vec<String>>,
}
@@ -523,11 +585,18 @@ impl<'de> Deserialize<'de> for BracketPairConfig {
}
}
+/// Describes a single bracket pair and how an editor should react to e.g. inserting
+/// an opening bracket or to a newline character insertion inbetween `start` and `end` characters.
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct BracketPair {
+ /// Starting substring for a bracket.
pub start: String,
+ /// Ending substring for a bracket.
pub end: String,
+ /// True if `end` should be automatically inserted right after `start` characters.
pub close: bool,
+ /// True if an extra newline should be inserted while the cursor is in the middle
+ /// of that bracket pair.
pub newline: bool,
}
@@ -1641,6 +1710,8 @@ impl LanguageScope {
self.language.config.collapsed_placeholder.as_ref()
}
+ /// Returns line prefix that is inserted in e.g. line continuations or
+ /// in `toggle comments` action.
pub fn line_comment_prefix(&self) -> Option<&Arc<str>> {
Override::as_option(
self.config_override().map(|o| &o.line_comment),
@@ -1656,6 +1727,11 @@ impl LanguageScope {
.map(|e| (&e.0, &e.1))
}
+ /// Returns a list of language-specific word characters.
+ ///
+ /// By default, Zed treats alphanumeric characters (and '_') as word characters for
+ /// the purpose of actions like 'move to next word end` or whole-word search.
+ /// It additionally accounts for language's additional word characters.
pub fn word_characters(&self) -> Option<&HashSet<char>> {
Override::as_option(
self.config_override().map(|o| &o.word_characters),
@@ -1663,6 +1739,8 @@ impl LanguageScope {
)
}
+ /// Returns a list of bracket pairs for a given language with an additional
+ /// piece of information about whether the particular bracket pair is currently active for a given language.
pub fn brackets(&self) -> impl Iterator<Item = (&BracketPair, bool)> {
let mut disabled_ids = self
.config_override()
@@ -1,3 +1,5 @@
+//! Provides `language`-related settings.
+
use crate::{File, Language};
use anyhow::Result;
use collections::{HashMap, HashSet};
@@ -11,10 +13,12 @@ use serde::{Deserialize, Serialize};
use settings::Settings;
use std::{num::NonZeroU32, path::Path, sync::Arc};
+/// Initializes the language settings.
pub fn init(cx: &mut AppContext) {
AllLanguageSettings::register(cx);
}
+/// Returns the settings for the specified language from the provided file.
pub fn language_settings<'a>(
language: Option<&Arc<Language>>,
file: Option<&Arc<dyn File>>,
@@ -24,6 +28,7 @@ pub fn language_settings<'a>(
all_language_settings(file, cx).language(language_name.as_deref())
}
+/// Returns the settings for all languages from the provided file.
pub fn all_language_settings<'a>(
file: Option<&Arc<dyn File>>,
cx: &'a AppContext,
@@ -32,51 +37,89 @@ pub fn all_language_settings<'a>(
AllLanguageSettings::get(location, cx)
}
+/// The settings for all languages.
#[derive(Debug, Clone)]
pub struct AllLanguageSettings {
+ /// The settings for GitHub Copilot.
pub copilot: CopilotSettings,
defaults: LanguageSettings,
languages: HashMap<Arc<str>, LanguageSettings>,
}
+/// The settings for a particular language.
#[derive(Debug, Clone, Deserialize)]
pub struct LanguageSettings {
+ /// How many columns a tab should occupy.
pub tab_size: NonZeroU32,
+ /// Whether to indent lines using tab characters, as opposed to multiple
+ /// spaces.
pub hard_tabs: bool,
+ /// How to soft-wrap long lines of text.
pub soft_wrap: SoftWrap,
+ /// The column at which to soft-wrap lines, for buffers where soft-wrap
+ /// is enabled.
pub preferred_line_length: u32,
+ /// Whether to show wrap guides in the editor. Setting this to true will
+ /// show a guide at the 'preferred_line_length' value if softwrap is set to
+ /// 'preferred_line_length', and will show any additional guides as specified
+ /// by the 'wrap_guides' setting.
pub show_wrap_guides: bool,
+ /// Character counts at which to show wrap guides in the editor.
pub wrap_guides: Vec<usize>,
+ /// Whether or not to perform a buffer format before saving.
pub format_on_save: FormatOnSave,
+ /// Whether or not to remove any trailing whitespace from lines of a buffer
+ /// before saving it.
pub remove_trailing_whitespace_on_save: bool,
+ /// Whether or not to ensure there's a single newline at the end of a buffer
+ /// when saving it.
pub ensure_final_newline_on_save: bool,
+ /// How to perform a buffer format.
pub formatter: Formatter,
+ /// Zed's Prettier integration settings.
+ /// If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if
+ /// the project has no other Prettier installed.
pub prettier: HashMap<String, serde_json::Value>,
+ /// Whether to use language servers to provide code intelligence.
pub enable_language_server: bool,
+ /// Controls whether Copilot provides suggestion immediately (true)
+ /// or waits for a `copilot::Toggle` (false).
pub show_copilot_suggestions: bool,
+ /// Whether to show tabs and spaces in the editor.
pub show_whitespaces: ShowWhitespaceSetting,
+ /// Whether to start a new line with a comment when a previous line is a comment as well.
pub extend_comment_on_newline: bool,
+ /// Inlay hint related settings.
pub inlay_hints: InlayHintSettings,
}
+/// The settings for [GitHub Copilot](https://github.com/features/copilot).
#[derive(Clone, Debug, Default)]
pub struct CopilotSettings {
+ /// Whether Copilot is enabled.
pub feature_enabled: bool,
+ /// A list of globs representing files that Copilot should be disabled for.
pub disabled_globs: Vec<GlobMatcher>,
}
+/// The settings for all languages.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct AllLanguageSettingsContent {
+ /// The settings for enabling/disabling features.
#[serde(default)]
pub features: Option<FeaturesContent>,
+ /// The settings for GitHub Copilot.
#[serde(default)]
pub copilot: Option<CopilotSettingsContent>,
+ /// The default language settings.
#[serde(flatten)]
pub defaults: LanguageSettingsContent,
+ /// The settings for individual languages.
#[serde(default, alias = "language_overrides")]
pub languages: HashMap<Arc<str>, LanguageSettingsContent>,
}
+/// The settings for a particular language.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
pub struct LanguageSettingsContent {
/// How many columns a tab should occupy.
@@ -138,7 +181,7 @@ pub struct LanguageSettingsContent {
pub formatter: Option<Formatter>,
/// Zed's Prettier integration settings.
/// If Prettier is enabled, Zed will use this its Prettier instance for any applicable file, if
- /// project has no other Prettier installed.
+ /// the project has no other Prettier installed.
///
/// Default: {}
#[serde(default)]
@@ -148,7 +191,7 @@ pub struct LanguageSettingsContent {
/// Default: true
#[serde(default)]
pub enable_language_server: Option<bool>,
- /// Controls whether copilot provides suggestion immediately (true)
+ /// Controls whether Copilot provides suggestion immediately (true)
/// or waits for a `copilot::Toggle` (false).
///
/// Default: true
@@ -167,18 +210,23 @@ pub struct LanguageSettingsContent {
pub inlay_hints: Option<InlayHintSettings>,
}
+/// The contents of the GitHub Copilot settings.
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct CopilotSettingsContent {
+ /// A list of globs representing files that Copilot should be disabled for.
#[serde(default)]
pub disabled_globs: Option<Vec<String>>,
}
+/// The settings for enabling/disabling features.
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct FeaturesContent {
+ /// Whether the GitHub Copilot feature is enabled.
pub copilot: Option<bool>,
}
+/// Controls the soft-wrapping behavior in the editor.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SoftWrap {
@@ -190,29 +238,38 @@ pub enum SoftWrap {
PreferredLineLength,
}
+/// Controls the behavior of formatting files when they are saved.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum FormatOnSave {
+ /// Files should be formatted on save.
On,
+ /// Files should not be formatted on save.
Off,
+ /// Files should be formatted using the current language server.
LanguageServer,
+ /// The external program to use to format the files on save.
External {
+ /// The external program to run.
command: Arc<str>,
+ /// The arguments to pass to the program.
arguments: Arc<[String]>,
},
}
+/// Controls how whitespace should be displayedin the editor.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ShowWhitespaceSetting {
- /// Draw tabs and spaces only for the selected text.
+ /// Draw whitespace only for the selected text.
Selection,
- /// Do not draw any tabs or spaces
+ /// Do not draw any tabs or spaces.
None,
- /// Draw all invisible symbols
+ /// Draw all invisible symbols.
All,
}
+/// Controls which formatter should be used when formatting code.
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Formatter {
@@ -226,11 +283,14 @@ pub enum Formatter {
Prettier,
/// Format code using an external command.
External {
+ /// The external program to run.
command: Arc<str>,
+ /// The arguments to pass to the program.
arguments: Arc<[String]>,
},
}
+/// The settings for inlay hints.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
pub struct InlayHintSettings {
/// Global switch to toggle hints on and off.
@@ -238,10 +298,19 @@ pub struct InlayHintSettings {
/// Default: false
#[serde(default)]
pub enabled: bool,
+ /// Whether type hints should be shown.
+ ///
+ /// Default: true
#[serde(default = "default_true")]
pub show_type_hints: bool,
+ /// Whether parameter hints should be shown.
+ ///
+ /// Default: true
#[serde(default = "default_true")]
pub show_parameter_hints: bool,
+ /// Whether other hints should be shown.
+ ///
+ /// Default: true
#[serde(default = "default_true")]
pub show_other_hints: bool,
}
@@ -251,6 +320,7 @@ fn default_true() -> bool {
}
impl InlayHintSettings {
+ /// Returns the kinds of inlay hints that are enabled based on the settings.
pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
let mut kinds = HashSet::default();
if self.show_type_hints {
@@ -267,6 +337,7 @@ impl InlayHintSettings {
}
impl AllLanguageSettings {
+ /// Returns the [`LanguageSettings`] for the language with the specified name.
pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
if let Some(name) = language_name {
if let Some(overrides) = self.languages.get(name) {
@@ -276,6 +347,7 @@ impl AllLanguageSettings {
&self.defaults
}
+ /// Returns whether GitHub Copilot is enabled for the given path.
pub fn copilot_enabled_for_path(&self, path: &Path) -> bool {
!self
.copilot
@@ -284,6 +356,7 @@ impl AllLanguageSettings {
.any(|glob| glob.is_match(path))
}
+ /// Returns whether GitHub Copilot is enabled for the given language and path.
pub fn copilot_enabled(&self, language: Option<&Arc<Language>>, path: Option<&Path>) -> bool {
if !self.copilot.feature_enabled {
return false;
@@ -300,13 +373,20 @@ impl AllLanguageSettings {
}
}
+/// The kind of an inlay hint.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InlayHintKind {
+ /// An inlay hint for a type.
Type,
+ /// An inlay hint for a parameter.
Parameter,
}
impl InlayHintKind {
+ /// Returns the [`InlayHintKind`] from the given name.
+ ///
+ /// Returns `None` if `name` does not match any of the expected
+ /// string representations.
pub fn from_name(name: &str) -> Option<Self> {
match name {
"type" => Some(InlayHintKind::Type),
@@ -315,6 +395,7 @@ impl InlayHintKind {
}
}
+ /// Returns the name of this [`InlayHintKind`].
pub fn name(&self) -> &'static str {
match self {
InlayHintKind::Type => "type",
@@ -1,3 +1,5 @@
+//! Provides Markdown-related constructs.
+
use std::sync::Arc;
use std::{ops::Range, path::PathBuf};
@@ -5,21 +7,30 @@ use crate::{HighlightId, Language, LanguageRegistry};
use gpui::{px, FontStyle, FontWeight, HighlightStyle, UnderlineStyle};
use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag};
+/// Parsed Markdown content.
#[derive(Debug, Clone)]
pub struct ParsedMarkdown {
+ /// The Markdown text.
pub text: String,
+ /// The list of highlights contained in the Markdown document.
pub highlights: Vec<(Range<usize>, MarkdownHighlight)>,
+ /// The regions of the various ranges in the Markdown document.
pub region_ranges: Vec<Range<usize>>,
+ /// The regions of the Markdown document.
pub regions: Vec<ParsedRegion>,
}
+/// A run of highlighted Markdown text.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MarkdownHighlight {
+ /// A styled Markdown highlight.
Style(MarkdownHighlightStyle),
+ /// A highlighted code block.
Code(HighlightId),
}
impl MarkdownHighlight {
+ /// Converts this [`MarkdownHighlight`] to a [`HighlightStyle`].
pub fn to_highlight_style(&self, theme: &theme::SyntaxTheme) -> Option<HighlightStyle> {
match self {
MarkdownHighlight::Style(style) => {
@@ -48,23 +59,39 @@ impl MarkdownHighlight {
}
}
+/// The style for a Markdown highlight.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct MarkdownHighlightStyle {
+ /// Whether the text should be italicized.
pub italic: bool,
+ /// Whether the text should be underlined.
pub underline: bool,
+ /// The weight of the text.
pub weight: FontWeight,
}
+/// A parsed region in a Markdown document.
#[derive(Debug, Clone)]
pub struct ParsedRegion {
+ /// Whether the region is a code block.
pub code: bool,
+ /// The link contained in this region, if it has one.
pub link: Option<Link>,
}
+/// A Markdown link.
#[derive(Debug, Clone)]
pub enum Link {
- Web { url: String },
- Path { path: PathBuf },
+ /// A link to a webpage.
+ Web {
+ /// The URL of the webpage.
+ url: String,
+ },
+ /// A link to a path on the filesystem.
+ Path {
+ /// The path to the item.
+ path: PathBuf,
+ },
}
impl Link {
@@ -82,6 +109,7 @@ impl Link {
}
}
+/// Parses a string of Markdown.
pub async fn parse_markdown(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
@@ -111,6 +139,7 @@ pub async fn parse_markdown(
}
}
+/// Parses a Markdown block.
pub async fn parse_markdown_block(
markdown: &str,
language_registry: &Arc<LanguageRegistry>,
@@ -261,6 +290,7 @@ pub async fn parse_markdown_block(
}
}
+/// Appends a highlighted run of text to the provided `text` buffer.
pub fn highlight_code(
text: &mut String,
highlights: &mut Vec<(Range<usize>, MarkdownHighlight)>,
@@ -275,6 +305,7 @@ pub fn highlight_code(
}
}
+/// Appends a new paragraph to the provided `text` buffer.
pub fn new_paragraph(text: &mut String, list_stack: &mut Vec<(Option<u64>, bool)>) {
let mut is_subsequent_paragraph_of_list = false;
if let Some((_, has_content)) = list_stack.last_mut() {
@@ -2,6 +2,7 @@ use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{BackgroundExecutor, HighlightStyle};
use std::ops::Range;
+/// An outline of all the symbols contained in a buffer.
#[derive(Debug)]
pub struct Outline<T> {
pub items: Vec<OutlineItem<T>>,
@@ -1,3 +1,5 @@
+//! Handles conversions of `language` items to and from the [`rpc`] protocol.
+
use crate::{
diagnostic_set::DiagnosticEntry, CodeAction, CodeLabel, Completion, CursorShape, Diagnostic,
Language,
@@ -11,15 +13,18 @@ use text::*;
pub use proto::{BufferState, Operation};
+/// Serializes a [`RopeFingerprint`] to be sent over RPC.
pub fn serialize_fingerprint(fingerprint: RopeFingerprint) -> String {
fingerprint.to_hex()
}
+/// Deserializes a [`RopeFingerprint`] from the RPC representation.
pub fn deserialize_fingerprint(fingerprint: &str) -> Result<RopeFingerprint> {
RopeFingerprint::from_hex(fingerprint)
.map_err(|error| anyhow!("invalid fingerprint: {}", error))
}
+/// Deserializes a `[text::LineEnding]` from the RPC representation.
pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
match message {
proto::LineEnding::Unix => text::LineEnding::Unix,
@@ -27,6 +32,7 @@ pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding {
}
}
+/// Serializes a [`text::LineEnding`] to be sent over RPC.
pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding {
match message {
text::LineEnding::Unix => proto::LineEnding::Unix,
@@ -34,6 +40,7 @@ pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding {
}
}
+/// Serializes a [`crate::Operation`] to be sent over RPC.
pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
proto::Operation {
variant: Some(match operation {
@@ -96,6 +103,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation {
}
}
+/// Serializes an [`operation::EditOperation`] to be sent over RPC.
pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::Edit {
proto::operation::Edit {
replica_id: operation.timestamp.replica_id as u32,
@@ -110,6 +118,7 @@ pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation::
}
}
+/// Serializes an entry in the undo map to be sent over RPC.
pub fn serialize_undo_map_entry(
(edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]),
) -> proto::UndoMapEntry {
@@ -127,6 +136,7 @@ pub fn serialize_undo_map_entry(
}
}
+/// Splits the given list of operations into chunks.
pub fn split_operations(
mut operations: Vec<proto::Operation>,
) -> impl Iterator<Item = Vec<proto::Operation>> {
@@ -152,10 +162,12 @@ pub fn split_operations(
})
}
+/// Serializes selections to be sent over RPC.
pub fn serialize_selections(selections: &Arc<[Selection<Anchor>]>) -> Vec<proto::Selection> {
selections.iter().map(serialize_selection).collect()
}
+/// Serializes a [`Selection`] to be sent over RPC.
pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
proto::Selection {
id: selection.id as u64,
@@ -171,6 +183,7 @@ pub fn serialize_selection(selection: &Selection<Anchor>) -> proto::Selection {
}
}
+/// Serializes a [`CursorShape`] to be sent over RPC.
pub fn serialize_cursor_shape(cursor_shape: &CursorShape) -> proto::CursorShape {
match cursor_shape {
CursorShape::Bar => proto::CursorShape::CursorBar,
@@ -180,6 +193,7 @@ pub fn serialize_cursor_shape(cursor_shape: &CursorShape) -> proto::CursorShape
}
}
+/// Deserializes a [`CursorShape`] from the RPC representation.
pub fn deserialize_cursor_shape(cursor_shape: proto::CursorShape) -> CursorShape {
match cursor_shape {
proto::CursorShape::CursorBar => CursorShape::Bar,
@@ -189,6 +203,7 @@ pub fn deserialize_cursor_shape(cursor_shape: proto::CursorShape) -> CursorShape
}
}
+/// Serializes a list of diagnostics to be sent over RPC.
pub fn serialize_diagnostics<'a>(
diagnostics: impl IntoIterator<Item = &'a DiagnosticEntry<Anchor>>,
) -> Vec<proto::Diagnostic> {
@@ -208,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,
@@ -216,6 +231,7 @@ pub fn serialize_diagnostics<'a>(
.collect()
}
+/// Serializes an [`Anchor`] to be sent over RPC.
pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
proto::Anchor {
replica_id: anchor.timestamp.replica_id as u32,
@@ -230,6 +246,7 @@ pub fn serialize_anchor(anchor: &Anchor) -> proto::Anchor {
}
// This behavior is currently copied in the collab database, for snapshotting channel notes
+/// Deserializes an [`crate::Operation`] from the RPC representation.
pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operation> {
Ok(
match message
@@ -312,6 +329,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result<crate::Operati
)
}
+/// Deserializes an [`EditOperation`] from the RPC representation.
pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation {
EditOperation {
timestamp: clock::Lamport {
@@ -324,6 +342,7 @@ pub fn deserialize_edit_operation(edit: proto::operation::Edit) -> EditOperation
}
}
+/// Deserializes an entry in the undo map from the RPC representation.
pub fn deserialize_undo_map_entry(
entry: proto::UndoMapEntry,
) -> (clock::Lamport, Vec<(clock::Lamport, u32)>) {
@@ -348,6 +367,7 @@ pub fn deserialize_undo_map_entry(
)
}
+/// Deserializes selections from the RPC representation.
pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selection<Anchor>]> {
Arc::from(
selections
@@ -357,6 +377,7 @@ pub fn deserialize_selections(selections: Vec<proto::Selection>) -> Arc<[Selecti
)
}
+/// Deserializes a [`Selection`] from the RPC representation.
pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<Anchor>> {
Some(Selection {
id: selection.id as usize,
@@ -367,6 +388,7 @@ pub fn deserialize_selection(selection: proto::Selection) -> Option<Selection<An
})
}
+/// Deserializes a list of diagnostics from the RPC representation.
pub fn deserialize_diagnostics(
diagnostics: Vec<proto::Diagnostic>,
) -> Arc<[DiagnosticEntry<Anchor>]> {
@@ -387,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,
@@ -397,6 +418,7 @@ pub fn deserialize_diagnostics(
.collect()
}
+/// Deserializes an [`Anchor`] from the RPC representation.
pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
Some(Anchor {
timestamp: clock::Lamport {
@@ -412,6 +434,7 @@ pub fn deserialize_anchor(anchor: proto::Anchor) -> Option<Anchor> {
})
}
+/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`].
pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<clock::Lamport> {
let replica_id;
let value;
@@ -444,6 +467,7 @@ pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option<c
})
}
+/// Serializes a [`Completion`] to be sent over RPC.
pub fn serialize_completion(completion: &Completion) -> proto::Completion {
proto::Completion {
old_start: Some(serialize_anchor(&completion.old_range.start)),
@@ -454,6 +478,7 @@ pub fn serialize_completion(completion: &Completion) -> proto::Completion {
}
}
+/// Deserializes a [`Completion`] from the RPC representation.
pub async fn deserialize_completion(
completion: proto::Completion,
language: Option<Arc<Language>>,
@@ -488,6 +513,7 @@ pub async fn deserialize_completion(
})
}
+/// Serializes a [`CodeAction`] to be sent over RPC.
pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
proto::CodeAction {
server_id: action.server_id.0 as u64,
@@ -497,6 +523,7 @@ pub fn serialize_code_action(action: &CodeAction) -> proto::CodeAction {
}
}
+/// Deserializes a [`CodeAction`] from the RPC representation.
pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction> {
let start = action
.start
@@ -514,6 +541,7 @@ pub fn deserialize_code_action(action: proto::CodeAction) -> Result<CodeAction>
})
}
+/// Serializes a [`Transaction`] to be sent over RPC.
pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
proto::Transaction {
id: Some(serialize_timestamp(transaction.id)),
@@ -527,6 +555,7 @@ pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction {
}
}
+/// Deserializes a [`Transaction`] from the RPC representation.
pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transaction> {
Ok(Transaction {
id: deserialize_timestamp(
@@ -543,6 +572,7 @@ pub fn deserialize_transaction(transaction: proto::Transaction) -> Result<Transa
})
}
+/// Serializes a [`clock::Lamport`] timestamp to be sent over RPC.
pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp {
proto::LamportTimestamp {
replica_id: timestamp.replica_id as u32,
@@ -550,6 +580,7 @@ pub fn serialize_timestamp(timestamp: clock::Lamport) -> proto::LamportTimestamp
}
}
+/// Deserializes a [`clock::Lamport`] timestamp from the RPC representation.
pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lamport {
clock::Lamport {
replica_id: timestamp.replica_id as ReplicaId,
@@ -557,6 +588,7 @@ pub fn deserialize_timestamp(timestamp: proto::LamportTimestamp) -> clock::Lampo
}
}
+/// Serializes a range of [`FullOffset`]s to be sent over RPC.
pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
proto::Range {
start: range.start.0 as u64,
@@ -564,10 +596,12 @@ pub fn serialize_range(range: &Range<FullOffset>) -> proto::Range {
}
}
+/// Deserializes a range of [`FullOffset`]s from the RPC representation.
pub fn deserialize_range(range: proto::Range) -> Range<FullOffset> {
FullOffset(range.start as usize)..FullOffset(range.end as usize)
}
+/// Deserializes a clock version from the RPC representation.
pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global {
let mut version = clock::Global::new();
for entry in message {
@@ -579,6 +613,7 @@ pub fn deserialize_version(message: &[proto::VectorClockEntry]) -> clock::Global
version
}
+/// Serializes a clock version to be sent over RPC.
pub fn serialize_version(version: &clock::Global) -> Vec<proto::VectorClockEntry> {
version
.iter()
@@ -29,7 +29,7 @@ pub struct SyntaxMap {
#[derive(Clone, Default)]
pub struct SyntaxSnapshot {
- layers: SumTree<SyntaxLayer>,
+ layers: SumTree<SyntaxLayerEntry>,
parsed_version: clock::Global,
interpolated_version: clock::Global,
language_registry_version: usize,
@@ -84,7 +84,7 @@ struct SyntaxMapMatchesLayer<'a> {
}
#[derive(Clone)]
-struct SyntaxLayer {
+struct SyntaxLayerEntry {
depth: usize,
range: Range<Anchor>,
content: SyntaxLayerContent,
@@ -117,17 +117,22 @@ impl SyntaxLayerContent {
}
}
+/// A layer of syntax highlighting, corresponding to a single syntax
+/// tree in a particular language.
#[derive(Debug)]
-pub struct SyntaxLayerInfo<'a> {
- pub depth: usize,
+pub struct SyntaxLayer<'a> {
+ /// The language for this layer.
pub language: &'a Arc<Language>,
+ depth: usize,
tree: &'a Tree,
offset: (usize, tree_sitter::Point),
}
+/// A layer of syntax highlighting. Like [SyntaxLayer], but holding
+/// owned data instead of references.
#[derive(Clone)]
-pub struct OwnedSyntaxLayerInfo {
- pub depth: usize,
+pub struct OwnedSyntaxLayer {
+ /// The language for this layer.
pub language: Arc<Language>,
tree: tree_sitter::Tree,
offset: (usize, tree_sitter::Point),
@@ -691,7 +696,7 @@ impl SyntaxSnapshot {
};
layers.push(
- SyntaxLayer {
+ SyntaxLayerEntry {
depth: step.depth,
range: step.range,
content,
@@ -741,7 +746,7 @@ impl SyntaxSnapshot {
SyntaxMapCaptures::new(
range.clone(),
text,
- [SyntaxLayerInfo {
+ [SyntaxLayer {
language,
tree,
depth: 0,
@@ -781,7 +786,7 @@ impl SyntaxSnapshot {
}
#[cfg(test)]
- pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec<SyntaxLayerInfo> {
+ pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec<SyntaxLayer> {
self.layers_for_range(0..buffer.len(), buffer).collect()
}
@@ -789,7 +794,7 @@ impl SyntaxSnapshot {
&'a self,
range: Range<T>,
buffer: &'a BufferSnapshot,
- ) -> impl 'a + Iterator<Item = SyntaxLayerInfo> {
+ ) -> impl 'a + Iterator<Item = SyntaxLayer> {
let start_offset = range.start.to_offset(buffer);
let end_offset = range.end.to_offset(buffer);
let start = buffer.anchor_before(start_offset);
@@ -813,7 +818,7 @@ impl SyntaxSnapshot {
let layer_start_offset = layer.range.start.to_offset(buffer);
let layer_start_point = layer.range.start.to_point(buffer).to_ts_point();
- info = Some(SyntaxLayerInfo {
+ info = Some(SyntaxLayer {
tree,
language,
depth: layer.depth,
@@ -842,7 +847,7 @@ impl<'a> SyntaxMapCaptures<'a> {
fn new(
range: Range<usize>,
text: &'a Rope,
- layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
+ layers: impl Iterator<Item = SyntaxLayer<'a>>,
query: fn(&Grammar) -> Option<&Query>,
) -> Self {
let mut result = Self {
@@ -964,7 +969,7 @@ impl<'a> SyntaxMapMatches<'a> {
fn new(
range: Range<usize>,
text: &'a Rope,
- layers: impl Iterator<Item = SyntaxLayerInfo<'a>>,
+ layers: impl Iterator<Item = SyntaxLayer<'a>>,
query: fn(&Grammar) -> Option<&Query>,
) -> Self {
let mut result = Self::default();
@@ -1436,23 +1441,25 @@ fn insert_newlines_between_ranges(
}
}
-impl OwnedSyntaxLayerInfo {
+impl OwnedSyntaxLayer {
+ /// Returns the root syntax node for this layer.
pub fn node(&self) -> Node {
self.tree
.root_node_with_offset(self.offset.0, self.offset.1)
}
}
-impl<'a> SyntaxLayerInfo<'a> {
- pub fn to_owned(&self) -> OwnedSyntaxLayerInfo {
- OwnedSyntaxLayerInfo {
+impl<'a> SyntaxLayer<'a> {
+ /// Returns an owned version of this layer.
+ pub fn to_owned(&self) -> OwnedSyntaxLayer {
+ OwnedSyntaxLayer {
tree: self.tree.clone(),
offset: self.offset,
- depth: self.depth,
language: self.language.clone(),
}
}
+ /// Returns the root node for this layer.
pub fn node(&self) -> Node<'a> {
self.tree
.root_node_with_offset(self.offset.0, self.offset.1)
@@ -1564,7 +1571,7 @@ impl ChangeRegionSet {
)
}
- fn intersects(&self, layer: &SyntaxLayer, text: &BufferSnapshot) -> bool {
+ fn intersects(&self, layer: &SyntaxLayerEntry, text: &BufferSnapshot) -> bool {
for region in &self.0 {
if region.depth < layer.depth {
continue;
@@ -1675,7 +1682,7 @@ impl<'a> SeekTarget<'a, SyntaxLayerSummary, SyntaxLayerSummary>
}
}
-impl sum_tree::Item for SyntaxLayer {
+impl sum_tree::Item for SyntaxLayerEntry {
type Summary = SyntaxLayerSummary;
fn summary(&self) -> Self::Summary {
@@ -1690,7 +1697,7 @@ impl sum_tree::Item for SyntaxLayer {
}
}
-impl std::fmt::Debug for SyntaxLayer {
+impl std::fmt::Debug for SyntaxLayerEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SyntaxLayer")
.field("depth", &self.depth)
@@ -5,7 +5,7 @@ use gpui::{
MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Render, Styled,
UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext,
};
-use language::{Buffer, OwnedSyntaxLayerInfo};
+use language::{Buffer, OwnedSyntaxLayer};
use std::{mem, ops::Range};
use theme::ActiveTheme;
use tree_sitter::{Node, TreeCursor};
@@ -54,7 +54,7 @@ struct EditorState {
struct BufferState {
buffer: Model<Buffer>,
excerpt_id: ExcerptId,
- active_layer: Option<OwnedSyntaxLayerInfo>,
+ active_layer: Option<OwnedSyntaxLayer>,
}
impl SyntaxTreeView {
@@ -477,7 +477,7 @@ impl SyntaxTreeToolbarItemView {
})
}
- fn render_header(active_layer: &OwnedSyntaxLayerInfo) -> ButtonLike {
+ fn render_header(active_layer: &OwnedSyntaxLayer) -> ButtonLike {
ButtonLike::new("syntax tree header")
.child(Label::new(active_layer.language.name()))
.child(Label::new(format_node_range(active_layer.node())))
@@ -3028,7 +3028,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;
}
}
@@ -3910,7 +3910,6 @@ impl Project {
message: diagnostic.message.clone(),
group_id,
is_primary: true,
- is_valid: true,
is_disk_based,
is_unnecessary,
},
@@ -3928,7 +3927,6 @@ impl Project {
message: info.message.clone(),
group_id,
is_primary: false,
- is_valid: true,
is_disk_based,
is_unnecessary: false,
},
@@ -1472,7 +1472,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;
@@ -11,6 +11,7 @@ use workspace::Workspace;
use crate::{
normal::normal_motion,
state::{Mode, Operator},
+ utils::coerce_punctuation,
visual::visual_motion,
Vim,
};
@@ -680,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)
@@ -710,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 = coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});
@@ -743,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'
});
@@ -1,4 +1,10 @@
-use crate::{motion::Motion, object::Object, state::Mode, utils::copy_selections_content, Vim};
+use crate::{
+ motion::Motion,
+ object::Object,
+ state::Mode,
+ utils::{coerce_punctuation, copy_selections_content},
+ Vim,
+};
use editor::{
display_map::DisplaySnapshot,
movement::{self, FindRange, TextLayoutDetails},
@@ -102,9 +108,9 @@ fn expand_changed_word_selection(
if in_word {
selection.end =
movement::find_boundary(map, selection.end, 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);
+ coerce_punctuation(char_kind(&scope, right), ignore_punctuation);
left_kind != right_kind && left_kind != CharKind::Whitespace
});
@@ -10,7 +10,10 @@ use language::{char_kind, CharKind, Selection};
use serde::Deserialize;
use workspace::Workspace;
-use crate::{motion::right, normal::normal_object, state::Mode, visual::visual_object, Vim};
+use crate::{
+ motion::right, normal::normal_object, state::Mode, utils::coerce_punctuation,
+ visual::visual_object, Vim,
+};
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Object {
@@ -213,14 +216,14 @@ fn in_word(
right(map, relative_to, 1),
movement::FindRange::SingleLine,
|left, right| {
- char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
- != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+ coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+ != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
},
);
let end = movement::find_boundary(map, relative_to, FindRange::SingleLine, |left, right| {
- char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
- != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+ coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+ != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
});
Some(start..end)
@@ -283,15 +286,15 @@ fn around_next_word(
right(map, relative_to, 1),
FindRange::SingleLine,
|left, right| {
- char_kind(&scope, left).coerce_punctuation(ignore_punctuation)
- != char_kind(&scope, right).coerce_punctuation(ignore_punctuation)
+ coerce_punctuation(char_kind(&scope, left), ignore_punctuation)
+ != coerce_punctuation(char_kind(&scope, right), ignore_punctuation)
},
);
let mut word_found = false;
let end = movement::find_boundary(map, relative_to, 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 found = (word_found && left_kind != right_kind) || right == '\n' && left == '\n';
@@ -1,6 +1,6 @@
use editor::{ClipboardSelection, Editor};
use gpui::{AppContext, ClipboardItem};
-use language::Point;
+use language::{CharKind, Point};
pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut AppContext) {
let selections = editor.selections.all_adjusted(cx);
@@ -48,3 +48,11 @@ pub fn copy_selections_content(editor: &mut Editor, linewise: bool, cx: &mut App
cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
}
+
+pub fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> CharKind {
+ if treat_punctuation_as_word && kind == CharKind::Punctuation {
+ CharKind::Word
+ } else {
+ kind
+ }
+}