mod bracket_ranges;pub mod row_chunk; pub use bracket_ranges::BracketMatch; use crate::{ ByteContent, DebuggerTextObject, LanguageScope, ModelineSettings, Outline, OutlineConfig, PLAIN_TEXT, RunnableTag, TextObject, TreeSitterOptions, analyze_byte_content, diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup}, language_settings::{AutoIndentMode, LanguageSettings}, outline::OutlineItem, row_chunk::RowChunks, runnable::{self, RunnableRange}, syntax_map::{ MAX_BYTES_TO_QUERY, SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatch, SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint, }, text_diff::text_diff, unified_diff_with_offsets,};pub use crate::{ Grammar, HighlightId, HighlightMap, Language, LanguageRegistry, diagnostic_set::DiagnosticSet, proto,}; use anyhow::{Context as _, Result};use clock::Lamport;pub use clock::ReplicaId;use collections::HashMap;use encoding_rs::Encoding;use fs::MTime;use futures::channel::oneshot;use futures_lite::future::yield_now;use gpui::{ App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText, Task, TextStyle,}; use lsp::LanguageServerId;use parking_lot::Mutex;use settings::WorktreeId;use smallvec::SmallVec;use std::{ any::Any, borrow::Cow, cell::Cell, cmp::{self, Ordering, Reverse}, collections::{BTreeMap, BTreeSet}, fmt::Write as _, future::Future, iter::{self, Iterator, Peekable}, mem, num::NonZeroU32, ops::{Deref, Range}, path::PathBuf, rc, sync::Arc, time::{Duration, Instant}, vec,};use sum_tree::TreeMap;use text::operation_queue::OperationQueue;use text::*;pub use text::{ Anchor, Bias, Buffer as TextBuffer, BufferId, BufferSnapshot as TextBufferSnapshot, Edit, LineIndent, OffsetRangeExt, OffsetUtf16, Patch, Point, PointUtf16, Rope, Selection, SelectionGoal, Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, ToPointUtf16, Transaction, TransactionId, Unclipped,};use theme::{ActiveTheme as _, SyntaxTheme};#[cfg(any(test, feature = "test-support"))]use util::RandomCharIter;use util::{RangeExt, debug_panic, maybe, paths::PathStyle, rel_path::RelPath}; #[cfg(any(test, feature = "test-support"))]pub use {tree_sitter_python, tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; /// 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 mutable replica, but toggled to be only readable. Read, /// The buffer is a read-only replica. ReadOnly,} impl Capability { /// Returns `true` if the capability is `ReadWrite`. pub fn editable(self) -> bool { matches!(self, Capability::ReadWrite) }} pub type BufferRow = u32; /// An in-memory representation of a source code file, including its text,/// syntax trees, git status, and diagnostics.pub struct Buffer { text: TextBuffer, branch_state: Option<BufferBranchState>, /// Filesystem state, `None` when there is no path. file: Option<Arc<dyn File>>, /// The mtime of the file when this buffer was last loaded from /// or saved to disk. saved_mtime: Option<MTime>, /// The version vector when this buffer was last loaded from /// or saved to disk. saved_version: clock::Global, preview_version: clock::Global, transaction_depth: usize, was_dirty_before_starting_transaction: Option<bool>, reload_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, autoindent_requests: Vec<Arc<AutoindentRequest>>, wait_for_autoindent_txs: Vec<oneshot::Sender<()>>, pending_autoindent: Option<Task<()>>, sync_parse_timeout: Option<Duration>, syntax_map: Mutex<SyntaxMap>, reparse: Option<Task<()>>, parse_status: (watch::Sender<ParseStatus>, watch::Receiver<ParseStatus>), non_text_state_update_count: usize, diagnostics: TreeMap<LanguageServerId, DiagnosticSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>, diagnostics_timestamp: clock::Lamport, completion_triggers: BTreeSet<String>, completion_triggers_per_language_server: HashMap<LanguageServerId, BTreeSet<String>>, completion_triggers_timestamp: clock::Lamport, deferred_ops: OperationQueue<Operation>, capability: Capability, has_conflict: bool, /// Memoize calls to has_changes_since(saved_version). /// The contents of a cell are (self.version, has_changes) at the time of a last call. has_unsaved_edits: Cell<(clock::Global, bool)>, change_bits: Vec<rc::Weak<Cell<bool>>>, modeline: Option<Arc<ModelineSettings>>, _subscriptions: Vec<gpui::Subscription>, tree_sitter_data: Arc<TreeSitterData>, encoding: &'static Encoding, has_bom: bool, reload_with_encoding_txns: HashMap<TransactionId, (&'static Encoding, bool)>,} #[derive(Debug)]pub struct TreeSitterData { chunks: RowChunks, brackets_by_chunks: Mutex<HashMap<usize, Vec<BracketMatch>>>,} const MAX_ROWS_IN_A_CHUNK: u32 = 50; impl TreeSitterData { fn clear(&mut self, snapshot: &text::BufferSnapshot) { self.chunks = RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK); self.brackets_by_chunks.get_mut().clear(); } fn new(snapshot: &text::BufferSnapshot) -> Self { Self { chunks: RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK), brackets_by_chunks: Mutex::new(HashMap::default()), } } fn version(&self) -> &clock::Global { self.chunks.version() }} #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub enum ParseStatus { Idle, Parsing,} struct BufferBranchState { base_buffer: Entity<Buffer>, merged_operations: Vec<Lamport>,} /// An immutable, cheaply cloneable representation of a fixed/// state of a buffer.pub struct BufferSnapshot { pub text: text::BufferSnapshot, pub(crate) syntax: SyntaxSnapshot, tree_sitter_data: Arc<TreeSitterData>, diagnostics: TreeMap<LanguageServerId, DiagnosticSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>, language: Option<Arc<Language>>, file: Option<Arc<dyn File>>, non_text_state_update_count: usize, pub capability: Capability, modeline: Option<Arc<ModelineSettings>>,} /// 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, Hash, 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, Hash, Default)]pub enum IndentKind { /// An ASCII space character. #[default] Space, /// An ASCII tab character. Tab,} /// The shape of a selection cursor.#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]pub enum CursorShape { /// A vertical bar #[default] Bar, /// A block that surrounds the following character Block, /// An underline that runs along the following character Underline, /// A box drawn around the following character Hollow,} impl From<settings::CursorShape> for CursorShape { fn from(shape: settings::CursorShape) -> Self { match shape { settings::CursorShape::Bar => CursorShape::Bar, settings::CursorShape::Block => CursorShape::Block, settings::CursorShape::Underline => CursorShape::Underline, settings::CursorShape::Hollow => CursorShape::Hollow, } }} #[derive(Clone, Debug)]struct SelectionSet { line_mode: bool, cursor_shape: CursorShape, selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport,} /// An operation used to synchronize this buffer with its other replicas.#[derive(Clone, Debug, PartialEq)]pub enum Operation { /// A text operation. Buffer(text::Operation), /// An update to the buffer's diagnostics. UpdateDiagnostics { /// The id of the language server that produced the new diagnostics. server_id: LanguageServerId, /// The diagnostics. diagnostics: Arc<[DiagnosticEntry<Anchor>]>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, }, /// An update to the most recent selections in this buffer. UpdateSelections { /// The selections. selections: Arc<[Selection<Anchor>]>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, /// Whether the selections are in 'line mode'. line_mode: bool, /// The [`CursorShape`] associated with these selections. cursor_shape: CursorShape, }, /// An update to the characters that should trigger autocompletion /// for this buffer. UpdateCompletionTriggers { /// The characters that trigger autocompletion. triggers: Vec<String>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, /// The language server ID. server_id: LanguageServerId, }, /// An update to the line ending type of this buffer. UpdateLineEnding { /// The line ending type. line_ending: LineEnding, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, },} #[derive(Clone, Copy, Debug, PartialEq, Eq)]pub enum BufferEditSource { User, Agent, Remote,} impl BufferEditSource { pub fn is_local(self) -> bool { !matches!(self, Self::Remote) }} /// An event that occurs in a buffer.#[derive(Clone, Debug, PartialEq)]pub enum BufferEvent { /// The buffer was changed in a way that must be /// propagated to its other replicas. Operation { operation: Operation, is_local: bool, }, /// The buffer was edited. Edited { source: BufferEditSource }, /// 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 is in need of a reload ReloadNeeded, /// The buffer's language was changed. /// The boolean indicates whether this buffer did not have a language before, but does now. LanguageChanged(bool), /// The buffer's syntax trees were updated. Reparsed, /// The buffer's diagnostics were updated. DiagnosticsUpdated, /// The buffer gained or lost editing capabilities. CapabilityChanged,} /// The file associated with a buffer.pub trait File: Send + Sync + Any { /// 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 whether the file is new, exists in storage, or has been deleted. Includes metadata /// only available in some states, such as modification time. fn disk_state(&self) -> DiskState; /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc<RelPath>; /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &App) -> PathBuf; /// Returns the path style of this file. fn path_style(&self, cx: &App) -> PathStyle; /// Returns the last component of this handle's absolute path. If this handle refers to the root /// of its worktree, then this method will return the name of the worktree itself. fn file_name<'a>(&'a self, cx: &'a App) -> &'a str; /// Returns the id of the worktree to which this file belongs. /// /// This is needed for looking up project-specific settings. fn worktree_id(&self, cx: &App) -> WorktreeId; /// Converts this file into a protobuf message. fn to_proto(&self, cx: &App) -> rpc::proto::File; /// Return whether Zed considers this to be a private file. fn is_private(&self) -> bool; fn can_open(&self) -> bool { !self.is_local() }} /// The file's storage status - whether it's stored (`Present`), and if so when it was last/// modified. In the case where the file is not stored, it can be either `New` or `Deleted`. In the/// UI these two states are distinguished. For example, the buffer tab does not display a deletion/// indicator for new files.#[derive(Copy, Clone, Debug, PartialEq)]pub enum DiskState { /// File created in Zed that has not been saved. New, /// File present on the filesystem. Present { mtime: MTime, size: u64 }, /// Deleted file that was previously present. Deleted, /// An old version of a file that was previously present /// usually from a version control system. e.g. A git blob Historic { was_deleted: bool },} impl DiskState { /// Returns the file's last known modification time on disk. pub fn mtime(self) -> Option<MTime> { match self { DiskState::New => None, DiskState::Present { mtime, .. } => Some(mtime), DiskState::Deleted => None, DiskState::Historic { .. } => None, } } /// Returns the file's size on disk in bytes. pub fn size(self) -> Option<u64> { match self { DiskState::New => None, DiskState::Present { size, .. } => Some(size), DiskState::Deleted => None, DiskState::Historic { .. } => None, } } pub fn exists(&self) -> bool { match self { DiskState::New => false, DiskState::Present { .. } => true, DiskState::Deleted => false, DiskState::Historic { .. } => false, } } /// Returns true if this state represents a deleted file. pub fn is_deleted(&self) -> bool { match self { DiskState::Deleted => true, DiskState::Historic { was_deleted } => *was_deleted, _ => false, } }} /// 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: &App) -> PathBuf; /// Loads the file contents from disk and returns them as a UTF-8 encoded string. fn load(&self, cx: &App) -> Task<Result<String>>; /// Loads the file's contents from disk. fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>;} /// 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. EachLine, /// Apply the same indentation adjustment to all of the lines /// in a given insertion. Block { /// The original indentation column of the first line of each /// insertion, if it has been copied. /// /// Knowing this makes it possible to preserve the relative indentation /// of every line in the insertion from when it was copied. /// /// If the original indent column is `a`, and the first line of insertion /// is then auto-indented to column `b`, then every other line of /// the insertion will be auto-indented to column `b - a` original_indent_columns: Vec<Option<u32>>, },} #[derive(Clone)]struct AutoindentRequest { before_edit: BufferSnapshot, entries: Vec<AutoindentRequestEntry>, is_block_mode: bool, ignore_empty_lines: bool,} #[derive(Debug, Clone)]struct AutoindentRequestEntry { /// A range of the buffer whose indentation should be adjusted. range: Range<Anchor>, /// The row of the edit start in the buffer before the edit was applied. /// This is stored here because the anchor in range is created after /// the edit, so it cannot be used with the before_edit snapshot. old_row: Option<u32>, indent_size: IndentSize, original_indent_column: Option<u32>,Lines 1–500 of 5862. Load more
mod bracket_ranges; pub mod row_chunk; pub use bracket_ranges::BracketMatch; use crate::{ ByteContent, DebuggerTextObject, LanguageScope, ModelineSettings, Outline, OutlineConfig, PLAIN_TEXT, RunnableTag, TextObject, TreeSitterOptions, analyze_byte_content, diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup}, language_settings::{AutoIndentMode, LanguageSettings}, outline::OutlineItem, row_chunk::RowChunks, runnable::{self, RunnableRange}, syntax_map::{ MAX_BYTES_TO_QUERY, SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxMapMatch, SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint, }, text_diff::text_diff, unified_diff_with_offsets, }; pub use crate::{ Grammar, HighlightId, HighlightMap, Language, LanguageRegistry, diagnostic_set::DiagnosticSet, proto, }; use anyhow::{Context as _, Result}; use clock::Lamport; pub use clock::ReplicaId; use collections::HashMap; use encoding_rs::Encoding; use fs::MTime; use futures::channel::oneshot; use futures_lite::future::yield_now; use gpui::{ App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText, Task, TextStyle, }; use lsp::LanguageServerId; use parking_lot::Mutex; use settings::WorktreeId; use smallvec::SmallVec; use std::{ any::Any, borrow::Cow, cell::Cell, cmp::{self, Ordering, Reverse}, collections::{BTreeMap, BTreeSet}, fmt::Write as _, future::Future, iter::{self, Iterator, Peekable}, mem, num::NonZeroU32, ops::{Deref, Range}, path::PathBuf, rc, sync::Arc, time::{Duration, Instant}, vec, }; use sum_tree::TreeMap; use text::operation_queue::OperationQueue; use text::*; pub use text::{ Anchor, Bias, Buffer as TextBuffer, BufferId, BufferSnapshot as TextBufferSnapshot, Edit, LineIndent, OffsetRangeExt, OffsetUtf16, Patch, Point, PointUtf16, Rope, Selection, SelectionGoal, Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint, ToPointUtf16, Transaction, TransactionId, Unclipped, }; use theme::{ActiveTheme as _, SyntaxTheme}; #[cfg(any(test, feature = "test-support"))] use util::RandomCharIter; use util::{RangeExt, debug_panic, maybe, paths::PathStyle, rel_path::RelPath}; #[cfg(any(test, feature = "test-support"))] pub use {tree_sitter_python, tree_sitter_rust, tree_sitter_typescript}; pub use lsp::DiagnosticSeverity; /// 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 mutable replica, but toggled to be only readable. Read, /// The buffer is a read-only replica. ReadOnly, } impl Capability { /// Returns `true` if the capability is `ReadWrite`. pub fn editable(self) -> bool { matches!(self, Capability::ReadWrite) } } pub type BufferRow = u32; /// An in-memory representation of a source code file, including its text, /// syntax trees, git status, and diagnostics. pub struct Buffer { text: TextBuffer, branch_state: Option<BufferBranchState>, /// Filesystem state, `None` when there is no path. file: Option<Arc<dyn File>>, /// The mtime of the file when this buffer was last loaded from /// or saved to disk. saved_mtime: Option<MTime>, /// The version vector when this buffer was last loaded from /// or saved to disk. saved_version: clock::Global, preview_version: clock::Global, transaction_depth: usize, was_dirty_before_starting_transaction: Option<bool>, reload_task: Option<Task<Result<()>>>, language: Option<Arc<Language>>, autoindent_requests: Vec<Arc<AutoindentRequest>>, wait_for_autoindent_txs: Vec<oneshot::Sender<()>>, pending_autoindent: Option<Task<()>>, sync_parse_timeout: Option<Duration>, syntax_map: Mutex<SyntaxMap>, reparse: Option<Task<()>>, parse_status: (watch::Sender<ParseStatus>, watch::Receiver<ParseStatus>), non_text_state_update_count: usize, diagnostics: TreeMap<LanguageServerId, DiagnosticSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>, diagnostics_timestamp: clock::Lamport, completion_triggers: BTreeSet<String>, completion_triggers_per_language_server: HashMap<LanguageServerId, BTreeSet<String>>, completion_triggers_timestamp: clock::Lamport, deferred_ops: OperationQueue<Operation>, capability: Capability, has_conflict: bool, /// Memoize calls to has_changes_since(saved_version). /// The contents of a cell are (self.version, has_changes) at the time of a last call. has_unsaved_edits: Cell<(clock::Global, bool)>, change_bits: Vec<rc::Weak<Cell<bool>>>, modeline: Option<Arc<ModelineSettings>>, _subscriptions: Vec<gpui::Subscription>, tree_sitter_data: Arc<TreeSitterData>, encoding: &'static Encoding, has_bom: bool, reload_with_encoding_txns: HashMap<TransactionId, (&'static Encoding, bool)>, } #[derive(Debug)] pub struct TreeSitterData { chunks: RowChunks, brackets_by_chunks: Mutex<HashMap<usize, Vec<BracketMatch>>>, } const MAX_ROWS_IN_A_CHUNK: u32 = 50; impl TreeSitterData { fn clear(&mut self, snapshot: &text::BufferSnapshot) { self.chunks = RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK); self.brackets_by_chunks.get_mut().clear(); } fn new(snapshot: &text::BufferSnapshot) -> Self { Self { chunks: RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK), brackets_by_chunks: Mutex::new(HashMap::default()), } } fn version(&self) -> &clock::Global { self.chunks.version() } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ParseStatus { Idle, Parsing, } struct BufferBranchState { base_buffer: Entity<Buffer>, merged_operations: Vec<Lamport>, } /// An immutable, cheaply cloneable representation of a fixed /// state of a buffer. pub struct BufferSnapshot { pub text: text::BufferSnapshot, pub(crate) syntax: SyntaxSnapshot, tree_sitter_data: Arc<TreeSitterData>, diagnostics: TreeMap<LanguageServerId, DiagnosticSet>, remote_selections: TreeMap<ReplicaId, SelectionSet>, language: Option<Arc<Language>>, file: Option<Arc<dyn File>>, non_text_state_update_count: usize, pub capability: Capability, modeline: Option<Arc<ModelineSettings>>, } /// 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, Hash, 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, Hash, Default)] pub enum IndentKind { /// An ASCII space character. #[default] Space, /// An ASCII tab character. Tab, } /// The shape of a selection cursor. #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] pub enum CursorShape { /// A vertical bar #[default] Bar, /// A block that surrounds the following character Block, /// An underline that runs along the following character Underline, /// A box drawn around the following character Hollow, } impl From<settings::CursorShape> for CursorShape { fn from(shape: settings::CursorShape) -> Self { match shape { settings::CursorShape::Bar => CursorShape::Bar, settings::CursorShape::Block => CursorShape::Block, settings::CursorShape::Underline => CursorShape::Underline, settings::CursorShape::Hollow => CursorShape::Hollow, } } } #[derive(Clone, Debug)] struct SelectionSet { line_mode: bool, cursor_shape: CursorShape, selections: Arc<[Selection<Anchor>]>, lamport_timestamp: clock::Lamport, } /// An operation used to synchronize this buffer with its other replicas. #[derive(Clone, Debug, PartialEq)] pub enum Operation { /// A text operation. Buffer(text::Operation), /// An update to the buffer's diagnostics. UpdateDiagnostics { /// The id of the language server that produced the new diagnostics. server_id: LanguageServerId, /// The diagnostics. diagnostics: Arc<[DiagnosticEntry<Anchor>]>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, }, /// An update to the most recent selections in this buffer. UpdateSelections { /// The selections. selections: Arc<[Selection<Anchor>]>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, /// Whether the selections are in 'line mode'. line_mode: bool, /// The [`CursorShape`] associated with these selections. cursor_shape: CursorShape, }, /// An update to the characters that should trigger autocompletion /// for this buffer. UpdateCompletionTriggers { /// The characters that trigger autocompletion. triggers: Vec<String>, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, /// The language server ID. server_id: LanguageServerId, }, /// An update to the line ending type of this buffer. UpdateLineEnding { /// The line ending type. line_ending: LineEnding, /// The buffer's lamport timestamp. lamport_timestamp: clock::Lamport, }, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum BufferEditSource { User, Agent, Remote, } impl BufferEditSource { pub fn is_local(self) -> bool { !matches!(self, Self::Remote) } } /// An event that occurs in a buffer. #[derive(Clone, Debug, PartialEq)] pub enum BufferEvent { /// The buffer was changed in a way that must be /// propagated to its other replicas. Operation { operation: Operation, is_local: bool, }, /// The buffer was edited. Edited { source: BufferEditSource }, /// 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 is in need of a reload ReloadNeeded, /// The buffer's language was changed. /// The boolean indicates whether this buffer did not have a language before, but does now. LanguageChanged(bool), /// The buffer's syntax trees were updated. Reparsed, /// The buffer's diagnostics were updated. DiagnosticsUpdated, /// The buffer gained or lost editing capabilities. CapabilityChanged, } /// The file associated with a buffer. pub trait File: Send + Sync + Any { /// 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 whether the file is new, exists in storage, or has been deleted. Includes metadata /// only available in some states, such as modification time. fn disk_state(&self) -> DiskState; /// Returns the path of this file relative to the worktree's root directory. fn path(&self) -> &Arc<RelPath>; /// Returns the path of this file relative to the worktree's parent directory (this means it /// includes the name of the worktree's root folder). fn full_path(&self, cx: &App) -> PathBuf; /// Returns the path style of this file. fn path_style(&self, cx: &App) -> PathStyle; /// Returns the last component of this handle's absolute path. If this handle refers to the root /// of its worktree, then this method will return the name of the worktree itself. fn file_name<'a>(&'a self, cx: &'a App) -> &'a str; /// Returns the id of the worktree to which this file belongs. /// /// This is needed for looking up project-specific settings. fn worktree_id(&self, cx: &App) -> WorktreeId; /// Converts this file into a protobuf message. fn to_proto(&self, cx: &App) -> rpc::proto::File; /// Return whether Zed considers this to be a private file. fn is_private(&self) -> bool; fn can_open(&self) -> bool { !self.is_local() } } /// The file's storage status - whether it's stored (`Present`), and if so when it was last /// modified. In the case where the file is not stored, it can be either `New` or `Deleted`. In the /// UI these two states are distinguished. For example, the buffer tab does not display a deletion /// indicator for new files. #[derive(Copy, Clone, Debug, PartialEq)] pub enum DiskState { /// File created in Zed that has not been saved. New, /// File present on the filesystem. Present { mtime: MTime, size: u64 }, /// Deleted file that was previously present. Deleted, /// An old version of a file that was previously present /// usually from a version control system. e.g. A git blob Historic { was_deleted: bool }, } impl DiskState { /// Returns the file's last known modification time on disk. pub fn mtime(self) -> Option<MTime> { match self { DiskState::New => None, DiskState::Present { mtime, .. } => Some(mtime), DiskState::Deleted => None, DiskState::Historic { .. } => None, } } /// Returns the file's size on disk in bytes. pub fn size(self) -> Option<u64> { match self { DiskState::New => None, DiskState::Present { size, .. } => Some(size), DiskState::Deleted => None, DiskState::Historic { .. } => None, } } pub fn exists(&self) -> bool { match self { DiskState::New => false, DiskState::Present { .. } => true, DiskState::Deleted => false, DiskState::Historic { .. } => false, } } /// Returns true if this state represents a deleted file. pub fn is_deleted(&self) -> bool { match self { DiskState::Deleted => true, DiskState::Historic { was_deleted } => *was_deleted, _ => false, } } } /// 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: &App) -> PathBuf; /// Loads the file contents from disk and returns them as a UTF-8 encoded string. fn load(&self, cx: &App) -> Task<Result<String>>; /// Loads the file's contents from disk. fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>; } /// 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. EachLine, /// Apply the same indentation adjustment to all of the lines /// in a given insertion. Block { /// The original indentation column of the first line of each /// insertion, if it has been copied. /// /// Knowing this makes it possible to preserve the relative indentation /// of every line in the insertion from when it was copied. /// /// If the original indent column is `a`, and the first line of insertion /// is then auto-indented to column `b`, then every other line of /// the insertion will be auto-indented to column `b - a` original_indent_columns: Vec<Option<u32>>, }, } #[derive(Clone)] struct AutoindentRequest { before_edit: BufferSnapshot, entries: Vec<AutoindentRequestEntry>, is_block_mode: bool, ignore_empty_lines: bool, } #[derive(Debug, Clone)] struct AutoindentRequestEntry { /// A range of the buffer whose indentation should be adjusted. range: Range<Anchor>, /// The row of the edit start in the buffer before the edit was applied. /// This is stored here because the anchor in range is created after /// the edit, so it cannot be used with the before_edit snapshot. old_row: Option<u32>, indent_size: IndentSize, original_indent_column: Option<u32>,