From 7fbe0b86386368456c6c61798f876f8a5c507add Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Jan 2024 10:14:05 -0800 Subject: [PATCH 1/8] Start work on language docs --- crates/language/src/buffer.rs | 103 +++++++++++++++++++++-- crates/language/src/language.rs | 36 +++++++- crates/language/src/language_settings.rs | 62 +++++++++++++- crates/language/src/markdown.rs | 35 +++++++- crates/language/src/proto.rs | 9 ++ 5 files changed, 231 insertions(+), 14 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index d9472e8a77afc2dc7222d003aa23f513448ed661..f4917780b318e7e3cc4d0e81eac5656822d17e6d 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -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; @@ -63,6 +69,7 @@ pub enum Capability { ReadOnly, } +/// An in-memory representation of a source code file. pub struct Buffer { text: TextBuffer, diff_base: Option, @@ -99,6 +106,8 @@ pub struct Buffer { capability: Capability, } +/// An immutable, cheaply cloneable representation of a certain +/// state of a buffer. pub struct BufferSnapshot { text: text::BufferSnapshot, pub git_diff: git::diff::BufferDiff, @@ -150,6 +159,7 @@ pub struct GroupId { id: usize, } +/// A diagnostic associated with a certain range of a buffer. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Diagnostic { pub source: Option, @@ -257,6 +267,7 @@ pub enum Event { Closed, } +/// The file associated with a buffer. pub trait File: Send + Sync { fn as_local(&self) -> Option<&dyn LocalFile>; @@ -306,6 +317,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. @@ -353,6 +368,8 @@ struct BufferChunkHighlights<'a> { highlight_maps: Vec, } +/// An iterator that yields chunks of a buffer's text, along with their +/// syntax highlights and diagnostic status. pub struct BufferChunks<'a> { range: Range, chunks: text::Chunks<'a>, @@ -365,6 +382,8 @@ pub struct BufferChunks<'a> { highlights: Option>, } +/// A chunk of a buffer's text, along with its syntax highlight and +/// diagnostic status. #[derive(Clone, Copy, Debug, Default)] pub struct Chunk<'a> { pub text: &'a str, @@ -375,6 +394,7 @@ pub struct Chunk<'a> { 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, @@ -407,6 +427,7 @@ impl CharKind { } impl Buffer { + /// Create a new buffer with the given base text. pub fn new>(replica_id: ReplicaId, id: u64, base_text: T) -> Self { Self::build( TextBuffer::new(replica_id, id, base_text.into()), @@ -416,6 +437,7 @@ impl Buffer { ) } + /// Create a new buffer that is a replica of a remote buffer. pub fn remote( remote_id: u64, replica_id: ReplicaId, @@ -430,6 +452,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, @@ -456,6 +480,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(), @@ -469,6 +494,7 @@ impl Buffer { } } + /// Serialize as protobufs all of the changes to the buffer since the given version. pub fn serialize_ops( &self, since: Option, @@ -515,6 +541,7 @@ impl Buffer { }) } + /// Assign a language to the buffer, returning the buffer. pub fn with_language(mut self, language: Arc, cx: &mut ModelContext) -> Self { self.set_language(Some(language), cx); self @@ -572,6 +599,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(); @@ -594,18 +623,22 @@ impl Buffer { } } - pub fn as_text_snapshot(&self) -> &text::BufferSnapshot { + 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> { 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 } @@ -614,10 +647,12 @@ impl Buffer { 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>, cx: &mut ModelContext) { self.syntax_map.lock().clear(); self.language = language; @@ -625,6 +660,8 @@ 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) { self.syntax_map .lock() @@ -935,6 +972,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, @@ -1164,9 +1202,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, @@ -1201,6 +1239,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 { let old_text = self.as_rope().clone(); let base_version = self.version(); @@ -1272,7 +1312,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 { let old_text = self.as_rope().clone(); @@ -1292,7 +1332,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) { let len = self.len(); @@ -1313,7 +1353,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) -> Option { @@ -1352,11 +1392,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 @@ -1365,14 +1408,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 { 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 { self.transaction_depth += 1; if self.was_dirty_before_starting_transaction.is_none() { @@ -1381,10 +1433,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) -> Option { 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, @@ -1405,26 +1463,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, @@ -1432,6 +1497,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, @@ -1439,14 +1505,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> { 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]>, @@ -1475,6 +1545,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) { if self .remote_selections @@ -1485,6 +1557,7 @@ impl Buffer { } } + /// Replaces the buffer's entire text. pub fn set_text(&mut self, text: T, cx: &mut ModelContext) -> Option where T: Into>, @@ -1493,6 +1566,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( &mut self, edits_iter: I, @@ -1626,6 +1708,7 @@ impl Buffer { cx.notify(); } + /// Applies the given remote operations to the buffer. pub fn apply_ops>( &mut self, ops: I, @@ -1773,11 +1856,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.remote_selections.remove(&replica_id); cx.notify(); } + /// Undoes the most recent transaction. pub fn undo(&mut self, cx: &mut ModelContext) -> Option { let was_dirty = self.is_dirty(); let old_version = self.version.clone(); @@ -1791,6 +1876,7 @@ impl Buffer { } } + /// Manually undoes a specific transaction in the buffer's undo history. pub fn undo_transaction( &mut self, transaction_id: TransactionId, @@ -1807,6 +1893,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, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 366d2b0098ca36437252044c50825bceaed96081..df12a270c95f64cd15bc5d4fb73908d0b4f3f9a3 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,3 +1,7 @@ +//! The `language` crate provides... ??? + +#![warn(missing_docs)] + mod buffer; mod diagnostic_set; mod highlight_map; @@ -58,6 +62,9 @@ pub use syntax_map::{OwnedSyntaxLayerInfo, SyntaxLayerInfo}; 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 +97,7 @@ thread_local! { } lazy_static! { - pub static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default(); + pub(crate) static ref NEXT_GRAMMAR_ID: AtomicUsize = Default::default(); pub static ref PLAIN_TEXT: Arc = Arc::new(Language::new( LanguageConfig { name: "Plain Text".into(), @@ -358,14 +365,22 @@ pub struct CodeLabel { #[derive(Clone, Deserialize)] pub struct LanguageConfig { + /// Human-readable name of the language. pub name: Arc, + // The name of the grammar in a WASM bundle. pub grammar_name: Option>, + /// 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, + /// List of 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, + /// 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 #[serde(default, deserialize_with = "deserialize_regex")] pub increase_indent_pattern: Option, #[serde(default, deserialize_with = "deserialize_regex")] @@ -382,12 +397,16 @@ pub struct LanguageConfig { pub scope_opt_in_language_servers: Vec, #[serde(default)] pub overrides: HashMap, + /// 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, #[serde(default)] pub prettier_parser_name: Option, } +/// Tree-sitter language queries for a given language. #[derive(Debug, Default)] pub struct LanguageQueries { pub highlights: Option>, @@ -399,6 +418,9 @@ pub struct LanguageQueries { pub overrides: Option>, } +/// 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, @@ -491,7 +513,10 @@ pub struct FakeLspAdapter { #[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, + /// 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>, } @@ -1641,6 +1666,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> { Override::as_option( self.config_override().map(|o| &o.line_comment), @@ -1656,6 +1683,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> { Override::as_option( self.config_override().map(|o| &o.word_characters), @@ -1663,6 +1695,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 { let mut disabled_ids = self .config_override() diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 5359d184d65a9249f8fc82b1eae9c95d71beda6c..3ac8842243abf28823db57c9b9d15eec29bc9c6c 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -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}; -pub fn init(cx: &mut AppContext) { +/// Initializes the language settings. +pub(crate) 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>, file: Option<&Arc>, @@ -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>, cx: &'a AppContext, @@ -32,36 +37,68 @@ 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, 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, + /// 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, + /// 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 Copilit is enabled. pub feature_enabled: bool, + /// A list of globs representing files that Copilot should be disabled for. pub disabled_globs: Vec, } @@ -138,7 +175,7 @@ pub struct LanguageSettingsContent { pub formatter: Option, /// 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 +185,7 @@ pub struct LanguageSettingsContent { /// Default: true #[serde(default)] pub enable_language_server: Option, - /// Controls whether copilot provides suggestion immediately (true) + /// Controls whether Copilot provides suggestion immediately (true) /// or waits for a `copilot::Toggle` (false). /// /// Default: true @@ -176,9 +213,11 @@ pub struct CopilotSettingsContent { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct FeaturesContent { + /// Whether the GitHub Copilot feature is enabled. pub copilot: Option, } +/// 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,14 +229,20 @@ 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, LanguageServer, + /// The external program to use to format the files on save. External { + /// The external program to run. command: Arc, + /// The arguments to pass to the program. arguments: Arc<[String]>, }, } @@ -231,6 +276,7 @@ pub enum Formatter { }, } +/// 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 +284,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 +306,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> { let mut kinds = HashSet::default(); if self.show_type_hints { diff --git a/crates/language/src/markdown.rs b/crates/language/src/markdown.rs index df75b610ef844ec858d74ae7a3a8bafa5a59edbe..863f38667f27c4124dc76702cffd12ed0a8ad0be 100644 --- a/crates/language/src/markdown.rs +++ b/crates/language/src/markdown.rs @@ -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, MarkdownHighlight)>, + /// The regions of the various ranges in the Markdown document. pub region_ranges: Vec>, + /// The regions of the Markdown document. pub regions: Vec, } +/// 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 { 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, } +/// 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, @@ -111,6 +139,7 @@ pub async fn parse_markdown( } } +/// Parses a Markdown block. pub async fn parse_markdown_block( markdown: &str, language_registry: &Arc, @@ -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, 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, bool)>) { let mut is_subsequent_paragraph_of_list = false; if let Some((_, has_content)) = list_stack.last_mut() { diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 957f4ee7fbc4da251698fd640d33d37d5cf4a06b..8d71e6e397af8ff11c24e3ba2b7c322395952d93 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -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 the wire. pub fn serialize_fingerprint(fingerprint: RopeFingerprint) -> String { fingerprint.to_hex() } +/// Deserializes a [`RopeFingerprint`] from the wire format. pub fn deserialize_fingerprint(fingerprint: &str) -> Result { RopeFingerprint::from_hex(fingerprint) .map_err(|error| anyhow!("invalid fingerprint: {}", error)) } +/// Deserializes a `[text::LineEnding]` from the wire format. 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 the wire. 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 the wire. 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 the wire. 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 the wire. pub fn serialize_undo_map_entry( (edit_id, counts): (&clock::Lamport, &[(clock::Lamport, u32)]), ) -> proto::UndoMapEntry { From b02f37083b2e703cadb6752ba75ed7f8be626d15 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 9 Jan 2024 10:53:57 -0800 Subject: [PATCH 2/8] More docs --- crates/language/src/buffer.rs | 26 +++++++++--- crates/language/src/language.rs | 32 ++++++++++++--- crates/language/src/language_settings.rs | 33 +++++++++++++-- crates/language/src/outline.rs | 1 + crates/language/src/proto.rs | 41 +++++++++++++++---- crates/language/src/syntax_map.rs | 36 ++++++++-------- crates/language_tools/src/syntax_tree_view.rs | 6 +-- 7 files changed, 133 insertions(+), 42 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index f4917780b318e7e3cc4d0e81eac5656822d17e6d..b745ff6f43fbb88908989ca71dcdc11d9ed6657a 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -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, @@ -69,7 +69,8 @@ pub enum Capability { ReadOnly, } -/// An in-memory representation of a source code file. +/// 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, @@ -123,12 +124,15 @@ 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 { pub len: u32, pub kind: IndentKind, } +/// A whitespace character that's used for indentation. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum IndentKind { #[default] @@ -136,6 +140,7 @@ pub enum IndentKind { Tab, } +/// The shape of a selection cursor. #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub enum CursorShape { #[default] @@ -300,6 +305,7 @@ pub trait File: Send + Sync { 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; @@ -409,6 +415,7 @@ 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, @@ -2048,6 +2055,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, @@ -2294,6 +2303,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(&self, range: Range, language_aware: bool) -> BufferChunks { let range = range.start.to_offset(self)..range.end.to_offset(self); @@ -2330,7 +2343,9 @@ impl BufferSnapshot { BufferChunks::new(self.text.as_rope(), range, syntax, diagnostic_endpoints) } - pub fn for_each_line(&self, range: Range, 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, mut callback: impl FnMut(u32, &str)) { let mut line = String::new(); let mut row = range.start.row; for chunk in self @@ -2349,11 +2364,12 @@ impl BufferSnapshot { } } - pub fn syntax_layers(&self) -> impl Iterator + '_ { + /// Iterates over every [`SyntaxLayer`] in the buffer. + pub fn syntax_layers(&self) -> impl Iterator + '_ { self.syntax.layers_for_range(0..self.len(), &self.text) } - pub fn syntax_layer_at(&self, position: D) -> Option { + pub fn syntax_layer_at(&self, position: D) -> Option { let offset = position.to_offset(self); self.syntax .layers_for_range(offset..offset, &self.text) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index df12a270c95f64cd15bc5d4fb73908d0b4f3f9a3..8182af1e54054c0b973afd296a18c1a3c993a405 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -58,7 +58,7 @@ 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}; @@ -246,6 +246,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; @@ -291,6 +293,10 @@ pub trait LspAdapter: 'static + Send + Sync { delegate: &dyn LspAdapterDelegate, ) -> Option; + /// 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 } @@ -302,6 +308,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( @@ -321,6 +330,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 { None } @@ -329,6 +339,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> { Some(vec![ CodeActionKind::EMPTY, @@ -380,19 +391,29 @@ pub struct LanguageConfig { /// 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 + /// 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, + /// 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, + /// 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>, + /// 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>, + /// Starting and closing characters of a block comment. #[serde(default)] pub block_comment: Option<(Arc, Arc)>, + /// 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, #[serde(default)] @@ -402,6 +423,7 @@ pub struct LanguageConfig { /// or a whole-word search in buffer search. #[serde(default)] pub word_characters: HashSet, + /// The name of a Prettier parser that should be used for this language. #[serde(default)] pub prettier_parser_name: Option, } @@ -480,9 +502,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(), } } } diff --git a/crates/language/src/language_settings.rs b/crates/language/src/language_settings.rs index 3ac8842243abf28823db57c9b9d15eec29bc9c6c..c1bd3aa57fa723231a3de6b36566adad5b749c55 100644 --- a/crates/language/src/language_settings.rs +++ b/crates/language/src/language_settings.rs @@ -96,24 +96,30 @@ pub struct LanguageSettings { /// The settings for [GitHub Copilot](https://github.com/features/copilot). #[derive(Clone, Debug, Default)] pub struct CopilotSettings { - /// Whether Copilit is enabled. + /// Whether Copilot is enabled. pub feature_enabled: bool, /// A list of globs representing files that Copilot should be disabled for. pub disabled_globs: Vec, } +/// 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, + /// The settings for GitHub Copilot. #[serde(default)] pub copilot: Option, + /// The default language settings. #[serde(flatten)] pub defaults: LanguageSettingsContent, + /// The settings for individual languages. #[serde(default, alias = "language_overrides")] pub languages: HashMap, LanguageSettingsContent>, } +/// The settings for a particular language. #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] pub struct LanguageSettingsContent { /// How many columns a tab should occupy. @@ -204,12 +210,15 @@ pub struct LanguageSettingsContent { pub inlay_hints: Option, } +/// 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>, } +/// The settings for enabling/disabling features. #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub struct FeaturesContent { @@ -237,6 +246,7 @@ pub enum FormatOnSave { 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 { @@ -247,17 +257,19 @@ pub enum FormatOnSave { }, } +/// 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 { @@ -271,7 +283,9 @@ pub enum Formatter { Prettier, /// Format code using an external command. External { + /// The external program to run. command: Arc, + /// The arguments to pass to the program. arguments: Arc<[String]>, }, } @@ -323,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) { @@ -332,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 @@ -340,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>, path: Option<&Path>) -> bool { if !self.copilot.feature_enabled { return false; @@ -356,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 { match name { "type" => Some(InlayHintKind::Type), @@ -371,6 +395,7 @@ impl InlayHintKind { } } + /// Returns the name of this [`InlayHintKind`]. pub fn name(&self) -> &'static str { match self { InlayHintKind::Type => "type", diff --git a/crates/language/src/outline.rs b/crates/language/src/outline.rs index df1a3c629e75e7695fcf9bd1f6c6a796df2c01f1..014b32676af586bb3b0f69f3b14e4ee8ec99f6c9 100644 --- a/crates/language/src/outline.rs +++ b/crates/language/src/outline.rs @@ -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 { pub items: Vec>, diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 8d71e6e397af8ff11c24e3ba2b7c322395952d93..0497a4c4593e8307fc3f90809e32e9b5e4a6ab7e 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -13,18 +13,18 @@ use text::*; pub use proto::{BufferState, Operation}; -/// Serializes a [`RopeFingerprint`] to be sent over the wire. +/// Serializes a [`RopeFingerprint`] to be sent over RPC. pub fn serialize_fingerprint(fingerprint: RopeFingerprint) -> String { fingerprint.to_hex() } -/// Deserializes a [`RopeFingerprint`] from the wire format. +/// Deserializes a [`RopeFingerprint`] from the RPC representation. pub fn deserialize_fingerprint(fingerprint: &str) -> Result { RopeFingerprint::from_hex(fingerprint) .map_err(|error| anyhow!("invalid fingerprint: {}", error)) } -/// Deserializes a `[text::LineEnding]` from the wire format. +/// 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, @@ -32,7 +32,7 @@ pub fn deserialize_line_ending(message: proto::LineEnding) -> text::LineEnding { } } -/// Serializes a [`text::LineEnding`] to be sent over the wire. +/// 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, @@ -40,7 +40,7 @@ pub fn serialize_line_ending(message: text::LineEnding) -> proto::LineEnding { } } -/// Serializes a [`crate::Operation`] to be sent over the wire. +/// Serializes a [`crate::Operation`] to be sent over RPC. pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation { proto::Operation { variant: Some(match operation { @@ -103,7 +103,7 @@ pub fn serialize_operation(operation: &crate::Operation) -> proto::Operation { } } -/// Serializes an [`operation::EditOperation`] to be sent over the wire. +/// 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, @@ -118,7 +118,7 @@ pub fn serialize_edit_operation(operation: &EditOperation) -> proto::operation:: } } -/// Serializes an entry in the undo map to be sent over the wire. +/// 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 { @@ -136,6 +136,7 @@ pub fn serialize_undo_map_entry( } } +/// Splits the given list of operations into chunks. pub fn split_operations( mut operations: Vec, ) -> impl Iterator> { @@ -161,10 +162,12 @@ pub fn split_operations( }) } +/// Serializes selections to be sent over RPC. pub fn serialize_selections(selections: &Arc<[Selection]>) -> Vec { selections.iter().map(serialize_selection).collect() } +/// Serializes a [`Selection`] to be sent over RPC. pub fn serialize_selection(selection: &Selection) -> proto::Selection { proto::Selection { id: selection.id as u64, @@ -180,6 +183,7 @@ pub fn serialize_selection(selection: &Selection) -> 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, @@ -189,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, @@ -198,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>, ) -> Vec { @@ -225,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, @@ -239,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 { Ok( match message @@ -321,6 +329,7 @@ pub fn deserialize_operation(message: proto::Operation) -> Result EditOperation { EditOperation { timestamp: clock::Lamport { @@ -333,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)>) { @@ -357,6 +367,7 @@ pub fn deserialize_undo_map_entry( ) } +/// Deserializes selections from the RPC representation. pub fn deserialize_selections(selections: Vec) -> Arc<[Selection]> { Arc::from( selections @@ -366,6 +377,7 @@ pub fn deserialize_selections(selections: Vec) -> Arc<[Selecti ) } +/// Deserializes a [`Selection`] from the RPC representation. pub fn deserialize_selection(selection: proto::Selection) -> Option> { Some(Selection { id: selection.id as usize, @@ -376,6 +388,7 @@ pub fn deserialize_selection(selection: proto::Selection) -> Option, ) -> Arc<[DiagnosticEntry]> { @@ -406,6 +419,7 @@ pub fn deserialize_diagnostics( .collect() } +/// Deserializes an [`Anchor`] from the RPC representation. pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { Some(Anchor { timestamp: clock::Lamport { @@ -421,6 +435,7 @@ pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { }) } +/// Returns a `[clock::Lamport`] timestamp for the given [`proto::Operation`]. pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option { let replica_id; let value; @@ -453,6 +468,7 @@ pub fn lamport_timestamp_for_operation(operation: &proto::Operation) -> Option proto::Completion { proto::Completion { old_start: Some(serialize_anchor(&completion.old_range.start)), @@ -463,6 +479,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>, @@ -497,6 +514,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, @@ -506,6 +524,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 { let start = action .start @@ -523,6 +542,7 @@ pub fn deserialize_code_action(action: proto::CodeAction) -> Result }) } +/// Serializes a [`Transaction`] to be sent over RPC. pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction { proto::Transaction { id: Some(serialize_timestamp(transaction.id)), @@ -536,6 +556,7 @@ pub fn serialize_transaction(transaction: &Transaction) -> proto::Transaction { } } +/// Deserializes a [`Transaction`] from the RPC representation. pub fn deserialize_transaction(transaction: proto::Transaction) -> Result { Ok(Transaction { id: deserialize_timestamp( @@ -552,6 +573,7 @@ pub fn deserialize_transaction(transaction: proto::Transaction) -> Result proto::LamportTimestamp { proto::LamportTimestamp { replica_id: timestamp.replica_id as u32, @@ -559,6 +581,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, @@ -566,6 +589,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) -> proto::Range { proto::Range { start: range.start.0 as u64, @@ -573,10 +597,12 @@ pub fn serialize_range(range: &Range) -> proto::Range { } } +/// Deserializes a range of [`FullOffset`]s from the RPC representation. pub fn deserialize_range(range: proto::Range) -> Range { 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 { @@ -588,6 +614,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 { version .iter() diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index c22ece48aff0b41a089538cba70424f81cd4e042..9174dc7df9e8e45663a75a268ede925460cc6139 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -29,7 +29,7 @@ pub struct SyntaxMap { #[derive(Clone, Default)] pub struct SyntaxSnapshot { - layers: SumTree, + layers: SumTree, 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, content: SyntaxLayerContent, @@ -118,7 +118,7 @@ impl SyntaxLayerContent { } #[derive(Debug)] -pub struct SyntaxLayerInfo<'a> { +pub struct SyntaxLayer<'a> { pub depth: usize, pub language: &'a Arc, tree: &'a Tree, @@ -126,7 +126,7 @@ pub struct SyntaxLayerInfo<'a> { } #[derive(Clone)] -pub struct OwnedSyntaxLayerInfo { +pub struct OwnedSyntaxLayer { pub depth: usize, pub language: Arc, tree: tree_sitter::Tree, @@ -691,7 +691,7 @@ impl SyntaxSnapshot { }; layers.push( - SyntaxLayer { + SyntaxLayerEntry { depth: step.depth, range: step.range, content, @@ -741,7 +741,7 @@ impl SyntaxSnapshot { SyntaxMapCaptures::new( range.clone(), text, - [SyntaxLayerInfo { + [SyntaxLayer { language, tree, depth: 0, @@ -781,7 +781,7 @@ impl SyntaxSnapshot { } #[cfg(test)] - pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec { + pub fn layers<'a>(&'a self, buffer: &'a BufferSnapshot) -> Vec { self.layers_for_range(0..buffer.len(), buffer).collect() } @@ -789,7 +789,7 @@ impl SyntaxSnapshot { &'a self, range: Range, buffer: &'a BufferSnapshot, - ) -> impl 'a + Iterator { + ) -> impl 'a + Iterator { 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 +813,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 +842,7 @@ impl<'a> SyntaxMapCaptures<'a> { fn new( range: Range, text: &'a Rope, - layers: impl Iterator>, + layers: impl Iterator>, query: fn(&Grammar) -> Option<&Query>, ) -> Self { let mut result = Self { @@ -964,7 +964,7 @@ impl<'a> SyntaxMapMatches<'a> { fn new( range: Range, text: &'a Rope, - layers: impl Iterator>, + layers: impl Iterator>, query: fn(&Grammar) -> Option<&Query>, ) -> Self { let mut result = Self::default(); @@ -1436,16 +1436,16 @@ fn insert_newlines_between_ranges( } } -impl OwnedSyntaxLayerInfo { +impl OwnedSyntaxLayer { 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> { + pub fn to_owned(&self) -> OwnedSyntaxLayer { + OwnedSyntaxLayer { tree: self.tree.clone(), offset: self.offset, depth: self.depth, @@ -1564,7 +1564,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 +1675,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 +1690,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) diff --git a/crates/language_tools/src/syntax_tree_view.rs b/crates/language_tools/src/syntax_tree_view.rs index c30564e9bfe3f8c0e0fd1f5c2f6827a4f070fd81..493ce162cb060f3a7df890b481c1d7cba1485535 100644 --- a/crates/language_tools/src/syntax_tree_view.rs +++ b/crates/language_tools/src/syntax_tree_view.rs @@ -5,7 +5,7 @@ use gpui::{ MouseButton, MouseDownEvent, MouseMoveEvent, ParentElement, Pixels, Render, Styled, UniformListScrollHandle, View, ViewContext, VisualContext, WeakView, WindowContext, }; -use language::{Buffer, OwnedSyntaxLayerInfo}; +use language::{Buffer, OwnedSyntaxLayer}; use settings::Settings; use std::{mem, ops::Range}; use theme::{ActiveTheme, ThemeSettings}; @@ -57,7 +57,7 @@ struct EditorState { struct BufferState { buffer: Model, excerpt_id: ExcerptId, - active_layer: Option, + active_layer: Option, } impl SyntaxTreeView { @@ -491,7 +491,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()))) From 686dce85dda1fef979c532a749c3c80c0739e6b4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:32:50 +0100 Subject: [PATCH 3/8] Document bracket pairs --- crates/language/src/language.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8182af1e54054c0b973afd296a18c1a3c993a405..49a87df35d72d71d4071a4239d6d88724b2e8f01 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -98,6 +98,8 @@ thread_local! { lazy_static! { 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 = Arc::new(Language::new( LanguageConfig { name: "Plain Text".into(), @@ -382,7 +384,7 @@ pub struct LanguageConfig { pub grammar_name: Option>, /// 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, - /// List of + /// 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")] @@ -522,6 +524,7 @@ fn deserialize_regex<'de, D: Deserializer<'de>>(d: D) -> Result, D } } +#[doc(hidden)] #[cfg(any(test, feature = "test-support"))] pub struct FakeLspAdapter { pub name: &'static str, @@ -533,6 +536,10 @@ 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. @@ -570,11 +577,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, } From 6144ee1b5dcf793d90d05c0404e7cbf7f2e4aa78 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:50:34 +0100 Subject: [PATCH 4/8] Docs for indent_size_for_line and co --- crates/language/src/buffer.rs | 10 +++++++++- crates/language/src/highlight_map.rs | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index b745ff6f43fbb88908989ca71dcdc11d9ed6657a..7863be8b9396c3da6a9a165365b68d17d7619ff7 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1920,6 +1920,7 @@ impl Buffer { undone } + /// Manually redoes a specific transaction in the buffer's redo history. pub fn redo(&mut self, cx: &mut ModelContext) -> Option { let was_dirty = self.is_dirty(); let old_version = self.version.clone(); @@ -1933,6 +1934,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, @@ -1952,6 +1954,7 @@ impl Buffer { redone } + /// Override current completion triggers with the user-provided completion triggers. pub fn set_completion_triggers(&mut self, triggers: Vec, cx: &mut ModelContext) { self.completion_triggers = triggers.clone(); self.completion_triggers_timestamp = self.text.lamport_clock.tick(); @@ -1965,11 +1968,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( @@ -2042,10 +2048,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(&self, position: T, cx: &AppContext) -> IndentSize { let settings = language_settings(self.language_at(position), self.file(), cx); if settings.hard_tabs { diff --git a/crates/language/src/highlight_map.rs b/crates/language/src/highlight_map.rs index 270ac259c9d78eff8d36a2ee8c8038f117d33260..8829eb94ac576be4b1ad7bc6512fd4720cf81dcb 100644 --- a/crates/language/src/highlight_map.rs +++ b/crates/language/src/highlight_map.rs @@ -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 } From ebe2c3658cf93fc88d12d6b284ae40875b6d3a93 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:08:01 +0100 Subject: [PATCH 5/8] Add short top-level description of a crate --- crates/language/src/language.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 49a87df35d72d71d4071a4239d6d88724b2e8f01..fdb086ebc6ac85a91ccb1e0ece95385d8bc94fd3 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,7 +1,12 @@ -//! The `language` crate provides... ??? - #![warn(missing_docs)] - +//! 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; From 6457ccf9ece3b36a37e675783abee9748a443115 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 17 Jan 2024 10:08:42 -0800 Subject: [PATCH 6/8] Add docs for buffer.rs Co-authored-by: Antonio --- crates/language/src/buffer.rs | 203 ++++++++++++++++++++---- crates/language/src/proto.rs | 3 +- crates/multi_buffer/src/multi_buffer.rs | 2 +- crates/project/src/project.rs | 2 - crates/rpc/proto/zed.proto | 3 + crates/vim/src/motion.rs | 20 ++- 6 files changed, 194 insertions(+), 39 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 7863be8b9396c3da6a9a165365b68d17d7619ff7..8feb566c7e7b0a9819eeed6e644856690fc46e86 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -60,12 +60,15 @@ pub use {tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; lazy_static! { - pub static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new(); + static ref BUFFER_DIFF_TASK: TaskLabel = TaskLabel::new(); } +/// Indicate whether a [Buffer] has permissions to edit. #[derive(PartialEq, Clone, Copy, Debug)] pub enum Capability { + /// The buffer is a mutable replica. ReadWrite, + /// The buffer is a read-only replica. ReadOnly, } @@ -107,11 +110,11 @@ pub struct Buffer { capability: Capability, } -/// An immutable, cheaply cloneable representation of a certain +/// An immutable, cheaply cloneable representation of a fixed /// state of a buffer. pub struct BufferSnapshot { text: text::BufferSnapshot, - pub git_diff: git::diff::BufferDiff, + git_diff: git::diff::BufferDiff, pub(crate) syntax: SyntaxSnapshot, file: Option>, 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, - 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, + /// A machine-readable code that identifies this diagnostic. pub code: Option, + /// 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, @@ -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, + /// 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, + /// 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, + /// 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]>, + /// 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]>, + /// 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, + /// 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>; + /// 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, + /// The highlight style that has been applied to this chunk in + /// the editor. pub highlight_style: Option, + /// The severity of diagnostic associated with this chunk, if any. pub diagnostic_severity: Option, + /// 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>(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, @@ -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, @@ -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, cx: &mut ModelContext) { 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, cx: &mut ModelContext) { 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) -> Option> { 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) { - cx.emit(Event::Closed); - } - + /// Returns the primary [Language] assigned to this [Buffer]. pub fn language(&self) -> Option<&Arc> { self.language.as_ref() } + /// Returns the [Language] at the given location. pub fn language_at(&self, position: D) -> Option> { 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(&self, position: D) -> Option { + fn syntax_layer_at(&self, position: D) -> Option { 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(&self, position: D) -> Option<&Arc> { 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(&self, position: D) -> Option { 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(&self, start: T) -> (Range, Option) { 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(&self, range: Range) -> Option> { let range = range.start.to_offset(self)..range.end.to_offset(self); let mut result: Option> = 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> { 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( &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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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> { 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 { 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) -> IndentSize { +fn indent_size_for_text(text: impl Iterator) -> 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 { 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, max_len: usize, ) -> impl Iterator> { @@ -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, c: char) -> CharKind { if c.is_whitespace() { return CharKind::Whitespace; diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 0497a4c4593e8307fc3f90809e32e9b5e4a6ab7e..d4b553de47966847916794a327b315272bbff3d1 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -223,7 +223,7 @@ pub fn serialize_diagnostics<'a>( } as i32, group_id: entry.diagnostic.group_id as u64, is_primary: entry.diagnostic.is_primary, - is_valid: entry.diagnostic.is_valid, + is_valid: true, code: entry.diagnostic.code.clone(), is_disk_based: entry.diagnostic.is_disk_based, is_unnecessary: entry.diagnostic.is_unnecessary, @@ -409,7 +409,6 @@ pub fn deserialize_diagnostics( message: diagnostic.message, group_id: diagnostic.group_id as usize, code: diagnostic.code, - is_valid: diagnostic.is_valid, is_primary: diagnostic.is_primary, is_disk_based: diagnostic.is_disk_based, is_unnecessary: diagnostic.is_unnecessary, diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 946e6af5ab5fd9a97439edb5acb296972068cf44..e02f19155bdda86748fc6b0daf09a886c9ecb086 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -3023,7 +3023,7 @@ impl MultiBufferSnapshot { pub fn has_git_diffs(&self) -> bool { for excerpt in self.excerpts.iter() { - if !excerpt.buffer.git_diff.is_empty() { + if excerpt.buffer.has_git_diff() { return true; } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 044b750ad9d7cecce1a00f491e0fd66199989bb7..fdaab47bc1bdeb9a677c9f9c7d1817c0f8762144 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3912,7 +3912,6 @@ impl Project { message: diagnostic.message.clone(), group_id, is_primary: true, - is_valid: true, is_disk_based, is_unnecessary, }, @@ -3930,7 +3929,6 @@ impl Project { message: info.message.clone(), group_id, is_primary: false, - is_valid: true, is_disk_based, is_unnecessary: false, }, diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index e423441a30218674c4d6b68098a8f724c8a32654..086deeef119153d151c02b59b4184ec9b3cae06a 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -1471,7 +1471,10 @@ message Diagnostic { optional string code = 6; uint64 group_id = 7; bool is_primary = 8; + + // TODO: remove this field bool is_valid = 9; + bool is_disk_based = 10; bool is_unnecessary = 11; diff --git a/crates/vim/src/motion.rs b/crates/vim/src/motion.rs index 73ba70d5add11f33d55e001e2637c05bcca0afe5..685c7a28f9b91de84686d7a9e876ab1e49dd5594 100644 --- a/crates/vim/src/motion.rs +++ b/crates/vim/src/motion.rs @@ -681,8 +681,8 @@ pub(crate) fn next_word_start( for _ in 0..times { let mut crossed_newline = false; point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| { - let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); + let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation); + let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation); let at_newline = right == '\n'; let found = (left_kind != right_kind && right_kind != CharKind::Whitespace) @@ -711,8 +711,8 @@ fn next_word_end( *point.column_mut() = 0; } point = movement::find_boundary(map, point, FindRange::MultiLine, |left, right| { - let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); + let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation); + let right_kind = ccoerce_punctuation(har_kind(&scope, right), ignore_punctuation); left_kind != right_kind && left_kind != CharKind::Whitespace }); @@ -744,8 +744,8 @@ fn previous_word_start( // cursor because the newline is checked only once. point = movement::find_preceding_boundary(map, point, FindRange::MultiLine, |left, right| { - let left_kind = char_kind(&scope, left).coerce_punctuation(ignore_punctuation); - let right_kind = char_kind(&scope, right).coerce_punctuation(ignore_punctuation); + let left_kind = coerce_punctuation(char_kind(&scope, left), ignore_punctuation); + let right_kind = coerce_punctuation(char_kind(&scope, right), ignore_punctuation); (left_kind != right_kind && !right.is_whitespace()) || left == '\n' }); @@ -952,6 +952,14 @@ pub(crate) fn next_line_end( end_of_line(map, false, point) } +fn coerce_punctuation(kind: CharKind, treat_punctuation_as_word: bool) -> Self { + if treat_punctuation_as_word && kind == CharKind::Punctuation { + CharKind::Word + } else { + kind + } +} + #[cfg(test)] mod test { From 058f39c180b6dc36d86ff58586d7fe99633a1dc2 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Jan 2024 15:34:04 -0800 Subject: [PATCH 7/8] Document DiagnosticSet, SyntaxMap --- crates/language/src/buffer.rs | 2 +- crates/language/src/diagnostic_set.rs | 31 ++++++++++++++++++++++++++- crates/language/src/language.rs | 4 ++++ crates/language/src/syntax_map.rs | 13 ++++++++--- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index bfb37e91ebe80fd289a8d94279f42453ee014933..ce8b5281f01dc786a7505146a4fbc4444b4ae82d 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -145,7 +145,7 @@ pub enum IndentKind { /// An ASCII space character. #[default] Space, - /// An ASCII tab chracter. + /// An ASCII tab character. Tab, } diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index f269fce88d694c8efe2f255e302bbef7aed865ea..48e3bd78c7c24066a3dd0b4c29237f347dc835f5 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -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>, } +/// 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 { + /// The range of the buffer where the diagnostic applies. pub range: Range, + /// 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 { + /// The diagnostics. pub entries: Vec>, + /// The index into `entries` where the primary diagnostic is stored. pub primary_ix: usize, } @@ -36,7 +52,8 @@ pub struct Summary { } impl DiagnosticEntry { - // 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 DiagnosticEntry { } impl DiagnosticSet { + /// Constructs a [DiagnosticSet] from a sequence of entries, ordered by + /// their position in the buffer. pub fn from_sorted_entries(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, @@ -62,6 +81,7 @@ impl DiagnosticSet { } } + /// Constructs a [DiagnosticSet] from a sequence of entries in an arbitrary order. pub fn new(iter: I, buffer: &text::BufferSnapshot) -> Self where I: IntoIterator>, @@ -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> { 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, @@ -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 { type Summary = Summary; @@ -198,6 +226,7 @@ impl sum_tree::Item for DiagnosticEntry { } impl DiagnosticEntry { + /// Converts the [DiagnosticEntry] to a different buffer coordinate type. pub fn resolve(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry { DiagnosticEntry { range: O::from_anchor(&self.range.start, buffer) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 9a292cbd8819615f11f96a8d12bd3c017568c6c8..534d313453f08ec54b559dd798b15a2e10bab6ae 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -114,10 +114,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); diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 9174dc7df9e8e45663a75a268ede925460cc6139..8c489a30cbcadc10ee248510d112c99cddcefb13 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -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 SyntaxLayer<'a> { - pub depth: usize, + /// The language for this layer. pub language: &'a Arc, + 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 OwnedSyntaxLayer { - pub depth: usize, + /// The language for this layer. pub language: Arc, tree: tree_sitter::Tree, offset: (usize, tree_sitter::Point), @@ -1437,6 +1442,7 @@ fn insert_newlines_between_ranges( } 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) @@ -1444,15 +1450,16 @@ impl OwnedSyntaxLayer { } 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) From f11d676641d6a8260d23bcddd1a1e3bc344f54bc Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 18 Jan 2024 15:51:25 -0800 Subject: [PATCH 8/8] Remove missing docs warning for now --- crates/language/src/language.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 534d313453f08ec54b559dd798b15a2e10bab6ae..7d44250a0f21b289ff51b4ef88b589aed8bc62c3 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1,4 +1,3 @@ -#![warn(missing_docs)] //! 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: @@ -389,7 +388,7 @@ pub struct CodeLabel { pub struct LanguageConfig { /// Human-readable name of the language. pub name: Arc, - // The name of the grammar in a WASM bundle. + // The name of the grammar in a WASM bundle (experimental). pub grammar_name: Option>, /// 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,