Detailed changes
@@ -15,7 +15,7 @@ use gpui::{
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
Task, WeakEntity, Window, actions, div,
};
-use language::{Buffer, DiagnosticEntry, DiagnosticEntryRef, Point};
+use language::{Buffer, Capability, DiagnosticEntry, DiagnosticEntryRef, Point};
use project::{
DiagnosticSummary, Event, Project, ProjectItem, ProjectPath,
project_settings::{DiagnosticSeverity, ProjectSettings},
@@ -763,8 +763,8 @@ impl Item for BufferDiagnosticsEditor {
self.multibuffer.read(cx).is_dirty(cx)
}
- fn is_read_only(&self, cx: &App) -> bool {
- self.multibuffer.read(cx).read_only()
+ fn capability(&self, cx: &App) -> Capability {
+ self.multibuffer.read(cx).capability()
}
fn navigate(
@@ -3162,6 +3162,14 @@ impl Editor {
}
}
+ pub fn capability(&self, cx: &App) -> Capability {
+ if self.read_only {
+ Capability::ReadOnly
+ } else {
+ self.buffer.read(cx).capability()
+ }
+ }
+
pub fn read_only(&self, cx: &App) -> bool {
self.read_only || self.buffer.read(cx).read_only()
}
@@ -805,8 +805,8 @@ impl Item for Editor {
self.buffer().read(cx).read(cx).is_dirty()
}
- fn is_read_only(&self, cx: &App) -> bool {
- self.read_only(cx)
+ fn capability(&self, cx: &App) -> Capability {
+ self.capability(cx)
}
// Note: this mirrors the logic in `Editor::toggle_read_only`, but is reachable
@@ -85,7 +85,7 @@ pub static BUFFER_DIFF_TASK: LazyLock<TaskLabel> = 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.
+ /// The buffer is a mutable replica, but toggled to be only readable.
Read,
/// The buffer is a read-only replica.
ReadOnly,
@@ -1198,6 +1198,10 @@ impl MultiBuffer {
!self.capability.editable()
}
+ pub fn capability(&self) -> Capability {
+ self.capability
+ }
+
/// Returns an up-to-date snapshot of the MultiBuffer.
#[ztracing::instrument(skip_all)]
pub fn snapshot(&self, cx: &App) -> MultiBufferSnapshot {
@@ -15,6 +15,7 @@ use gpui::{
EventEmitter, FocusHandle, Focusable, Font, HighlightStyle, Pixels, Point, Render,
SharedString, Task, WeakEntity, Window,
};
+use language::Capability;
use project::{Project, ProjectEntryId, ProjectPath};
pub use settings::{
ActivateOnClose, ClosePosition, RegisterSetting, Settings, SettingsLocation, ShowCloseButton,
@@ -255,8 +256,8 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
fn is_dirty(&self, _: &App) -> bool {
false
}
- fn is_read_only(&self, _: &App) -> bool {
- false
+ fn capability(&self, _: &App) -> Capability {
+ Capability::ReadWrite
}
fn toggle_read_only(&mut self, _window: &mut Window, _cx: &mut Context<Self>) {}
@@ -482,7 +483,7 @@ pub trait ItemHandle: 'static + Send {
fn item_id(&self) -> EntityId;
fn to_any_view(&self) -> AnyView;
fn is_dirty(&self, cx: &App) -> bool;
- fn is_read_only(&self, cx: &App) -> bool;
+ fn capability(&self, cx: &App) -> Capability;
fn toggle_read_only(&self, window: &mut Window, cx: &mut App);
fn has_deleted_file(&self, cx: &App) -> bool;
fn has_conflict(&self, cx: &App) -> bool;
@@ -957,8 +958,8 @@ impl<T: Item> ItemHandle for Entity<T> {
self.read(cx).is_dirty(cx)
}
- fn is_read_only(&self, cx: &App) -> bool {
- self.read(cx).is_read_only(cx)
+ fn capability(&self, cx: &App) -> Capability {
+ self.read(cx).capability(cx)
}
fn toggle_read_only(&self, window: &mut Window, cx: &mut App) {
@@ -26,7 +26,7 @@ use gpui::{
actions, anchored, deferred, prelude::*,
};
use itertools::Itertools;
-use language::DiagnosticSeverity;
+use language::{Capability, DiagnosticSeverity};
use parking_lot::Mutex;
use project::{DirectoryLister, Project, ProjectEntryId, ProjectPath, WorktreeId};
use schemars::JsonSchema;
@@ -2678,12 +2678,13 @@ impl Pane {
let is_pinned = self.is_tab_pinned(ix);
let position_relative_to_active_item = ix.cmp(&self.active_item_index);
- let read_only_toggle = || {
+ let read_only_toggle = |toggleable: bool| {
IconButton::new("toggle_read_only", IconName::FileLock)
.size(ButtonSize::None)
.shape(IconButtonShape::Square)
.icon_color(Color::Muted)
.icon_size(IconSize::Small)
+ .disabled(!toggleable)
.tooltip(move |_, cx| {
Tooltip::with_meta("Unlock File", None, "This will make this file editable", cx)
})
@@ -2696,6 +2697,7 @@ impl Pane {
let has_file_icon = icon.is_some() | decorated_icon.is_some();
+ let capability = item.capability(cx);
let tab = Tab::new(ix)
.position(if is_first_item {
TabPosition::First
@@ -2836,21 +2838,21 @@ impl Pane {
Some(decorated_icon.into_any_element())
} else if let Some(icon) = icon {
Some(icon.into_any_element())
- } else if item.is_read_only(cx) {
- Some(read_only_toggle().into_any_element())
+ } else if !capability.editable() {
+ Some(read_only_toggle(capability == Capability::Read).into_any_element())
} else {
None
})
.child(label)
.map(|this| match tab_tooltip_content {
Some(TabTooltipContent::Text(text)) => {
- if item.is_read_only(cx) {
+ if capability.editable() {
+ this.tooltip(Tooltip::text(text))
+ } else {
this.tooltip(move |_, cx| {
let text = text.clone();
Tooltip::with_meta(text, None, "Read-Only File", cx)
})
- } else {
- this.tooltip(Tooltip::text(text))
}
}
Some(TabTooltipContent::Custom(element_fn)) => {
@@ -2858,8 +2860,8 @@ impl Pane {
}
None => this,
})
- .when(item.is_read_only(cx) && has_file_icon, |this| {
- this.child(read_only_toggle())
+ .when(capability == Capability::Read && has_file_icon, |this| {
+ this.child(read_only_toggle(true))
}),
);
@@ -2876,7 +2878,6 @@ impl Pane {
let has_items_to_right = ix < total_items - 1;
let has_clean_items = self.items.iter().any(|item| !item.is_dirty(cx));
let is_pinned = self.is_tab_pinned(ix);
- let is_read_only = item.is_read_only(cx);
let pane = cx.entity().downgrade();
let menu_context = item.item_focus_handle(cx);
@@ -3028,20 +3029,22 @@ impl Pane {
})
};
- let read_only_label = if is_read_only {
- "Make File Editable"
- } else {
- "Make File Read-Only"
- };
- menu = menu.separator().entry(
- read_only_label,
- None,
- window.handler_for(&pane, move |pane, window, cx| {
- if let Some(item) = pane.item_for_index(ix) {
- item.toggle_read_only(window, cx);
- }
- }),
- );
+ if capability != Capability::ReadOnly {
+ let read_only_label = if capability.editable() {
+ "Make File Read-Only"
+ } else {
+ "Make File Editable"
+ };
+ menu = menu.separator().entry(
+ read_only_label,
+ None,
+ window.handler_for(&pane, move |pane, window, cx| {
+ if let Some(item) = pane.item_for_index(ix) {
+ item.toggle_read_only(window, cx);
+ }
+ }),
+ );
+ }
if let Some(entry) = single_entry_to_resolve {
let project_path = pane