@@ -44,6 +44,25 @@ pub async fn get_buffer_content_or_outline(
.collect::<Vec<_>>()
})?;
+ // If no outline exists, fall back to first 1KB so the agent has some context
+ if outline_items.is_empty() {
+ let text = buffer.read_with(cx, |buffer, _| {
+ let snapshot = buffer.snapshot();
+ let len = snapshot.len().min(1024);
+ let content = snapshot.text_for_range(0..len).collect::<String>();
+ if let Some(path) = path {
+ format!("# First 1KB of {path} (file too large to show full content, and no outline available)\n\n{content}")
+ } else {
+ format!("# First 1KB of file (file too large to show full content, and no outline available)\n\n{content}")
+ }
+ })?;
+
+ return Ok(BufferContent {
+ text,
+ is_outline: false,
+ });
+ }
+
let outline_text = render_outline(outline_items, None, 0, usize::MAX).await?;
let text = if let Some(path) = path {
@@ -140,3 +159,62 @@ fn render_entries(
entries_rendered
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use fs::FakeFs;
+ use gpui::TestAppContext;
+ use project::Project;
+ use settings::SettingsStore;
+
+ #[gpui::test]
+ async fn test_large_file_fallback_to_subset(cx: &mut TestAppContext) {
+ cx.update(|cx| {
+ let settings = SettingsStore::test(cx);
+ cx.set_global(settings);
+ });
+
+ let fs = FakeFs::new(cx.executor());
+ let project = Project::test(fs, [], cx).await;
+
+ let content = "A".repeat(100 * 1024); // 100KB
+ let content_len = content.len();
+ let buffer = project
+ .update(cx, |project, cx| project.create_buffer(true, cx))
+ .await
+ .expect("failed to create buffer");
+
+ buffer.update(cx, |buffer, cx| buffer.set_text(content, cx));
+
+ let result = cx
+ .spawn(|cx| async move { get_buffer_content_or_outline(buffer, None, &cx).await })
+ .await
+ .unwrap();
+
+ // Should contain some of the actual file content
+ assert!(
+ result.text.contains("AAAAAAAAAA"),
+ "Result did not contain content subset"
+ );
+
+ // Should be marked as not an outline (it's truncated content)
+ assert!(
+ !result.is_outline,
+ "Large file without outline should not be marked as outline"
+ );
+
+ // Should be reasonably sized (much smaller than original)
+ assert!(
+ result.text.len() < 50 * 1024,
+ "Result size {} should be smaller than 50KB",
+ result.text.len()
+ );
+
+ // Should be significantly smaller than the original content
+ assert!(
+ result.text.len() < content_len / 10,
+ "Result should be much smaller than original content"
+ );
+ }
+}
@@ -2671,13 +2671,14 @@ mod tests {
}
#[gpui::test]
- async fn test_large_file_mention_uses_outline(cx: &mut TestAppContext) {
+ async fn test_large_file_mention_fallback(cx: &mut TestAppContext) {
init_test(cx);
let fs = FakeFs::new(cx.executor());
// Create a large file that exceeds AUTO_OUTLINE_SIZE
- const LINE: &str = "fn example_function() { /* some code */ }\n";
+ // Using plain text without a configured language, so no outline is available
+ const LINE: &str = "This is a line of text in the file\n";
let large_content = LINE.repeat(2 * (outline::AUTO_OUTLINE_SIZE / LINE.len()));
assert!(large_content.len() > outline::AUTO_OUTLINE_SIZE);
@@ -2688,8 +2689,8 @@ mod tests {
fs.insert_tree(
"/project",
json!({
- "large_file.rs": large_content.clone(),
- "small_file.rs": small_content,
+ "large_file.txt": large_content.clone(),
+ "small_file.txt": small_content,
}),
)
.await;
@@ -2735,7 +2736,7 @@ mod tests {
let large_file_abs_path = project.read_with(cx, |project, cx| {
let worktree = project.worktrees(cx).next().unwrap();
let worktree_root = worktree.read(cx).abs_path();
- worktree_root.join("large_file.rs")
+ worktree_root.join("large_file.txt")
});
let large_file_task = message_editor.update(cx, |editor, cx| {
editor.confirm_mention_for_file(large_file_abs_path, cx)
@@ -2744,11 +2745,20 @@ mod tests {
let large_file_mention = large_file_task.await.unwrap();
match large_file_mention {
Mention::Text { content, .. } => {
- // Should contain outline header for large files
- assert!(content.contains("File outline for"));
- assert!(content.contains("file too large to show full content"));
- // Should not contain the full repeated content
- assert!(!content.contains(&LINE.repeat(100)));
+ // Should contain some of the content but not all of it
+ assert!(
+ content.contains(LINE),
+ "Should contain some of the file content"
+ );
+ assert!(
+ !content.contains(&LINE.repeat(100)),
+ "Should not contain the full file"
+ );
+ // Should be much smaller than original
+ assert!(
+ content.len() < large_content.len() / 10,
+ "Should be significantly truncated"
+ );
}
_ => panic!("Expected Text mention for large file"),
}
@@ -2758,7 +2768,7 @@ mod tests {
let small_file_abs_path = project.read_with(cx, |project, cx| {
let worktree = project.worktrees(cx).next().unwrap();
let worktree_root = worktree.read(cx).abs_path();
- worktree_root.join("small_file.rs")
+ worktree_root.join("small_file.txt")
});
let small_file_task = message_editor.update(cx, |editor, cx| {
editor.confirm_mention_for_file(small_file_abs_path, cx)
@@ -2767,10 +2777,8 @@ mod tests {
let small_file_mention = small_file_task.await.unwrap();
match small_file_mention {
Mention::Text { content, .. } => {
- // Should contain the actual content
+ // Should contain the full actual content
assert_eq!(content, small_content);
- // Should not contain outline header
- assert!(!content.contains("File outline for"));
}
_ => panic!("Expected Text mention for small file"),
}
@@ -1089,7 +1089,7 @@ mod tests {
}
#[gpui::test]
- async fn test_large_file_uses_outline(cx: &mut TestAppContext) {
+ async fn test_large_file_uses_fallback(cx: &mut TestAppContext) {
init_test_settings(cx);
// Create a large file that exceeds AUTO_OUTLINE_SIZE
@@ -1101,16 +1101,16 @@ mod tests {
let file_context = load_context_for("file.txt", large_content, cx).await;
+ // Should contain some of the actual file content
assert!(
- file_context
- .text
- .contains(&format!("# File outline for {}", path!("test/file.txt"))),
- "Large files should not get an outline"
+ file_context.text.contains(LINE),
+ "Should contain some of the file content"
);
+ // Should be much smaller than original
assert!(
- file_context.text.len() < content_len,
- "Outline should be smaller than original content"
+ file_context.text.len() < content_len / 10,
+ "Should be significantly smaller than original content"
);
}