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:
@@ -355,9 +355,7 @@ fn collect_files(
|
||||
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot())?;
|
||||
append_buffer_to_output(
|
||||
&snapshot,
|
||||
Some(Path::new(
|
||||
path_including_worktree_name.display(path_style).as_ref(),
|
||||
)),
|
||||
Some(path_including_worktree_name.display(path_style).as_ref()),
|
||||
&mut output,
|
||||
)
|
||||
.log_err();
|
||||
@@ -382,18 +380,18 @@ fn collect_files(
|
||||
}
|
||||
|
||||
pub fn codeblock_fence_for_path(
|
||||
path: Option<&Path>,
|
||||
path: Option<&str>,
|
||||
row_range: Option<RangeInclusive<u32>>,
|
||||
) -> String {
|
||||
let mut text = String::new();
|
||||
write!(text, "```").unwrap();
|
||||
|
||||
if let Some(path) = path {
|
||||
if let Some(extension) = path.extension().and_then(|ext| ext.to_str()) {
|
||||
if let Some(extension) = Path::new(path).extension().and_then(|ext| ext.to_str()) {
|
||||
write!(text, "{} ", extension).unwrap();
|
||||
}
|
||||
|
||||
write!(text, "{}", path.display()).unwrap();
|
||||
write!(text, "{path}").unwrap();
|
||||
} else {
|
||||
write!(text, "untitled").unwrap();
|
||||
}
|
||||
@@ -413,12 +411,12 @@ pub struct FileCommandMetadata {
|
||||
|
||||
pub fn build_entry_output_section(
|
||||
range: Range<usize>,
|
||||
path: Option<&Path>,
|
||||
path: Option<&str>,
|
||||
is_directory: bool,
|
||||
line_range: Option<Range<u32>>,
|
||||
) -> SlashCommandOutputSection<usize> {
|
||||
let mut label = if let Some(path) = path {
|
||||
path.to_string_lossy().to_string()
|
||||
path.to_string()
|
||||
} else {
|
||||
"untitled".to_string()
|
||||
};
|
||||
@@ -441,7 +439,7 @@ pub fn build_entry_output_section(
|
||||
} else {
|
||||
path.and_then(|path| {
|
||||
serde_json::to_value(FileCommandMetadata {
|
||||
path: path.to_string_lossy().to_string(),
|
||||
path: path.to_string(),
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
@@ -532,7 +530,7 @@ mod custom_path_matcher {
|
||||
|
||||
pub fn append_buffer_to_output(
|
||||
buffer: &BufferSnapshot,
|
||||
path: Option<&Path>,
|
||||
path: Option<&str>,
|
||||
output: &mut SlashCommandOutput,
|
||||
) -> Result<()> {
|
||||
let prev_len = output.text.len();
|
||||
|
||||
@@ -137,7 +137,9 @@ pub fn selections_creases(
|
||||
None
|
||||
};
|
||||
let language_name = language_name.as_deref().unwrap_or("");
|
||||
let filename = snapshot.file_at(range.start).map(|file| file.full_path(cx));
|
||||
let filename = snapshot
|
||||
.file_at(range.start)
|
||||
.map(|file| file.full_path(cx).to_string_lossy().to_string());
|
||||
let text = if language_name == "markdown" {
|
||||
selected_text
|
||||
.lines()
|
||||
@@ -187,9 +189,9 @@ pub fn selections_creases(
|
||||
let start_line = range.start.row + 1;
|
||||
let end_line = range.end.row + 1;
|
||||
if start_line == end_line {
|
||||
format!("{}, Line {}", path.display(), start_line)
|
||||
format!("{path}, Line {start_line}")
|
||||
} else {
|
||||
format!("{}, Lines {} to {}", path.display(), start_line, end_line)
|
||||
format!("{path}, Lines {start_line} to {end_line}")
|
||||
}
|
||||
} else {
|
||||
"Quoted selection".to_string()
|
||||
|
||||
@@ -7,8 +7,8 @@ use editor::Editor;
|
||||
use gpui::{AppContext as _, Task, WeakEntity};
|
||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, sync::atomic::AtomicBool};
|
||||
use ui::{App, IconName, Window};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use ui::{App, IconName, SharedString, Window};
|
||||
use workspace::Workspace;
|
||||
|
||||
pub struct OutlineSlashCommand;
|
||||
@@ -67,13 +67,13 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
};
|
||||
|
||||
let snapshot = buffer.read(cx).snapshot();
|
||||
let path = snapshot.resolve_file_path(cx, true);
|
||||
let path = snapshot.resolve_file_path(true, cx);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let outline = snapshot.outline(None);
|
||||
|
||||
let path = path.as_deref().unwrap_or(Path::new("untitled"));
|
||||
let mut outline_text = format!("Symbols for {}:\n", path.display());
|
||||
let path = path.as_deref().unwrap_or("untitled");
|
||||
let mut outline_text = format!("Symbols for {path}:\n");
|
||||
for item in &outline.path_candidates {
|
||||
outline_text.push_str("- ");
|
||||
outline_text.push_str(&item.string);
|
||||
@@ -84,7 +84,7 @@ impl SlashCommand for OutlineSlashCommand {
|
||||
sections: vec![SlashCommandOutputSection {
|
||||
range: 0..outline_text.len(),
|
||||
icon: IconName::ListTree,
|
||||
label: path.to_string_lossy().to_string().into(),
|
||||
label: SharedString::new(path),
|
||||
metadata: None,
|
||||
}],
|
||||
text: outline_text,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user