Skip to repository content6306 lines · 233.1 KB · rust
tenant.openagents/omega
No repository description is available.
OpenAgents Git authority 2026-07-28T03:53:25.446Z Public web read
NIP-34 coordinate
30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omegaMaintainersHidden in public view
References2 branches · 1 tag
Read-only clone
git clone https://openagents.com/git/tenant.openagents/omega.gitBrowse files
buffer.rs
1pub mod row_chunk;
2
3use crate::{
4 ByteContent, DebuggerTextObject, LanguageScope, ModelineSettings, Outline, OutlineConfig,
5 PLAIN_TEXT, RunnableTag, TextObject, TreeSitterOptions, analyze_byte_content,
6 diagnostic_set::{DiagnosticEntry, DiagnosticEntryRef, DiagnosticGroup},
7 language_settings::{AutoIndentMode, LanguageSettings},
8 outline::OutlineItem,
9 row_chunk::RowChunks,
10 runnable::{self, RunnableRange},
11 syntax_map::{
12 MAX_BYTES_TO_QUERY, SyntaxLayer, SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures,
13 SyntaxMapMatch, SyntaxMapMatches, SyntaxSnapshot, ToTreeSitterPoint,
14 },
15 text_diff::text_diff,
16 unified_diff_with_offsets,
17};
18pub use crate::{
19 Grammar, HighlightId, HighlightMap, Language, LanguageRegistry, diagnostic_set::DiagnosticSet,
20 proto,
21};
22
23use anyhow::{Context as _, Result};
24use clock::Lamport;
25pub use clock::ReplicaId;
26use collections::{HashMap, HashSet};
27use encoding_rs::Encoding;
28use fs::MTime;
29use futures::channel::oneshot;
30use futures_lite::future::yield_now;
31use gpui::{
32 App, AppContext as _, Context, Entity, EventEmitter, HighlightStyle, SharedString, StyledText,
33 Task, TextStyle,
34};
35
36use lsp::LanguageServerId;
37use parking_lot::Mutex;
38use settings::WorktreeId;
39use smallvec::SmallVec;
40use std::{
41 any::Any,
42 borrow::Cow,
43 cell::Cell,
44 cmp::{self, Ordering, Reverse},
45 collections::{BTreeMap, BTreeSet},
46 fmt::Write as _,
47 future::Future,
48 iter::{self, Iterator, Peekable},
49 mem,
50 num::NonZeroU32,
51 ops::{Deref, Range},
52 path::PathBuf,
53 rc,
54 sync::Arc,
55 time::{Duration, Instant},
56 vec,
57};
58use sum_tree::TreeMap;
59use text::operation_queue::OperationQueue;
60use text::*;
61pub use text::{
62 Anchor, Bias, Buffer as TextBuffer, BufferId, BufferSnapshot as TextBufferSnapshot, Edit,
63 LineIndent, OffsetRangeExt, OffsetUtf16, Patch, Point, PointUtf16, Rope, Selection,
64 SelectionGoal, Subscription, TextDimension, TextSummary, ToOffset, ToOffsetUtf16, ToPoint,
65 ToPointUtf16, Transaction, TransactionId, Unclipped,
66};
67use theme::{ActiveTheme as _, SyntaxTheme};
68#[cfg(any(test, feature = "test-support"))]
69use util::RandomCharIter;
70use util::{RangeExt, debug_panic, maybe, paths::PathStyle, rel_path::RelPath};
71
72#[cfg(any(test, feature = "test-support"))]
73pub use {tree_sitter_python, tree_sitter_rust, tree_sitter_typescript};
74
75pub use lsp::DiagnosticSeverity;
76
77/// Indicate whether a [`Buffer`] has permissions to edit.
78#[derive(PartialEq, Clone, Copy, Debug)]
79pub enum Capability {
80 /// The buffer is a mutable replica.
81 ReadWrite,
82 /// The buffer is a mutable replica, but toggled to be only readable.
83 Read,
84 /// The buffer is a read-only replica.
85 ReadOnly,
86}
87
88impl Capability {
89 /// Returns `true` if the capability is `ReadWrite`.
90 pub fn editable(self) -> bool {
91 matches!(self, Capability::ReadWrite)
92 }
93}
94
95pub type BufferRow = u32;
96
97/// An in-memory representation of a source code file, including its text,
98/// syntax trees, git status, and diagnostics.
99pub struct Buffer {
100 text: TextBuffer,
101 branch_state: Option<BufferBranchState>,
102 /// Filesystem state, `None` when there is no path.
103 file: Option<Arc<dyn File>>,
104 /// The mtime of the file when this buffer was last loaded from
105 /// or saved to disk.
106 saved_mtime: Option<MTime>,
107 /// The version vector when this buffer was last loaded from
108 /// or saved to disk.
109 saved_version: clock::Global,
110 preview_version: clock::Global,
111 transaction_depth: usize,
112 was_dirty_before_starting_transaction: Option<bool>,
113 reload_task: Option<Task<Result<()>>>,
114 language: Option<Arc<Language>>,
115 autoindent_requests: Vec<Arc<AutoindentRequest>>,
116 wait_for_autoindent_txs: Vec<oneshot::Sender<()>>,
117 pending_autoindent: Option<Task<()>>,
118 sync_parse_timeout: Option<Duration>,
119 syntax_map: Mutex<SyntaxMap>,
120 reparse: Option<Task<()>>,
121 parse_status: (watch::Sender<ParseStatus>, watch::Receiver<ParseStatus>),
122 non_text_state_update_count: usize,
123 diagnostics: TreeMap<LanguageServerId, DiagnosticSet>,
124 remote_selections: TreeMap<ReplicaId, SelectionSet>,
125 diagnostics_timestamp: clock::Lamport,
126 completion_triggers: BTreeSet<String>,
127 completion_triggers_per_language_server: HashMap<LanguageServerId, BTreeSet<String>>,
128 completion_triggers_timestamp: clock::Lamport,
129 deferred_ops: OperationQueue<Operation>,
130 capability: Capability,
131 has_conflict: bool,
132 /// Memoize calls to has_changes_since(saved_version).
133 /// The contents of a cell are (self.version, has_changes) at the time of a last call.
134 has_unsaved_edits: Cell<(clock::Global, bool)>,
135 change_bits: Vec<rc::Weak<Cell<bool>>>,
136 modeline: Option<Arc<ModelineSettings>>,
137 _subscriptions: Vec<gpui::Subscription>,
138 tree_sitter_data: Arc<TreeSitterData>,
139 encoding: &'static Encoding,
140 has_bom: bool,
141 reload_with_encoding_txns: HashMap<TransactionId, (&'static Encoding, bool)>,
142}
143
144#[derive(Debug)]
145pub struct TreeSitterData {
146 chunks: RowChunks,
147 brackets_by_chunks: Mutex<HashMap<usize, Vec<BracketMatch<usize>>>>,
148}
149
150const MAX_ROWS_IN_A_CHUNK: u32 = 50;
151
152impl TreeSitterData {
153 fn clear(&mut self, snapshot: &text::BufferSnapshot) {
154 self.chunks = RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK);
155 self.brackets_by_chunks.get_mut().clear();
156 }
157
158 fn new(snapshot: &text::BufferSnapshot) -> Self {
159 Self {
160 chunks: RowChunks::new(snapshot, MAX_ROWS_IN_A_CHUNK),
161 brackets_by_chunks: Mutex::new(HashMap::default()),
162 }
163 }
164
165 fn version(&self) -> &clock::Global {
166 self.chunks.version()
167 }
168}
169
170#[derive(Copy, Clone, Debug, PartialEq, Eq)]
171pub enum ParseStatus {
172 Idle,
173 Parsing,
174}
175
176struct BufferBranchState {
177 base_buffer: Entity<Buffer>,
178 merged_operations: Vec<Lamport>,
179}
180
181/// An immutable, cheaply cloneable representation of a fixed
182/// state of a buffer.
183pub struct BufferSnapshot {
184 pub text: text::BufferSnapshot,
185 pub(crate) syntax: SyntaxSnapshot,
186 tree_sitter_data: Arc<TreeSitterData>,
187 diagnostics: TreeMap<LanguageServerId, DiagnosticSet>,
188 remote_selections: TreeMap<ReplicaId, SelectionSet>,
189 language: Option<Arc<Language>>,
190 file: Option<Arc<dyn File>>,
191 non_text_state_update_count: usize,
192 pub capability: Capability,
193 modeline: Option<Arc<ModelineSettings>>,
194}
195
196/// The kind and amount of indentation in a particular line. For now,
197/// assumes that indentation is all the same character.
198#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
199pub struct IndentSize {
200 /// The number of bytes that comprise the indentation.
201 pub len: u32,
202 /// The kind of whitespace used for indentation.
203 pub kind: IndentKind,
204}
205
206/// A whitespace character that's used for indentation.
207#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
208pub enum IndentKind {
209 /// An ASCII space character.
210 #[default]
211 Space,
212 /// An ASCII tab character.
213 Tab,
214}
215
216/// The shape of a selection cursor.
217#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
218pub enum CursorShape {
219 /// A vertical bar
220 #[default]
221 Bar,
222 /// A block that surrounds the following character
223 Block,
224 /// An underline that runs along the following character
225 Underline,
226 /// A box drawn around the following character
227 Hollow,
228}
229
230impl From<settings::CursorShape> for CursorShape {
231 fn from(shape: settings::CursorShape) -> Self {
232 match shape {
233 settings::CursorShape::Bar => CursorShape::Bar,
234 settings::CursorShape::Block => CursorShape::Block,
235 settings::CursorShape::Underline => CursorShape::Underline,
236 settings::CursorShape::Hollow => CursorShape::Hollow,
237 }
238 }
239}
240
241#[derive(Clone, Debug)]
242struct SelectionSet {
243 line_mode: bool,
244 cursor_shape: CursorShape,
245 selections: Arc<[Selection<Anchor>]>,
246 lamport_timestamp: clock::Lamport,
247}
248
249/// An operation used to synchronize this buffer with its other replicas.
250#[derive(Clone, Debug, PartialEq)]
251pub enum Operation {
252 /// A text operation.
253 Buffer(text::Operation),
254
255 /// An update to the buffer's diagnostics.
256 UpdateDiagnostics {
257 /// The id of the language server that produced the new diagnostics.
258 server_id: LanguageServerId,
259 /// The diagnostics.
260 diagnostics: Arc<[DiagnosticEntry<Anchor>]>,
261 /// The buffer's lamport timestamp.
262 lamport_timestamp: clock::Lamport,
263 },
264
265 /// An update to the most recent selections in this buffer.
266 UpdateSelections {
267 /// The selections.
268 selections: Arc<[Selection<Anchor>]>,
269 /// The buffer's lamport timestamp.
270 lamport_timestamp: clock::Lamport,
271 /// Whether the selections are in 'line mode'.
272 line_mode: bool,
273 /// The [`CursorShape`] associated with these selections.
274 cursor_shape: CursorShape,
275 },
276
277 /// An update to the characters that should trigger autocompletion
278 /// for this buffer.
279 UpdateCompletionTriggers {
280 /// The characters that trigger autocompletion.
281 triggers: Vec<String>,
282 /// The buffer's lamport timestamp.
283 lamport_timestamp: clock::Lamport,
284 /// The language server ID.
285 server_id: LanguageServerId,
286 },
287
288 /// An update to the line ending type of this buffer.
289 UpdateLineEnding {
290 /// The line ending type.
291 line_ending: LineEnding,
292 /// The buffer's lamport timestamp.
293 lamport_timestamp: clock::Lamport,
294 },
295}
296
297#[derive(Clone, Copy, Debug, PartialEq, Eq)]
298pub enum BufferEditSource {
299 User,
300 Agent,
301 Remote,
302}
303
304impl BufferEditSource {
305 pub fn is_local(self) -> bool {
306 !matches!(self, Self::Remote)
307 }
308}
309
310/// An event that occurs in a buffer.
311#[derive(Clone, Debug, PartialEq)]
312pub enum BufferEvent {
313 /// The buffer was changed in a way that must be
314 /// propagated to its other replicas.
315 Operation {
316 operation: Operation,
317 is_local: bool,
318 },
319 /// The buffer was edited.
320 Edited { source: BufferEditSource },
321 /// The buffer's `dirty` bit changed.
322 DirtyChanged,
323 /// The buffer was saved.
324 Saved,
325 /// The buffer's file was changed on disk.
326 FileHandleChanged,
327 /// The buffer was reloaded.
328 Reloaded,
329 /// The buffer is in need of a reload
330 ReloadNeeded,
331 /// The buffer's language was changed.
332 /// The boolean indicates whether this buffer did not have a language before, but does now.
333 LanguageChanged(bool),
334 /// The buffer's syntax trees were updated.
335 Reparsed,
336 /// The buffer's diagnostics were updated.
337 DiagnosticsUpdated,
338 /// The buffer gained or lost editing capabilities.
339 CapabilityChanged,
340}
341
342/// The file associated with a buffer.
343pub trait File: Send + Sync + Any {
344 /// Returns the [`LocalFile`] associated with this file, if the
345 /// file is local.
346 fn as_local(&self) -> Option<&dyn LocalFile>;
347
348 /// Returns whether this file is local.
349 fn is_local(&self) -> bool {
350 self.as_local().is_some()
351 }
352
353 /// Returns whether the file is new, exists in storage, or has been deleted. Includes metadata
354 /// only available in some states, such as modification time.
355 fn disk_state(&self) -> DiskState;
356
357 /// Returns the path of this file relative to the worktree's root directory.
358 fn path(&self) -> &Arc<RelPath>;
359
360 /// Returns the path of this file relative to the worktree's parent directory (this means it
361 /// includes the name of the worktree's root folder).
362 fn full_path(&self, cx: &App) -> PathBuf;
363
364 /// Returns the path style of this file.
365 fn path_style(&self, cx: &App) -> PathStyle;
366
367 /// Returns the last component of this handle's absolute path. If this handle refers to the root
368 /// of its worktree, then this method will return the name of the worktree itself.
369 fn file_name<'a>(&'a self, cx: &'a App) -> &'a str;
370
371 /// Returns the id of the worktree to which this file belongs.
372 ///
373 /// This is needed for looking up project-specific settings.
374 fn worktree_id(&self, cx: &App) -> WorktreeId;
375
376 /// Converts this file into a protobuf message.
377 fn to_proto(&self, cx: &App) -> rpc::proto::File;
378
379 /// Return whether Zed considers this to be a private file.
380 fn is_private(&self) -> bool;
381
382 fn can_open(&self) -> bool {
383 !self.is_local()
384 }
385}
386
387/// The file's storage status - whether it's stored (`Present`), and if so when it was last
388/// modified. In the case where the file is not stored, it can be either `New` or `Deleted`. In the
389/// UI these two states are distinguished. For example, the buffer tab does not display a deletion
390/// indicator for new files.
391#[derive(Copy, Clone, Debug, PartialEq)]
392pub enum DiskState {
393 /// File created in Zed that has not been saved.
394 New,
395 /// File present on the filesystem.
396 Present { mtime: MTime, size: u64 },
397 /// Deleted file that was previously present.
398 Deleted,
399 /// An old version of a file that was previously present
400 /// usually from a version control system. e.g. A git blob
401 Historic { was_deleted: bool },
402}
403
404impl DiskState {
405 /// Returns the file's last known modification time on disk.
406 pub fn mtime(self) -> Option<MTime> {
407 match self {
408 DiskState::New => None,
409 DiskState::Present { mtime, .. } => Some(mtime),
410 DiskState::Deleted => None,
411 DiskState::Historic { .. } => None,
412 }
413 }
414
415 /// Returns the file's size on disk in bytes.
416 pub fn size(self) -> Option<u64> {
417 match self {
418 DiskState::New => None,
419 DiskState::Present { size, .. } => Some(size),
420 DiskState::Deleted => None,
421 DiskState::Historic { .. } => None,
422 }
423 }
424
425 pub fn exists(&self) -> bool {
426 match self {
427 DiskState::New => false,
428 DiskState::Present { .. } => true,
429 DiskState::Deleted => false,
430 DiskState::Historic { .. } => false,
431 }
432 }
433
434 /// Returns true if this state represents a deleted file.
435 pub fn is_deleted(&self) -> bool {
436 match self {
437 DiskState::Deleted => true,
438 DiskState::Historic { was_deleted } => *was_deleted,
439 _ => false,
440 }
441 }
442}
443
444/// The file associated with a buffer, in the case where the file is on the local disk.
445pub trait LocalFile: File {
446 /// Returns the absolute path of this file
447 fn abs_path(&self, cx: &App) -> PathBuf;
448
449 /// Loads the file contents from disk and returns them as a UTF-8 encoded string.
450 fn load(&self, cx: &App) -> Task<Result<String>>;
451
452 /// Loads the file's contents from disk.
453 fn load_bytes(&self, cx: &App) -> Task<Result<Vec<u8>>>;
454}
455
456/// The auto-indent behavior associated with an editing operation.
457/// For some editing operations, each affected line of text has its
458/// indentation recomputed. For other operations, the entire block
459/// of edited text is adjusted uniformly.
460#[derive(Clone, Debug)]
461pub enum AutoindentMode {
462 /// Indent each line of inserted text.
463 EachLine,
464 /// Apply the same indentation adjustment to all of the lines
465 /// in a given insertion.
466 Block {
467 /// The original indentation column of the first line of each
468 /// insertion, if it has been copied.
469 ///
470 /// Knowing this makes it possible to preserve the relative indentation
471 /// of every line in the insertion from when it was copied.
472 ///
473 /// If the original indent column is `a`, and the first line of insertion
474 /// is then auto-indented to column `b`, then every other line of
475 /// the insertion will be auto-indented to column `b - a`
476 original_indent_columns: Vec<Option<u32>>,
477 },
478}
479
480#[derive(Clone)]
481struct AutoindentRequest {
482 before_edit: BufferSnapshot,
483 entries: Vec<AutoindentRequestEntry>,
484 is_block_mode: bool,
485 ignore_empty_lines: bool,
486}
487
488#[derive(Debug, Clone)]
489struct AutoindentRequestEntry {
490 /// A range of the buffer whose indentation should be adjusted.
491 range: Range<Anchor>,
492 /// The row of the edit start in the buffer before the edit was applied.
493 /// This is stored here because the anchor in range is created after
494 /// the edit, so it cannot be used with the before_edit snapshot.
495 old_row: Option<u32>,
496 indent_size: IndentSize,
497 original_indent_column: Option<u32>,
498}
499
500#[derive(Debug)]
501struct IndentSuggestion {
502 basis_row: u32,
503 delta: Ordering,
504 within_error: bool,
505}
506
507struct BufferChunkHighlights<'a> {
508 captures: SyntaxMapCaptures<'a>,
509 next_capture: Option<SyntaxMapCapture<'a>>,
510 stack: Vec<(usize, HighlightId)>,
511 highlight_maps: Vec<HighlightMap>,
512}
513
514/// An iterator that yields chunks of a buffer's text, along with their
515/// syntax highlights and diagnostic status.
516pub struct BufferChunks<'a> {
517 buffer_snapshot: Option<&'a BufferSnapshot>,
518 range: Range<usize>,
519 chunks: text::Chunks<'a>,
520 diagnostic_endpoints: Option<Peekable<vec::IntoIter<DiagnosticEndpoint>>>,
521 error_depth: usize,
522 warning_depth: usize,
523 information_depth: usize,
524 hint_depth: usize,
525 unnecessary_depth: usize,
526 underline: bool,
527 highlights: Option<BufferChunkHighlights<'a>>,
528}
529
530/// A chunk of a buffer's text, along with its syntax highlight and
531/// diagnostic status.
532#[derive(Clone, Debug, Default)]
533pub struct Chunk<'a> {
534 /// The text of the chunk.
535 pub text: &'a str,
536 /// The syntax highlighting style of the chunk.
537 pub syntax_highlight_id: Option<HighlightId>,
538 /// The highlight style that has been applied to this chunk in
539 /// the editor.
540 pub highlight_style: Option<HighlightStyle>,
541 /// The severity of diagnostic associated with this chunk, if any.
542 pub diagnostic_severity: Option<DiagnosticSeverity>,
543 /// A bitset of which characters are tabs in this string.
544 pub tabs: u128,
545 /// Bitmap of character indices in this chunk
546 pub chars: u128,
547 /// Bitmap of newline indices in this chunk
548 pub newlines: u128,
549 /// Whether this chunk of text is marked as unnecessary.
550 pub is_unnecessary: bool,
551 /// Whether this chunk of text was originally a tab character.
552 pub is_tab: bool,
553 /// Whether this chunk of text was originally an inlay.
554 pub is_inlay: bool,
555 /// Whether to underline the corresponding text range in the editor.
556 pub underline: bool,
557}
558
559/// A set of edits to a given version of a buffer, computed asynchronously.
560#[derive(Debug, Clone)]
561pub struct Diff {
562 pub base_version: clock::Global,
563 pub line_ending: LineEnding,
564 pub edits: Vec<(Range<usize>, Arc<str>)>,
565}
566
567#[derive(Debug, Clone, Copy)]
568pub(crate) struct DiagnosticEndpoint {
569 offset: usize,
570 is_start: bool,
571 underline: bool,
572 severity: DiagnosticSeverity,
573 is_unnecessary: bool,
574}
575
576/// A class of characters, used for characterizing a run of text.
577#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
578pub enum CharKind {
579 /// Whitespace.
580 Whitespace,
581 /// Punctuation.
582 Punctuation,
583 /// Word.
584 Word,
585}
586
587/// Context for character classification within a specific scope.
588#[derive(Copy, Clone, Eq, PartialEq, Debug)]
589pub enum CharScopeContext {
590 /// Character classification for completion queries.
591 ///
592 /// This context treats certain characters as word constituents that would
593 /// normally be considered punctuation, such as '-' in Tailwind classes
594 /// ("bg-yellow-100") or '.' in import paths ("foo.ts").
595 Completion,
596 /// Character classification for linked edits.
597 ///
598 /// This context handles characters that should be treated as part of
599 /// identifiers during linked editing operations, such as '.' in JSX
600 /// component names like `<Animated.View>`.
601 LinkedEdit,
602}
603
604/// A runnable is a set of data about a region that could be resolved into a task
605pub struct Runnable {
606 pub tags: SmallVec<[RunnableTag; 1]>,
607 pub language: Arc<Language>,
608 pub buffer: BufferId,
609}
610
611#[derive(Default, Clone, Debug)]
612pub struct HighlightedText {
613 pub text: SharedString,
614 pub highlights: Vec<(Range<usize>, HighlightStyle)>,
615}
616
617#[derive(Default, Debug)]
618pub struct HighlightedTextBuilder {
619 text: String,
620 highlights: Vec<(Range<usize>, HighlightStyle)>,
621}
622
623impl HighlightedText {
624 pub fn from_buffer_range<T: ToOffset>(
625 range: Range<T>,
626 snapshot: &text::BufferSnapshot,
627 syntax_snapshot: &SyntaxSnapshot,
628 override_style: Option<HighlightStyle>,
629 syntax_theme: &SyntaxTheme,
630 ) -> Self {
631 let mut highlighted_text = HighlightedTextBuilder::default();
632 highlighted_text.add_text_from_buffer_range(
633 range,
634 snapshot,
635 syntax_snapshot,
636 override_style,
637 syntax_theme,
638 );
639 highlighted_text.build()
640 }
641
642 pub fn to_styled_text(&self, default_style: &TextStyle) -> StyledText {
643 gpui::StyledText::new(self.text.clone())
644 .with_default_highlights(default_style, self.highlights.iter().cloned())
645 }
646
647 /// Returns the first line without leading whitespace unless highlighted
648 /// and a boolean indicating if there are more lines after
649 pub fn first_line_preview(self) -> (Self, bool) {
650 let newline_ix = self.text.find('\n').unwrap_or(self.text.len());
651 let first_line = &self.text[..newline_ix];
652
653 // Trim leading whitespace, unless an edit starts prior to it.
654 let mut preview_start_ix = first_line.len() - first_line.trim_start().len();
655 if let Some((first_highlight_range, _)) = self.highlights.first() {
656 preview_start_ix = preview_start_ix.min(first_highlight_range.start);
657 }
658
659 let preview_text = &first_line[preview_start_ix..];
660 let preview_highlights = self
661 .highlights
662 .into_iter()
663 .skip_while(|(range, _)| range.end <= preview_start_ix)
664 .take_while(|(range, _)| range.start < newline_ix)
665 .filter_map(|(mut range, highlight)| {
666 range.start = range.start.saturating_sub(preview_start_ix);
667 range.end = range.end.min(newline_ix).saturating_sub(preview_start_ix);
668 if range.is_empty() {
669 None
670 } else {
671 Some((range, highlight))
672 }
673 });
674
675 let preview = Self {
676 text: SharedString::new(preview_text),
677 highlights: preview_highlights.collect(),
678 };
679
680 (preview, self.text.len() > newline_ix)
681 }
682}
683
684impl HighlightedTextBuilder {
685 pub fn build(self) -> HighlightedText {
686 HighlightedText {
687 text: self.text.into(),
688 highlights: self.highlights,
689 }
690 }
691
692 /// Append a displayable value to the text, highlighting its range with
693 /// `style`.
694 pub fn push_styled(&mut self, value: impl std::fmt::Display, style: HighlightStyle) {
695 let start = self.text.len();
696 let _ = write!(&mut self.text, "{value}");
697 let end = self.text.len();
698 if end > start {
699 self.highlights.push((start..end, style));
700 }
701 }
702
703 /// Append a displayable value to the text without any highlighting.
704 pub fn push_plain(&mut self, value: impl std::fmt::Display) {
705 let _ = write!(&mut self.text, "{value}");
706 }
707
708 pub fn add_text_from_buffer_range<T: ToOffset>(
709 &mut self,
710 range: Range<T>,
711 snapshot: &text::BufferSnapshot,
712 syntax_snapshot: &SyntaxSnapshot,
713 override_style: Option<HighlightStyle>,
714 syntax_theme: &SyntaxTheme,
715 ) {
716 let range = range.to_offset(snapshot);
717 for chunk in Self::highlighted_chunks(range, snapshot, syntax_snapshot) {
718 let start = self.text.len();
719 self.text.push_str(chunk.text);
720 let end = self.text.len();
721
722 if let Some(highlight_style) = chunk
723 .syntax_highlight_id
724 .and_then(|id| syntax_theme.get(id).cloned())
725 {
726 let highlight_style = override_style.map_or(highlight_style, |override_style| {
727 highlight_style.highlight(override_style)
728 });
729 self.highlights.push((start..end, highlight_style));
730 } else if let Some(override_style) = override_style {
731 self.highlights.push((start..end, override_style));
732 }
733 }
734 }
735
736 fn highlighted_chunks<'a>(
737 range: Range<usize>,
738 snapshot: &'a text::BufferSnapshot,
739 syntax_snapshot: &'a SyntaxSnapshot,
740 ) -> BufferChunks<'a> {
741 let captures = syntax_snapshot.captures(range.clone(), snapshot, |grammar| {
742 grammar
743 .highlights_config
744 .as_ref()
745 .map(|config| &config.query)
746 });
747
748 let highlight_maps = captures
749 .grammars()
750 .iter()
751 .map(|grammar| grammar.highlight_map())
752 .collect();
753
754 BufferChunks::new(
755 snapshot.as_rope(),
756 range,
757 Some((captures, highlight_maps)),
758 false,
759 None,
760 )
761 }
762}
763
764#[derive(Clone)]
765pub struct EditPreview {
766 old_snapshot: text::BufferSnapshot,
767 applied_edits_snapshot: text::BufferSnapshot,
768 syntax_snapshot: SyntaxSnapshot,
769}
770
771impl EditPreview {
772 pub fn unchanged(snapshot: &BufferSnapshot) -> Self {
773 Self {
774 old_snapshot: snapshot.text.clone(),
775 applied_edits_snapshot: snapshot.text.clone(),
776 syntax_snapshot: snapshot.syntax.clone(),
777 }
778 }
779
780 pub fn as_unified_diff(
781 &self,
782 file: Option<&Arc<dyn File>>,
783 edits: &[(Range<Anchor>, impl AsRef<str>)],
784 ) -> Option<String> {
785 let (first, _) = edits.first()?;
786 let (last, _) = edits.last()?;
787
788 let start = first.start.to_point(&self.old_snapshot);
789 let old_end = last.end.to_point(&self.old_snapshot);
790 let new_end = last
791 .end
792 .bias_right(&self.old_snapshot)
793 .to_point(&self.applied_edits_snapshot);
794
795 let start = Point::new(start.row.saturating_sub(3), 0);
796 let old_end = Point::new(old_end.row + 4, 0).min(self.old_snapshot.max_point());
797 let new_end = Point::new(new_end.row + 4, 0).min(self.applied_edits_snapshot.max_point());
798
799 let diff_body = unified_diff_with_offsets(
800 &self
801 .old_snapshot
802 .text_for_range(start..old_end)
803 .collect::<String>(),
804 &self
805 .applied_edits_snapshot
806 .text_for_range(start..new_end)
807 .collect::<String>(),
808 start.row,
809 start.row,
810 );
811
812 let path = file.map(|f| f.path().as_unix_str());
813 let header = match path {
814 Some(p) => format!("--- a/{}\n+++ b/{}\n", p, p),
815 None => String::new(),
816 };
817
818 Some(format!("{}{}", header, diff_body))
819 }
820
821 pub fn highlight_edits(
822 &self,
823 current_snapshot: &BufferSnapshot,
824 edits: &[(Range<Anchor>, impl AsRef<str>)],
825 include_deletions: bool,
826 cx: &App,
827 ) -> HighlightedText {
828 let Some(visible_range_in_preview_snapshot) = self.compute_visible_range(edits) else {
829 return HighlightedText::default();
830 };
831
832 let mut highlighted_text = HighlightedTextBuilder::default();
833
834 let visible_range_in_preview_snapshot =
835 visible_range_in_preview_snapshot.to_offset(&self.applied_edits_snapshot);
836 let mut offset_in_preview_snapshot = visible_range_in_preview_snapshot.start;
837
838 let insertion_highlight_style = HighlightStyle {
839 background_color: Some(cx.theme().status().created_background),
840 ..Default::default()
841 };
842 let deletion_highlight_style = HighlightStyle {
843 background_color: Some(cx.theme().status().deleted_background),
844 ..Default::default()
845 };
846 let syntax_theme = cx.theme().syntax();
847
848 for (range, edit_text) in edits {
849 let edit_new_end_in_preview_snapshot = range
850 .end
851 .bias_right(&self.old_snapshot)
852 .to_offset(&self.applied_edits_snapshot);
853 let edit_start_in_preview_snapshot =
854 edit_new_end_in_preview_snapshot - edit_text.as_ref().len();
855
856 let unchanged_range_in_preview_snapshot =
857 offset_in_preview_snapshot..edit_start_in_preview_snapshot;
858 if !unchanged_range_in_preview_snapshot.is_empty() {
859 highlighted_text.add_text_from_buffer_range(
860 unchanged_range_in_preview_snapshot,
861 &self.applied_edits_snapshot,
862 &self.syntax_snapshot,
863 None,
864 syntax_theme,
865 );
866 }
867
868 let range_in_current_snapshot = range.to_offset(current_snapshot);
869 if include_deletions && !range_in_current_snapshot.is_empty() {
870 highlighted_text.add_text_from_buffer_range(
871 range_in_current_snapshot,
872 ¤t_snapshot.text,
873 ¤t_snapshot.syntax,
874 Some(deletion_highlight_style),
875 syntax_theme,
876 );
877 }
878
879 if !edit_text.as_ref().is_empty() {
880 highlighted_text.add_text_from_buffer_range(
881 edit_start_in_preview_snapshot..edit_new_end_in_preview_snapshot,
882 &self.applied_edits_snapshot,
883 &self.syntax_snapshot,
884 Some(insertion_highlight_style),
885 syntax_theme,
886 );
887 }
888
889 offset_in_preview_snapshot = edit_new_end_in_preview_snapshot;
890 }
891
892 highlighted_text.add_text_from_buffer_range(
893 offset_in_preview_snapshot..visible_range_in_preview_snapshot.end,
894 &self.applied_edits_snapshot,
895 &self.syntax_snapshot,
896 None,
897 syntax_theme,
898 );
899
900 highlighted_text.build()
901 }
902
903 pub fn build_result_buffer(&self, cx: &mut App) -> Entity<Buffer> {
904 cx.new(|cx| {
905 let mut buffer = Buffer::local_normalized(
906 self.applied_edits_snapshot.as_rope().clone(),
907 self.applied_edits_snapshot.line_ending(),
908 cx,
909 );
910 buffer.set_language_async(self.syntax_snapshot.root_language(), cx);
911 buffer
912 })
913 }
914
915 pub fn result_text_snapshot(&self) -> &text::BufferSnapshot {
916 &self.applied_edits_snapshot
917 }
918
919 pub fn result_syntax_snapshot(&self) -> &SyntaxSnapshot {
920 &self.syntax_snapshot
921 }
922
923 pub fn anchor_to_offset_in_result(&self, anchor: Anchor) -> usize {
924 anchor
925 .bias_right(&self.old_snapshot)
926 .to_offset(&self.applied_edits_snapshot)
927 }
928
929 pub fn compute_visible_range<T>(&self, edits: &[(Range<Anchor>, T)]) -> Option<Range<Point>> {
930 let (first, _) = edits.first()?;
931 let (last, _) = edits.last()?;
932
933 let start = first
934 .start
935 .bias_left(&self.old_snapshot)
936 .to_point(&self.applied_edits_snapshot);
937 let end = last
938 .end
939 .bias_right(&self.old_snapshot)
940 .to_point(&self.applied_edits_snapshot);
941
942 // Ensure that the first line of the first edit and the last line of the last edit are always fully visible
943 let range = Point::new(start.row, 0)
944 ..Point::new(end.row, self.applied_edits_snapshot.line_len(end.row));
945
946 Some(range)
947 }
948}
949
950#[derive(Clone, Debug, PartialEq, Eq)]
951pub struct BracketMatch<T> {
952 pub open_range: Range<T>,
953 pub close_range: Range<T>,
954 pub newline_only: bool,
955 pub syntax_layer_depth: usize,
956 pub color_index: Option<usize>,
957}
958
959impl<T> BracketMatch<T> {
960 pub fn bracket_ranges(self) -> (Range<T>, Range<T>) {
961 (self.open_range, self.close_range)
962 }
963}
964
965/// A single bracket pair candidate produced by a brackets query, before
966/// bogus tree-sitter matches are repaired and color indices are assigned.
967#[derive(Clone, Debug)]
968struct BracketMatchCandidate {
969 bracket_match: BracketMatch<usize>,
970 pattern: BracketPatternKey,
971 rainbow_exclude: bool,
972}
973
974impl BracketMatchCandidate {
975 fn open_delimiter(&self) -> BracketDelimiter {
976 BracketDelimiter {
977 start: self.bracket_match.open_range.start,
978 end: self.bracket_match.open_range.end,
979 pattern: self.pattern,
980 }
981 }
982
983 fn close_delimiter(&self) -> BracketDelimiter {
984 BracketDelimiter {
985 start: self.bracket_match.close_range.start,
986 end: self.bracket_match.close_range.end,
987 pattern: self.pattern,
988 }
989 }
990}
991
992/// Identifies a brackets query pattern, unique across all grammars of a syntax map.
993#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
994struct BracketPatternKey {
995 grammar_index: usize,
996 pattern_index: usize,
997}
998
999/// One delimiter (open or close) of a bracket pair candidate.
1000/// Ordered by buffer position first, so sorting yields document order.
1001#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1002struct BracketDelimiter {
1003 start: usize,
1004 end: usize,
1005 pattern: BracketPatternKey,
1006}
1007
1008impl Buffer {
1009 /// Create a new buffer with the given base text.
1010 pub fn local<T: Into<String>>(base_text: T, cx: &Context<Self>) -> Self {
1011 Self::build(
1012 TextBuffer::new(
1013 ReplicaId::LOCAL,
1014 cx.entity_id().as_non_zero_u64().into(),
1015 base_text.into(),
1016 ),
1017 None,
1018 Capability::ReadWrite,
1019 )
1020 }
1021
1022 /// Create a new buffer with the given base text that has proper line endings and other normalization applied.
1023 pub fn local_normalized(
1024 base_text_normalized: Rope,
1025 line_ending: LineEnding,
1026 cx: &Context<Self>,
1027 ) -> Self {
1028 Self::build(
1029 TextBuffer::new_normalized(
1030 ReplicaId::LOCAL,
1031 cx.entity_id().as_non_zero_u64().into(),
1032 line_ending,
1033 base_text_normalized,
1034 ),
1035 None,
1036 Capability::ReadWrite,
1037 )
1038 }
1039
1040 /// Create a new buffer that is a replica of a remote buffer.
1041 pub fn remote(
1042 remote_id: BufferId,
1043 replica_id: ReplicaId,
1044 capability: Capability,
1045 base_text: impl Into<String>,
1046 ) -> Self {
1047 Self::build(
1048 TextBuffer::new(replica_id, remote_id, base_text.into()),
1049 None,
1050 capability,
1051 )
1052 }
1053
1054 /// Create a new buffer that is a replica of a remote buffer, populating its
1055 /// state from the given protobuf message.
1056 pub fn from_proto(
1057 replica_id: ReplicaId,
1058 capability: Capability,
1059 message: proto::BufferState,
1060 file: Option<Arc<dyn File>>,
1061 ) -> Result<Self> {
1062 let buffer_id = BufferId::new(message.id).context("Could not deserialize buffer_id")?;
1063 let buffer = TextBuffer::new(replica_id, buffer_id, message.base_text);
1064 let mut this = Self::build(buffer, file, capability);
1065 this.text.set_line_ending(proto::deserialize_line_ending(
1066 rpc::proto::LineEnding::from_i32(message.line_ending).context("missing line_ending")?,
1067 ));
1068 this.saved_version = proto::deserialize_version(&message.saved_version);
1069 this.saved_mtime = message.saved_mtime.map(|time| time.into());
1070 Ok(this)
1071 }
1072
1073 /// Serialize the buffer's state to a protobuf message.
1074 pub fn to_proto(&self, cx: &App) -> proto::BufferState {
1075 proto::BufferState {
1076 id: self.remote_id().into(),
1077 file: self.file.as_ref().map(|f| f.to_proto(cx)),
1078 base_text: self.base_text().to_string(),
1079 line_ending: proto::serialize_line_ending(self.line_ending()) as i32,
1080 saved_version: proto::serialize_version(&self.saved_version),
1081 saved_mtime: self.saved_mtime.map(|time| time.into()),
1082 }
1083 }
1084
1085 /// Serialize as protobufs all of the changes to the buffer since the given version.
1086 pub fn serialize_ops(
1087 &self,
1088 since: Option<clock::Global>,
1089 cx: &App,
1090 ) -> Task<Vec<proto::Operation>> {
1091 let mut operations = Vec::new();
1092 operations.extend(self.deferred_ops.iter().map(proto::serialize_operation));
1093
1094 operations.extend(self.remote_selections.iter().map(|(_, set)| {
1095 proto::serialize_operation(&Operation::UpdateSelections {
1096 selections: set.selections.clone(),
1097 lamport_timestamp: set.lamport_timestamp,
1098 line_mode: set.line_mode,
1099 cursor_shape: set.cursor_shape,
1100 })
1101 }));
1102
1103 for (server_id, diagnostics) in self.diagnostics.iter() {
1104 operations.push(proto::serialize_operation(&Operation::UpdateDiagnostics {
1105 lamport_timestamp: self.diagnostics_timestamp,
1106 server_id: *server_id,
1107 diagnostics: diagnostics.iter().cloned().collect(),
1108 }));
1109 }
1110
1111 for (server_id, completions) in &self.completion_triggers_per_language_server {
1112 operations.push(proto::serialize_operation(
1113 &Operation::UpdateCompletionTriggers {
1114 triggers: completions.iter().cloned().collect(),
1115 lamport_timestamp: self.completion_triggers_timestamp,
1116 server_id: *server_id,
1117 },
1118 ));
1119 }
1120
1121 let text_operations = self.text.operations().clone();
1122 cx.background_spawn(async move {
1123 let since = since.unwrap_or_default();
1124 operations.extend(
1125 text_operations
1126 .iter()
1127 .filter(|(_, op)| !since.observed(op.timestamp()))
1128 .map(|(_, op)| proto::serialize_operation(&Operation::Buffer(op.clone()))),
1129 );
1130 operations.sort_unstable_by_key(proto::lamport_timestamp_for_operation);
1131 operations
1132 })
1133 }
1134
1135 /// Assign a language to the buffer, returning the buffer.
1136 pub fn with_language_async(mut self, language: Arc<Language>, cx: &mut Context<Self>) -> Self {
1137 self.set_language_async(Some(language), cx);
1138 self
1139 }
1140
1141 /// Assign a language to the buffer, blocking for up to 1ms to reparse the buffer, returning the buffer.
1142 #[ztracing::instrument(skip_all, fields(lang = language.config.name.0.as_str()))]
1143 pub fn with_language(mut self, language: Arc<Language>, cx: &mut Context<Self>) -> Self {
1144 self.set_language(Some(language), cx);
1145 self
1146 }
1147
1148 /// Returns the [`Capability`] of this buffer.
1149 pub fn capability(&self) -> Capability {
1150 self.capability
1151 }
1152
1153 /// Whether this buffer can only be read.
1154 pub fn read_only(&self) -> bool {
1155 !self.capability.editable()
1156 }
1157
1158 /// Builds a [`Buffer`] with the given underlying [`TextBuffer`], diff base, [`File`] and [`Capability`].
1159 pub fn build(buffer: TextBuffer, file: Option<Arc<dyn File>>, capability: Capability) -> Self {
1160 let saved_mtime = file.as_ref().and_then(|file| file.disk_state().mtime());
1161 let snapshot = buffer.snapshot();
1162 let syntax_map = Mutex::new(SyntaxMap::new(&snapshot));
1163 let tree_sitter_data = TreeSitterData::new(snapshot);
1164 Self {
1165 saved_mtime,
1166 tree_sitter_data: Arc::new(tree_sitter_data),
1167 saved_version: buffer.version(),
1168 preview_version: buffer.version(),
1169 reload_task: None,
1170 transaction_depth: 0,
1171 was_dirty_before_starting_transaction: None,
1172 has_unsaved_edits: Cell::new((buffer.version(), false)),
1173 text: buffer,
1174 branch_state: None,
1175 file,
1176 capability,
1177 syntax_map,
1178 reparse: None,
1179 non_text_state_update_count: 0,
1180 sync_parse_timeout: if cfg!(any(test, feature = "test-support")) {
1181 Some(Duration::from_millis(10))
1182 } else {
1183 Some(Duration::from_millis(1))
1184 },
1185 parse_status: watch::channel(ParseStatus::Idle),
1186 autoindent_requests: Default::default(),
1187 wait_for_autoindent_txs: Default::default(),
1188 pending_autoindent: Default::default(),
1189 language: None,
1190 remote_selections: Default::default(),
1191 diagnostics: Default::default(),
1192 diagnostics_timestamp: Lamport::MIN,
1193 completion_triggers: Default::default(),
1194 completion_triggers_per_language_server: Default::default(),
1195 completion_triggers_timestamp: Lamport::MIN,
1196 deferred_ops: OperationQueue::new(),
1197 has_conflict: false,
1198 change_bits: Default::default(),
1199 modeline: None,
1200 _subscriptions: Vec::new(),
1201 encoding: encoding_rs::UTF_8,
1202 has_bom: false,
1203 reload_with_encoding_txns: HashMap::default(),
1204 }
1205 }
1206
1207 #[ztracing::instrument(skip_all)]
1208 pub fn build_snapshot(
1209 text: Rope,
1210 language: Option<Arc<Language>>,
1211 language_registry: Option<Arc<LanguageRegistry>>,
1212 modeline: Option<Arc<ModelineSettings>>,
1213 cx: &mut App,
1214 ) -> impl Future<Output = BufferSnapshot> + use<> {
1215 let entity_id = cx.reserve_entity::<Self>().entity_id();
1216 let buffer_id = entity_id.as_non_zero_u64().into();
1217 async move {
1218 let text =
1219 TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text);
1220 let text = text.into_snapshot();
1221 let mut syntax = SyntaxMap::new(&text).snapshot();
1222 if let Some(language) = language.clone() {
1223 let language_registry = language_registry.clone();
1224 syntax.reparse(&text, language_registry, language);
1225 }
1226 let tree_sitter_data = TreeSitterData::new(&text);
1227 BufferSnapshot {
1228 text,
1229 syntax,
1230 file: None,
1231 diagnostics: Default::default(),
1232 remote_selections: Default::default(),
1233 tree_sitter_data: Arc::new(tree_sitter_data),
1234 language,
1235 non_text_state_update_count: 0,
1236 capability: Capability::ReadOnly,
1237 modeline,
1238 }
1239 }
1240 }
1241
1242 pub fn build_empty_snapshot(cx: &mut App) -> BufferSnapshot {
1243 let entity_id = cx.reserve_entity::<Self>().entity_id();
1244 let buffer_id = entity_id.as_non_zero_u64().into();
1245 let text = TextBuffer::new_normalized(
1246 ReplicaId::LOCAL,
1247 buffer_id,
1248 Default::default(),
1249 Rope::new(),
1250 );
1251 let text = text.into_snapshot();
1252 let syntax = SyntaxMap::new(&text).snapshot();
1253 let tree_sitter_data = TreeSitterData::new(&text);
1254 BufferSnapshot {
1255 text,
1256 syntax,
1257 tree_sitter_data: Arc::new(tree_sitter_data),
1258 file: None,
1259 diagnostics: Default::default(),
1260 remote_selections: Default::default(),
1261 language: None,
1262 non_text_state_update_count: 0,
1263 capability: Capability::ReadOnly,
1264 modeline: None,
1265 }
1266 }
1267
1268 #[cfg(any(test, feature = "test-support"))]
1269 pub fn build_snapshot_sync(
1270 text: Rope,
1271 language: Option<Arc<Language>>,
1272 language_registry: Option<Arc<LanguageRegistry>>,
1273 cx: &mut App,
1274 ) -> BufferSnapshot {
1275 let entity_id = cx.reserve_entity::<Self>().entity_id();
1276 let buffer_id = entity_id.as_non_zero_u64().into();
1277 let text =
1278 TextBuffer::new_normalized(ReplicaId::LOCAL, buffer_id, Default::default(), text)
1279 .into_snapshot();
1280 let mut syntax = SyntaxMap::new(&text).snapshot();
1281 if let Some(language) = language.clone() {
1282 syntax.reparse(&text, language_registry, language);
1283 }
1284 let tree_sitter_data = TreeSitterData::new(&text);
1285 BufferSnapshot {
1286 text,
1287 syntax,
1288 tree_sitter_data: Arc::new(tree_sitter_data),
1289 file: None,
1290 diagnostics: Default::default(),
1291 remote_selections: Default::default(),
1292 language,
1293 non_text_state_update_count: 0,
1294 capability: Capability::ReadOnly,
1295 modeline: None,
1296 }
1297 }
1298
1299 /// Retrieve a snapshot of the buffer's current state. This is computationally
1300 /// cheap, and allows reading from the buffer on a background thread.
1301 pub fn snapshot(&self) -> BufferSnapshot {
1302 let text = self.text.snapshot();
1303
1304 let syntax = {
1305 let mut syntax_map = self.syntax_map.lock();
1306 syntax_map.interpolate(text);
1307 syntax_map.snapshot()
1308 };
1309
1310 let tree_sitter_data = if self.text.version() != *self.tree_sitter_data.version() {
1311 Arc::new(TreeSitterData::new(text))
1312 } else {
1313 self.tree_sitter_data.clone()
1314 };
1315
1316 BufferSnapshot {
1317 text: text.clone(),
1318 syntax,
1319 tree_sitter_data,
1320 file: self.file.clone(),
1321 remote_selections: self.remote_selections.clone(),
1322 diagnostics: self.diagnostics.clone(),
1323 language: self.language.clone(),
1324 non_text_state_update_count: self.non_text_state_update_count,
1325 capability: self.capability,
1326 modeline: self.modeline.clone(),
1327 }
1328 }
1329
1330 pub fn branch(&mut self, cx: &mut Context<Self>) -> Entity<Self> {
1331 let this = cx.entity();
1332 cx.new(|cx| {
1333 let mut branch = Self {
1334 branch_state: Some(BufferBranchState {
1335 base_buffer: this.clone(),
1336 merged_operations: Default::default(),
1337 }),
1338 language: self.language.clone(),
1339 has_conflict: self.has_conflict,
1340 has_unsaved_edits: Cell::new(self.has_unsaved_edits.get_mut().clone()),
1341 _subscriptions: vec![cx.subscribe(&this, Self::on_base_buffer_event)],
1342 ..Self::build(self.text.branch(), self.file.clone(), self.capability())
1343 };
1344 if let Some(language_registry) = self.language_registry() {
1345 branch.set_language_registry(language_registry);
1346 }
1347
1348 // Reparse the branch buffer so that we get syntax highlighting immediately.
1349 branch.reparse(cx, true);
1350
1351 branch
1352 })
1353 }
1354
1355 #[ztracing::instrument(skip_all)]
1356 pub fn preview_edits(
1357 &self,
1358 edits: Arc<[(Range<Anchor>, Arc<str>)]>,
1359 cx: &App,
1360 ) -> Task<EditPreview> {
1361 let registry = self.language_registry();
1362 let language = self.language().cloned();
1363 let old_snapshot = self.text.snapshot().clone();
1364 let mut branch_buffer = self.text.branch();
1365 let mut syntax_snapshot = self.syntax_map.lock().snapshot();
1366 cx.background_spawn(async move {
1367 if !edits.is_empty() {
1368 if let Some(language) = language.clone() {
1369 syntax_snapshot.reparse(&old_snapshot, registry.clone(), language);
1370 }
1371
1372 branch_buffer.edit(edits.iter().cloned());
1373 let snapshot = branch_buffer.snapshot();
1374 syntax_snapshot.interpolate(&snapshot);
1375
1376 if let Some(language) = language {
1377 syntax_snapshot.reparse(&snapshot, registry, language);
1378 }
1379 }
1380 EditPreview {
1381 old_snapshot,
1382 applied_edits_snapshot: branch_buffer.into_snapshot(),
1383 syntax_snapshot,
1384 }
1385 })
1386 }
1387
1388 /// Applies all of the changes in this buffer that intersect any of the
1389 /// given `ranges` to its base buffer.
1390 ///
1391 /// If `ranges` is empty, then all changes will be applied. This buffer must
1392 /// be a branch buffer to call this method.
1393 pub fn merge_into_base(&mut self, ranges: Vec<Range<usize>>, cx: &mut Context<Self>) {
1394 let Some(base_buffer) = self.base_buffer() else {
1395 debug_panic!("not a branch buffer");
1396 return;
1397 };
1398
1399 let mut ranges = if ranges.is_empty() {
1400 &[0..usize::MAX]
1401 } else {
1402 ranges.as_slice()
1403 }
1404 .iter()
1405 .peekable();
1406
1407 let mut edits = Vec::new();
1408 for edit in self.edits_since::<usize>(&base_buffer.read(cx).version()) {
1409 let mut is_included = false;
1410 while let Some(range) = ranges.peek() {
1411 if range.end < edit.new.start {
1412 ranges.next().unwrap();
1413 } else {
1414 if range.start <= edit.new.end {
1415 is_included = true;
1416 }
1417 break;
1418 }
1419 }
1420
1421 if is_included {
1422 edits.push((
1423 edit.old.clone(),
1424 self.text_for_range(edit.new.clone()).collect::<String>(),
1425 ));
1426 }
1427 }
1428
1429 let operation = base_buffer.update(cx, |base_buffer, cx| {
1430 // cx.emit(BufferEvent::DiffBaseChanged);
1431 base_buffer.edit(edits, None, cx)
1432 });
1433
1434 if let Some(operation) = operation
1435 && let Some(BufferBranchState {
1436 merged_operations, ..
1437 }) = &mut self.branch_state
1438 {
1439 merged_operations.push(operation);
1440 }
1441 }
1442
1443 fn on_base_buffer_event(
1444 &mut self,
1445 _: Entity<Buffer>,
1446 event: &BufferEvent,
1447 cx: &mut Context<Self>,
1448 ) {
1449 let BufferEvent::Operation { operation, .. } = event else {
1450 return;
1451 };
1452 let Some(BufferBranchState {
1453 merged_operations, ..
1454 }) = &mut self.branch_state
1455 else {
1456 return;
1457 };
1458
1459 let mut operation_to_undo = None;
1460 if let Operation::Buffer(text::Operation::Edit(operation)) = &operation
1461 && let Ok(ix) = merged_operations.binary_search(&operation.timestamp)
1462 {
1463 merged_operations.remove(ix);
1464 operation_to_undo = Some(operation.timestamp);
1465 }
1466
1467 self.apply_ops([operation.clone()], cx);
1468
1469 if let Some(timestamp) = operation_to_undo {
1470 let counts = [(timestamp, u32::MAX)].into_iter().collect();
1471 self.undo_operations(counts, cx);
1472 }
1473 }
1474
1475 pub fn as_text_snapshot(&self) -> &text::BufferSnapshot {
1476 &self.text
1477 }
1478
1479 /// Retrieve a snapshot of the buffer's raw text, without any
1480 /// language-related state like the syntax tree or diagnostics.
1481 #[ztracing::instrument(skip_all)]
1482 pub fn text_snapshot(&self) -> text::BufferSnapshot {
1483 // todo lw
1484 self.text.snapshot().clone()
1485 }
1486
1487 /// The file associated with the buffer, if any.
1488 pub fn file(&self) -> Option<&Arc<dyn File>> {
1489 self.file.as_ref()
1490 }
1491
1492 /// The version of the buffer that was last saved or reloaded from disk.
1493 pub fn saved_version(&self) -> &clock::Global {
1494 &self.saved_version
1495 }
1496
1497 /// The mtime of the buffer's file when the buffer was last saved or reloaded from disk.
1498 pub fn saved_mtime(&self) -> Option<MTime> {
1499 self.saved_mtime
1500 }
1501
1502 /// Returns the character encoding of the buffer's file.
1503 pub fn encoding(&self) -> &'static Encoding {
1504 self.encoding
1505 }
1506
1507 /// Sets the character encoding of the buffer.
1508 pub fn set_encoding(&mut self, encoding: &'static Encoding) {
1509 self.encoding = encoding;
1510 }
1511
1512 /// Returns whether the buffer has a Byte Order Mark.
1513 pub fn has_bom(&self) -> bool {
1514 self.has_bom
1515 }
1516
1517 /// Sets whether the buffer has a Byte Order Mark.
1518 pub fn set_has_bom(&mut self, has_bom: bool) {
1519 self.has_bom = has_bom;
1520 }
1521
1522 /// Assign a language to the buffer.
1523 pub fn set_language_async(&mut self, language: Option<Arc<Language>>, cx: &mut Context<Self>) {
1524 self.set_language_(language, cfg!(any(test, feature = "test-support")), cx);
1525 }
1526
1527 /// Assign a language to the buffer, blocking for up to 1ms to reparse the buffer.
1528 pub fn set_language(&mut self, language: Option<Arc<Language>>, cx: &mut Context<Self>) {
1529 self.set_language_(language, true, cx);
1530 }
1531
1532 #[ztracing::instrument(skip_all)]
1533 fn set_language_(
1534 &mut self,
1535 language: Option<Arc<Language>>,
1536 may_block: bool,
1537 cx: &mut Context<Self>,
1538 ) {
1539 if language == self.language {
1540 return;
1541 }
1542 self.non_text_state_update_count += 1;
1543 self.syntax_map.lock().clear(&self.text);
1544 let old_language = std::mem::replace(&mut self.language, language);
1545 self.was_changed();
1546 self.reparse(cx, may_block);
1547 let has_fresh_language =
1548 self.language.is_some() && old_language.is_none_or(|old| old == *PLAIN_TEXT);
1549 cx.emit(BufferEvent::LanguageChanged(has_fresh_language));
1550 }
1551
1552 /// Assign a language registry to the buffer. This allows the buffer to retrieve
1553 /// other languages if parts of the buffer are written in different languages.
1554 pub fn set_language_registry(&self, language_registry: Arc<LanguageRegistry>) {
1555 self.syntax_map
1556 .lock()
1557 .set_language_registry(language_registry);
1558 }
1559
1560 pub fn language_registry(&self) -> Option<Arc<LanguageRegistry>> {
1561 self.syntax_map.lock().language_registry()
1562 }
1563
1564 /// Assign the line ending type to the buffer.
1565 pub fn set_line_ending(&mut self, line_ending: LineEnding, cx: &mut Context<Self>) {
1566 self.text.set_line_ending(line_ending);
1567
1568 let lamport_timestamp = self.text.lamport_clock.tick();
1569 self.send_operation(
1570 Operation::UpdateLineEnding {
1571 line_ending,
1572 lamport_timestamp,
1573 },
1574 true,
1575 cx,
1576 );
1577 }
1578
1579 /// Assign the buffer [`ModelineSettings`].
1580 pub fn set_modeline(&mut self, modeline: Option<ModelineSettings>) -> bool {
1581 if modeline.as_ref() != self.modeline.as_deref() {
1582 self.modeline = modeline.map(Arc::new);
1583 true
1584 } else {
1585 false
1586 }
1587 }
1588
1589 /// Returns the [`ModelineSettings`].
1590 pub fn modeline(&self) -> Option<&Arc<ModelineSettings>> {
1591 self.modeline.as_ref()
1592 }
1593
1594 /// Assign the buffer a new [`Capability`].
1595 pub fn set_capability(&mut self, capability: Capability, cx: &mut Context<Self>) {
1596 if self.capability != capability {
1597 self.capability = capability;
1598 cx.emit(BufferEvent::CapabilityChanged)
1599 }
1600 }
1601
1602 /// This method is called to signal that the buffer has been saved.
1603 pub fn did_save(
1604 &mut self,
1605 version: clock::Global,
1606 mtime: Option<MTime>,
1607 cx: &mut Context<Self>,
1608 ) {
1609 self.saved_version = version.clone();
1610 self.has_unsaved_edits.set((version, false));
1611 self.has_conflict = false;
1612 self.saved_mtime = mtime;
1613 self.was_changed();
1614 cx.emit(BufferEvent::Saved);
1615 cx.notify();
1616 }
1617
1618 /// Reloads the contents of the buffer from disk.
1619 pub fn reload(&mut self, cx: &Context<Self>) -> oneshot::Receiver<Option<Transaction>> {
1620 self.reload_impl(None, cx)
1621 }
1622
1623 /// Reloads the contents of the buffer from disk using the specified encoding.
1624 ///
1625 /// This bypasses automatic encoding detection heuristics (like BOM checks) for non-Unicode encodings,
1626 /// allowing users to force a specific interpretation of the bytes.
1627 pub fn reload_with_encoding(
1628 &mut self,
1629 encoding: &'static Encoding,
1630 cx: &Context<Self>,
1631 ) -> oneshot::Receiver<Option<Transaction>> {
1632 self.reload_impl(Some(encoding), cx)
1633 }
1634
1635 fn reload_impl(
1636 &mut self,
1637 force_encoding: Option<&'static Encoding>,
1638 cx: &Context<Self>,
1639 ) -> oneshot::Receiver<Option<Transaction>> {
1640 let (tx, rx) = futures::channel::oneshot::channel();
1641 let prev_version = self.text.version();
1642
1643 self.reload_task = Some(cx.spawn(async move |this, cx| {
1644 let Some((new_mtime, load_bytes_task, current_encoding)) =
1645 this.update(cx, |this, cx| {
1646 let file = this.file.as_ref()?.as_local()?;
1647 Some((
1648 file.disk_state().mtime(),
1649 file.load_bytes(cx),
1650 this.encoding,
1651 ))
1652 })?
1653 else {
1654 return Ok(());
1655 };
1656
1657 let target_encoding = force_encoding.unwrap_or(current_encoding);
1658
1659 let bytes = load_bytes_task.await?;
1660
1661 anyhow::ensure!(
1662 analyze_byte_content(&bytes) != ByteContent::Binary,
1663 "Binary files are not supported"
1664 );
1665
1666 let is_unicode = target_encoding == encoding_rs::UTF_8
1667 || target_encoding == encoding_rs::UTF_16LE
1668 || target_encoding == encoding_rs::UTF_16BE;
1669
1670 let (new_text, has_bom, encoding_used) = if force_encoding.is_some() && !is_unicode {
1671 let (cow, _had_errors) = target_encoding.decode_without_bom_handling(&bytes);
1672 (cow.into_owned(), false, target_encoding)
1673 } else {
1674 let (cow, used_enc, _had_errors) = target_encoding.decode(&bytes);
1675
1676 let actual_has_bom = if used_enc == encoding_rs::UTF_8 {
1677 bytes.starts_with(&[0xEF, 0xBB, 0xBF])
1678 } else if used_enc == encoding_rs::UTF_16LE {
1679 bytes.starts_with(&[0xFF, 0xFE])
1680 } else if used_enc == encoding_rs::UTF_16BE {
1681 bytes.starts_with(&[0xFE, 0xFF])
1682 } else {
1683 false
1684 };
1685 (cow.into_owned(), actual_has_bom, used_enc)
1686 };
1687
1688 let diff = this.update(cx, |this, cx| this.diff(new_text, cx))?.await;
1689 this.update(cx, |this, cx| {
1690 if this.version() == diff.base_version {
1691 this.finalize_last_transaction();
1692 let old_encoding = this.encoding;
1693 let old_has_bom = this.has_bom;
1694 this.apply_diff(diff, cx);
1695 this.encoding = encoding_used;
1696 this.has_bom = has_bom;
1697 let transaction = this.finalize_last_transaction().cloned();
1698 if let Some(ref txn) = transaction {
1699 if old_encoding != encoding_used || old_has_bom != has_bom {
1700 this.reload_with_encoding_txns
1701 .insert(txn.id, (old_encoding, old_has_bom));
1702 }
1703 }
1704 tx.send(transaction).ok();
1705 this.has_conflict = false;
1706 this.did_reload(this.version(), this.line_ending(), new_mtime, cx);
1707 } else {
1708 if !diff.edits.is_empty()
1709 || this
1710 .edits_since::<usize>(&diff.base_version)
1711 .next()
1712 .is_some()
1713 {
1714 this.has_conflict = true;
1715 }
1716
1717 this.did_reload(prev_version, this.line_ending(), this.saved_mtime, cx);
1718 }
1719
1720 this.reload_task.take();
1721 })
1722 }));
1723 rx
1724 }
1725
1726 /// This method is called to signal that the buffer has been reloaded.
1727 pub fn did_reload(
1728 &mut self,
1729 version: clock::Global,
1730 line_ending: LineEnding,
1731 mtime: Option<MTime>,
1732 cx: &mut Context<Self>,
1733 ) {
1734 self.saved_version = version;
1735 self.has_unsaved_edits
1736 .set((self.saved_version.clone(), false));
1737 self.text.set_line_ending(line_ending);
1738 self.saved_mtime = mtime;
1739 cx.emit(BufferEvent::Reloaded);
1740 cx.notify();
1741 }
1742
1743 /// Updates the [`File`] backing this buffer. This should be called when
1744 /// the file has changed or has been deleted.
1745 pub fn file_updated(&mut self, new_file: Arc<dyn File>, cx: &mut Context<Self>) {
1746 let was_dirty = self.is_dirty();
1747 let mut file_changed = false;
1748
1749 if let Some(old_file) = self.file.as_ref() {
1750 if new_file.path() != old_file.path() {
1751 file_changed = true;
1752 }
1753
1754 let old_state = old_file.disk_state();
1755 let new_state = new_file.disk_state();
1756 if old_state != new_state {
1757 file_changed = true;
1758 if !was_dirty && matches!(new_state, DiskState::Present { .. }) {
1759 cx.emit(BufferEvent::ReloadNeeded)
1760 }
1761 }
1762 } else {
1763 file_changed = true;
1764 };
1765
1766 self.file = Some(new_file);
1767 if file_changed {
1768 self.was_changed();
1769 self.non_text_state_update_count += 1;
1770 if was_dirty != self.is_dirty() {
1771 cx.emit(BufferEvent::DirtyChanged);
1772 }
1773 cx.emit(BufferEvent::FileHandleChanged);
1774 cx.notify();
1775 }
1776 }
1777
1778 pub fn base_buffer(&self) -> Option<Entity<Self>> {
1779 Some(self.branch_state.as_ref()?.base_buffer.clone())
1780 }
1781
1782 /// Returns the primary [`Language`] assigned to this [`Buffer`].
1783 pub fn language(&self) -> Option<&Arc<Language>> {
1784 self.language.as_ref()
1785 }
1786
1787 /// Returns the [`Language`] at the given location.
1788 pub fn language_at<D: ToOffset>(&self, position: D) -> Option<Arc<Language>> {
1789 let offset = position.to_offset(self);
1790 let text: &TextBufferSnapshot = &self.text;
1791 self.syntax_map
1792 .lock()
1793 .layers_for_range(offset..offset, text, false)
1794 .filter(|layer| {
1795 layer
1796 .included_sub_ranges
1797 .is_none_or(|ranges| offset_in_sub_ranges(ranges, offset, text))
1798 })
1799 .last()
1800 .map(|info| info.language.clone())
1801 .or_else(|| self.language.clone())
1802 }
1803
1804 /// Returns each [`Language`] for the active syntax layers at the given location.
1805 pub fn languages_at<D: ToOffset>(&self, position: D) -> Vec<Arc<Language>> {
1806 let offset = position.to_offset(self);
1807 let text: &TextBufferSnapshot = &self.text;
1808 let mut languages: Vec<Arc<Language>> = self
1809 .syntax_map
1810 .lock()
1811 .layers_for_range(offset..offset, text, false)
1812 .filter(|layer| {
1813 // For combined injections, check if offset is within the actual sub-ranges.
1814 layer
1815 .included_sub_ranges
1816 .is_none_or(|ranges| offset_in_sub_ranges(ranges, offset, text))
1817 })
1818 .map(|info| info.language.clone())
1819 .collect();
1820
1821 if languages.is_empty()
1822 && let Some(buffer_language) = self.language()
1823 {
1824 languages.push(buffer_language.clone());
1825 }
1826
1827 languages
1828 }
1829
1830 /// An integer version number that accounts for all updates besides
1831 /// the buffer's text itself (which is versioned via a version vector).
1832 pub fn non_text_state_update_count(&self) -> usize {
1833 self.non_text_state_update_count
1834 }
1835
1836 /// Whether the buffer is being parsed in the background.
1837 #[cfg(any(test, feature = "test-support"))]
1838 pub fn is_parsing(&self) -> bool {
1839 self.reparse.is_some()
1840 }
1841
1842 /// Indicates whether the buffer contains any regions that may be
1843 /// written in a language that hasn't been loaded yet.
1844 pub fn contains_unknown_injections(&self) -> bool {
1845 self.syntax_map.lock().contains_unknown_injections()
1846 }
1847
1848 /// Sets the sync parse timeout for this buffer.
1849 ///
1850 /// Setting this to `None` disables sync parsing entirely.
1851 pub fn set_sync_parse_timeout(&mut self, timeout: Option<Duration>) {
1852 self.sync_parse_timeout = timeout;
1853 }
1854
1855 fn invalidate_tree_sitter_data(
1856 tree_sitter_data: &mut Arc<TreeSitterData>,
1857 snapshot: &text::BufferSnapshot,
1858 ) {
1859 match Arc::get_mut(tree_sitter_data) {
1860 Some(tree_sitter_data) => tree_sitter_data.clear(snapshot),
1861 None => {
1862 let new_tree_sitter_data = TreeSitterData::new(snapshot);
1863 *tree_sitter_data = Arc::new(new_tree_sitter_data)
1864 }
1865 }
1866 }
1867
1868 /// Called after an edit to synchronize the buffer's main parse tree with
1869 /// the buffer's new underlying state.
1870 ///
1871 /// Locks the syntax map and interpolates the edits since the last reparse
1872 /// into the foreground syntax tree.
1873 ///
1874 /// Then takes a stable snapshot of the syntax map before unlocking it.
1875 /// The snapshot with the interpolated edits is sent to a background thread,
1876 /// where we ask Tree-sitter to perform an incremental parse.
1877 ///
1878 /// Meanwhile, in the foreground if `may_block` is true, we block the main
1879 /// thread for up to 1ms waiting on the parse to complete. As soon as it
1880 /// completes, we proceed synchronously, unless a 1ms timeout elapses.
1881 ///
1882 /// If we time out waiting on the parse, we spawn a second task waiting
1883 /// until the parse does complete and return with the interpolated tree still
1884 /// in the foreground. When the background parse completes, call back into
1885 /// the main thread and assign the foreground parse state.
1886 ///
1887 /// If the buffer or grammar changed since the start of the background parse,
1888 /// initiate an additional reparse recursively. To avoid concurrent parses
1889 /// for the same buffer, we only initiate a new parse if we are not already
1890 /// parsing in the background.
1891 #[ztracing::instrument(skip_all)]
1892 pub fn reparse(&mut self, cx: &mut Context<Self>, may_block: bool) {
1893 if self.text.version() != *self.tree_sitter_data.version() {
1894 Self::invalidate_tree_sitter_data(&mut self.tree_sitter_data, self.text.snapshot());
1895 }
1896 if self.reparse.is_some() {
1897 return;
1898 }
1899 let language = if let Some(language) = self.language.clone() {
1900 language
1901 } else {
1902 return;
1903 };
1904
1905 let text = self.text_snapshot();
1906 let parsed_version = self.version();
1907
1908 let mut syntax_map = self.syntax_map.lock();
1909 syntax_map.interpolate(&text);
1910 let language_registry = syntax_map.language_registry();
1911 let mut syntax_snapshot = syntax_map.snapshot();
1912 drop(syntax_map);
1913
1914 self.parse_status.0.send(ParseStatus::Parsing).unwrap();
1915 if may_block && let Some(sync_parse_timeout) = self.sync_parse_timeout {
1916 if let Ok(()) = syntax_snapshot.reparse_with_timeout(
1917 &text,
1918 language_registry.clone(),
1919 language.clone(),
1920 sync_parse_timeout,
1921 ) {
1922 self.did_finish_parsing(syntax_snapshot, Some(Duration::from_millis(300)), cx);
1923 self.reparse = None;
1924 return;
1925 }
1926 }
1927
1928 let parse_task = cx.background_spawn({
1929 let language = language.clone();
1930 let language_registry = language_registry.clone();
1931 async move {
1932 syntax_snapshot.reparse(&text, language_registry, language);
1933 syntax_snapshot
1934 }
1935 });
1936
1937 self.reparse = Some(cx.spawn(async move |this, cx| {
1938 let new_syntax_map = parse_task.await;
1939 this.update(cx, move |this, cx| {
1940 let grammar_changed = || {
1941 this.language
1942 .as_ref()
1943 .is_none_or(|current_language| !Arc::ptr_eq(&language, current_language))
1944 };
1945 let language_registry_changed = || {
1946 new_syntax_map.contains_unknown_injections()
1947 && language_registry.is_some_and(|registry| {
1948 registry.version() != new_syntax_map.language_registry_version()
1949 })
1950 };
1951 let parse_again = this.version.changed_since(&parsed_version)
1952 || language_registry_changed()
1953 || grammar_changed();
1954 this.did_finish_parsing(new_syntax_map, None, cx);
1955 this.reparse = None;
1956 if parse_again {
1957 this.reparse(cx, false);
1958 }
1959 })
1960 .ok();
1961 }));
1962 }
1963
1964 fn did_finish_parsing(
1965 &mut self,
1966 syntax_snapshot: SyntaxSnapshot,
1967 block_budget: Option<Duration>,
1968 cx: &mut Context<Self>,
1969 ) {
1970 self.non_text_state_update_count += 1;
1971 self.syntax_map.lock().did_parse(syntax_snapshot);
1972 self.was_changed();
1973 self.request_autoindent(cx, block_budget);
1974 self.parse_status.0.send(ParseStatus::Idle).unwrap();
1975 Self::invalidate_tree_sitter_data(&mut self.tree_sitter_data, &self.text.snapshot());
1976 cx.emit(BufferEvent::Reparsed);
1977 cx.notify();
1978 }
1979
1980 pub fn parse_status(&self) -> watch::Receiver<ParseStatus> {
1981 self.parse_status.1.clone()
1982 }
1983
1984 /// Wait until the buffer is no longer parsing
1985 pub fn parsing_idle(&self) -> impl Future<Output = ()> + use<> {
1986 let mut parse_status = self.parse_status();
1987 async move {
1988 while *parse_status.borrow() != ParseStatus::Idle {
1989 if parse_status.changed().await.is_err() {
1990 break;
1991 }
1992 }
1993 }
1994 }
1995
1996 /// Assign to the buffer a set of diagnostics created by a given language server.
1997 pub fn update_diagnostics(
1998 &mut self,
1999 server_id: LanguageServerId,
2000 diagnostics: DiagnosticSet,
2001 cx: &mut Context<Self>,
2002 ) {
2003 let lamport_timestamp = self.text.lamport_clock.tick();
2004 let op = Operation::UpdateDiagnostics {
2005 server_id,
2006 diagnostics: diagnostics.iter().cloned().collect(),
2007 lamport_timestamp,
2008 };
2009
2010 self.apply_diagnostic_update(server_id, diagnostics, lamport_timestamp, cx);
2011 self.send_operation(op, true, cx);
2012 }
2013
2014 pub fn buffer_diagnostics(
2015 &self,
2016 for_server: Option<LanguageServerId>,
2017 ) -> Vec<&DiagnosticEntry<Anchor>> {
2018 match for_server {
2019 Some(server_id) => self
2020 .diagnostics
2021 .get(&server_id)
2022 .map_or_else(Vec::new, |diagnostics| diagnostics.iter().collect()),
2023 None => self
2024 .diagnostics
2025 .iter()
2026 .flat_map(|(_, diagnostic_set)| diagnostic_set.iter())
2027 .collect(),
2028 }
2029 }
2030
2031 fn request_autoindent(&mut self, cx: &mut Context<Self>, block_budget: Option<Duration>) {
2032 if let Some(indent_sizes) = self.compute_autoindents() {
2033 let indent_sizes = cx.background_spawn(indent_sizes);
2034 let Some(block_budget) = block_budget else {
2035 self.pending_autoindent = Some(cx.spawn(async move |this, cx| {
2036 let indent_sizes = indent_sizes.await;
2037 this.update(cx, |this, cx| {
2038 this.apply_autoindents(indent_sizes, cx);
2039 })
2040 .ok();
2041 }));
2042 return;
2043 };
2044 match cx
2045 .foreground_executor()
2046 .block_with_timeout(block_budget, indent_sizes)
2047 {
2048 Ok(indent_sizes) => self.apply_autoindents(indent_sizes, cx),
2049 Err(indent_sizes) => {
2050 self.pending_autoindent = Some(cx.spawn(async move |this, cx| {
2051 let indent_sizes = indent_sizes.await;
2052 this.update(cx, |this, cx| {
2053 this.apply_autoindents(indent_sizes, cx);
2054 })
2055 .ok();
2056 }));
2057 }
2058 }
2059 } else {
2060 self.autoindent_requests.clear();
2061 for tx in self.wait_for_autoindent_txs.drain(..) {
2062 tx.send(()).ok();
2063 }
2064 }
2065 }
2066
2067 fn compute_autoindents(
2068 &self,
2069 ) -> Option<impl Future<Output = BTreeMap<u32, IndentSize>> + use<>> {
2070 let max_rows_between_yields = 100;
2071 let snapshot = self.snapshot();
2072 if snapshot.syntax.is_empty() || self.autoindent_requests.is_empty() {
2073 return None;
2074 }
2075
2076 let autoindent_requests = self.autoindent_requests.clone();
2077 Some(async move {
2078 let mut indent_sizes = BTreeMap::<u32, (IndentSize, bool)>::new();
2079 for request in autoindent_requests {
2080 // Resolve each edited range to its row in the current buffer and in the
2081 // buffer before this batch of edits.
2082 let mut row_ranges = Vec::new();
2083 let mut old_to_new_rows = BTreeMap::new();
2084 let mut language_indent_sizes_by_new_row = Vec::new();
2085 for entry in &request.entries {
2086 let position = entry.range.start;
2087 let new_row = position.to_point(&snapshot).row;
2088 let new_end_row = entry.range.end.to_point(&snapshot).row + 1;
2089 language_indent_sizes_by_new_row.push((new_row, entry.indent_size));
2090
2091 if let Some(old_row) = entry.old_row {
2092 old_to_new_rows.insert(old_row, new_row);
2093 }
2094 row_ranges.push((new_row..new_end_row, entry.original_indent_column));
2095 }
2096
2097 // Build a map containing the suggested indentation for each of the edited lines
2098 // with respect to the state of the buffer before these edits. This map is keyed
2099 // by the rows for these lines in the current state of the buffer.
2100 let mut old_suggestions = BTreeMap::<u32, (IndentSize, bool)>::default();
2101 let old_edited_ranges =
2102 contiguous_ranges(old_to_new_rows.keys().copied(), max_rows_between_yields);
2103 let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable();
2104 let mut language_indent_size = IndentSize::default();
2105 for old_edited_range in old_edited_ranges {
2106 let suggestions = request
2107 .before_edit
2108 .suggest_autoindents(old_edited_range.clone())
2109 .into_iter()
2110 .flatten();
2111 for (old_row, suggestion) in old_edited_range.zip(suggestions) {
2112 if let Some(suggestion) = suggestion {
2113 let new_row = *old_to_new_rows.get(&old_row).unwrap();
2114
2115 // Find the indent size based on the language for this row.
2116 while let Some((row, size)) = language_indent_sizes.peek() {
2117 if *row > new_row {
2118 break;
2119 }
2120 language_indent_size = *size;
2121 language_indent_sizes.next();
2122 }
2123
2124 let suggested_indent = old_to_new_rows
2125 .get(&suggestion.basis_row)
2126 .and_then(|from_row| {
2127 Some(old_suggestions.get(from_row).copied()?.0)
2128 })
2129 .unwrap_or_else(|| {
2130 request
2131 .before_edit
2132 .indent_size_for_line(suggestion.basis_row)
2133 })
2134 .with_delta(suggestion.delta, language_indent_size);
2135 old_suggestions
2136 .insert(new_row, (suggested_indent, suggestion.within_error));
2137 }
2138 }
2139 yield_now().await;
2140 }
2141
2142 // Compute new suggestions for each line, but only include them in the result
2143 // if they differ from the old suggestion for that line.
2144 let mut language_indent_sizes = language_indent_sizes_by_new_row.iter().peekable();
2145 let mut language_indent_size = IndentSize::default();
2146 for (row_range, original_indent_column) in row_ranges {
2147 let new_edited_row_range = if request.is_block_mode {
2148 row_range.start..row_range.start + 1
2149 } else {
2150 row_range.clone()
2151 };
2152
2153 let suggestions = snapshot
2154 .suggest_autoindents(new_edited_row_range.clone())
2155 .into_iter()
2156 .flatten();
2157 for (new_row, suggestion) in new_edited_row_range.zip(suggestions) {
2158 if let Some(suggestion) = suggestion {
2159 // Find the indent size based on the language for this row.
2160 while let Some((row, size)) = language_indent_sizes.peek() {
2161 if *row > new_row {
2162 break;
2163 }
2164 language_indent_size = *size;
2165 language_indent_sizes.next();
2166 }
2167
2168 let suggested_indent = indent_sizes
2169 .get(&suggestion.basis_row)
2170 .copied()
2171 .map(|e| e.0)
2172 .unwrap_or_else(|| {
2173 snapshot.indent_size_for_line(suggestion.basis_row)
2174 })
2175 .with_delta(suggestion.delta, language_indent_size);
2176
2177 if old_suggestions.get(&new_row).is_none_or(
2178 |(old_indentation, was_within_error)| {
2179 suggested_indent != *old_indentation
2180 && (!suggestion.within_error || *was_within_error)
2181 },
2182 ) {
2183 indent_sizes.insert(
2184 new_row,
2185 (suggested_indent, request.ignore_empty_lines),
2186 );
2187 }
2188 }
2189 }
2190
2191 if let (true, Some(original_indent_column)) =
2192 (request.is_block_mode, original_indent_column)
2193 {
2194 let new_indent =
2195 if let Some((indent, _)) = indent_sizes.get(&row_range.start) {
2196 *indent
2197 } else {
2198 snapshot.indent_size_for_line(row_range.start)
2199 };
2200 let delta = new_indent.len as i64 - original_indent_column as i64;
2201 if delta != 0 {
2202 for row in row_range.skip(1) {
2203 indent_sizes.entry(row).or_insert_with(|| {
2204 let mut size = snapshot.indent_size_for_line(row);
2205 // A line with no indentation has an arbitrary
2206 // indent kind, so it can adopt the new kind.
2207 if size.len == 0 {
2208 size.kind = new_indent.kind;
2209 }
2210 if size.kind == new_indent.kind {
2211 match delta.cmp(&0) {
2212 Ordering::Greater => size.len += delta as u32,
2213 Ordering::Less => {
2214 size.len = size.len.saturating_sub(-delta as u32)
2215 }
2216 Ordering::Equal => {}
2217 }
2218 }
2219 (size, request.ignore_empty_lines)
2220 });
2221 }
2222 }
2223 }
2224
2225 yield_now().await;
2226 }
2227 }
2228
2229 indent_sizes
2230 .into_iter()
2231 .filter_map(|(row, (indent, ignore_empty_lines))| {
2232 if ignore_empty_lines && snapshot.line_len(row) == 0 {
2233 None
2234 } else {
2235 Some((row, indent))
2236 }
2237 })
2238 .collect()
2239 })
2240 }
2241
2242 fn apply_autoindents(
2243 &mut self,
2244 indent_sizes: BTreeMap<u32, IndentSize>,
2245 cx: &mut Context<Self>,
2246 ) {
2247 self.autoindent_requests.clear();
2248 for tx in self.wait_for_autoindent_txs.drain(..) {
2249 tx.send(()).ok();
2250 }
2251
2252 let edits: Vec<_> = indent_sizes
2253 .into_iter()
2254 .filter_map(|(row, indent_size)| {
2255 let current_size = indent_size_for_line(self, row);
2256 Self::edit_for_indent_size_adjustment(row, current_size, indent_size)
2257 })
2258 .collect();
2259
2260 let preserve_preview = self.preserve_preview();
2261 self.edit(edits, None, cx);
2262 if preserve_preview {
2263 self.refresh_preview();
2264 }
2265 }
2266
2267 /// Create a minimal edit that will cause the given row to be indented
2268 /// with the given size. After applying this edit, the length of the line
2269 /// will always be at least `new_size.len`.
2270 pub fn edit_for_indent_size_adjustment(
2271 row: u32,
2272 current_size: IndentSize,
2273 new_size: IndentSize,
2274 ) -> Option<(Range<Point>, String)> {
2275 if new_size.kind == current_size.kind {
2276 match new_size.len.cmp(¤t_size.len) {
2277 Ordering::Greater => {
2278 let point = Point::new(row, 0);
2279 Some((
2280 point..point,
2281 iter::repeat(new_size.char())
2282 .take((new_size.len - current_size.len) as usize)
2283 .collect::<String>(),
2284 ))
2285 }
2286
2287 Ordering::Less => Some((
2288 Point::new(row, 0)..Point::new(row, current_size.len - new_size.len),
2289 String::new(),
2290 )),
2291
2292 Ordering::Equal => None,
2293 }
2294 } else {
2295 Some((
2296 Point::new(row, 0)..Point::new(row, current_size.len),
2297 iter::repeat(new_size.char())
2298 .take(new_size.len as usize)
2299 .collect::<String>(),
2300 ))
2301 }
2302 }
2303
2304 /// Spawns a background task that asynchronously computes a `Diff` between the buffer's text
2305 /// and the given new text.
2306 pub fn diff<T>(&self, new_text: T, cx: &App) -> Task<Diff>
2307 where
2308 T: AsRef<str> + Send + 'static,
2309 {
2310 let old_text = self.as_rope().clone();
2311 let base_version = self.version();
2312 cx.background_spawn(async move {
2313 let old_text = old_text.to_string();
2314 let mut new_text = new_text.as_ref().to_owned();
2315 let line_ending = LineEnding::detect(&new_text);
2316 LineEnding::normalize(&mut new_text);
2317 let edits = text_diff(&old_text, &new_text);
2318 Diff {
2319 base_version,
2320 line_ending,
2321 edits,
2322 }
2323 })
2324 }
2325
2326 /// Spawns a background task that returns a `Diff` removing trailing whitespace from line ends.
2327 ///
2328 /// When `modified_rows` is `Some`, only lines whose row falls within one of the given ranges
2329 /// are trimmed; when it is `None`, the whole buffer is scanned.
2330 pub fn remove_trailing_whitespace(
2331 &self,
2332 modified_rows: Option<&[Range<u32>]>,
2333 cx: &App,
2334 ) -> Task<Diff> {
2335 let old_text = self.as_rope().clone();
2336 let line_ending = self.line_ending();
2337 let base_version = self.version();
2338 let modified_rows = modified_rows.map(|rows| rows.to_vec());
2339 cx.background_spawn(async move {
2340 let ranges = trailing_whitespace_ranges(&old_text, modified_rows.as_deref());
2341 let empty = Arc::<str>::from("");
2342 Diff {
2343 base_version,
2344 line_ending,
2345 edits: ranges
2346 .into_iter()
2347 .map(|range| (range, empty.clone()))
2348 .collect(),
2349 }
2350 })
2351 }
2352
2353 /// Returns a `Diff` ensuring the buffer ends with a trailing newline.
2354 ///
2355 /// When `modified_rows` is `None`, the whole buffer is considered: trailing whitespace and
2356 /// blank lines at the end of the file are collapsed into a single newline.
2357 ///
2358 /// When `modified_rows` is `Some`, the operation is scoped to a "Format Selection": a single
2359 /// newline is appended only when the last line is non-empty and its row falls within one of
2360 /// the ranges. Trailing blank lines are left intact so that formatting a selection cannot
2361 /// delete unselected rows.
2362 pub fn ensure_final_newline(&self, modified_rows: Option<&[Range<u32>]>) -> Diff {
2363 let len = self.len();
2364 let line_ending = self.line_ending();
2365 let base_version = self.version();
2366 let newline = Arc::<str>::from("\n");
2367
2368 let edits = if len == 0 {
2369 Vec::new()
2370 } else if let Some(modified_rows) = modified_rows {
2371 let max_point = self.max_point();
2372 let last_line_is_empty = max_point.column == 0;
2373 let last_row_is_modified = modified_rows
2374 .iter()
2375 .any(|range| range.contains(&max_point.row));
2376 if last_line_is_empty || !last_row_is_modified {
2377 Vec::new()
2378 } else {
2379 Vec::from([(len..len, newline)])
2380 }
2381 } else {
2382 let mut offset = len;
2383 let mut already_normalized = false;
2384 for chunk in self.as_rope().reversed_chunks_in_range(0..len) {
2385 let non_whitespace_len = chunk
2386 .trim_end_matches(|c: char| c.is_ascii_whitespace())
2387 .len();
2388 offset -= chunk.len();
2389 offset += non_whitespace_len;
2390 if non_whitespace_len != 0 {
2391 already_normalized =
2392 offset == len - 1 && chunk.get(non_whitespace_len..) == Some("\n");
2393 break;
2394 }
2395 }
2396 if already_normalized {
2397 Vec::new()
2398 } else {
2399 Vec::from([(offset..len, newline)])
2400 }
2401 };
2402 Diff {
2403 base_version,
2404 line_ending,
2405 edits,
2406 }
2407 }
2408
2409 /// Applies a diff to the buffer. If the buffer has changed since the given diff was
2410 /// calculated, then adjust the diff to account for those changes, and discard any
2411 /// parts of the diff that conflict with those changes.
2412 pub fn apply_diff(&mut self, diff: Diff, cx: &mut Context<Self>) -> Option<TransactionId> {
2413 let snapshot = self.snapshot();
2414 let mut edits_since = snapshot.edits_since::<usize>(&diff.base_version).peekable();
2415 let mut delta = 0;
2416 let adjusted_edits = diff.edits.into_iter().filter_map(|(range, new_text)| {
2417 while let Some(edit_since) = edits_since.peek() {
2418 // If the edit occurs after a diff hunk, then it does not
2419 // affect that hunk.
2420 if edit_since.old.start > range.end {
2421 break;
2422 }
2423 // If the edit precedes the diff hunk, then adjust the hunk
2424 // to reflect the edit.
2425 else if edit_since.old.end < range.start {
2426 delta += edit_since.new_len() as i64 - edit_since.old_len() as i64;
2427 edits_since.next();
2428 }
2429 // If the edit intersects a diff hunk, then discard that hunk.
2430 else {
2431 return None;
2432 }
2433 }
2434
2435 let start = (range.start as i64 + delta) as usize;
2436 let end = (range.end as i64 + delta) as usize;
2437 Some((start..end, new_text))
2438 });
2439
2440 self.start_transaction();
2441 self.text.set_line_ending(diff.line_ending);
2442 self.edit(adjusted_edits, None, cx);
2443 self.end_transaction(cx)
2444 }
2445
2446 pub fn has_unsaved_edits(&self) -> bool {
2447 let (last_version, has_unsaved_edits) = self.has_unsaved_edits.take();
2448
2449 if last_version == self.version {
2450 self.has_unsaved_edits
2451 .set((last_version, has_unsaved_edits));
2452 return has_unsaved_edits;
2453 }
2454
2455 let has_edits = self.has_edits_since(&self.saved_version);
2456 self.has_unsaved_edits
2457 .set((self.version.clone(), has_edits));
2458 has_edits
2459 }
2460
2461 /// Checks if the buffer has unsaved changes.
2462 pub fn is_dirty(&self) -> bool {
2463 if self.capability == Capability::ReadOnly {
2464 return false;
2465 }
2466 if self.has_conflict {
2467 return true;
2468 }
2469 match self.file.as_ref().map(|f| f.disk_state()) {
2470 Some(DiskState::New) | Some(DiskState::Deleted) => {
2471 !self.is_empty() && self.has_unsaved_edits()
2472 }
2473 _ => self.has_unsaved_edits(),
2474 }
2475 }
2476
2477 /// Marks the buffer as having a conflict regardless of current buffer state.
2478 pub fn set_conflict(&mut self) {
2479 self.has_conflict = true;
2480 }
2481
2482 /// Checks if the buffer and its file have both changed since the buffer
2483 /// was last saved or reloaded.
2484 pub fn has_conflict(&self) -> bool {
2485 if self.has_conflict {
2486 return true;
2487 }
2488 let Some(file) = self.file.as_ref() else {
2489 return false;
2490 };
2491 match file.disk_state() {
2492 DiskState::New => false,
2493 DiskState::Present { mtime, .. } => match self.saved_mtime {
2494 Some(saved_mtime) => {
2495 mtime.bad_is_greater_than(saved_mtime) && self.has_unsaved_edits()
2496 }
2497 None => true,
2498 },
2499 DiskState::Deleted => false,
2500 DiskState::Historic { .. } => false,
2501 }
2502 }
2503
2504 /// Gets a [`Subscription`] that tracks all of the changes to the buffer's text.
2505 pub fn subscribe(&mut self) -> Subscription<usize> {
2506 self.text.subscribe()
2507 }
2508
2509 /// Adds a bit to the list of bits that are set when the buffer's text changes.
2510 ///
2511 /// This allows downstream code to check if the buffer's text has changed without
2512 /// waiting for an effect cycle, which would be required if using eents.
2513 pub fn record_changes(&mut self, bit: rc::Weak<Cell<bool>>) {
2514 if let Err(ix) = self
2515 .change_bits
2516 .binary_search_by_key(&rc::Weak::as_ptr(&bit), rc::Weak::as_ptr)
2517 {
2518 self.change_bits.insert(ix, bit);
2519 }
2520 }
2521
2522 /// Set the change bit for all "listeners".
2523 fn was_changed(&mut self) {
2524 self.change_bits.retain(|change_bit| {
2525 change_bit
2526 .upgrade()
2527 .inspect(|bit| {
2528 _ = bit.replace(true);
2529 })
2530 .is_some()
2531 });
2532 }
2533
2534 /// Starts a transaction, if one is not already in-progress. When undoing or
2535 /// redoing edits, all of the edits performed within a transaction are undone
2536 /// or redone together.
2537 pub fn start_transaction(&mut self) -> Option<TransactionId> {
2538 self.start_transaction_at(Instant::now())
2539 }
2540
2541 /// Starts a transaction, providing the current time. Subsequent transactions
2542 /// that occur within a short period of time will be grouped together. This
2543 /// is controlled by the buffer's undo grouping duration.
2544 pub fn start_transaction_at(&mut self, now: Instant) -> Option<TransactionId> {
2545 self.transaction_depth += 1;
2546 if self.was_dirty_before_starting_transaction.is_none() {
2547 self.was_dirty_before_starting_transaction = Some(self.is_dirty());
2548 }
2549 self.text.start_transaction_at(now)
2550 }
2551
2552 /// Terminates the current transaction, if this is the outermost transaction.
2553 pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
2554 self.end_transaction_at(Instant::now(), cx)
2555 }
2556
2557 pub fn end_transaction_with_source(
2558 &mut self,
2559 source: BufferEditSource,
2560 cx: &mut Context<Self>,
2561 ) -> Option<TransactionId> {
2562 self.end_transaction_at_internal(Instant::now(), source, cx)
2563 }
2564
2565 /// Terminates the current transaction, providing the current time. Subsequent transactions
2566 /// that occur within a short period of time will be grouped together. This
2567 /// is controlled by the buffer's undo grouping duration.
2568 pub fn end_transaction_at(
2569 &mut self,
2570 now: Instant,
2571 cx: &mut Context<Self>,
2572 ) -> Option<TransactionId> {
2573 self.end_transaction_at_internal(now, BufferEditSource::User, cx)
2574 }
2575
2576 fn end_transaction_at_internal(
2577 &mut self,
2578 now: Instant,
2579 source: BufferEditSource,
2580 cx: &mut Context<Self>,
2581 ) -> Option<TransactionId> {
2582 assert!(self.transaction_depth > 0);
2583 self.transaction_depth -= 1;
2584 let was_dirty = if self.transaction_depth == 0 {
2585 self.was_dirty_before_starting_transaction.take().unwrap()
2586 } else {
2587 false
2588 };
2589 if let Some((transaction_id, start_version)) = self.text.end_transaction_at(now) {
2590 self.did_edit(&start_version, was_dirty, source, cx);
2591 Some(transaction_id)
2592 } else {
2593 None
2594 }
2595 }
2596
2597 /// Manually add a transaction to the buffer's undo history.
2598 pub fn push_transaction(&mut self, transaction: Transaction, now: Instant) {
2599 self.text.push_transaction(transaction, now);
2600 }
2601
2602 /// Differs from `push_transaction` in that it does not clear the redo
2603 /// stack. Intended to be used to create a parent transaction to merge
2604 /// potential child transactions into.
2605 ///
2606 /// The caller is responsible for removing it from the undo history using
2607 /// `forget_transaction` if no edits are merged into it. Otherwise, if edits
2608 /// are merged into this transaction, the caller is responsible for ensuring
2609 /// the redo stack is cleared. The easiest way to ensure the redo stack is
2610 /// cleared is to create transactions with the usual `start_transaction` and
2611 /// `end_transaction` methods and merging the resulting transactions into
2612 /// the transaction created by this method
2613 pub fn push_empty_transaction(&mut self, now: Instant) -> TransactionId {
2614 self.text.push_empty_transaction(now)
2615 }
2616
2617 /// Prevent the last transaction from being grouped with any subsequent transactions,
2618 /// even if they occur with the buffer's undo grouping duration.
2619 pub fn finalize_last_transaction(&mut self) -> Option<&Transaction> {
2620 self.text.finalize_last_transaction()
2621 }
2622
2623 /// Manually group all changes since a given transaction.
2624 pub fn group_until_transaction(&mut self, transaction_id: TransactionId) {
2625 self.text.group_until_transaction(transaction_id);
2626 }
2627
2628 /// Manually remove a transaction from the buffer's undo history
2629 pub fn forget_transaction(&mut self, transaction_id: TransactionId) -> Option<Transaction> {
2630 self.text.forget_transaction(transaction_id)
2631 }
2632
2633 /// Retrieve a transaction from the buffer's undo history
2634 pub fn get_transaction(&self, transaction_id: TransactionId) -> Option<&Transaction> {
2635 self.text.get_transaction(transaction_id)
2636 }
2637
2638 /// Manually merge two transactions in the buffer's undo history.
2639 pub fn merge_transactions(&mut self, transaction: TransactionId, destination: TransactionId) {
2640 self.text.merge_transactions(transaction, destination);
2641 }
2642
2643 /// Waits for the buffer to receive operations with the given timestamps.
2644 pub fn wait_for_edits<It: IntoIterator<Item = clock::Lamport>>(
2645 &mut self,
2646 edit_ids: It,
2647 ) -> impl Future<Output = Result<()>> + use<It> {
2648 self.text.wait_for_edits(edit_ids)
2649 }
2650
2651 /// Waits for the buffer to receive the operations necessary for resolving the given anchors.
2652 pub fn wait_for_anchors<It: IntoIterator<Item = Anchor>>(
2653 &mut self,
2654 anchors: It,
2655 ) -> impl 'static + Future<Output = Result<()>> + use<It> {
2656 self.text.wait_for_anchors(anchors)
2657 }
2658
2659 /// Waits for the buffer to receive operations up to the given version.
2660 pub fn wait_for_version(
2661 &mut self,
2662 version: clock::Global,
2663 ) -> impl Future<Output = Result<()>> + use<> {
2664 self.text.wait_for_version(version)
2665 }
2666
2667 /// Forces all futures returned by [`Buffer::wait_for_version`], [`Buffer::wait_for_edits`], or
2668 /// [`Buffer::wait_for_version`] to resolve with an error.
2669 pub fn give_up_waiting(&mut self) {
2670 self.text.give_up_waiting();
2671 }
2672
2673 pub fn wait_for_autoindent_applied(&mut self) -> Option<oneshot::Receiver<()>> {
2674 let mut rx = None;
2675 if !self.autoindent_requests.is_empty() {
2676 let channel = oneshot::channel();
2677 self.wait_for_autoindent_txs.push(channel.0);
2678 rx = Some(channel.1);
2679 }
2680 rx
2681 }
2682
2683 /// Stores a set of selections that should be broadcasted to all of the buffer's replicas.
2684 pub fn set_active_selections(
2685 &mut self,
2686 selections: Arc<[Selection<Anchor>]>,
2687 line_mode: bool,
2688 cursor_shape: CursorShape,
2689 cx: &mut Context<Self>,
2690 ) {
2691 let lamport_timestamp = self.text.lamport_clock.tick();
2692 self.remote_selections.insert(
2693 self.text.replica_id(),
2694 SelectionSet {
2695 selections: selections.clone(),
2696 lamport_timestamp,
2697 line_mode,
2698 cursor_shape,
2699 },
2700 );
2701 self.send_operation(
2702 Operation::UpdateSelections {
2703 selections,
2704 line_mode,
2705 lamport_timestamp,
2706 cursor_shape,
2707 },
2708 true,
2709 cx,
2710 );
2711 self.non_text_state_update_count += 1;
2712 cx.notify();
2713 }
2714
2715 /// Clears the selections, so that other replicas of the buffer do not see any selections for
2716 /// this replica.
2717 pub fn remove_active_selections(&mut self, cx: &mut Context<Self>) {
2718 if self
2719 .remote_selections
2720 .get(&self.text.replica_id())
2721 .is_none_or(|set| !set.selections.is_empty())
2722 {
2723 self.set_active_selections(Arc::default(), false, Default::default(), cx);
2724 }
2725 }
2726
2727 pub fn set_agent_selections(
2728 &mut self,
2729 selections: Arc<[Selection<Anchor>]>,
2730 line_mode: bool,
2731 cursor_shape: CursorShape,
2732 cx: &mut Context<Self>,
2733 ) {
2734 let lamport_timestamp = self.text.lamport_clock.tick();
2735 self.remote_selections.insert(
2736 ReplicaId::AGENT,
2737 SelectionSet {
2738 selections,
2739 lamport_timestamp,
2740 line_mode,
2741 cursor_shape,
2742 },
2743 );
2744 self.non_text_state_update_count += 1;
2745 cx.notify();
2746 }
2747
2748 pub fn remove_agent_selections(&mut self, cx: &mut Context<Self>) {
2749 self.set_agent_selections(Arc::default(), false, Default::default(), cx);
2750 }
2751
2752 /// Replaces the buffer's entire text.
2753 pub fn set_text<T>(&mut self, text: T, cx: &mut Context<Self>) -> Option<clock::Lamport>
2754 where
2755 T: Into<Arc<str>>,
2756 {
2757 self.autoindent_requests.clear();
2758 self.edit([(0..self.len(), text)], None, cx)
2759 }
2760
2761 /// Appends the given text to the end of the buffer.
2762 pub fn append<T>(&mut self, text: T, cx: &mut Context<Self>) -> Option<clock::Lamport>
2763 where
2764 T: Into<Arc<str>>,
2765 {
2766 self.edit([(self.len()..self.len(), text)], None, cx)
2767 }
2768
2769 /// Applies the given edits to the buffer. Each edit is specified as a range of text to
2770 /// delete, and a string of text to insert at that location. Adjacent edits are coalesced.
2771 /// Inserted text is normalized to LF line endings before being applied.
2772 ///
2773 /// If an [`AutoindentMode`] is provided, then the buffer will enqueue an auto-indent
2774 /// request for the edited ranges, which will be processed when the buffer finishes
2775 /// parsing.
2776 ///
2777 /// Parsing takes place at the end of a transaction, and may compute synchronously
2778 /// or asynchronously, depending on the changes.
2779 pub fn edit<I, S, T>(
2780 &mut self,
2781 edits_iter: I,
2782 autoindent_mode: Option<AutoindentMode>,
2783 cx: &mut Context<Self>,
2784 ) -> Option<clock::Lamport>
2785 where
2786 I: IntoIterator<Item = (Range<S>, T)>,
2787 S: ToOffset,
2788 T: Into<Arc<str>>,
2789 {
2790 self.edit_internal(edits_iter, autoindent_mode, true, cx)
2791 }
2792
2793 /// Like [`edit`](Self::edit), but does not coalesce adjacent edits.
2794 pub fn edit_non_coalesce<I, S, T>(
2795 &mut self,
2796 edits_iter: I,
2797 autoindent_mode: Option<AutoindentMode>,
2798 cx: &mut Context<Self>,
2799 ) -> Option<clock::Lamport>
2800 where
2801 I: IntoIterator<Item = (Range<S>, T)>,
2802 S: ToOffset,
2803 T: Into<Arc<str>>,
2804 {
2805 self.edit_internal(edits_iter, autoindent_mode, false, cx)
2806 }
2807
2808 fn edit_internal<I, S, T>(
2809 &mut self,
2810 edits_iter: I,
2811 autoindent_mode: Option<AutoindentMode>,
2812 coalesce_adjacent: bool,
2813 cx: &mut Context<Self>,
2814 ) -> Option<clock::Lamport>
2815 where
2816 I: IntoIterator<Item = (Range<S>, T)>,
2817 S: ToOffset,
2818 T: Into<Arc<str>>,
2819 {
2820 // Skip invalid edits and coalesce contiguous ones.
2821 let mut edits: Vec<(Range<usize>, Arc<str>)> = Vec::new();
2822
2823 for (range, new_text) in edits_iter {
2824 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
2825
2826 if range.start > range.end {
2827 mem::swap(&mut range.start, &mut range.end);
2828 }
2829 let new_text = new_text.into();
2830 if !new_text.is_empty() || !range.is_empty() {
2831 let prev_edit = edits.last_mut();
2832 let should_coalesce = prev_edit.as_ref().is_some_and(|(prev_range, _)| {
2833 if coalesce_adjacent {
2834 prev_range.end >= range.start
2835 } else {
2836 prev_range.end > range.start
2837 }
2838 });
2839
2840 if let Some((prev_range, prev_text)) = prev_edit
2841 && should_coalesce
2842 {
2843 prev_range.end = cmp::max(prev_range.end, range.end);
2844 *prev_text = format!("{prev_text}{new_text}").into();
2845 } else {
2846 edits.push((range, new_text));
2847 }
2848 }
2849 }
2850 if edits.is_empty() {
2851 return None;
2852 }
2853
2854 self.start_transaction();
2855 self.pending_autoindent.take();
2856 let autoindent_request = autoindent_mode
2857 .and_then(|mode| self.language.as_ref().map(|_| (self.snapshot(), mode)));
2858
2859 let edit_operation = self.text.edit(edits.iter().cloned());
2860 let edit_id = edit_operation.timestamp();
2861
2862 if let Some((before_edit, mode)) = autoindent_request {
2863 let mut delta = 0isize;
2864 let mut previous_setting = None;
2865 let entries: Vec<_> = edits
2866 .into_iter()
2867 .enumerate()
2868 .zip(&edit_operation.as_edit().unwrap().new_text)
2869 .filter(|((_, (range, _)), _)| {
2870 let language = before_edit.language_at(range.start);
2871 let language_id = language.map(|l| l.id());
2872 if let Some((cached_language_id, apply_syntax_indent)) = previous_setting
2873 && cached_language_id == language_id
2874 {
2875 apply_syntax_indent
2876 } else {
2877 // The auto-indent setting is not present in editorconfigs, hence
2878 // we can avoid passing the file here.
2879 let auto_indent_mode = LanguageSettings::resolve(
2880 None,
2881 language.map(|l| l.name()).as_ref(),
2882 cx,
2883 )
2884 .auto_indent;
2885 let apply_syntax_indent = auto_indent_mode == AutoIndentMode::SyntaxAware;
2886 previous_setting = Some((language_id, apply_syntax_indent));
2887 apply_syntax_indent
2888 }
2889 })
2890 .map(|((ix, (range, _)), new_text)| {
2891 let new_text_length = new_text.len();
2892 let old_start = range.start.to_point(&before_edit);
2893 let new_start = (delta + range.start as isize) as usize;
2894 let range_len = range.end - range.start;
2895 delta += new_text_length as isize - range_len as isize;
2896
2897 // Decide what range of the insertion to auto-indent, and whether
2898 // the first line of the insertion should be considered a newly-inserted line
2899 // or an edit to an existing line.
2900 let mut range_of_insertion_to_indent = 0..new_text_length;
2901 let mut first_line_is_new = true;
2902
2903 let old_line_start = before_edit.indent_size_for_line(old_start.row).len;
2904 let old_line_end = before_edit.line_len(old_start.row);
2905
2906 if old_start.column > old_line_start {
2907 first_line_is_new = false;
2908 }
2909
2910 if !new_text.contains('\n')
2911 && (old_start.column + (range_len as u32) < old_line_end
2912 || old_line_end == old_line_start)
2913 {
2914 first_line_is_new = false;
2915 }
2916
2917 // When inserting text starting with a newline, avoid auto-indenting the
2918 // previous line.
2919 if new_text.starts_with('\n') {
2920 range_of_insertion_to_indent.start += 1;
2921 first_line_is_new = true;
2922 }
2923
2924 let mut original_indent_column = None;
2925 if let AutoindentMode::Block {
2926 original_indent_columns,
2927 } = &mode
2928 {
2929 original_indent_column = Some(if new_text.starts_with('\n') {
2930 indent_size_for_text(
2931 new_text[range_of_insertion_to_indent.clone()].chars(),
2932 )
2933 .len
2934 } else {
2935 original_indent_columns
2936 .get(ix)
2937 .copied()
2938 .flatten()
2939 .unwrap_or_else(|| {
2940 indent_size_for_text(
2941 new_text[range_of_insertion_to_indent.clone()].chars(),
2942 )
2943 .len
2944 })
2945 });
2946
2947 // Avoid auto-indenting the line after the edit.
2948 if new_text[range_of_insertion_to_indent.clone()].ends_with('\n') {
2949 range_of_insertion_to_indent.end -= 1;
2950 }
2951 }
2952
2953 AutoindentRequestEntry {
2954 original_indent_column,
2955 old_row: if first_line_is_new {
2956 None
2957 } else {
2958 Some(old_start.row)
2959 },
2960 indent_size: before_edit.language_indent_size_at(range.start, cx),
2961 range: self.anchor_before(new_start + range_of_insertion_to_indent.start)
2962 ..self.anchor_after(new_start + range_of_insertion_to_indent.end),
2963 }
2964 })
2965 .collect();
2966
2967 if !entries.is_empty() {
2968 self.autoindent_requests.push(Arc::new(AutoindentRequest {
2969 before_edit,
2970 entries,
2971 is_block_mode: matches!(mode, AutoindentMode::Block { .. }),
2972 ignore_empty_lines: false,
2973 }));
2974 }
2975 }
2976
2977 self.end_transaction(cx);
2978 self.send_operation(Operation::Buffer(edit_operation), true, cx);
2979 Some(edit_id)
2980 }
2981
2982 fn did_edit(
2983 &mut self,
2984 old_version: &clock::Global,
2985 was_dirty: bool,
2986 source: BufferEditSource,
2987 cx: &mut Context<Self>,
2988 ) {
2989 self.was_changed();
2990
2991 if self.edits_since::<usize>(old_version).next().is_none() {
2992 return;
2993 }
2994
2995 self.reparse(cx, true);
2996 cx.emit(BufferEvent::Edited { source });
2997 let is_dirty = self.is_dirty();
2998 if was_dirty != is_dirty {
2999 cx.emit(BufferEvent::DirtyChanged);
3000 }
3001 if was_dirty && !is_dirty {
3002 if let Some(file) = self.file.as_ref() {
3003 if matches!(file.disk_state(), DiskState::Present { .. })
3004 && file.disk_state().mtime() != self.saved_mtime
3005 {
3006 cx.emit(BufferEvent::ReloadNeeded);
3007 }
3008 }
3009 }
3010 cx.notify();
3011 }
3012
3013 pub fn autoindent_ranges<I, T>(&mut self, ranges: I, cx: &mut Context<Self>)
3014 where
3015 I: IntoIterator<Item = Range<T>>,
3016 T: ToOffset + Copy,
3017 {
3018 let before_edit = self.snapshot();
3019 let entries = ranges
3020 .into_iter()
3021 .map(|range| AutoindentRequestEntry {
3022 range: before_edit.anchor_before(range.start)..before_edit.anchor_after(range.end),
3023 old_row: None,
3024 indent_size: before_edit.language_indent_size_at(range.start, cx),
3025 original_indent_column: None,
3026 })
3027 .collect();
3028 self.autoindent_requests.push(Arc::new(AutoindentRequest {
3029 before_edit,
3030 entries,
3031 is_block_mode: false,
3032 ignore_empty_lines: true,
3033 }));
3034 self.request_autoindent(cx, Some(Duration::from_micros(300)));
3035 }
3036
3037 // Inserts newlines at the given position to create an empty line, returning the start of the new line.
3038 // You can also request the insertion of empty lines above and below the line starting at the returned point.
3039 pub fn insert_empty_line(
3040 &mut self,
3041 position: impl ToPoint,
3042 space_above: bool,
3043 space_below: bool,
3044 cx: &mut Context<Self>,
3045 ) -> Point {
3046 let mut position = position.to_point(self);
3047
3048 self.start_transaction();
3049
3050 self.edit(
3051 [(position..position, "\n")],
3052 Some(AutoindentMode::EachLine),
3053 cx,
3054 );
3055
3056 if position.column > 0 {
3057 position += Point::new(1, 0);
3058 }
3059
3060 if !self.is_line_blank(position.row) {
3061 self.edit(
3062 [(position..position, "\n")],
3063 Some(AutoindentMode::EachLine),
3064 cx,
3065 );
3066 }
3067
3068 if space_above && position.row > 0 && !self.is_line_blank(position.row - 1) {
3069 self.edit(
3070 [(position..position, "\n")],
3071 Some(AutoindentMode::EachLine),
3072 cx,
3073 );
3074 position.row += 1;
3075 }
3076
3077 if space_below
3078 && (position.row == self.max_point().row || !self.is_line_blank(position.row + 1))
3079 {
3080 self.edit(
3081 [(position..position, "\n")],
3082 Some(AutoindentMode::EachLine),
3083 cx,
3084 );
3085 }
3086
3087 self.end_transaction(cx);
3088
3089 position
3090 }
3091
3092 /// Applies the given remote operations to the buffer.
3093 pub fn apply_ops<I: IntoIterator<Item = Operation>>(&mut self, ops: I, cx: &mut Context<Self>) {
3094 self.pending_autoindent.take();
3095 let was_dirty = self.is_dirty();
3096 let old_version = self.version.clone();
3097 let mut deferred_ops = Vec::new();
3098 let buffer_ops = ops
3099 .into_iter()
3100 .filter_map(|op| match op {
3101 Operation::Buffer(op) => Some(op),
3102 _ => {
3103 if self.can_apply_op(&op) {
3104 self.apply_op(op, cx);
3105 } else {
3106 deferred_ops.push(op);
3107 }
3108 None
3109 }
3110 })
3111 .collect::<Vec<_>>();
3112 for operation in buffer_ops.iter() {
3113 self.send_operation(Operation::Buffer(operation.clone()), false, cx);
3114 }
3115 self.text.apply_ops(buffer_ops);
3116 self.deferred_ops.insert(deferred_ops);
3117 self.flush_deferred_ops(cx);
3118 self.did_edit(&old_version, was_dirty, BufferEditSource::Remote, cx);
3119 // Notify independently of whether the buffer was edited as the operations could include a
3120 // selection update.
3121 cx.notify();
3122 }
3123
3124 fn flush_deferred_ops(&mut self, cx: &mut Context<Self>) {
3125 let mut deferred_ops = Vec::new();
3126 for op in self.deferred_ops.drain().iter().cloned() {
3127 if self.can_apply_op(&op) {
3128 self.apply_op(op, cx);
3129 } else {
3130 deferred_ops.push(op);
3131 }
3132 }
3133 self.deferred_ops.insert(deferred_ops);
3134 }
3135
3136 pub fn has_deferred_ops(&self) -> bool {
3137 !self.deferred_ops.is_empty() || self.text.has_deferred_ops()
3138 }
3139
3140 fn can_apply_op(&self, operation: &Operation) -> bool {
3141 match operation {
3142 Operation::Buffer(_) => {
3143 unreachable!("buffer operations should never be applied at this layer")
3144 }
3145 Operation::UpdateDiagnostics {
3146 diagnostics: diagnostic_set,
3147 ..
3148 } => diagnostic_set.iter().all(|diagnostic| {
3149 self.text.can_resolve(&diagnostic.range.start)
3150 && self.text.can_resolve(&diagnostic.range.end)
3151 }),
3152 Operation::UpdateSelections { selections, .. } => selections
3153 .iter()
3154 .all(|s| self.can_resolve(&s.start) && self.can_resolve(&s.end)),
3155 Operation::UpdateCompletionTriggers { .. } | Operation::UpdateLineEnding { .. } => true,
3156 }
3157 }
3158
3159 fn apply_op(&mut self, operation: Operation, cx: &mut Context<Self>) {
3160 match operation {
3161 Operation::Buffer(_) => {
3162 unreachable!("buffer operations should never be applied at this layer")
3163 }
3164 Operation::UpdateDiagnostics {
3165 server_id,
3166 diagnostics: diagnostic_set,
3167 lamport_timestamp,
3168 } => {
3169 let snapshot = self.snapshot();
3170 self.apply_diagnostic_update(
3171 server_id,
3172 DiagnosticSet::from_sorted_entries(diagnostic_set.iter().cloned(), &snapshot),
3173 lamport_timestamp,
3174 cx,
3175 );
3176 }
3177 Operation::UpdateSelections {
3178 selections,
3179 lamport_timestamp,
3180 line_mode,
3181 cursor_shape,
3182 } => {
3183 if let Some(set) = self.remote_selections.get(&lamport_timestamp.replica_id)
3184 && set.lamport_timestamp > lamport_timestamp
3185 {
3186 return;
3187 }
3188
3189 self.remote_selections.insert(
3190 lamport_timestamp.replica_id,
3191 SelectionSet {
3192 selections,
3193 lamport_timestamp,
3194 line_mode,
3195 cursor_shape,
3196 },
3197 );
3198 self.text.lamport_clock.observe(lamport_timestamp);
3199 self.non_text_state_update_count += 1;
3200 }
3201 Operation::UpdateCompletionTriggers {
3202 triggers,
3203 lamport_timestamp,
3204 server_id,
3205 } => {
3206 if triggers.is_empty() {
3207 self.completion_triggers_per_language_server
3208 .remove(&server_id);
3209 } else {
3210 self.completion_triggers_per_language_server
3211 .insert(server_id, triggers.into_iter().collect());
3212 }
3213 self.completion_triggers = self
3214 .completion_triggers_per_language_server
3215 .values()
3216 .flat_map(|triggers| triggers.iter().cloned())
3217 .collect();
3218 self.text.lamport_clock.observe(lamport_timestamp);
3219 }
3220 Operation::UpdateLineEnding {
3221 line_ending,
3222 lamport_timestamp,
3223 } => {
3224 self.text.set_line_ending(line_ending);
3225 self.text.lamport_clock.observe(lamport_timestamp);
3226 }
3227 }
3228 }
3229
3230 fn apply_diagnostic_update(
3231 &mut self,
3232 server_id: LanguageServerId,
3233 diagnostics: DiagnosticSet,
3234 lamport_timestamp: clock::Lamport,
3235 cx: &mut Context<Self>,
3236 ) {
3237 if lamport_timestamp > self.diagnostics_timestamp {
3238 if diagnostics.is_empty() {
3239 self.diagnostics.remove(&server_id);
3240 } else {
3241 self.diagnostics.insert(server_id, diagnostics);
3242 }
3243 self.diagnostics_timestamp = lamport_timestamp;
3244 self.non_text_state_update_count += 1;
3245 self.text.lamport_clock.observe(lamport_timestamp);
3246 cx.notify();
3247 cx.emit(BufferEvent::DiagnosticsUpdated);
3248 }
3249 }
3250
3251 fn send_operation(&mut self, operation: Operation, is_local: bool, cx: &mut Context<Self>) {
3252 self.was_changed();
3253 cx.emit(BufferEvent::Operation {
3254 operation,
3255 is_local,
3256 });
3257 }
3258
3259 /// Removes the selections for a given peer.
3260 pub fn remove_peer(&mut self, replica_id: ReplicaId, cx: &mut Context<Self>) {
3261 self.remote_selections.remove(&replica_id);
3262 cx.notify();
3263 }
3264
3265 /// Undoes the most recent transaction.
3266 pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
3267 let was_dirty = self.is_dirty();
3268 let old_version = self.version.clone();
3269
3270 if let Some((transaction_id, operation)) = self.text.undo() {
3271 self.send_operation(Operation::Buffer(operation), true, cx);
3272 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
3273 self.restore_encoding_for_transaction(transaction_id, was_dirty);
3274 Some(transaction_id)
3275 } else {
3276 None
3277 }
3278 }
3279
3280 /// Manually undoes a specific transaction in the buffer's undo history.
3281 pub fn undo_transaction(
3282 &mut self,
3283 transaction_id: TransactionId,
3284 cx: &mut Context<Self>,
3285 ) -> bool {
3286 let was_dirty = self.is_dirty();
3287 let old_version = self.version.clone();
3288 if let Some(operation) = self.text.undo_transaction(transaction_id) {
3289 self.send_operation(Operation::Buffer(operation), true, cx);
3290 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
3291 true
3292 } else {
3293 false
3294 }
3295 }
3296
3297 /// Manually undoes all changes after a given transaction in the buffer's undo history.
3298 pub fn undo_to_transaction(
3299 &mut self,
3300 transaction_id: TransactionId,
3301 cx: &mut Context<Self>,
3302 ) -> bool {
3303 let was_dirty = self.is_dirty();
3304 let old_version = self.version.clone();
3305
3306 let operations = self.text.undo_to_transaction(transaction_id);
3307 let undone = !operations.is_empty();
3308 for operation in operations {
3309 self.send_operation(Operation::Buffer(operation), true, cx);
3310 }
3311 if undone {
3312 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx)
3313 }
3314 undone
3315 }
3316
3317 pub fn undo_operations(&mut self, counts: HashMap<Lamport, u32>, cx: &mut Context<Buffer>) {
3318 let was_dirty = self.is_dirty();
3319 let operation = self.text.undo_operations(counts);
3320 let old_version = self.version.clone();
3321 self.send_operation(Operation::Buffer(operation), true, cx);
3322 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
3323 }
3324
3325 /// Manually redoes a specific transaction in the buffer's redo history.
3326 pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
3327 let was_dirty = self.is_dirty();
3328 let old_version = self.version.clone();
3329
3330 if let Some((transaction_id, operation)) = self.text.redo() {
3331 self.send_operation(Operation::Buffer(operation), true, cx);
3332 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
3333 self.restore_encoding_for_transaction(transaction_id, was_dirty);
3334 Some(transaction_id)
3335 } else {
3336 None
3337 }
3338 }
3339
3340 fn restore_encoding_for_transaction(&mut self, transaction_id: TransactionId, was_dirty: bool) {
3341 if let Some((old_encoding, old_has_bom)) =
3342 self.reload_with_encoding_txns.get(&transaction_id)
3343 {
3344 let current_encoding = self.encoding;
3345 let current_has_bom = self.has_bom;
3346 self.encoding = *old_encoding;
3347 self.has_bom = *old_has_bom;
3348 if !was_dirty {
3349 self.saved_version = self.version.clone();
3350 self.has_unsaved_edits
3351 .set((self.saved_version.clone(), false));
3352 }
3353 self.reload_with_encoding_txns
3354 .insert(transaction_id, (current_encoding, current_has_bom));
3355 }
3356 }
3357
3358 /// Manually undoes all changes until a given transaction in the buffer's redo history.
3359 pub fn redo_to_transaction(
3360 &mut self,
3361 transaction_id: TransactionId,
3362 cx: &mut Context<Self>,
3363 ) -> bool {
3364 let was_dirty = self.is_dirty();
3365 let old_version = self.version.clone();
3366
3367 let operations = self.text.redo_to_transaction(transaction_id);
3368 let redone = !operations.is_empty();
3369 for operation in operations {
3370 self.send_operation(Operation::Buffer(operation), true, cx);
3371 }
3372 if redone {
3373 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx)
3374 }
3375 redone
3376 }
3377
3378 /// Override current completion triggers with the user-provided completion triggers.
3379 pub fn set_completion_triggers(
3380 &mut self,
3381 server_id: LanguageServerId,
3382 triggers: BTreeSet<String>,
3383 cx: &mut Context<Self>,
3384 ) {
3385 self.completion_triggers_timestamp = self.text.lamport_clock.tick();
3386 if triggers.is_empty() {
3387 self.completion_triggers_per_language_server
3388 .remove(&server_id);
3389 } else {
3390 self.completion_triggers_per_language_server
3391 .insert(server_id, triggers.clone());
3392 }
3393 self.completion_triggers = self
3394 .completion_triggers_per_language_server
3395 .values()
3396 .flat_map(|triggers| triggers.iter().cloned())
3397 .collect();
3398 self.send_operation(
3399 Operation::UpdateCompletionTriggers {
3400 triggers: triggers.into_iter().collect(),
3401 lamport_timestamp: self.completion_triggers_timestamp,
3402 server_id,
3403 },
3404 true,
3405 cx,
3406 );
3407 cx.notify();
3408 }
3409
3410 /// Returns a list of strings which trigger a completion menu for this language.
3411 /// Usually this is driven by LSP server which returns a list of trigger characters for completions.
3412 pub fn completion_triggers(&self) -> &BTreeSet<String> {
3413 &self.completion_triggers
3414 }
3415
3416 /// Call this directly after performing edits to prevent the preview tab
3417 /// from being dismissed by those edits. It causes `should_dismiss_preview`
3418 /// to return false until there are additional edits.
3419 pub fn refresh_preview(&mut self) {
3420 self.preview_version = self.version.clone();
3421 }
3422
3423 /// Whether we should preserve the preview status of a tab containing this buffer.
3424 pub fn preserve_preview(&self) -> bool {
3425 !self.has_edits_since(&self.preview_version)
3426 }
3427
3428 pub fn set_group_interval(&mut self, group_interval: Duration) {
3429 self.text.set_group_interval(group_interval);
3430 }
3431
3432 // TODO: see if ep can use this instead of Buffer::branch
3433 pub fn snapshot_with_edits<I, S, T>(
3434 &mut self,
3435 edits: I,
3436 cx: &mut Context<Self>,
3437 ) -> Task<EditedBufferSnapshot>
3438 where
3439 I: IntoIterator<Item = (Range<S>, T)>,
3440 S: ToOffset,
3441 T: Into<Arc<str>>,
3442 {
3443 let mut snapshot = self.snapshot();
3444 let text = snapshot.text.clone();
3445 let mut syntax = snapshot.syntax.clone();
3446 let language = self.language().cloned();
3447 let registry = self.language_registry();
3448 let new_text = self.text.snapshot_with_edits(edits);
3449 cx.background_spawn(async move {
3450 if let Some(language) = language.clone() {
3451 syntax.reparse(&text, registry.clone(), language);
3452 }
3453
3454 syntax.interpolate(&new_text.snapshot);
3455
3456 if let Some(language) = language {
3457 syntax.reparse(&new_text.snapshot, registry, language);
3458 }
3459
3460 snapshot.text = new_text.snapshot.clone();
3461 snapshot.syntax = syntax;
3462
3463 EditedBufferSnapshot {
3464 text: new_text,
3465 snapshot,
3466 }
3467 })
3468 }
3469
3470 pub fn fast_forward(&mut self, edited: EditedBufferSnapshot, cx: &mut Context<Self>) {
3471 let base_version = edited.text.base_version.clone();
3472 let did_edit = edited.text.did_edit;
3473 self.text.fast_forward(edited.text);
3474 if edited.snapshot.language == self.language {
3475 self.reparse = None;
3476 self.did_finish_parsing(edited.snapshot.syntax, None, cx);
3477 if did_edit {
3478 cx.emit(BufferEvent::Edited {
3479 source: BufferEditSource::User,
3480 });
3481 }
3482 } else {
3483 self.did_edit(&base_version, false, BufferEditSource::User, cx);
3484 }
3485 }
3486}
3487
3488pub struct EditedBufferSnapshot {
3489 text: text::EditedBufferSnapshot,
3490 snapshot: BufferSnapshot,
3491}
3492
3493impl EditedBufferSnapshot {
3494 pub fn snapshot(&self) -> &BufferSnapshot {
3495 &self.snapshot
3496 }
3497
3498 pub fn base_version(&self) -> &clock::Global {
3499 &self.text.base_version
3500 }
3501}
3502
3503#[doc(hidden)]
3504#[cfg(any(test, feature = "test-support"))]
3505impl Buffer {
3506 pub fn edit_via_marked_text(
3507 &mut self,
3508 marked_string: &str,
3509 autoindent_mode: Option<AutoindentMode>,
3510 cx: &mut Context<Self>,
3511 ) {
3512 let edits = self.edits_for_marked_text(marked_string);
3513 self.edit(edits, autoindent_mode, cx);
3514 }
3515
3516 pub fn randomly_edit<T>(&mut self, rng: &mut T, old_range_count: usize, cx: &mut Context<Self>)
3517 where
3518 T: rand::Rng,
3519 {
3520 let mut edits: Vec<(Range<usize>, String)> = Vec::new();
3521 let mut last_end = None;
3522 for _ in 0..old_range_count {
3523 if last_end.is_some_and(|last_end| last_end >= self.len()) {
3524 break;
3525 }
3526
3527 let new_start = last_end.map_or(0, |last_end| last_end + 1);
3528 let mut range = self.random_byte_range(new_start, rng);
3529 if rng.random_bool(0.2) {
3530 mem::swap(&mut range.start, &mut range.end);
3531 }
3532 last_end = Some(range.end);
3533
3534 let new_text_len = rng.random_range(0..10);
3535 let mut new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
3536 new_text = new_text.to_uppercase();
3537
3538 edits.push((range, new_text));
3539 }
3540 log::info!("mutating buffer {:?} with {:?}", self.replica_id(), edits);
3541 self.edit(edits, None, cx);
3542 }
3543
3544 pub fn randomly_undo_redo(&mut self, rng: &mut impl rand::Rng, cx: &mut Context<Self>) {
3545 let was_dirty = self.is_dirty();
3546 let old_version = self.version.clone();
3547
3548 let ops = self.text.randomly_undo_redo(rng);
3549 if !ops.is_empty() {
3550 for op in ops {
3551 self.send_operation(Operation::Buffer(op), true, cx);
3552 self.did_edit(&old_version, was_dirty, BufferEditSource::User, cx);
3553 }
3554 }
3555 }
3556}
3557
3558impl EventEmitter<BufferEvent> for Buffer {}
3559
3560fn offset_in_sub_ranges(
3561 sub_ranges: &[Range<Anchor>],
3562 offset: usize,
3563 snapshot: &TextBufferSnapshot,
3564) -> bool {
3565 let start_anchor = snapshot.anchor_before(offset);
3566 let end_anchor = snapshot.anchor_after(offset);
3567
3568 sub_ranges.iter().any(|sub_range| {
3569 let is_before_start = sub_range.end.cmp(&start_anchor, snapshot).is_lt();
3570 let is_after_end = sub_range.start.cmp(&end_anchor, snapshot).is_gt();
3571 !is_before_start && !is_after_end
3572 })
3573}
3574
3575impl Deref for Buffer {
3576 type Target = TextBuffer;
3577
3578 fn deref(&self) -> &Self::Target {
3579 &self.text
3580 }
3581}
3582
3583impl BufferSnapshot {
3584 /// Returns [`IndentSize`] for a given line that respects user settings and
3585 /// language preferences.
3586 pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
3587 indent_size_for_line(self, row)
3588 }
3589
3590 /// Returns [`IndentSize`] for a given position that respects user settings
3591 /// and language preferences.
3592 pub fn language_indent_size_at<T: ToOffset>(&self, position: T, cx: &App) -> IndentSize {
3593 let settings = self.settings_at(position, cx);
3594 if settings.hard_tabs {
3595 IndentSize::tab()
3596 } else {
3597 IndentSize::spaces(settings.tab_size.get())
3598 }
3599 }
3600
3601 /// Retrieve the suggested indent size for all of the given rows. The unit of indentation
3602 /// is passed in as `single_indent_size`.
3603 pub fn suggested_indents(
3604 &self,
3605 rows: impl Iterator<Item = u32>,
3606 single_indent_size: IndentSize,
3607 ) -> BTreeMap<u32, IndentSize> {
3608 let mut result = BTreeMap::new();
3609
3610 for row_range in contiguous_ranges(rows, 10) {
3611 let suggestions = match self.suggest_autoindents(row_range.clone()) {
3612 Some(suggestions) => suggestions,
3613 _ => break,
3614 };
3615
3616 for (row, suggestion) in row_range.zip(suggestions) {
3617 let indent_size = if let Some(suggestion) = suggestion {
3618 result
3619 .get(&suggestion.basis_row)
3620 .copied()
3621 .unwrap_or_else(|| self.indent_size_for_line(suggestion.basis_row))
3622 .with_delta(suggestion.delta, single_indent_size)
3623 } else {
3624 self.indent_size_for_line(row)
3625 };
3626
3627 result.insert(row, indent_size);
3628 }
3629 }
3630
3631 result
3632 }
3633
3634 fn suggest_autoindents(
3635 &self,
3636 row_range: Range<u32>,
3637 ) -> Option<impl Iterator<Item = Option<IndentSuggestion>> + '_> {
3638 let config = &self.language.as_ref()?.config;
3639 let prev_non_blank_row = self.prev_non_blank_row(row_range.start);
3640
3641 #[derive(Debug, Clone)]
3642 struct StartPosition {
3643 start: Point,
3644 suffix: SharedString,
3645 language: Arc<Language>,
3646 }
3647
3648 // Find the suggested indentation ranges based on the syntax tree.
3649 let start = Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0);
3650 let end = Point::new(row_range.end, 0);
3651 let range = (start..end).to_offset(&self.text);
3652 let mut matches = self.syntax.matches_with_options(
3653 range.clone(),
3654 &self.text,
3655 TreeSitterOptions {
3656 max_bytes_to_query: Some(MAX_BYTES_TO_QUERY),
3657 max_start_depth: None,
3658 },
3659 |grammar| Some(&grammar.indents_config.as_ref()?.query),
3660 );
3661 let indent_configs = matches
3662 .grammars()
3663 .iter()
3664 .map(|grammar| {
3665 grammar
3666 .indents_config
3667 .as_ref()
3668 .expect("grammar in indent match set has indents_config")
3669 })
3670 .collect::<Vec<_>>();
3671
3672 let mut indent_ranges = Vec::<Range<Point>>::new();
3673 let mut start_positions = Vec::<StartPosition>::new();
3674 let mut outdent_positions = Vec::<Point>::new();
3675 while let Some(mat) = matches.peek() {
3676 let mut start: Option<Point> = None;
3677 let mut end: Option<Point> = None;
3678
3679 let config = indent_configs[mat.grammar_index];
3680 for capture in mat.captures {
3681 if capture.index == config.indent_capture_ix {
3682 start.get_or_insert(Point::from_ts_point(capture.node.start_position()));
3683 end.get_or_insert(Point::from_ts_point(capture.node.end_position()));
3684 } else if Some(capture.index) == config.start_capture_ix {
3685 start = Some(Point::from_ts_point(capture.node.end_position()));
3686 } else if Some(capture.index) == config.end_capture_ix {
3687 end = Some(Point::from_ts_point(capture.node.start_position()));
3688 } else if Some(capture.index) == config.outdent_capture_ix {
3689 outdent_positions.push(Point::from_ts_point(capture.node.start_position()));
3690 } else if let Some(suffix) = config.suffixed_start_captures.get(&capture.index) {
3691 start_positions.push(StartPosition {
3692 start: Point::from_ts_point(capture.node.start_position()),
3693 suffix: suffix.clone(),
3694 language: mat.language.clone(),
3695 });
3696 }
3697 }
3698
3699 matches.advance();
3700 if let Some((start, end)) = start.zip(end) {
3701 if start.row == end.row {
3702 continue;
3703 }
3704 let range = start..end;
3705 match indent_ranges.binary_search_by_key(&range.start, |r| r.start) {
3706 Err(ix) => indent_ranges.insert(ix, range),
3707 Ok(ix) => {
3708 let prev_range = &mut indent_ranges[ix];
3709 prev_range.end = prev_range.end.max(range.end);
3710 }
3711 }
3712 }
3713 }
3714
3715 let mut error_ranges = Vec::<Range<Point>>::new();
3716 let mut matches = self
3717 .syntax
3718 .matches(range, &self.text, |grammar| grammar.error_query.as_ref());
3719 while let Some(mat) = matches.peek() {
3720 let node = mat.captures[0].node;
3721 let start = Point::from_ts_point(node.start_position());
3722 let end = Point::from_ts_point(node.end_position());
3723 let range = start..end;
3724 let ix = match error_ranges.binary_search_by_key(&range.start, |r| r.start) {
3725 Ok(ix) | Err(ix) => ix,
3726 };
3727 let mut end_ix = ix;
3728 while let Some(existing_range) = error_ranges.get(end_ix) {
3729 if existing_range.end < end {
3730 end_ix += 1;
3731 } else {
3732 break;
3733 }
3734 }
3735 error_ranges.splice(ix..end_ix, [range]);
3736 matches.advance();
3737 }
3738
3739 outdent_positions.sort();
3740 for outdent_position in outdent_positions {
3741 // find the innermost indent range containing this outdent_position
3742 // set its end to the outdent position
3743 if let Some(range_to_truncate) = indent_ranges
3744 .iter_mut()
3745 .rfind(|indent_range| indent_range.contains(&outdent_position))
3746 {
3747 range_to_truncate.end = outdent_position;
3748 }
3749 }
3750
3751 start_positions.sort_by_key(|b| b.start);
3752
3753 // Find the suggested indentation increases and decreased based on regexes.
3754 let mut regex_outdent_map = HashMap::default();
3755 let mut last_seen_suffix: HashMap<String, Vec<StartPosition>> = HashMap::default();
3756 let mut start_positions_iter = start_positions.iter().peekable();
3757
3758 let mut indent_change_rows = Vec::<(u32, Ordering)>::new();
3759 self.for_each_line(
3760 Point::new(prev_non_blank_row.unwrap_or(row_range.start), 0)
3761 ..Point::new(row_range.end, 0),
3762 |row, line| {
3763 let indent_len = self.indent_size_for_line(row).len;
3764 let row_language = self.language_at(Point::new(row, indent_len)).cloned();
3765 let row_language_config = row_language
3766 .as_ref()
3767 .map(|lang| lang.config())
3768 .unwrap_or(config);
3769
3770 if row_language_config
3771 .decrease_indent_pattern
3772 .as_ref()
3773 .is_some_and(|regex| regex.is_match(line))
3774 {
3775 indent_change_rows.push((row, Ordering::Less));
3776 }
3777 if row_language_config
3778 .increase_indent_pattern
3779 .as_ref()
3780 .is_some_and(|regex| regex.is_match(line))
3781 {
3782 indent_change_rows.push((row + 1, Ordering::Greater));
3783 }
3784 while let Some(pos) = start_positions_iter.peek() {
3785 if pos.start.row < row {
3786 let pos = start_positions_iter.next().unwrap().clone();
3787 last_seen_suffix
3788 .entry(pos.suffix.to_string())
3789 .or_default()
3790 .push(pos);
3791 } else {
3792 break;
3793 }
3794 }
3795 for rule in &row_language_config.decrease_indent_patterns {
3796 if rule.pattern.as_ref().is_some_and(|r| r.is_match(line)) {
3797 let row_start_column = self.indent_size_for_line(row).len;
3798 let basis_row = rule
3799 .valid_after
3800 .iter()
3801 .filter_map(|valid_suffix| last_seen_suffix.get(valid_suffix))
3802 .flatten()
3803 .filter(|pos| {
3804 row_language
3805 .as_ref()
3806 .or(self.language.as_ref())
3807 .is_some_and(|lang| Arc::ptr_eq(lang, &pos.language))
3808 })
3809 .filter(|pos| pos.start.column <= row_start_column)
3810 .max_by_key(|pos| pos.start.row);
3811 if let Some(outdent_to) = basis_row {
3812 regex_outdent_map.insert(row, outdent_to.start.row);
3813 }
3814 break;
3815 }
3816 }
3817 },
3818 );
3819
3820 let mut indent_changes = indent_change_rows.into_iter().peekable();
3821 let mut prev_row = if config.auto_indent_using_last_non_empty_line {
3822 prev_non_blank_row.unwrap_or(0)
3823 } else {
3824 row_range.start.saturating_sub(1)
3825 };
3826
3827 let mut prev_row_start = Point::new(prev_row, self.indent_size_for_line(prev_row).len);
3828 Some(row_range.map(move |row| {
3829 let row_start = Point::new(row, self.indent_size_for_line(row).len);
3830
3831 let mut indent_from_prev_row = false;
3832 let mut outdent_from_prev_row = false;
3833 let mut outdent_to_row = u32::MAX;
3834 let mut from_regex = false;
3835
3836 while let Some((indent_row, delta)) = indent_changes.peek() {
3837 match indent_row.cmp(&row) {
3838 Ordering::Equal => match delta {
3839 Ordering::Less => {
3840 from_regex = true;
3841 outdent_from_prev_row = true
3842 }
3843 Ordering::Greater => {
3844 indent_from_prev_row = true;
3845 from_regex = true
3846 }
3847 _ => {}
3848 },
3849
3850 Ordering::Greater => break,
3851 Ordering::Less => {}
3852 }
3853
3854 indent_changes.next();
3855 }
3856
3857 for range in &indent_ranges {
3858 if range.start.row >= row {
3859 break;
3860 }
3861 if range.start.row == prev_row && range.end > row_start {
3862 indent_from_prev_row = true;
3863 }
3864 if range.end > prev_row_start && range.end <= row_start {
3865 outdent_to_row = outdent_to_row.min(range.start.row);
3866 }
3867 }
3868
3869 if let Some(basis_row) = regex_outdent_map.get(&row) {
3870 indent_from_prev_row = false;
3871 outdent_to_row = *basis_row;
3872 from_regex = true;
3873 }
3874
3875 let within_error = error_ranges
3876 .iter()
3877 .any(|e| e.start.row < row && e.end > row_start);
3878
3879 let suggestion = if outdent_to_row == prev_row
3880 || (outdent_from_prev_row && indent_from_prev_row)
3881 {
3882 Some(IndentSuggestion {
3883 basis_row: prev_row,
3884 delta: Ordering::Equal,
3885 within_error: within_error && !from_regex,
3886 })
3887 } else if indent_from_prev_row {
3888 Some(IndentSuggestion {
3889 basis_row: prev_row,
3890 delta: Ordering::Greater,
3891 within_error: within_error && !from_regex,
3892 })
3893 } else if outdent_to_row < prev_row {
3894 Some(IndentSuggestion {
3895 basis_row: outdent_to_row,
3896 delta: Ordering::Equal,
3897 within_error: within_error && !from_regex,
3898 })
3899 } else if outdent_from_prev_row {
3900 Some(IndentSuggestion {
3901 basis_row: prev_row,
3902 delta: Ordering::Less,
3903 within_error: within_error && !from_regex,
3904 })
3905 } else if config.auto_indent_using_last_non_empty_line || !self.is_line_blank(prev_row)
3906 {
3907 Some(IndentSuggestion {
3908 basis_row: prev_row,
3909 delta: Ordering::Equal,
3910 within_error: within_error && !from_regex,
3911 })
3912 } else {
3913 None
3914 };
3915
3916 prev_row = row;
3917 prev_row_start = row_start;
3918 suggestion
3919 }))
3920 }
3921
3922 fn prev_non_blank_row(&self, mut row: u32) -> Option<u32> {
3923 while row > 0 {
3924 row -= 1;
3925 if !self.is_line_blank(row) {
3926 return Some(row);
3927 }
3928 }
3929 None
3930 }
3931
3932 pub fn captures(
3933 &self,
3934 range: Range<usize>,
3935 query: fn(&Grammar) -> Option<&tree_sitter::Query>,
3936 ) -> SyntaxMapCaptures<'_> {
3937 self.syntax.captures(range, &self.text, query)
3938 }
3939
3940 #[ztracing::instrument(skip_all)]
3941 fn get_highlights(&self, range: Range<usize>) -> (SyntaxMapCaptures<'_>, Vec<HighlightMap>) {
3942 let captures = self.syntax.captures(range, &self.text, |grammar| {
3943 grammar
3944 .highlights_config
3945 .as_ref()
3946 .map(|config| &config.query)
3947 });
3948 let highlight_maps = captures
3949 .grammars()
3950 .iter()
3951 .map(|grammar| grammar.highlight_map())
3952 .collect();
3953 (captures, highlight_maps)
3954 }
3955
3956 /// Iterates over chunks of text in the given range of the buffer. Text is chunked
3957 /// in an arbitrary way due to being stored in a [`Rope`](text::Rope). The text is also
3958 /// returned in chunks where each chunk has a single syntax highlighting style and
3959 /// diagnostic status.
3960 #[ztracing::instrument(skip_all)]
3961 pub fn chunks<T: ToOffset>(
3962 &self,
3963 range: Range<T>,
3964 language_aware: LanguageAwareStyling,
3965 ) -> BufferChunks<'_> {
3966 let range = range.start.to_offset(self)..range.end.to_offset(self);
3967
3968 let mut syntax = None;
3969 if language_aware.tree_sitter {
3970 syntax = Some(self.get_highlights(range.clone()));
3971 }
3972 BufferChunks::new(
3973 self.text.as_rope(),
3974 range,
3975 syntax,
3976 language_aware.diagnostics,
3977 Some(self),
3978 )
3979 }
3980
3981 pub fn highlighted_text_for_range<T: ToOffset>(
3982 &self,
3983 range: Range<T>,
3984 override_style: Option<HighlightStyle>,
3985 syntax_theme: &SyntaxTheme,
3986 ) -> HighlightedText {
3987 HighlightedText::from_buffer_range(
3988 range,
3989 &self.text,
3990 &self.syntax,
3991 override_style,
3992 syntax_theme,
3993 )
3994 }
3995
3996 /// Invokes the given callback for each line of text in the given range of the buffer.
3997 /// Uses callback to avoid allocating a string for each line.
3998 fn for_each_line(&self, range: Range<Point>, mut callback: impl FnMut(u32, &str)) {
3999 let mut line = String::new();
4000 let mut row = range.start.row;
4001 for chunk in self
4002 .as_rope()
4003 .chunks_in_range(range.to_offset(self))
4004 .chain(["\n"])
4005 {
4006 for (newline_ix, text) in chunk.split('\n').enumerate() {
4007 if newline_ix > 0 {
4008 callback(row, &line);
4009 row += 1;
4010 line.clear();
4011 }
4012 line.push_str(text);
4013 }
4014 }
4015 }
4016
4017 /// Iterates over every [`SyntaxLayer`] in the buffer.
4018 pub fn syntax_layers(&self) -> impl Iterator<Item = SyntaxLayer<'_>> + '_ {
4019 self.syntax_layers_for_range(0..self.len(), true)
4020 }
4021
4022 pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer<'_>> {
4023 let offset = position.to_offset(self);
4024 self.syntax_layers_for_range(offset..offset, false)
4025 .filter(|l| {
4026 if let Some(ranges) = l.included_sub_ranges {
4027 ranges.iter().any(|range| {
4028 let start = range.start.to_offset(self);
4029 start <= offset && {
4030 let end = range.end.to_offset(self);
4031 offset < end
4032 }
4033 })
4034 } else {
4035 l.node().start_byte() <= offset && l.node().end_byte() > offset
4036 }
4037 })
4038 .last()
4039 }
4040
4041 pub fn syntax_layers_for_range<D: ToOffset>(
4042 &self,
4043 range: Range<D>,
4044 include_hidden: bool,
4045 ) -> impl Iterator<Item = SyntaxLayer<'_>> + '_ {
4046 self.syntax
4047 .layers_for_range(range, &self.text, include_hidden)
4048 }
4049
4050 pub fn syntax_layers_languages(&self) -> impl Iterator<Item = &Arc<Language>> {
4051 self.syntax.languages(&self, true)
4052 }
4053
4054 pub fn smallest_syntax_layer_containing<D: ToOffset>(
4055 &self,
4056 range: Range<D>,
4057 ) -> Option<SyntaxLayer<'_>> {
4058 let range = range.to_offset(self);
4059 self.syntax
4060 .layers_for_range(range, &self.text, false)
4061 .max_by(|a, b| {
4062 if a.depth != b.depth {
4063 a.depth.cmp(&b.depth)
4064 } else if a.offset.0 != b.offset.0 {
4065 a.offset.0.cmp(&b.offset.0)
4066 } else {
4067 a.node().end_byte().cmp(&b.node().end_byte()).reverse()
4068 }
4069 })
4070 }
4071
4072 /// Returns the [`ModelineSettings`].
4073 pub fn modeline(&self) -> Option<&Arc<ModelineSettings>> {
4074 self.modeline.as_ref()
4075 }
4076
4077 /// Returns the main [`Language`].
4078 pub fn language(&self) -> Option<&Arc<Language>> {
4079 self.language.as_ref()
4080 }
4081
4082 /// Returns the [`Language`] at the given location.
4083 pub fn language_at<D: ToOffset>(&self, position: D) -> Option<&Arc<Language>> {
4084 self.syntax_layer_at(position)
4085 .map(|info| info.language)
4086 .or(self.language.as_ref())
4087 }
4088
4089 /// Returns the settings for the language at the given location.
4090 pub fn settings_at<'a, D: ToOffset>(
4091 &'a self,
4092 position: D,
4093 cx: &'a App,
4094 ) -> Cow<'a, LanguageSettings> {
4095 LanguageSettings::for_buffer_snapshot(self, Some(position.to_offset(self)), cx)
4096 }
4097
4098 pub fn char_classifier_at<T: ToOffset>(&self, point: T) -> CharClassifier {
4099 CharClassifier::new(self.language_scope_at(point))
4100 }
4101
4102 /// Returns the [`LanguageScope`] at the given location.
4103 pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
4104 let offset = position.to_offset(self);
4105 let mut scope = None;
4106 let mut smallest_range_and_depth: Option<(Range<usize>, usize)> = None;
4107 let text: &TextBufferSnapshot = self;
4108
4109 // Use the layer that has the smallest node intersecting the given point.
4110 for layer in self
4111 .syntax
4112 .layers_for_range(offset..offset, &self.text, false)
4113 {
4114 if let Some(ranges) = layer.included_sub_ranges
4115 && !offset_in_sub_ranges(ranges, offset, text)
4116 {
4117 continue;
4118 }
4119
4120 let mut cursor = layer.node().walk();
4121
4122 let mut range = None;
4123 loop {
4124 let child_range = cursor.node().byte_range();
4125 if !child_range.contains(&offset) {
4126 break;
4127 }
4128
4129 range = Some(child_range);
4130 if cursor.goto_first_child_for_byte(offset).is_none() {
4131 break;
4132 }
4133 }
4134
4135 if let Some(range) = range
4136 && smallest_range_and_depth.as_ref().is_none_or(
4137 |(smallest_range, smallest_range_depth)| {
4138 if layer.depth > *smallest_range_depth {
4139 true
4140 } else if layer.depth == *smallest_range_depth {
4141 range.len() < smallest_range.len()
4142 } else {
4143 false
4144 }
4145 },
4146 )
4147 {
4148 smallest_range_and_depth = Some((range, layer.depth));
4149 scope = Some(LanguageScope {
4150 language: layer.language.clone(),
4151 override_id: layer.override_id(offset, &self.text),
4152 });
4153 }
4154 }
4155
4156 scope.or_else(|| {
4157 self.language.clone().map(|language| LanguageScope {
4158 language,
4159 override_id: None,
4160 })
4161 })
4162 }
4163
4164 /// Returns a tuple of the range and character kind of the word
4165 /// surrounding the given position.
4166 pub fn surrounding_word<T: ToOffset>(
4167 &self,
4168 start: T,
4169 scope_context: Option<CharScopeContext>,
4170 ) -> (Range<usize>, Option<CharKind>) {
4171 let mut start = start.to_offset(self);
4172 let mut end = start;
4173 let mut next_chars = self.chars_at(start).take(128).peekable();
4174 let mut prev_chars = self.reversed_chars_at(start).take(128).peekable();
4175
4176 let classifier = self.char_classifier_at(start).scope_context(scope_context);
4177 let word_kind = cmp::max(
4178 prev_chars.peek().copied().map(|c| classifier.kind(c)),
4179 next_chars.peek().copied().map(|c| classifier.kind(c)),
4180 );
4181
4182 for ch in prev_chars {
4183 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4184 start -= ch.len_utf8();
4185 } else {
4186 break;
4187 }
4188 }
4189
4190 for ch in next_chars {
4191 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4192 end += ch.len_utf8();
4193 } else {
4194 break;
4195 }
4196 }
4197
4198 (start..end, word_kind)
4199 }
4200
4201 /// Moves the TreeCursor to the smallest descendant or ancestor syntax node enclosing the given
4202 /// range. When `require_larger` is true, the node found must be larger than the query range.
4203 ///
4204 /// Returns true if a node was found, and false otherwise. In the `false` case the cursor will
4205 /// be moved to the root of the tree.
4206 fn goto_node_enclosing_range(
4207 cursor: &mut tree_sitter::TreeCursor,
4208 query_range: &Range<usize>,
4209 require_larger: bool,
4210 ) -> bool {
4211 let mut ascending = false;
4212 loop {
4213 let mut range = cursor.node().byte_range();
4214 if query_range.is_empty() {
4215 // When the query range is empty and the current node starts after it, move to the
4216 // previous sibling to find the node the containing node.
4217 if range.start > query_range.start {
4218 cursor.goto_previous_sibling();
4219 range = cursor.node().byte_range();
4220 }
4221 } else {
4222 // When the query range is non-empty and the current node ends exactly at the start,
4223 // move to the next sibling to find a node that extends beyond the start.
4224 if range.end == query_range.start {
4225 cursor.goto_next_sibling();
4226 range = cursor.node().byte_range();
4227 }
4228 }
4229
4230 let encloses = range.contains_inclusive(query_range)
4231 && (!require_larger || range.len() > query_range.len());
4232 if !encloses {
4233 ascending = true;
4234 if !cursor.goto_parent() {
4235 return false;
4236 }
4237 continue;
4238 } else if ascending {
4239 return true;
4240 }
4241
4242 // Descend into the current node.
4243 if cursor
4244 .goto_first_child_for_byte(query_range.start)
4245 .is_none()
4246 {
4247 return true;
4248 }
4249 }
4250 }
4251
4252 pub fn syntax_ancestor<'a, T: ToOffset>(
4253 &'a self,
4254 range: Range<T>,
4255 ) -> Option<tree_sitter::Node<'a>> {
4256 let range = range.start.to_offset(self)..range.end.to_offset(self);
4257 let mut result: Option<tree_sitter::Node<'a>> = None;
4258 for layer in self
4259 .syntax
4260 .layers_for_range(range.clone(), &self.text, true)
4261 {
4262 let mut cursor = layer.node().walk();
4263
4264 // Find the node that both contains the range and is larger than it.
4265 if !Self::goto_node_enclosing_range(&mut cursor, &range, true) {
4266 continue;
4267 }
4268
4269 let left_node = cursor.node();
4270 let mut layer_result = left_node;
4271
4272 // For an empty range, try to find another node immediately to the right of the range.
4273 if left_node.end_byte() == range.start {
4274 let mut right_node = None;
4275 while !cursor.goto_next_sibling() {
4276 if !cursor.goto_parent() {
4277 break;
4278 }
4279 }
4280
4281 while cursor.node().start_byte() == range.start {
4282 right_node = Some(cursor.node());
4283 if !cursor.goto_first_child() {
4284 break;
4285 }
4286 }
4287
4288 // If there is a candidate node on both sides of the (empty) range, then
4289 // decide between the two by favoring a named node over an anonymous token.
4290 // If both nodes are the same in that regard, favor the right one.
4291 if let Some(right_node) = right_node
4292 && (right_node.is_named() || !left_node.is_named())
4293 {
4294 layer_result = right_node;
4295 }
4296 }
4297
4298 if let Some(previous_result) = &result
4299 && previous_result.byte_range().len() < layer_result.byte_range().len()
4300 {
4301 continue;
4302 }
4303 result = Some(layer_result);
4304 }
4305
4306 result
4307 }
4308
4309 /// Find the previous sibling syntax node at the given range.
4310 ///
4311 /// This function locates the syntax node that precedes the node containing
4312 /// the given range. It searches hierarchically by:
4313 /// 1. Finding the node that contains the given range
4314 /// 2. Looking for the previous sibling at the same tree level
4315 /// 3. If no sibling is found, moving up to parent levels and searching for siblings
4316 ///
4317 /// Returns `None` if there is no previous sibling at any ancestor level.
4318 pub fn syntax_prev_sibling<'a, T: ToOffset>(
4319 &'a self,
4320 range: Range<T>,
4321 ) -> Option<tree_sitter::Node<'a>> {
4322 let range = range.start.to_offset(self)..range.end.to_offset(self);
4323 let mut result: Option<tree_sitter::Node<'a>> = None;
4324
4325 for layer in self
4326 .syntax
4327 .layers_for_range(range.clone(), &self.text, true)
4328 {
4329 let mut cursor = layer.node().walk();
4330
4331 // Find the node that contains the range
4332 if !Self::goto_node_enclosing_range(&mut cursor, &range, false) {
4333 continue;
4334 }
4335
4336 // Look for the previous sibling, moving up ancestor levels if needed
4337 loop {
4338 if cursor.goto_previous_sibling() {
4339 let layer_result = cursor.node();
4340
4341 if let Some(previous_result) = &result {
4342 if previous_result.byte_range().end < layer_result.byte_range().end {
4343 continue;
4344 }
4345 }
4346 result = Some(layer_result);
4347 break;
4348 }
4349
4350 // No sibling found at this level, try moving up to parent
4351 if !cursor.goto_parent() {
4352 break;
4353 }
4354 }
4355 }
4356
4357 result
4358 }
4359
4360 /// Find the next sibling syntax node at the given range.
4361 ///
4362 /// This function locates the syntax node that follows the node containing
4363 /// the given range. It searches hierarchically by:
4364 /// 1. Finding the node that contains the given range
4365 /// 2. Looking for the next sibling at the same tree level
4366 /// 3. If no sibling is found, moving up to parent levels and searching for siblings
4367 ///
4368 /// Returns `None` if there is no next sibling at any ancestor level.
4369 pub fn syntax_next_sibling<'a, T: ToOffset>(
4370 &'a self,
4371 range: Range<T>,
4372 ) -> Option<tree_sitter::Node<'a>> {
4373 let range = range.start.to_offset(self)..range.end.to_offset(self);
4374 let mut result: Option<tree_sitter::Node<'a>> = None;
4375
4376 for layer in self
4377 .syntax
4378 .layers_for_range(range.clone(), &self.text, true)
4379 {
4380 let mut cursor = layer.node().walk();
4381
4382 // Find the node that contains the range
4383 if !Self::goto_node_enclosing_range(&mut cursor, &range, false) {
4384 continue;
4385 }
4386
4387 // Look for the next sibling, moving up ancestor levels if needed
4388 loop {
4389 if cursor.goto_next_sibling() {
4390 let layer_result = cursor.node();
4391
4392 if let Some(previous_result) = &result {
4393 if previous_result.byte_range().start > layer_result.byte_range().start {
4394 continue;
4395 }
4396 }
4397 result = Some(layer_result);
4398 break;
4399 }
4400
4401 // No sibling found at this level, try moving up to parent
4402 if !cursor.goto_parent() {
4403 break;
4404 }
4405 }
4406 }
4407
4408 result
4409 }
4410
4411 /// Returns the root syntax node within the given row
4412 pub fn syntax_root_ancestor(&self, position: Anchor) -> Option<tree_sitter::Node<'_>> {
4413 let start_offset = position.to_offset(self);
4414
4415 let row = self.summary_for_anchor::<text::PointUtf16>(&position).row as usize;
4416
4417 let layer = self
4418 .syntax
4419 .layers_for_range(start_offset..start_offset, &self.text, true)
4420 .next()?;
4421
4422 let mut cursor = layer.node().walk();
4423
4424 // Descend to the first leaf that touches the start of the range.
4425 while cursor.goto_first_child_for_byte(start_offset).is_some() {
4426 if cursor.node().end_byte() == start_offset {
4427 cursor.goto_next_sibling();
4428 }
4429 }
4430
4431 // Ascend to the root node within the same row.
4432 while cursor.goto_parent() {
4433 if cursor.node().start_position().row != row {
4434 break;
4435 }
4436 }
4437
4438 Some(cursor.node())
4439 }
4440
4441 /// Returns the outline for the buffer.
4442 ///
4443 /// This method allows passing an optional [`SyntaxTheme`] to
4444 /// syntax-highlight the returned symbols.
4445 pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Outline<Anchor> {
4446 Outline::new(self.outline_items_containing(0..self.len(), true, theme))
4447 }
4448
4449 /// Returns all the symbols that contain the given position.
4450 ///
4451 /// This method allows passing an optional [`SyntaxTheme`] to
4452 /// syntax-highlight the returned symbols.
4453 pub fn symbols_containing<T: ToOffset>(
4454 &self,
4455 position: T,
4456 theme: Option<&SyntaxTheme>,
4457 ) -> Vec<OutlineItem<Anchor>> {
4458 let position = position.to_offset(self);
4459 let start = self.clip_offset(position.saturating_sub(1), Bias::Left);
4460 let end = self.clip_offset(position + 1, Bias::Right);
4461 let mut items = self.outline_items_containing(start..end, false, theme);
4462 let mut prev_depth = None;
4463 items.retain(|item| {
4464 let result = prev_depth.is_none_or(|prev_depth| item.depth > prev_depth);
4465 prev_depth = Some(item.depth);
4466 result
4467 });
4468 items
4469 }
4470
4471 pub fn outline_ranges_containing<T: ToOffset>(
4472 &self,
4473 range: Range<T>,
4474 ) -> impl Iterator<Item = Range<Point>> + '_ {
4475 let range = range.to_offset(self);
4476 let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
4477 grammar.outline_config.as_ref().map(|c| &c.query)
4478 });
4479 let configs = matches
4480 .grammars()
4481 .iter()
4482 .map(|g| g.outline_config.as_ref().unwrap())
4483 .collect::<Vec<_>>();
4484
4485 std::iter::from_fn(move || {
4486 while let Some(mat) = matches.peek() {
4487 let config = &configs[mat.grammar_index];
4488 let containing_item_node = maybe!({
4489 let item_node = mat.captures.iter().find_map(|cap| {
4490 if cap.index == config.item_capture_ix {
4491 Some(cap.node)
4492 } else {
4493 None
4494 }
4495 })?;
4496
4497 let item_byte_range = item_node.byte_range();
4498 if item_byte_range.end < range.start || item_byte_range.start > range.end {
4499 None
4500 } else {
4501 Some(item_node)
4502 }
4503 });
4504
4505 let range = containing_item_node.as_ref().map(|item_node| {
4506 Point::from_ts_point(item_node.start_position())
4507 ..Point::from_ts_point(item_node.end_position())
4508 });
4509 matches.advance();
4510 if range.is_some() {
4511 return range;
4512 }
4513 }
4514 None
4515 })
4516 }
4517
4518 pub fn outline_range_containing<T: ToOffset>(&self, range: Range<T>) -> Option<Range<Point>> {
4519 self.outline_ranges_containing(range).next()
4520 }
4521
4522 pub fn outline_items_containing<T: ToOffset>(
4523 &self,
4524 range: Range<T>,
4525 include_extra_context: bool,
4526 theme: Option<&SyntaxTheme>,
4527 ) -> Vec<OutlineItem<Anchor>> {
4528 self.outline_items_containing_internal(
4529 range,
4530 include_extra_context,
4531 theme,
4532 |this, range| this.anchor_after(range.start)..this.anchor_before(range.end),
4533 )
4534 }
4535
4536 pub fn outline_items_as_points_containing<T: ToOffset>(
4537 &self,
4538 range: Range<T>,
4539 include_extra_context: bool,
4540 theme: Option<&SyntaxTheme>,
4541 ) -> Vec<OutlineItem<Point>> {
4542 self.outline_items_containing_internal(range, include_extra_context, theme, |_, range| {
4543 range
4544 })
4545 }
4546
4547 pub fn outline_items_as_offsets_containing<T: ToOffset>(
4548 &self,
4549 range: Range<T>,
4550 include_extra_context: bool,
4551 theme: Option<&SyntaxTheme>,
4552 ) -> Vec<OutlineItem<usize>> {
4553 self.outline_items_containing_internal(
4554 range,
4555 include_extra_context,
4556 theme,
4557 |buffer, range| range.to_offset(buffer),
4558 )
4559 }
4560
4561 fn outline_items_containing_internal<T: ToOffset, U>(
4562 &self,
4563 range: Range<T>,
4564 include_extra_context: bool,
4565 theme: Option<&SyntaxTheme>,
4566 range_callback: fn(&Self, Range<Point>) -> Range<U>,
4567 ) -> Vec<OutlineItem<U>> {
4568 let range = range.to_offset(self);
4569 let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
4570 grammar.outline_config.as_ref().map(|c| &c.query)
4571 });
4572
4573 let mut items = Vec::new();
4574 let mut annotation_row_ranges: Vec<Range<u32>> = Vec::new();
4575 while let Some(mat) = matches.peek() {
4576 let config = matches.grammars()[mat.grammar_index]
4577 .outline_config
4578 .as_ref()
4579 .unwrap();
4580 if let Some(item) =
4581 self.next_outline_item(config, &mat, &range, include_extra_context, theme)
4582 {
4583 items.push(item);
4584 } else if let Some(capture) = mat
4585 .captures
4586 .iter()
4587 .find(|capture| Some(capture.index) == config.annotation_capture_ix)
4588 {
4589 let capture_range = capture.node.start_position()..capture.node.end_position();
4590 let mut capture_row_range =
4591 capture_range.start.row as u32..capture_range.end.row as u32;
4592 if capture_range.end.row > capture_range.start.row && capture_range.end.column == 0
4593 {
4594 capture_row_range.end -= 1;
4595 }
4596 if let Some(last_row_range) = annotation_row_ranges.last_mut() {
4597 if last_row_range.end >= capture_row_range.start.saturating_sub(1) {
4598 last_row_range.end = capture_row_range.end;
4599 } else {
4600 annotation_row_ranges.push(capture_row_range);
4601 }
4602 } else {
4603 annotation_row_ranges.push(capture_row_range);
4604 }
4605 }
4606 matches.advance();
4607 }
4608
4609 items.sort_by_key(|item| (item.range.start, Reverse(item.range.end)));
4610
4611 // Assign depths based on containment relationships and convert to anchors.
4612 let mut item_ends_stack = Vec::<Point>::new();
4613 let mut anchor_items = Vec::new();
4614 let mut annotation_row_ranges = annotation_row_ranges.into_iter().peekable();
4615 for item in items {
4616 while let Some(last_end) = item_ends_stack.last().copied() {
4617 if last_end < item.range.end {
4618 item_ends_stack.pop();
4619 } else {
4620 break;
4621 }
4622 }
4623
4624 let mut annotation_row_range = None;
4625 while let Some(next_annotation_row_range) = annotation_row_ranges.peek() {
4626 let row_preceding_item = item.range.start.row.saturating_sub(1);
4627 if next_annotation_row_range.end < row_preceding_item {
4628 annotation_row_ranges.next();
4629 } else {
4630 if next_annotation_row_range.end == row_preceding_item {
4631 annotation_row_range = Some(next_annotation_row_range.clone());
4632 annotation_row_ranges.next();
4633 }
4634 break;
4635 }
4636 }
4637
4638 anchor_items.push(OutlineItem {
4639 depth: item_ends_stack.len(),
4640 range: range_callback(self, item.range.clone()),
4641 selection_range: range_callback(self, item.selection_range.clone()),
4642 source_range_for_text: range_callback(self, item.source_range_for_text.clone()),
4643 text: item.text,
4644 highlight_ranges: item.highlight_ranges,
4645 name_ranges: item.name_ranges,
4646 body_range: item.body_range.map(|r| range_callback(self, r)),
4647 annotation_range: annotation_row_range.map(|annotation_range| {
4648 let point_range = Point::new(annotation_range.start, 0)
4649 ..Point::new(annotation_range.end, self.line_len(annotation_range.end));
4650 range_callback(self, point_range)
4651 }),
4652 });
4653 item_ends_stack.push(item.range.end);
4654 }
4655
4656 anchor_items
4657 }
4658
4659 fn next_outline_item(
4660 &self,
4661 config: &OutlineConfig,
4662 mat: &SyntaxMapMatch,
4663 range: &Range<usize>,
4664 include_extra_context: bool,
4665 theme: Option<&SyntaxTheme>,
4666 ) -> Option<OutlineItem<Point>> {
4667 let item_node = mat.captures.iter().find_map(|cap| {
4668 if cap.index == config.item_capture_ix {
4669 Some(cap.node)
4670 } else {
4671 None
4672 }
4673 })?;
4674
4675 let item_byte_range = item_node.byte_range();
4676 if item_byte_range.end < range.start || item_byte_range.start > range.end {
4677 return None;
4678 }
4679 let item_point_range = Point::from_ts_point(item_node.start_position())
4680 ..Point::from_ts_point(item_node.end_position());
4681
4682 let mut open_point = None;
4683 let mut close_point = None;
4684
4685 let mut buffer_ranges = Vec::new();
4686 let mut add_to_buffer_ranges = |node: tree_sitter::Node, node_is_name| {
4687 let mut range = node.start_byte()..node.end_byte();
4688 let start = node.start_position();
4689 if node.end_position().row > start.row {
4690 range.end = range.start + self.line_len(start.row as u32) as usize - start.column;
4691 }
4692
4693 if !range.is_empty() {
4694 buffer_ranges.push((range, node_is_name));
4695 }
4696 };
4697
4698 for capture in mat.captures {
4699 if capture.index == config.name_capture_ix {
4700 add_to_buffer_ranges(capture.node, true);
4701 } else if Some(capture.index) == config.context_capture_ix
4702 || (Some(capture.index) == config.extra_context_capture_ix && include_extra_context)
4703 {
4704 add_to_buffer_ranges(capture.node, false);
4705 } else {
4706 if Some(capture.index) == config.open_capture_ix {
4707 open_point = Some(Point::from_ts_point(capture.node.end_position()));
4708 } else if Some(capture.index) == config.close_capture_ix {
4709 close_point = Some(Point::from_ts_point(capture.node.start_position()));
4710 }
4711 }
4712 }
4713
4714 if buffer_ranges.is_empty() {
4715 return None;
4716 }
4717 let source_range_for_text =
4718 buffer_ranges.first().unwrap().0.start..buffer_ranges.last().unwrap().0.end;
4719 let selection_range = buffer_ranges
4720 .iter()
4721 .filter(|(_, node_is_name)| *node_is_name)
4722 .map(|(buffer_range, _)| buffer_range.clone())
4723 .reduce(|mut combined_range, next_range| {
4724 combined_range.end = next_range.end;
4725 combined_range
4726 })?;
4727
4728 let mut text = String::new();
4729 let mut highlight_ranges = Vec::new();
4730 let mut name_ranges = Vec::new();
4731 let mut chunks = self.chunks(
4732 source_range_for_text.clone(),
4733 LanguageAwareStyling {
4734 tree_sitter: true,
4735 diagnostics: true,
4736 },
4737 );
4738 let mut last_buffer_range_end = 0;
4739 for (buffer_range, is_name) in buffer_ranges {
4740 let space_added = !text.is_empty() && buffer_range.start > last_buffer_range_end;
4741 if space_added {
4742 text.push(' ');
4743 }
4744 let before_append_len = text.len();
4745 let mut offset = buffer_range.start;
4746 chunks.seek(buffer_range.clone());
4747 for mut chunk in chunks.by_ref() {
4748 if chunk.text.len() > buffer_range.end - offset {
4749 chunk.text = &chunk.text[0..(buffer_range.end - offset)];
4750 offset = buffer_range.end;
4751 } else {
4752 offset += chunk.text.len();
4753 }
4754 let style = chunk
4755 .syntax_highlight_id
4756 .zip(theme)
4757 .and_then(|(highlight, theme)| theme.get(highlight).cloned());
4758
4759 if let Some(style) = style {
4760 let start = text.len();
4761 let end = start + chunk.text.len();
4762 highlight_ranges.push((start..end, style));
4763 }
4764 text.push_str(chunk.text);
4765 if offset >= buffer_range.end {
4766 break;
4767 }
4768 }
4769 if is_name {
4770 let after_append_len = text.len();
4771 let start = if space_added && !name_ranges.is_empty() {
4772 before_append_len - 1
4773 } else {
4774 before_append_len
4775 };
4776 name_ranges.push(start..after_append_len);
4777 }
4778 last_buffer_range_end = buffer_range.end;
4779 }
4780
4781 Some(OutlineItem {
4782 depth: 0, // We'll calculate the depth later
4783 range: item_point_range,
4784 selection_range: selection_range.to_point(self),
4785 source_range_for_text: source_range_for_text.to_point(self),
4786 text: text.into(),
4787 highlight_ranges,
4788 name_ranges,
4789 body_range: open_point.zip(close_point).map(|(start, end)| start..end),
4790 annotation_range: None,
4791 })
4792 }
4793
4794 pub fn function_body_fold_ranges<T: ToOffset>(
4795 &self,
4796 within: Range<T>,
4797 ) -> impl Iterator<Item = Range<usize>> + '_ {
4798 self.text_object_ranges(within, TreeSitterOptions::default())
4799 .filter_map(|(range, obj)| (obj == TextObject::InsideFunction).then_some(range))
4800 }
4801
4802 /// For each grammar in the language, runs the provided
4803 /// [`tree_sitter::Query`] against the given range.
4804 pub fn matches(
4805 &self,
4806 range: Range<usize>,
4807 query: fn(&Grammar) -> Option<&tree_sitter::Query>,
4808 ) -> SyntaxMapMatches<'_> {
4809 self.syntax.matches(range, self, query)
4810 }
4811
4812 /// Finds all [`RowChunks`] applicable to the given range, then returns all bracket pairs that intersect with those chunks.
4813 /// Hence, may return more bracket pairs than the range contains.
4814 ///
4815 /// Will omit known chunks.
4816 /// The resulting bracket match collections are not ordered.
4817 pub fn fetch_bracket_ranges(
4818 &self,
4819 range: Range<usize>,
4820 known_chunks: Option<&HashSet<Range<BufferRow>>>,
4821 ) -> HashMap<Range<BufferRow>, Vec<BracketMatch<usize>>> {
4822 let mut all_bracket_matches = HashMap::default();
4823 if self
4824 .language
4825 .as_ref()
4826 .is_none_or(|language| language.grammar().is_none())
4827 {
4828 return all_bracket_matches;
4829 }
4830
4831 for chunk in self
4832 .tree_sitter_data
4833 .chunks
4834 .applicable_chunks(&[range.to_point(self)])
4835 {
4836 if known_chunks.is_some_and(|chunks| chunks.contains(&chunk.row_range())) {
4837 continue;
4838 }
4839 let chunk_range = chunk.anchor_range();
4840 let chunk_range = chunk_range.to_offset(&self);
4841
4842 if let Some(cached_brackets) = self
4843 .tree_sitter_data
4844 .brackets_by_chunks
4845 .lock()
4846 .get(&chunk.id)
4847 {
4848 all_bracket_matches.insert(chunk.row_range(), cached_brackets.clone());
4849 continue;
4850 }
4851
4852 let mut all_brackets = Vec::new();
4853 let mut opens = Vec::new();
4854 let mut color_pairs = Vec::new();
4855
4856 let bounded_query = (
4857 chunk_range.clone(),
4858 TreeSitterOptions {
4859 max_bytes_to_query: Some(MAX_BYTES_TO_QUERY),
4860 max_start_depth: None,
4861 },
4862 );
4863 // The bounded query drops any pair spanning more than `MAX_BYTES_TO_QUERY`
4864 // (tree-sitter's containing byte range requires full containment), which
4865 // resets bracket depth at chunk boundaries. Every such pair encloses a
4866 // chunk edge, so recover them with two point-sized unbounded queries that
4867 // only traverse the syntax tree spine around each edge.
4868 let boundary_queries = [chunk_range.start, chunk_range.end].map(|offset| {
4869 (
4870 self.clip_offset(offset.saturating_sub(1), Bias::Left)
4871 ..self.clip_offset(offset.saturating_add(1), Bias::Right),
4872 TreeSitterOptions::default(),
4873 )
4874 });
4875
4876 let mut grammar_ids = Vec::new();
4877 let mut configs = Vec::new();
4878 let mut seen_delimiters = HashSet::default();
4879 // Group matches by open delimiter so we can either trust grammar output
4880 // or repair it by picking a single closest close per open.
4881 let mut close_delimiters_by_open = BTreeMap::new();
4882 let mut bogus_patterns = HashSet::default();
4883 for (query_range, options) in iter::once(bounded_query).chain(boundary_queries) {
4884 let mut matches =
4885 self.syntax
4886 .matches_with_options(query_range, &self.text, options, |grammar| {
4887 grammar.brackets_config.as_ref().map(|c| &c.query)
4888 });
4889 let grammar_indices = matches
4890 .grammars()
4891 .iter()
4892 .map(|grammar| {
4893 grammar_ids
4894 .iter()
4895 .position(|&id| id == grammar.id())
4896 .unwrap_or_else(|| {
4897 grammar_ids.push(grammar.id());
4898 configs.push(grammar.brackets_config.as_ref().unwrap());
4899 grammar_ids.len() - 1
4900 })
4901 })
4902 .collect::<Vec<_>>();
4903
4904 while let Some(mat) = matches.peek() {
4905 let mut open = None;
4906 let mut close = None;
4907 let syntax_layer_depth = mat.depth;
4908 let grammar_index = grammar_indices[mat.grammar_index];
4909 let pattern_key = BracketPatternKey {
4910 grammar_index,
4911 pattern_index: mat.pattern_index,
4912 };
4913 let config = configs[grammar_index];
4914 let pattern = &config.patterns[mat.pattern_index];
4915 for capture in mat.captures {
4916 if capture.index == config.open_capture_ix {
4917 open = Some(capture.node.byte_range());
4918 } else if capture.index == config.close_capture_ix {
4919 close = Some(capture.node.byte_range());
4920 }
4921 }
4922
4923 matches.advance();
4924
4925 let Some((open_range, close_range)) = open.zip(close) else {
4926 continue;
4927 };
4928
4929 let bracket_range = open_range.start..=close_range.end;
4930 if !bracket_range.overlaps(&chunk_range) {
4931 continue;
4932 }
4933
4934 let candidate = BracketMatchCandidate {
4935 bracket_match: BracketMatch {
4936 open_range,
4937 close_range,
4938 syntax_layer_depth,
4939 newline_only: pattern.newline_only,
4940 color_index: None,
4941 },
4942 pattern: pattern_key,
4943 rainbow_exclude: pattern.rainbow_exclude,
4944 };
4945
4946 if !seen_delimiters
4947 .insert((candidate.open_delimiter(), candidate.close_delimiter()))
4948 {
4949 continue;
4950 }
4951
4952 let close_delimiters = close_delimiters_by_open
4953 .entry(candidate.open_delimiter())
4954 .or_insert_with(BTreeSet::new);
4955 close_delimiters.insert(candidate.close_delimiter());
4956 if close_delimiters.len() > 1 {
4957 bogus_patterns.insert(pattern_key);
4958 }
4959
4960 all_brackets.push(candidate);
4961 }
4962 }
4963
4964 if !bogus_patterns.is_empty() {
4965 // Certain patterns produce bogus matches where one open is paired with multiple
4966 // closes (e.g. same-character delimiters inside a single parent node).
4967 // Repair only those patterns, keeping trustworthy grammar output intact:
4968 // clean patterns may legitimately pair an open in one chunk with a close in
4969 // another, and must not be dropped by the chunk-local repair below.
4970 // For each close, we know the expected open_len from tree-sitter matches.
4971 let is_bogus =
4972 |candidate: &BracketMatchCandidate| bogus_patterns.contains(&candidate.pattern);
4973
4974 // Map each close to its expected open length (for inferring opens)
4975 let close_to_open_len = all_brackets
4976 .iter()
4977 .filter(|candidate| is_bogus(candidate))
4978 .map(|candidate| {
4979 (
4980 candidate.close_delimiter(),
4981 candidate.bracket_match.open_range.len(),
4982 )
4983 })
4984 .collect::<HashMap<_, _>>();
4985
4986 // Collect unique opens and closes within this chunk
4987 let unique_opens = all_brackets
4988 .iter()
4989 .filter(|candidate| is_bogus(candidate))
4990 .map(|candidate| candidate.open_delimiter())
4991 .filter(|open| chunk_range.contains(&open.start))
4992 .collect::<HashSet<_>>();
4993
4994 let mut unique_closes = all_brackets
4995 .iter()
4996 .filter(|candidate| is_bogus(candidate))
4997 .map(|candidate| candidate.close_delimiter())
4998 .filter(|close| chunk_range.contains(&close.start))
4999 .collect::<Vec<_>>();
5000 unique_closes.sort_unstable();
5001 unique_closes.dedup();
5002
5003 // Build valid pairs by walking through closes in order
5004 let mut sorted_opens = unique_opens.into_iter().collect::<Vec<_>>();
5005 sorted_opens.sort_unstable();
5006
5007 let mut valid_pairs = HashSet::default();
5008 let mut open_stacks = HashMap::default();
5009 let mut open_idx = 0;
5010
5011 for close in &unique_closes {
5012 // Push all opens before this close onto stack
5013 while open_idx < sorted_opens.len()
5014 && sorted_opens[open_idx].start < close.start
5015 {
5016 let open = sorted_opens[open_idx];
5017 open_stacks
5018 .entry(open.pattern)
5019 .or_insert_with(Vec::new)
5020 .push(open);
5021 open_idx += 1;
5022 }
5023
5024 // Try to match with most recent open
5025 if let Some(open) = open_stacks
5026 .get_mut(&close.pattern)
5027 .and_then(|open_stack| open_stack.pop())
5028 {
5029 valid_pairs.insert((open, *close));
5030 } else if let Some(&open_len) = close_to_open_len.get(close) {
5031 // No open on stack - infer one based on expected open_len
5032 if close.start >= open_len {
5033 let inferred = BracketDelimiter {
5034 start: close.start - open_len,
5035 end: close.start,
5036 pattern: close.pattern,
5037 };
5038 valid_pairs.insert((inferred, *close));
5039 let pattern = &configs[close.pattern.grammar_index].patterns
5040 [close.pattern.pattern_index];
5041 all_brackets.push(BracketMatchCandidate {
5042 bracket_match: BracketMatch {
5043 open_range: inferred.start..inferred.end,
5044 close_range: close.start..close.end,
5045 newline_only: pattern.newline_only,
5046 syntax_layer_depth: 0,
5047 color_index: None,
5048 },
5049 pattern: close.pattern,
5050 rainbow_exclude: pattern.rainbow_exclude,
5051 });
5052 }
5053 }
5054 }
5055
5056 all_brackets.retain(|candidate| {
5057 !is_bogus(candidate)
5058 || valid_pairs
5059 .contains(&(candidate.open_delimiter(), candidate.close_delimiter()))
5060 });
5061 }
5062
5063 let mut all_brackets = all_brackets
5064 .into_iter()
5065 .enumerate()
5066 .map(|(index, candidate)| {
5067 let bracket_match = candidate.bracket_match;
5068 // Certain languages have "brackets" that are not brackets, e.g. tags. and such
5069 // bracket will match the entire tag with all text inside.
5070 // For now, avoid highlighting any pair that has more than single char in each bracket.
5071 // We need to colorize `<Element/>` bracket pairs, so cannot make this check stricter.
5072 let should_color = !candidate.rainbow_exclude
5073 && (bracket_match.open_range.len() == 1
5074 || bracket_match.close_range.len() == 1);
5075 if should_color {
5076 opens.push(bracket_match.open_range.clone());
5077 color_pairs.push((
5078 bracket_match.open_range.clone(),
5079 bracket_match.close_range.clone(),
5080 index,
5081 ));
5082 }
5083 bracket_match
5084 })
5085 .collect::<Vec<_>>();
5086
5087 opens.sort_by_key(|r| (r.start, r.end));
5088 opens.dedup_by(|a, b| a.start == b.start && a.end == b.end);
5089 color_pairs.sort_by_key(|(_, close, _)| close.end);
5090
5091 let mut open_stack = Vec::new();
5092 let mut open_index = 0;
5093 for (open, close, index) in color_pairs {
5094 while open_index < opens.len() && opens[open_index].start < close.start {
5095 open_stack.push(opens[open_index].clone());
5096 open_index += 1;
5097 }
5098
5099 if open_stack.last() == Some(&open) {
5100 let depth_index = open_stack.len() - 1;
5101 all_brackets[index].color_index = Some(depth_index);
5102 open_stack.pop();
5103 }
5104 }
5105
5106 all_brackets.sort_by_key(|bracket_match| {
5107 (bracket_match.open_range.start, bracket_match.open_range.end)
5108 });
5109
5110 self.tree_sitter_data
5111 .brackets_by_chunks
5112 .lock()
5113 .entry(chunk.id)
5114 .or_insert_with(|| all_brackets.clone());
5115 all_bracket_matches.insert(chunk.row_range(), all_brackets);
5116 }
5117
5118 all_bracket_matches
5119 }
5120
5121 pub fn all_bracket_ranges(
5122 &self,
5123 range: Range<usize>,
5124 ) -> impl Iterator<Item = BracketMatch<usize>> {
5125 self.fetch_bracket_ranges(range.clone(), None)
5126 .into_values()
5127 .flatten()
5128 .filter(move |bracket_match| {
5129 let bracket_range = bracket_match.open_range.start..bracket_match.close_range.end;
5130 bracket_range.overlaps(&range)
5131 })
5132 }
5133
5134 /// Returns bracket range pairs overlapping or adjacent to `range`
5135 pub fn bracket_ranges<T: ToOffset>(
5136 &self,
5137 range: Range<T>,
5138 ) -> impl Iterator<Item = BracketMatch<usize>> + '_ {
5139 // Find bracket pairs that *inclusively* contain the given range.
5140 let range = range.start.to_previous_offset(self)..range.end.to_next_offset(self);
5141 self.all_bracket_ranges(range)
5142 .filter(|pair| !pair.newline_only)
5143 }
5144
5145 pub fn debug_variables_query<T: ToOffset>(
5146 &self,
5147 range: Range<T>,
5148 ) -> impl Iterator<Item = (Range<usize>, DebuggerTextObject)> + '_ {
5149 let range = range.start.to_previous_offset(self)..range.end.to_next_offset(self);
5150
5151 let mut matches = self.syntax.matches_with_options(
5152 range.clone(),
5153 &self.text,
5154 TreeSitterOptions::default(),
5155 |grammar| grammar.debug_variables_config.as_ref().map(|c| &c.query),
5156 );
5157
5158 let configs = matches
5159 .grammars()
5160 .iter()
5161 .map(|grammar| grammar.debug_variables_config.as_ref())
5162 .collect::<Vec<_>>();
5163
5164 let mut captures = Vec::<(Range<usize>, DebuggerTextObject)>::new();
5165
5166 iter::from_fn(move || {
5167 loop {
5168 while let Some(capture) = captures.pop() {
5169 if capture.0.overlaps(&range) {
5170 return Some(capture);
5171 }
5172 }
5173
5174 let mat = matches.peek()?;
5175
5176 let Some(config) = configs[mat.grammar_index].as_ref() else {
5177 matches.advance();
5178 continue;
5179 };
5180
5181 for capture in mat.captures {
5182 let Some(ix) = config
5183 .objects_by_capture_ix
5184 .binary_search_by_key(&capture.index, |e| e.0)
5185 .ok()
5186 else {
5187 continue;
5188 };
5189 let text_object = config.objects_by_capture_ix[ix].1;
5190 let byte_range = capture.node.byte_range();
5191
5192 let mut found = false;
5193 for (range, existing) in captures.iter_mut() {
5194 if existing == &text_object {
5195 range.start = range.start.min(byte_range.start);
5196 range.end = range.end.max(byte_range.end);
5197 found = true;
5198 break;
5199 }
5200 }
5201
5202 if !found {
5203 captures.push((byte_range, text_object));
5204 }
5205 }
5206
5207 matches.advance();
5208 }
5209 })
5210 }
5211
5212 pub fn text_object_ranges<T: ToOffset>(
5213 &self,
5214 range: Range<T>,
5215 options: TreeSitterOptions,
5216 ) -> impl Iterator<Item = (Range<usize>, TextObject)> + '_ {
5217 let range =
5218 range.start.to_previous_offset(self)..self.len().min(range.end.to_next_offset(self));
5219
5220 let mut matches =
5221 self.syntax
5222 .matches_with_options(range.clone(), &self.text, options, |grammar| {
5223 grammar.text_object_config.as_ref().map(|c| &c.query)
5224 });
5225
5226 let configs = matches
5227 .grammars()
5228 .iter()
5229 .map(|grammar| grammar.text_object_config.as_ref())
5230 .collect::<Vec<_>>();
5231
5232 let mut captures = Vec::<(Range<usize>, TextObject)>::new();
5233
5234 iter::from_fn(move || {
5235 loop {
5236 while let Some(capture) = captures.pop() {
5237 if capture.0.overlaps(&range) {
5238 return Some(capture);
5239 }
5240 }
5241
5242 let mat = matches.peek()?;
5243
5244 let Some(config) = configs[mat.grammar_index].as_ref() else {
5245 matches.advance();
5246 continue;
5247 };
5248
5249 for capture in mat.captures {
5250 let Some(ix) = config
5251 .text_objects_by_capture_ix
5252 .binary_search_by_key(&capture.index, |e| e.0)
5253 .ok()
5254 else {
5255 continue;
5256 };
5257 let text_object = config.text_objects_by_capture_ix[ix].1;
5258 let byte_range = capture.node.byte_range();
5259
5260 let mut found = false;
5261 for (range, existing) in captures.iter_mut() {
5262 if existing == &text_object {
5263 range.start = range.start.min(byte_range.start);
5264 range.end = range.end.max(byte_range.end);
5265 found = true;
5266 break;
5267 }
5268 }
5269
5270 if !found {
5271 captures.push((byte_range, text_object));
5272 }
5273 }
5274
5275 matches.advance();
5276 }
5277 })
5278 }
5279
5280 /// Returns enclosing bracket ranges containing the given range
5281 pub fn enclosing_bracket_ranges<T: ToOffset>(
5282 &self,
5283 range: Range<T>,
5284 ) -> impl Iterator<Item = BracketMatch<usize>> + '_ {
5285 let range = range.start.to_offset(self)..range.end.to_offset(self);
5286
5287 let result: Vec<_> = self.bracket_ranges(range.clone()).collect();
5288 let max_depth = result
5289 .iter()
5290 .map(|mat| mat.syntax_layer_depth)
5291 .max()
5292 .unwrap_or(0);
5293 result.into_iter().filter(move |pair| {
5294 pair.open_range.start <= range.start
5295 && pair.close_range.end >= range.end
5296 && pair.syntax_layer_depth == max_depth
5297 })
5298 }
5299
5300 /// Returns the smallest enclosing bracket ranges containing the given range or None if no brackets contain range
5301 ///
5302 /// Can optionally pass a range_filter to filter the ranges of brackets to consider
5303 pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
5304 &self,
5305 range: Range<T>,
5306 range_filter: Option<&dyn Fn(Range<usize>, Range<usize>) -> bool>,
5307 ) -> Option<(Range<usize>, Range<usize>)> {
5308 let range = range.start.to_offset(self)..range.end.to_offset(self);
5309
5310 // Get the ranges of the innermost pair of brackets.
5311 let mut result: Option<(Range<usize>, Range<usize>)> = None;
5312
5313 for pair in self.enclosing_bracket_ranges(range) {
5314 if let Some(range_filter) = range_filter
5315 && !range_filter(pair.open_range.clone(), pair.close_range.clone())
5316 {
5317 continue;
5318 }
5319
5320 let len = pair.close_range.end - pair.open_range.start;
5321
5322 if let Some((existing_open, existing_close)) = &result {
5323 let existing_len = existing_close.end - existing_open.start;
5324 if len > existing_len {
5325 continue;
5326 }
5327 }
5328
5329 result = Some((pair.open_range, pair.close_range));
5330 }
5331
5332 result
5333 }
5334
5335 /// Returns anchor ranges for any matches of the redaction query.
5336 /// The buffer can be associated with multiple languages, and the redaction query associated with each
5337 /// will be run on the relevant section of the buffer.
5338 pub fn redacted_ranges<T: ToOffset>(
5339 &self,
5340 range: Range<T>,
5341 ) -> impl Iterator<Item = Range<usize>> + '_ {
5342 let offset_range = range.start.to_offset(self)..range.end.to_offset(self);
5343 let mut syntax_matches = self.syntax.matches(offset_range, self, |grammar| {
5344 grammar
5345 .redactions_config
5346 .as_ref()
5347 .map(|config| &config.query)
5348 });
5349
5350 let configs = syntax_matches
5351 .grammars()
5352 .iter()
5353 .map(|grammar| grammar.redactions_config.as_ref())
5354 .collect::<Vec<_>>();
5355
5356 iter::from_fn(move || {
5357 let redacted_range = syntax_matches
5358 .peek()
5359 .and_then(|mat| {
5360 configs[mat.grammar_index].and_then(|config| {
5361 mat.captures
5362 .iter()
5363 .find(|capture| capture.index == config.redaction_capture_ix)
5364 })
5365 })
5366 .map(|mat| mat.node.byte_range());
5367 syntax_matches.advance();
5368 redacted_range
5369 })
5370 }
5371
5372 pub fn injections_intersecting_range<T: ToOffset>(
5373 &self,
5374 range: Range<T>,
5375 ) -> impl Iterator<Item = (Range<usize>, &Arc<Language>)> + '_ {
5376 let offset_range = range.start.to_offset(self)..range.end.to_offset(self);
5377
5378 let mut syntax_matches = self.syntax.matches(offset_range, self, |grammar| {
5379 grammar
5380 .injection_config
5381 .as_ref()
5382 .map(|config| &config.query)
5383 });
5384
5385 let configs = syntax_matches
5386 .grammars()
5387 .iter()
5388 .map(|grammar| grammar.injection_config.as_ref())
5389 .collect::<Vec<_>>();
5390
5391 iter::from_fn(move || {
5392 let ranges = syntax_matches.peek().and_then(|mat| {
5393 let config = &configs[mat.grammar_index]?;
5394 let content_capture_range = mat.captures.iter().find_map(|capture| {
5395 if capture.index == config.content_capture_ix {
5396 Some(capture.node.byte_range())
5397 } else {
5398 None
5399 }
5400 })?;
5401 let language = self.language_at(content_capture_range.start)?;
5402 Some((content_capture_range, language))
5403 });
5404 syntax_matches.advance();
5405 ranges
5406 })
5407 }
5408
5409 pub fn runnable_ranges(
5410 &self,
5411 offset_range: Range<usize>,
5412 ) -> impl Iterator<Item = RunnableRange> + '_ {
5413 runnable::runnable_ranges(self, offset_range)
5414 }
5415
5416 /// Returns selections for remote peers intersecting the given range.
5417 #[allow(clippy::type_complexity)]
5418 pub fn selections_in_range(
5419 &self,
5420 range: Range<Anchor>,
5421 include_local: bool,
5422 ) -> impl Iterator<
5423 Item = (
5424 ReplicaId,
5425 bool,
5426 CursorShape,
5427 impl Iterator<Item = &Selection<Anchor>> + '_,
5428 ),
5429 > + '_ {
5430 self.remote_selections
5431 .iter()
5432 .filter(move |(replica_id, set)| {
5433 (include_local || **replica_id != self.text.replica_id())
5434 && !set.selections.is_empty()
5435 })
5436 .map(move |(replica_id, set)| {
5437 let start_ix = match set.selections.binary_search_by(|probe| {
5438 probe.end.cmp(&range.start, self).then(Ordering::Greater)
5439 }) {
5440 Ok(ix) | Err(ix) => ix,
5441 };
5442 let end_ix = match set.selections.binary_search_by(|probe| {
5443 probe.start.cmp(&range.end, self).then(Ordering::Less)
5444 }) {
5445 Ok(ix) | Err(ix) => ix,
5446 };
5447
5448 (
5449 *replica_id,
5450 set.line_mode,
5451 set.cursor_shape,
5452 set.selections[start_ix..end_ix].iter(),
5453 )
5454 })
5455 }
5456
5457 /// Returns if the buffer contains any diagnostics.
5458 pub fn has_diagnostics(&self) -> bool {
5459 !self.diagnostics.is_empty()
5460 }
5461
5462 /// Returns all the diagnostics intersecting the given range.
5463 pub fn diagnostics_in_range<'a, T, O>(
5464 &'a self,
5465 search_range: Range<T>,
5466 reversed: bool,
5467 ) -> impl 'a + Iterator<Item = DiagnosticEntryRef<'a, O>>
5468 where
5469 T: 'a + Clone + ToOffset,
5470 O: 'a + FromAnchor,
5471 {
5472 let mut iterators: Vec<_> = self
5473 .diagnostics
5474 .iter()
5475 .map(|(_, collection)| {
5476 collection
5477 .range::<T, text::Anchor>(search_range.clone(), self, true, reversed)
5478 .peekable()
5479 })
5480 .collect();
5481
5482 std::iter::from_fn(move || {
5483 let (next_ix, _) = iterators
5484 .iter_mut()
5485 .enumerate()
5486 .flat_map(|(ix, iter)| Some((ix, iter.peek()?)))
5487 .min_by(|(_, a), (_, b)| {
5488 let cmp = a
5489 .range
5490 .start
5491 .cmp(&b.range.start, self)
5492 // when range is equal, sort by diagnostic severity
5493 .then(a.diagnostic.severity.cmp(&b.diagnostic.severity))
5494 // and stabilize order with group_id
5495 .then(a.diagnostic.group_id.cmp(&b.diagnostic.group_id));
5496 if reversed { cmp.reverse() } else { cmp }
5497 })?;
5498 iterators[next_ix]
5499 .next()
5500 .map(
5501 |DiagnosticEntryRef { range, diagnostic }| DiagnosticEntryRef {
5502 diagnostic,
5503 range: FromAnchor::from_anchor(&range.start, self)
5504 ..FromAnchor::from_anchor(&range.end, self),
5505 },
5506 )
5507 })
5508 }
5509
5510 /// Returns all the diagnostic groups associated with the given
5511 /// language server ID. If no language server ID is provided,
5512 /// all diagnostics groups are returned.
5513 pub fn diagnostic_groups(
5514 &self,
5515 language_server_id: Option<LanguageServerId>,
5516 ) -> Vec<(LanguageServerId, DiagnosticGroup<'_, Anchor>)> {
5517 let mut groups = Vec::new();
5518
5519 if let Some(language_server_id) = language_server_id {
5520 if let Some(set) = self.diagnostics.get(&language_server_id) {
5521 set.groups(language_server_id, &mut groups, self);
5522 }
5523 } else {
5524 for (language_server_id, diagnostics) in self.diagnostics.iter() {
5525 diagnostics.groups(*language_server_id, &mut groups, self);
5526 }
5527 }
5528
5529 groups.sort_by(|(id_a, group_a), (id_b, group_b)| {
5530 let a_start = &group_a.entries[group_a.primary_ix].range.start;
5531 let b_start = &group_b.entries[group_b.primary_ix].range.start;
5532 a_start.cmp(b_start, self).then_with(|| id_a.cmp(id_b))
5533 });
5534
5535 groups
5536 }
5537
5538 /// Returns an iterator over the diagnostics for the given group.
5539 pub fn diagnostic_group<O>(
5540 &self,
5541 group_id: usize,
5542 ) -> impl Iterator<Item = DiagnosticEntryRef<'_, O>> + use<'_, O>
5543 where
5544 O: FromAnchor + 'static,
5545 {
5546 self.diagnostics
5547 .iter()
5548 .flat_map(move |(_, set)| set.group(group_id, self))
5549 }
5550
5551 /// An integer version number that accounts for all updates besides
5552 /// the buffer's text itself (which is versioned via a version vector).
5553 pub fn non_text_state_update_count(&self) -> usize {
5554 self.non_text_state_update_count
5555 }
5556
5557 /// An integer version that changes when the buffer's syntax changes.
5558 pub fn syntax_update_count(&self) -> usize {
5559 self.syntax.update_count()
5560 }
5561
5562 /// Returns a snapshot of underlying file.
5563 pub fn file(&self) -> Option<&Arc<dyn File>> {
5564 self.file.as_ref()
5565 }
5566
5567 pub fn resolve_file_path(&self, include_root: bool, cx: &App) -> Option<String> {
5568 if let Some(file) = self.file() {
5569 if file.path().file_name().is_none() || include_root {
5570 Some(file.full_path(cx).to_string_lossy().into_owned())
5571 } else {
5572 Some(file.path().display(file.path_style(cx)).to_string())
5573 }
5574 } else {
5575 None
5576 }
5577 }
5578
5579 pub fn words_in_range(&self, query: WordsQuery) -> BTreeMap<String, Range<Anchor>> {
5580 let query_str = query.fuzzy_contents;
5581 if query_str.is_some_and(|query| query.is_empty()) {
5582 return BTreeMap::default();
5583 }
5584
5585 let classifier = CharClassifier::new(self.language.clone().map(|language| LanguageScope {
5586 language,
5587 override_id: None,
5588 }));
5589
5590 let mut query_ix = 0;
5591 let query_chars = query_str.map(|query| query.chars().collect::<Vec<_>>());
5592 let query_len = query_chars.as_ref().map_or(0, |query| query.len());
5593
5594 let mut words = BTreeMap::default();
5595 let mut current_word_start_ix = None;
5596 let mut chunk_ix = query.range.start;
5597 for chunk in self.chunks(
5598 query.range,
5599 LanguageAwareStyling {
5600 tree_sitter: false,
5601 diagnostics: false,
5602 },
5603 ) {
5604 for (i, c) in chunk.text.char_indices() {
5605 let ix = chunk_ix + i;
5606 if classifier.is_word(c) {
5607 if current_word_start_ix.is_none() {
5608 current_word_start_ix = Some(ix);
5609 }
5610
5611 if let Some(query_chars) = &query_chars
5612 && query_ix < query_len
5613 && c.to_lowercase().eq(query_chars[query_ix].to_lowercase())
5614 {
5615 query_ix += 1;
5616 }
5617 continue;
5618 } else if let Some(word_start) = current_word_start_ix.take()
5619 && query_ix == query_len
5620 {
5621 let word_range = self.anchor_before(word_start)..self.anchor_after(ix);
5622 let mut word_text = self.text_for_range(word_start..ix).peekable();
5623 let first_char = word_text
5624 .peek()
5625 .and_then(|first_chunk| first_chunk.chars().next());
5626 // Skip empty and "words" starting with digits as a heuristic to reduce useless completions
5627 if !query.skip_digits
5628 || first_char.is_none_or(|first_char| !first_char.is_digit(10))
5629 {
5630 words.insert(word_text.collect(), word_range);
5631 }
5632 }
5633 query_ix = 0;
5634 }
5635 chunk_ix += chunk.text.len();
5636 }
5637
5638 words
5639 }
5640}
5641
5642/// A configuration to use when producing styled text chunks.
5643#[derive(Clone, Copy)]
5644pub struct LanguageAwareStyling {
5645 /// Whether to highlight text chunks using tree-sitter.
5646 pub tree_sitter: bool,
5647 /// Whether to highlight text chunks based on the diagnostics data.
5648 pub diagnostics: bool,
5649}
5650
5651pub struct WordsQuery<'a> {
5652 /// Only returns words with all chars from the fuzzy string in them.
5653 pub fuzzy_contents: Option<&'a str>,
5654 /// Skips words that start with a digit.
5655 pub skip_digits: bool,
5656 /// Buffer offset range, to look for words.
5657 pub range: Range<usize>,
5658}
5659
5660fn indent_size_for_line(text: &text::BufferSnapshot, row: u32) -> IndentSize {
5661 indent_size_for_text(text.chars_at(Point::new(row, 0)))
5662}
5663
5664fn indent_size_for_text(text: impl Iterator<Item = char>) -> IndentSize {
5665 let mut result = IndentSize::spaces(0);
5666 for c in text {
5667 let kind = match c {
5668 ' ' => IndentKind::Space,
5669 '\t' => IndentKind::Tab,
5670 _ => break,
5671 };
5672 if result.len == 0 {
5673 result.kind = kind;
5674 }
5675 result.len += 1;
5676 }
5677 result
5678}
5679
5680impl Clone for BufferSnapshot {
5681 fn clone(&self) -> Self {
5682 Self {
5683 text: self.text.clone(),
5684 syntax: self.syntax.clone(),
5685 file: self.file.clone(),
5686 remote_selections: self.remote_selections.clone(),
5687 diagnostics: self.diagnostics.clone(),
5688 language: self.language.clone(),
5689 tree_sitter_data: self.tree_sitter_data.clone(),
5690 non_text_state_update_count: self.non_text_state_update_count,
5691 capability: self.capability,
5692 modeline: self.modeline.clone(),
5693 }
5694 }
5695}
5696
5697impl Deref for BufferSnapshot {
5698 type Target = text::BufferSnapshot;
5699
5700 fn deref(&self) -> &Self::Target {
5701 &self.text
5702 }
5703}
5704
5705unsafe impl Send for BufferChunks<'_> {}
5706
5707impl<'a> BufferChunks<'a> {
5708 pub(crate) fn new(
5709 text: &'a Rope,
5710 range: Range<usize>,
5711 syntax: Option<(SyntaxMapCaptures<'a>, Vec<HighlightMap>)>,
5712 diagnostics: bool,
5713 buffer_snapshot: Option<&'a BufferSnapshot>,
5714 ) -> Self {
5715 let mut highlights = None;
5716 if let Some((captures, highlight_maps)) = syntax {
5717 highlights = Some(BufferChunkHighlights {
5718 captures,
5719 next_capture: None,
5720 stack: Default::default(),
5721 highlight_maps,
5722 })
5723 }
5724
5725 let diagnostic_endpoints = diagnostics.then(|| Vec::new().into_iter().peekable());
5726 let chunks = text.chunks_in_range(range.clone());
5727
5728 let mut this = BufferChunks {
5729 range,
5730 buffer_snapshot,
5731 chunks,
5732 diagnostic_endpoints,
5733 error_depth: 0,
5734 warning_depth: 0,
5735 information_depth: 0,
5736 hint_depth: 0,
5737 unnecessary_depth: 0,
5738 underline: true,
5739 highlights,
5740 };
5741 this.initialize_diagnostic_endpoints();
5742 this
5743 }
5744
5745 /// Seeks to the given byte offset in the buffer.
5746 pub fn seek(&mut self, range: Range<usize>) {
5747 let old_range = std::mem::replace(&mut self.range, range.clone());
5748 self.chunks.set_range(self.range.clone());
5749 if let Some(highlights) = self.highlights.as_mut() {
5750 if old_range.start <= self.range.start && old_range.end >= self.range.end {
5751 // Reuse existing highlights stack, as the new range is a subrange of the old one.
5752 highlights
5753 .stack
5754 .retain(|(end_offset, _)| *end_offset > range.start);
5755 if let Some(capture) = &highlights.next_capture
5756 && range.start >= capture.node.start_byte()
5757 {
5758 let next_capture_end = capture.node.end_byte();
5759 if range.start < next_capture_end
5760 && let Some(capture_id) =
5761 highlights.highlight_maps[capture.grammar_index].get(capture.index)
5762 {
5763 highlights.stack.push((next_capture_end, capture_id));
5764 }
5765 highlights.next_capture.take();
5766 }
5767 } else if let Some(snapshot) = self.buffer_snapshot {
5768 let (captures, highlight_maps) = snapshot.get_highlights(self.range.clone());
5769 *highlights = BufferChunkHighlights {
5770 captures,
5771 next_capture: None,
5772 stack: Default::default(),
5773 highlight_maps,
5774 };
5775 } else {
5776 // We cannot obtain new highlights for a language-aware buffer iterator, as we don't have a buffer snapshot.
5777 // Seeking such BufferChunks is not supported.
5778 debug_assert!(
5779 false,
5780 "Attempted to seek on a language-aware buffer iterator without associated buffer snapshot"
5781 );
5782 }
5783
5784 highlights.captures.set_byte_range(self.range.clone());
5785 self.initialize_diagnostic_endpoints();
5786 }
5787 }
5788
5789 fn initialize_diagnostic_endpoints(&mut self) {
5790 if let Some(diagnostics) = self.diagnostic_endpoints.as_mut()
5791 && let Some(buffer) = self.buffer_snapshot
5792 {
5793 let mut diagnostic_endpoints = Vec::new();
5794 for entry in buffer.diagnostics_in_range::<_, usize>(self.range.clone(), false) {
5795 diagnostic_endpoints.push(DiagnosticEndpoint {
5796 offset: entry.range.start,
5797 is_start: true,
5798 severity: entry.diagnostic.severity,
5799 is_unnecessary: entry.diagnostic.is_unnecessary,
5800 underline: entry.diagnostic.underline,
5801 });
5802 diagnostic_endpoints.push(DiagnosticEndpoint {
5803 offset: entry.range.end,
5804 is_start: false,
5805 severity: entry.diagnostic.severity,
5806 is_unnecessary: entry.diagnostic.is_unnecessary,
5807 underline: entry.diagnostic.underline,
5808 });
5809 }
5810 diagnostic_endpoints
5811 .sort_unstable_by_key(|endpoint| (endpoint.offset, !endpoint.is_start));
5812 *diagnostics = diagnostic_endpoints.into_iter().peekable();
5813 self.hint_depth = 0;
5814 self.error_depth = 0;
5815 self.warning_depth = 0;
5816 self.information_depth = 0;
5817 }
5818 }
5819
5820 /// The current byte offset in the buffer.
5821 pub fn offset(&self) -> usize {
5822 self.range.start
5823 }
5824
5825 pub fn range(&self) -> Range<usize> {
5826 self.range.clone()
5827 }
5828
5829 fn update_diagnostic_depths(&mut self, endpoint: DiagnosticEndpoint) {
5830 let depth = match endpoint.severity {
5831 DiagnosticSeverity::ERROR => &mut self.error_depth,
5832 DiagnosticSeverity::WARNING => &mut self.warning_depth,
5833 DiagnosticSeverity::INFORMATION => &mut self.information_depth,
5834 DiagnosticSeverity::HINT => &mut self.hint_depth,
5835 _ => return,
5836 };
5837 if endpoint.is_start {
5838 *depth += 1;
5839 } else {
5840 *depth -= 1;
5841 }
5842
5843 if endpoint.is_unnecessary {
5844 if endpoint.is_start {
5845 self.unnecessary_depth += 1;
5846 } else {
5847 self.unnecessary_depth -= 1;
5848 }
5849 }
5850 }
5851
5852 fn current_diagnostic_severity(&self) -> Option<DiagnosticSeverity> {
5853 if self.error_depth > 0 {
5854 Some(DiagnosticSeverity::ERROR)
5855 } else if self.warning_depth > 0 {
5856 Some(DiagnosticSeverity::WARNING)
5857 } else if self.information_depth > 0 {
5858 Some(DiagnosticSeverity::INFORMATION)
5859 } else if self.hint_depth > 0 {
5860 Some(DiagnosticSeverity::HINT)
5861 } else {
5862 None
5863 }
5864 }
5865
5866 fn current_code_is_unnecessary(&self) -> bool {
5867 self.unnecessary_depth > 0
5868 }
5869}
5870
5871impl<'a> Iterator for BufferChunks<'a> {
5872 type Item = Chunk<'a>;
5873
5874 fn next(&mut self) -> Option<Self::Item> {
5875 let mut next_capture_start = usize::MAX;
5876 let mut next_diagnostic_endpoint = usize::MAX;
5877
5878 if let Some(highlights) = self.highlights.as_mut() {
5879 while let Some((parent_capture_end, _)) = highlights.stack.last() {
5880 if *parent_capture_end <= self.range.start {
5881 highlights.stack.pop();
5882 } else {
5883 break;
5884 }
5885 }
5886
5887 if highlights.next_capture.is_none() {
5888 highlights.next_capture = highlights.captures.next();
5889 }
5890
5891 while let Some(capture) = highlights.next_capture.as_ref() {
5892 if self.range.start < capture.node.start_byte() {
5893 next_capture_start = capture.node.start_byte();
5894 break;
5895 } else {
5896 let highlight_id =
5897 highlights.highlight_maps[capture.grammar_index].get(capture.index);
5898 if let Some(highlight_id) = highlight_id {
5899 highlights
5900 .stack
5901 .push((capture.node.end_byte(), highlight_id));
5902 }
5903 highlights.next_capture = highlights.captures.next();
5904 }
5905 }
5906 }
5907
5908 let mut diagnostic_endpoints = std::mem::take(&mut self.diagnostic_endpoints);
5909 if let Some(diagnostic_endpoints) = diagnostic_endpoints.as_mut() {
5910 while let Some(endpoint) = diagnostic_endpoints.peek().copied() {
5911 if endpoint.offset <= self.range.start {
5912 self.update_diagnostic_depths(endpoint);
5913 diagnostic_endpoints.next();
5914 self.underline = endpoint.underline;
5915 } else {
5916 next_diagnostic_endpoint = endpoint.offset;
5917 break;
5918 }
5919 }
5920 }
5921 self.diagnostic_endpoints = diagnostic_endpoints;
5922
5923 if let Some(ChunkBitmaps {
5924 text: chunk,
5925 chars: chars_map,
5926 tabs,
5927 newlines,
5928 }) = self.chunks.peek_with_bitmaps()
5929 {
5930 let chunk_start = self.range.start;
5931 let mut chunk_end = (self.chunks.offset() + chunk.len())
5932 .min(next_capture_start)
5933 .min(next_diagnostic_endpoint);
5934 let mut highlight_id = None;
5935 if let Some(highlights) = self.highlights.as_ref()
5936 && let Some((parent_capture_end, parent_highlight_id)) = highlights.stack.last()
5937 {
5938 chunk_end = chunk_end.min(*parent_capture_end);
5939 highlight_id = Some(*parent_highlight_id);
5940 }
5941 let bit_start = chunk_start - self.chunks.offset();
5942 let bit_end = chunk_end - self.chunks.offset();
5943
5944 let slice = &chunk[bit_start..bit_end];
5945
5946 let mask = 1u128
5947 .unbounded_shl((bit_end - bit_start) as u32)
5948 .wrapping_sub(1);
5949 let tabs = (tabs >> bit_start) & mask;
5950 let chars = (chars_map >> bit_start) & mask;
5951 let newlines = (newlines >> bit_start) & mask;
5952
5953 self.range.start = chunk_end;
5954 if self.range.start == self.chunks.offset() + chunk.len() {
5955 self.chunks.next().unwrap();
5956 }
5957
5958 Some(Chunk {
5959 text: slice,
5960 syntax_highlight_id: highlight_id,
5961 underline: self.underline,
5962 diagnostic_severity: self.current_diagnostic_severity(),
5963 is_unnecessary: self.current_code_is_unnecessary(),
5964 tabs,
5965 chars,
5966 newlines,
5967 ..Chunk::default()
5968 })
5969 } else {
5970 None
5971 }
5972 }
5973}
5974
5975impl operation_queue::Operation for Operation {
5976 fn lamport_timestamp(&self) -> clock::Lamport {
5977 match self {
5978 Operation::Buffer(_) => {
5979 unreachable!("buffer operations should never be deferred at this layer")
5980 }
5981 Operation::UpdateDiagnostics {
5982 lamport_timestamp, ..
5983 }
5984 | Operation::UpdateSelections {
5985 lamport_timestamp, ..
5986 }
5987 | Operation::UpdateCompletionTriggers {
5988 lamport_timestamp, ..
5989 }
5990 | Operation::UpdateLineEnding {
5991 lamport_timestamp, ..
5992 } => *lamport_timestamp,
5993 }
5994 }
5995}
5996
5997impl IndentSize {
5998 /// Returns an [`IndentSize`] representing the given spaces.
5999 pub fn spaces(len: u32) -> Self {
6000 Self {
6001 len,
6002 kind: IndentKind::Space,
6003 }
6004 }
6005
6006 /// Returns an [`IndentSize`] representing a tab.
6007 pub fn tab() -> Self {
6008 Self {
6009 len: 1,
6010 kind: IndentKind::Tab,
6011 }
6012 }
6013
6014 /// An iterator over the characters represented by this [`IndentSize`].
6015 pub fn chars(&self) -> impl Iterator<Item = char> {
6016 iter::repeat(self.char()).take(self.len as usize)
6017 }
6018
6019 /// The character representation of this [`IndentSize`].
6020 pub fn char(&self) -> char {
6021 match self.kind {
6022 IndentKind::Space => ' ',
6023 IndentKind::Tab => '\t',
6024 }
6025 }
6026
6027 /// Consumes the current [`IndentSize`] and returns a new one that has
6028 /// been shrunk or enlarged by the given size along the given direction.
6029 pub fn with_delta(mut self, direction: Ordering, size: IndentSize) -> Self {
6030 match direction {
6031 Ordering::Less => {
6032 if self.kind == size.kind && self.len >= size.len {
6033 self.len -= size.len;
6034 }
6035 }
6036 Ordering::Equal => {}
6037 Ordering::Greater => {
6038 if self.len == 0 {
6039 self = size;
6040 } else if self.kind == size.kind {
6041 self.len += size.len;
6042 }
6043 }
6044 }
6045 self
6046 }
6047
6048 /// Returns the number of indentation characters to remove when outdenting to the
6049 /// previous editor tab stop.
6050 pub fn outdent_len(self, tab_size: NonZeroU32) -> u32 {
6051 if self.len == 0 {
6052 return 0;
6053 }
6054
6055 match self.kind {
6056 IndentKind::Space => {
6057 let tab_size = tab_size.get();
6058 let columns_to_prev_tab_stop = self.len % tab_size;
6059 if columns_to_prev_tab_stop == 0 {
6060 tab_size
6061 } else {
6062 columns_to_prev_tab_stop
6063 }
6064 }
6065 IndentKind::Tab => 1,
6066 }
6067 }
6068
6069 pub fn len_with_expanded_tabs(&self, tab_size: NonZeroU32) -> usize {
6070 match self.kind {
6071 IndentKind::Space => self.len as usize,
6072 IndentKind::Tab => self.len as usize * tab_size.get() as usize,
6073 }
6074 }
6075}
6076
6077#[cfg(any(test, feature = "test-support"))]
6078pub struct TestFile {
6079 pub path: Arc<RelPath>,
6080 pub root_name: String,
6081 pub local_root: Option<PathBuf>,
6082}
6083
6084#[cfg(any(test, feature = "test-support"))]
6085impl File for TestFile {
6086 fn path(&self) -> &Arc<RelPath> {
6087 &self.path
6088 }
6089
6090 fn full_path(&self, _: &gpui::App) -> PathBuf {
6091 PathBuf::from(self.root_name.clone()).join(self.path.as_std_path())
6092 }
6093
6094 fn as_local(&self) -> Option<&dyn LocalFile> {
6095 if self.local_root.is_some() {
6096 Some(self)
6097 } else {
6098 None
6099 }
6100 }
6101
6102 fn disk_state(&self) -> DiskState {
6103 unimplemented!()
6104 }
6105
6106 fn file_name<'a>(&'a self, _: &'a gpui::App) -> &'a str {
6107 self.path().file_name().unwrap_or(self.root_name.as_ref())
6108 }
6109
6110 fn worktree_id(&self, _: &App) -> WorktreeId {
6111 WorktreeId::from_usize(0)
6112 }
6113
6114 fn to_proto(&self, _: &App) -> rpc::proto::File {
6115 unimplemented!()
6116 }
6117
6118 fn is_private(&self) -> bool {
6119 false
6120 }
6121
6122 fn path_style(&self, _cx: &App) -> PathStyle {
6123 PathStyle::local()
6124 }
6125}
6126
6127#[cfg(any(test, feature = "test-support"))]
6128impl LocalFile for TestFile {
6129 fn abs_path(&self, _cx: &App) -> PathBuf {
6130 PathBuf::from(self.local_root.as_ref().unwrap())
6131 .join(&self.root_name)
6132 .join(self.path.as_std_path())
6133 }
6134
6135 fn load(&self, _cx: &App) -> Task<Result<String>> {
6136 unimplemented!()
6137 }
6138
6139 fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
6140 unimplemented!()
6141 }
6142}
6143
6144pub(crate) fn contiguous_ranges(
6145 values: impl Iterator<Item = u32>,
6146 max_len: usize,
6147) -> impl Iterator<Item = Range<u32>> {
6148 let mut values = values;
6149 let mut current_range: Option<Range<u32>> = None;
6150 std::iter::from_fn(move || {
6151 loop {
6152 if let Some(value) = values.next() {
6153 if let Some(range) = &mut current_range
6154 && value == range.end
6155 && range.len() < max_len
6156 {
6157 range.end += 1;
6158 continue;
6159 }
6160
6161 let prev_range = current_range.clone();
6162 current_range = Some(value..(value + 1));
6163 if prev_range.is_some() {
6164 return prev_range;
6165 }
6166 } else {
6167 return current_range.take();
6168 }
6169 }
6170 })
6171}
6172
6173#[derive(Default, Debug)]
6174pub struct CharClassifier {
6175 scope: Option<LanguageScope>,
6176 scope_context: Option<CharScopeContext>,
6177 ignore_punctuation: bool,
6178}
6179
6180impl CharClassifier {
6181 pub fn new(scope: Option<LanguageScope>) -> Self {
6182 Self {
6183 scope,
6184 scope_context: None,
6185 ignore_punctuation: false,
6186 }
6187 }
6188
6189 pub fn scope_context(self, scope_context: Option<CharScopeContext>) -> Self {
6190 Self {
6191 scope_context,
6192 ..self
6193 }
6194 }
6195
6196 pub fn ignore_punctuation(self, ignore_punctuation: bool) -> Self {
6197 Self {
6198 ignore_punctuation,
6199 ..self
6200 }
6201 }
6202
6203 pub fn is_whitespace(&self, c: char) -> bool {
6204 self.kind(c) == CharKind::Whitespace
6205 }
6206
6207 pub fn is_word(&self, c: char) -> bool {
6208 self.kind(c) == CharKind::Word
6209 }
6210
6211 pub fn is_punctuation(&self, c: char) -> bool {
6212 self.kind(c) == CharKind::Punctuation
6213 }
6214
6215 pub fn kind_with(&self, c: char, ignore_punctuation: bool) -> CharKind {
6216 if c.is_alphanumeric() || c == '_' {
6217 return CharKind::Word;
6218 }
6219
6220 if let Some(scope) = &self.scope {
6221 let characters = match self.scope_context {
6222 Some(CharScopeContext::Completion) => scope.completion_query_characters(),
6223 Some(CharScopeContext::LinkedEdit) => scope.linked_edit_characters(),
6224 None => scope.word_characters(),
6225 };
6226 if let Some(characters) = characters
6227 && characters.contains(&c)
6228 {
6229 return CharKind::Word;
6230 }
6231 }
6232
6233 if c.is_whitespace() {
6234 return CharKind::Whitespace;
6235 }
6236
6237 if ignore_punctuation {
6238 CharKind::Word
6239 } else {
6240 CharKind::Punctuation
6241 }
6242 }
6243
6244 pub fn kind(&self, c: char) -> CharKind {
6245 self.kind_with(c, self.ignore_punctuation)
6246 }
6247}
6248
6249/// Find all of the ranges of whitespace that occur at the ends of lines
6250/// in the given rope.
6251///
6252/// This could also be done with a regex search, but this implementation
6253/// avoids copying text.
6254/// Returns the byte ranges of trailing whitespace at the end of each line.
6255///
6256/// When `row_ranges` is `Some`, only lines whose row falls within one of the ranges are
6257/// included. The single pass keeps filtering cheap, avoiding collecting every range up front.
6258pub(crate) fn trailing_whitespace_ranges(
6259 rope: &Rope,
6260 row_ranges: Option<&[Range<u32>]>,
6261) -> Vec<Range<usize>> {
6262 let mut ranges = Vec::new();
6263
6264 let is_row_included = |row: u32| match row_ranges {
6265 Some(row_ranges) => row_ranges.iter().any(|range| range.contains(&row)),
6266 None => true,
6267 };
6268
6269 let mut offset = 0;
6270 let mut current_row: u32 = 0;
6271 let mut prev_row: Option<u32> = None;
6272 let mut prev_chunk_trailing_whitespace_range = 0..0;
6273 for chunk in rope.chunks() {
6274 let mut prev_line_trailing_whitespace_range = 0..0;
6275 for (i, line) in chunk.split('\n').enumerate() {
6276 let line_end_offset = offset + line.len();
6277 let trimmed_line_len = line.trim_end_matches([' ', '\t']).len();
6278 let mut trailing_whitespace_range = (offset + trimmed_line_len)..line_end_offset;
6279
6280 if i == 0 && trimmed_line_len == 0 {
6281 trailing_whitespace_range.start = prev_chunk_trailing_whitespace_range.start;
6282 }
6283 if let Some(row) = prev_row {
6284 if !prev_line_trailing_whitespace_range.is_empty() && is_row_included(row) {
6285 ranges.push(prev_line_trailing_whitespace_range);
6286 }
6287 }
6288
6289 prev_row = Some(current_row);
6290 offset = line_end_offset + 1;
6291 current_row += 1;
6292 prev_line_trailing_whitespace_range = trailing_whitespace_range;
6293 }
6294
6295 offset -= 1;
6296 current_row -= 1;
6297 prev_chunk_trailing_whitespace_range = prev_line_trailing_whitespace_range;
6298 }
6299
6300 if !prev_chunk_trailing_whitespace_range.is_empty() && is_row_included(current_row) {
6301 ranges.push(prev_chunk_trailing_whitespace_range);
6302 }
6303
6304 ranges
6305}
6306