From 8cd3fa5a6c7c8c89a20e1c74273d1a58ab38e5cf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 11 Dec 2025 17:16:57 +0100 Subject: [PATCH] Introduce a new mutable read capability --- crates/editor/src/editor.rs | 12 ++++++------ crates/editor/src/element.rs | 13 ++++--------- crates/language/src/buffer.rs | 11 ++++++++++- crates/multi_buffer/src/multi_buffer.rs | 2 +- crates/project/src/buffer_store.rs | 2 +- crates/project/src/project.rs | 2 +- 6 files changed, 23 insertions(+), 19 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 12d47557f09064cb091fd901803cbcfd82cacb09..a2d410bf06cee3e01dc712e9731261f9eba03f77 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3353,7 +3353,7 @@ impl Editor { let position_matches = start_offset == completion_position.to_offset(buffer); let continue_showing = if let Some((snap, ..)) = buffer.point_to_buffer_offset(completion_position) - && snap.capability == Capability::ReadOnly + && !snap.capability.editable() { false } else if position_matches { @@ -4322,13 +4322,13 @@ impl Editor { { if snapshot .point_to_buffer_point(selection.head()) - .is_none_or(|(snapshot, ..)| snapshot.capability == Capability::ReadOnly) + .is_none_or(|(snapshot, ..)| !snapshot.capability.editable()) { continue; } if snapshot .point_to_buffer_point(selection.tail()) - .is_none_or(|(snapshot, ..)| snapshot.capability == Capability::ReadOnly) + .is_none_or(|(snapshot, ..)| !snapshot.capability.editable()) { // note, ideally we'd clip the tail to the closest writeable region towards the head continue; @@ -11008,13 +11008,13 @@ impl Editor { } pub fn toggle_read_only(&mut self, _: &ToggleReadOnly, _: &mut Window, cx: &mut Context) { - // todo(lw): We should not allow toggling every editor to read-write. Some we want to keep read-only, always as they might be read-only replicated etc. if let Some(buffer) = self.buffer.read(cx).as_singleton() { buffer.update(cx, |buffer, cx| { buffer.set_capability( match buffer.capability() { - Capability::ReadWrite => Capability::ReadOnly, - Capability::ReadOnly => Capability::ReadWrite, + Capability::ReadWrite => Capability::Read, + Capability::Read => Capability::ReadWrite, + Capability::ReadOnly => Capability::ReadOnly, }, cx, ); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 38e809dfbdeeb93281439e189c541f3909474a2d..7459358ec8201e60ec4a011cdd3324a920eb728b 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -51,7 +51,7 @@ use gpui::{ quad, relative, size, solid_background, transparent_black, }; use itertools::Itertools; -use language::{Capability, IndentGuideSettings, language_settings::ShowWhitespaceSetting}; +use language::{IndentGuideSettings, language_settings::ShowWhitespaceSetting}; use markdown::Markdown; use multi_buffer::{ Anchor, ExcerptId, ExcerptInfo, ExpandExcerptDirection, ExpandInfo, MultiBufferPoint, @@ -4147,14 +4147,9 @@ impl EditorElement { } })), ) - .when( - for_excerpt.buffer.capability == Capability::ReadOnly, - |el| { - el.child( - Icon::new(IconName::FileLock).color(Color::Muted), - ) - }, - ) + .when(!for_excerpt.buffer.capability.editable(), |el| { + el.child(Icon::new(IconName::FileLock).color(Color::Muted)) + }) .when_some(parent_path, |then, path| { then.child(Label::new(path).truncate().color( if file_status.is_some_and(FileStatus::is_deleted) { diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 0e4dd56449e4810d7c6e8040667d9dcba1358ca8..de631ff544efcfb043d3867f92bf8911ca8aa2d2 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -85,10 +85,19 @@ pub static BUFFER_DIFF_TASK: LazyLock = LazyLock::new(TaskLabel::new) pub enum Capability { /// The buffer is a mutable replica. ReadWrite, + /// The buffer is a mutable replica, but toggled to read-only. + Read, /// The buffer is a read-only replica. ReadOnly, } +impl Capability { + /// Returns `true` if the capability is `ReadWrite`. + pub fn editable(self) -> bool { + matches!(self, Capability::ReadWrite) + } +} + pub type BufferRow = u32; /// An in-memory representation of a source code file, including its text, @@ -1063,7 +1072,7 @@ impl Buffer { /// Whether this buffer can only be read. pub fn read_only(&self) -> bool { - self.capability == Capability::ReadOnly + !self.capability.editable() } /// Builds a [`Buffer`] with the given underlying [`TextBuffer`], diff base, [`File`] and [`Capability`]. diff --git a/crates/multi_buffer/src/multi_buffer.rs b/crates/multi_buffer/src/multi_buffer.rs index 8424c85f9670aea69952da055eb5a8932ca83f9e..2fa607da442b3880ce13e3a4a87fdf147d965eb2 100644 --- a/crates/multi_buffer/src/multi_buffer.rs +++ b/crates/multi_buffer/src/multi_buffer.rs @@ -1198,7 +1198,7 @@ impl MultiBuffer { } pub fn read_only(&self) -> bool { - self.capability == Capability::ReadOnly + !self.capability.editable() } /// Returns an up-to-date snapshot of the MultiBuffer. diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 6ad2ca50b73a2bd0687fbe8bddc2bd5f50183613..a8fd7e2dd194d4d0eac6be25fa3f290852d6ff5f 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -677,7 +677,7 @@ impl LocalBufferStore { }; if is_read_only { buffer.update(cx, |buffer, cx| { - buffer.set_capability(Capability::ReadOnly, cx); + buffer.set_capability(Capability::Read, cx); }); } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 25a19788fdb464f5f289ef3bc3513f21743e3a9a..e036c16d740934e76b9c2da8009b058f9a2550e6 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2650,7 +2650,7 @@ impl Project { #[inline] pub fn is_read_only(&self, cx: &App) -> bool { - self.is_disconnected(cx) || self.capability() == Capability::ReadOnly + self.is_disconnected(cx) || !self.capability().editable() } #[inline]