Detailed changes
@@ -21936,10 +21936,17 @@ impl Editor {
};
for (buffer, (ranges, scroll_offset)) in new_selections_by_buffer {
- let editor = buffer
- .read(cx)
- .file()
- .is_none()
+ let buffer_read = buffer.read(cx);
+ let (has_file, is_project_file) = if let Some(file) = buffer_read.file() {
+ (true, project::File::from_dyn(Some(file)).is_some())
+ } else {
+ (false, false)
+ };
+
+ // If project file is none workspace.open_project_item will fail to open the excerpt
+ // in a pre existing workspace item if one exists, because Buffer entity_id will be None
+ // so we check if there's a tab match in that case first
+ let editor = (!has_file || !is_project_file)
.then(|| {
// Handle file-less buffers separately: those are not really the project items, so won't have a project path or entity id,
// so `workspace.open_project_item` will never find them, always opening a new editor.
@@ -21973,6 +21980,9 @@ impl Editor {
});
editor.update(cx, |editor, cx| {
+ if has_file && !is_project_file {
+ editor.set_read_only(true);
+ }
let autoscroll = match scroll_offset {
Some(scroll_offset) => Autoscroll::top_relative(scroll_offset as usize),
None => Autoscroll::newest(),
@@ -21996,10 +22006,11 @@ impl Editor {
});
}
- // For now, don't allow opening excerpts in buffers that aren't backed by
- // regular project files.
+ // Allow opening excerpts for buffers that either belong to the current project
+ // or represent synthetic/non-local files (e.g., git blobs). File-less buffers
+ // are also supported so tests and other in-memory views keep working.
fn can_open_excerpts_in_file(file: Option<&Arc<dyn language::File>>) -> bool {
- file.is_none_or(|file| project::File::from_dyn(Some(file)).is_some())
+ file.is_none_or(|file| project::File::from_dyn(Some(file)).is_some() || !file.is_local())
}
fn marked_text_ranges(&self, cx: &App) -> Option<Vec<Range<MultiBufferOffsetUtf16>>> {
@@ -1891,15 +1891,20 @@ fn path_for_buffer<'a>(
cx: &'a App,
) -> Option<Cow<'a, str>> {
let file = buffer.read(cx).as_singleton()?.read(cx).file()?;
- path_for_file(file.as_ref(), height, include_filename, cx)
+ path_for_file(file, height, include_filename, cx)
}
fn path_for_file<'a>(
- file: &'a dyn language::File,
+ file: &'a Arc<dyn language::File>,
mut height: usize,
include_filename: bool,
cx: &'a App,
) -> Option<Cow<'a, str>> {
+ if project::File::from_dyn(Some(file)).is_none() {
+ return None;
+ }
+
+ let file = file.as_ref();
// Ensure we always render at least the filename.
height += 1;
@@ -1946,11 +1951,11 @@ mod tests {
#[gpui::test]
fn test_path_for_file(cx: &mut App) {
- let file = TestFile {
+ let file: Arc<dyn language::File> = Arc::new(TestFile {
path: RelPath::empty().into(),
root_name: String::new(),
local_root: None,
- };
+ });
assert_eq!(path_for_file(&file, 0, false, cx), None);
}
@@ -68,6 +68,7 @@ struct GitBlob {
path: RepoPath,
worktree_id: WorktreeId,
is_deleted: bool,
+ display_name: Arc<str>,
}
const FILE_NAMESPACE_SORT_PREFIX: u64 = 1;
@@ -157,6 +158,7 @@ impl CommitView {
});
editor
});
+ let commit_sha = Arc::<str>::from(commit.sha.as_ref());
let first_worktree_id = project
.read(cx)
@@ -180,10 +182,20 @@ impl CommitView {
.or(first_worktree_id)
})?
.context("project has no worktrees")?;
+ let short_sha = commit_sha.get(0..7).unwrap_or(&commit_sha);
+ let file_name = file
+ .path
+ .file_name()
+ .map(|name| name.to_string())
+ .unwrap_or_else(|| file.path.display(PathStyle::Posix).to_string());
+ let display_name: Arc<str> =
+ Arc::from(format!("{short_sha} - {file_name}").into_boxed_str());
+
let file = Arc::new(GitBlob {
path: file.path.clone(),
is_deleted,
worktree_id,
+ display_name,
}) as Arc<dyn language::File>;
let buffer = build_buffer(new_text, file, &language_registry, cx).await?;
@@ -647,7 +659,7 @@ impl language::File for GitBlob {
}
fn file_name<'a>(&'a self, _: &'a App) -> &'a str {
- self.path.file_name().unwrap()
+ self.display_name.as_ref()
}
fn worktree_id(&self, _: &App) -> WorktreeId {