Ensure context inserted via commands is syntax-highlighted (#13133)

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2024-06-17 11:57:56 +02:00 committed by GitHub
parent bedf57db89
commit 2b46a4a0e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 47 deletions

View file

@ -1,10 +1,13 @@
use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput}; use super::{
file_command::{codeblock_fence_for_path, FilePlaceholder},
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection; use assistant_slash_command::SlashCommandOutputSection;
use editor::Editor; use editor::Editor;
use gpui::{AppContext, Task, WeakView}; use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate; use language::LspAdapterDelegate;
use std::{borrow::Cow, sync::Arc}; use std::sync::Arc;
use ui::{IntoElement, WindowContext}; use ui::{IntoElement, WindowContext};
use workspace::Workspace; use workspace::Workspace;
@ -60,14 +63,8 @@ impl SlashCommand for ActiveSlashCommand {
let text = cx.background_executor().spawn({ let text = cx.background_executor().spawn({
let path = path.clone(); let path = path.clone();
async move { async move {
let path = path let mut output = String::new();
.as_ref() output.push_str(&codeblock_fence_for_path(path.as_deref(), None));
.map(|path| path.to_string_lossy())
.unwrap_or_else(|| Cow::Borrowed("untitled"));
let mut output = String::with_capacity(path.len() + snapshot.len() + 9);
output.push_str("```");
output.push_str(&path);
output.push('\n'); output.push('\n');
for chunk in snapshot.as_rope().chunks() { for chunk in snapshot.as_rope().chunks() {
output.push_str(chunk); output.push_str(chunk);

View file

@ -6,6 +6,7 @@ use gpui::{AppContext, RenderOnce, SharedString, Task, View, WeakView};
use language::{LineEnding, LspAdapterDelegate}; use language::{LineEnding, LspAdapterDelegate};
use project::PathMatchCandidateSet; use project::PathMatchCandidateSet;
use std::{ use std::{
fmt::Write,
ops::Range, ops::Range,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
@ -155,20 +156,20 @@ impl SlashCommand for FileSlashCommand {
}; };
let fs = workspace.read(cx).app_state().fs.clone(); let fs = workspace.read(cx).app_state().fs.clone();
let argument = argument.to_string(); let text = cx.background_executor().spawn({
let text = cx.background_executor().spawn(async move { let path = path.clone();
let mut content = fs.load(&abs_path).await?; async move {
LineEnding::normalize(&mut content); let mut content = fs.load(&abs_path).await?;
let mut output = String::with_capacity(argument.len() + content.len() + 9); LineEnding::normalize(&mut content);
output.push_str("```"); let mut output = String::new();
output.push_str(&argument); output.push_str(&codeblock_fence_for_path(Some(&path), None));
output.push('\n'); output.push_str(&content);
output.push_str(&content); if !output.ends_with('\n') {
if !output.ends_with('\n') { output.push('\n');
output.push('\n'); }
output.push_str("```");
anyhow::Ok(output)
} }
output.push_str("```");
anyhow::Ok(output)
}); });
cx.foreground_executor().spawn(async move { cx.foreground_executor().spawn(async move {
let text = text.await?; let text = text.await?;
@ -224,3 +225,25 @@ impl RenderOnce for FilePlaceholder {
.on_click(move |_, cx| unfold(cx)) .on_click(move |_, cx| unfold(cx))
} }
} }
pub fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option<Range<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()) {
write!(text, "{} ", extension).unwrap();
}
write!(text, "{}", path.display()).unwrap();
} else {
write!(text, "untitled").unwrap();
}
if let Some(row_range) = row_range {
write!(text, ":{}-{}", row_range.start + 1, row_range.end + 1).unwrap();
}
text.push('\n');
text
}

View file

@ -1,4 +1,7 @@
use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput}; use super::{
file_command::{codeblock_fence_for_path, FilePlaceholder},
SlashCommand, SlashCommandOutput,
};
use anyhow::Result; use anyhow::Result;
use assistant_slash_command::SlashCommandOutputSection; use assistant_slash_command::SlashCommandOutputSection;
use gpui::{AppContext, Task, WeakView}; use gpui::{AppContext, Task, WeakView};
@ -125,9 +128,8 @@ impl SlashCommand for SearchSlashCommand {
let range_start = result.range.start.min(file_content.len()); let range_start = result.range.start.min(file_content.len());
let range_end = result.range.end.min(file_content.len()); let range_end = result.range.end.min(file_content.len());
let start_line = let start_row = file_content[0..range_start].matches('\n').count() as u32;
file_content[0..range_start].matches('\n').count() as u32 + 1; let end_row = file_content[0..range_end].matches('\n').count() as u32;
let end_line = file_content[0..range_end].matches('\n').count() as u32 + 1;
let start_line_byte_offset = file_content[0..range_start] let start_line_byte_offset = file_content[0..range_start]
.rfind('\n') .rfind('\n')
.map(|pos| pos + 1) .map(|pos| pos + 1)
@ -138,14 +140,11 @@ impl SlashCommand for SearchSlashCommand {
.unwrap_or_else(|| file_content.len()); .unwrap_or_else(|| file_content.len());
let section_start_ix = text.len(); let section_start_ix = text.len();
writeln!( text.push_str(&codeblock_fence_for_path(
text, Some(&result.path),
"```{}:{}-{}", Some(start_row..end_row),
result.path.display(), ));
start_line,
end_line,
)
.unwrap();
let mut excerpt = let mut excerpt =
file_content[start_line_byte_offset..end_line_byte_offset].to_string(); file_content[start_line_byte_offset..end_line_byte_offset].to_string();
LineEnding::normalize(&mut excerpt); LineEnding::normalize(&mut excerpt);
@ -159,7 +158,7 @@ impl SlashCommand for SearchSlashCommand {
FilePlaceholder { FilePlaceholder {
id, id,
path: Some(full_path.clone()), path: Some(full_path.clone()),
line_range: Some(start_line..end_line), line_range: Some(start_row..end_row),
unfold, unfold,
} }
.into_any_element() .into_any_element()

View file

@ -1,11 +1,14 @@
use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput}; use super::{
file_command::{codeblock_fence_for_path, FilePlaceholder},
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assistant_slash_command::SlashCommandOutputSection; use assistant_slash_command::SlashCommandOutputSection;
use collections::HashMap; use collections::HashMap;
use editor::Editor; use editor::Editor;
use gpui::{AppContext, Entity, Task, WeakView}; use gpui::{AppContext, Entity, Task, WeakView};
use language::LspAdapterDelegate; use language::LspAdapterDelegate;
use std::{fmt::Write, path::Path, sync::Arc}; use std::{fmt::Write, sync::Arc};
use ui::{IntoElement, WindowContext}; use ui::{IntoElement, WindowContext};
use workspace::Workspace; use workspace::Workspace;
@ -77,15 +80,7 @@ impl SlashCommand for TabsSlashCommand {
let mut text = String::new(); let mut text = String::new();
for (full_path, buffer, _) in open_buffers { for (full_path, buffer, _) in open_buffers {
let section_start_ix = text.len(); let section_start_ix = text.len();
writeln!( text.push_str(&codeblock_fence_for_path(full_path.as_deref(), None));
text,
"```{}\n",
full_path
.as_deref()
.unwrap_or(Path::new("untitled"))
.display()
)
.unwrap();
for chunk in buffer.as_rope().chunks() { for chunk in buffer.as_rope().chunks() {
text.push_str(chunk); text.push_str(chunk);
} }