windows: Fix inconsistent separators in buffer headers and breadcrumbs (#38898)

Make `resolve_full_path` use the appropriate separators, and return a
`String`.

As part of fixing the fallout from that type change, this also fixes a
bunch of places in the agent code that were using `std::path::Path`
operations on paths that could be non-local, by changing them to operate
instead on strings and use the project's `PathStyle`.

This clears the way a bit for making `full_path` also return a string
instead of a `PathBuf`, but I've left that for a follow-up.

Release Notes:

- N/A
This commit is contained in:
Cole Miller
2025-09-25 22:24:32 +00:00
committed by GitHub
parent 6470443271
commit d83d7d35cb
16 changed files with 239 additions and 170 deletions
@@ -8,12 +8,9 @@ use editor::Editor;
use futures::future::join_all;
use gpui::{Task, WeakEntity};
use language::{BufferSnapshot, CodeLabel, HighlightId, LspAdapterDelegate};
use std::{
path::PathBuf,
sync::{Arc, atomic::AtomicBool},
};
use std::sync::{Arc, atomic::AtomicBool};
use ui::{ActiveTheme, App, Window, prelude::*};
use util::ResultExt;
use util::{ResultExt, paths::PathStyle};
use workspace::Workspace;
use crate::file_command::append_buffer_to_output;
@@ -72,35 +69,42 @@ impl SlashCommand for TabSlashCommand {
return Task::ready(Ok(Vec::new()));
}
let active_item_path = workspace.as_ref().and_then(|workspace| {
workspace
.update(cx, |workspace, cx| {
let snapshot = active_item_buffer(workspace, cx).ok()?;
snapshot.resolve_file_path(cx, true)
})
.ok()
.flatten()
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
return Task::ready(Err(anyhow::anyhow!("no workspace")));
};
let active_item_path = workspace.update(cx, |workspace, cx| {
let snapshot = active_item_buffer(workspace, cx).ok()?;
snapshot.resolve_file_path(true, cx)
});
let path_style = workspace.read(cx).path_style(cx);
let current_query = arguments.last().cloned().unwrap_or_default();
let tab_items_search =
tab_items_for_queries(workspace, &[current_query], cancel, false, window, cx);
let tab_items_search = tab_items_for_queries(
Some(workspace.downgrade()),
&[current_query],
cancel,
false,
window,
cx,
);
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
window.spawn(cx, async move |_| {
let tab_items = tab_items_search.await?;
let run_command = tab_items.len() == 1;
let tab_completion_items = tab_items.into_iter().filter_map(|(path, ..)| {
let path_string = path.as_deref()?.to_string_lossy().to_string();
if argument_set.contains(&path_string) {
let path = path?;
if argument_set.contains(&path) {
return None;
}
if active_item_path.is_some() && active_item_path == path {
if active_item_path.as_ref() == Some(&path) {
return None;
}
let label = create_tab_completion_label(path.as_ref()?, comment_id);
let label = create_tab_completion_label(&path, path_style, comment_id);
Some(ArgumentCompletion {
label,
new_text: path_string,
new_text: path,
replace_previous_arguments: false,
after_completion: run_command.into(),
})
@@ -109,8 +113,9 @@ impl SlashCommand for TabSlashCommand {
let active_item_completion = active_item_path
.as_deref()
.map(|active_item_path| {
let path_string = active_item_path.to_string_lossy().to_string();
let label = create_tab_completion_label(active_item_path, comment_id);
let path_string = active_item_path.to_string();
let label =
create_tab_completion_label(active_item_path, path_style, comment_id);
ArgumentCompletion {
label,
new_text: path_string,
@@ -169,7 +174,7 @@ fn tab_items_for_queries(
strict_match: bool,
window: &mut Window,
cx: &mut App,
) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
) -> Task<anyhow::Result<Vec<(Option<String>, BufferSnapshot, usize)>>> {
let empty_query = queries.is_empty() || queries.iter().all(|query| query.trim().is_empty());
let queries = queries.to_owned();
window.spawn(cx, async move |cx| {
@@ -179,7 +184,7 @@ fn tab_items_for_queries(
.update(cx, |workspace, cx| {
if strict_match && empty_query {
let snapshot = active_item_buffer(workspace, cx)?;
let full_path = snapshot.resolve_file_path(cx, true);
let full_path = snapshot.resolve_file_path(true, cx);
return anyhow::Ok(vec![(full_path, snapshot, 0)]);
}
@@ -201,7 +206,7 @@ fn tab_items_for_queries(
&& visited_buffers.insert(buffer.read(cx).remote_id())
{
let snapshot = buffer.read(cx).snapshot();
let full_path = snapshot.resolve_file_path(cx, true);
let full_path = snapshot.resolve_file_path(true, cx);
open_buffers.push((full_path, snapshot, *timestamp));
}
}
@@ -224,10 +229,7 @@ fn tab_items_for_queries(
let match_candidates = open_buffers
.iter()
.enumerate()
.filter_map(|(id, (full_path, ..))| {
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
Some((id, path_string))
})
.filter_map(|(id, (full_path, ..))| Some((id, full_path.clone()?)))
.fold(HashMap::default(), |mut candidates, (id, path_string)| {
candidates
.entry(path_string)
@@ -249,8 +251,7 @@ fn tab_items_for_queries(
.iter()
.enumerate()
.filter_map(|(id, (full_path, ..))| {
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
Some(fuzzy::StringMatchCandidate::new(id, &path_string))
Some(fuzzy::StringMatchCandidate::new(id, full_path.as_ref()?))
})
.collect::<Vec<_>>();
let mut processed_matches = HashSet::default();
@@ -302,21 +303,15 @@ fn active_item_buffer(
}
fn create_tab_completion_label(
path: &std::path::Path,
path: &str,
path_style: PathStyle,
comment_id: Option<HighlightId>,
) -> CodeLabel {
let file_name = path
.file_name()
.map(|f| f.to_string_lossy())
.unwrap_or_default();
let parent_path = path
.parent()
.map(|p| p.to_string_lossy())
.unwrap_or_default();
let (parent_path, file_name) = path_style.split(path);
let mut label = CodeLabel::default();
label.push_str(&file_name, None);
label.push_str(file_name, None);
label.push_str(" ", None);
label.push_str(&parent_path, comment_id);
label.push_str(parent_path.unwrap_or_default(), comment_id);
label.filter_range = 0..file_name.len();
label
}