use collections::{HashMap, HashSet};use gpui::{App, AppContext as _, AsyncApp, Entity, EntityId};use language::{Buffer, BufferSnapshot, Point, ToPoint as _};use project::{Project, ProjectPath};use std::{ ops::Range, path::{Path, PathBuf}, sync::Arc,};use text::Anchor;use util::{paths::PathStyle, rel_path::RelPath};use zeta_prompt::{ContextSource, RelatedExcerpt, RelatedFile, multi_region::is_good_block_start}; use crate::{ bm25_context::{Bm25ContextCandidate, collect_bm25_context}, git_log_context::build_git_log_index,}; /// This module contains collectors for editable context:/// excerpts or full files that are likely to be edited.const CURSOR_CONTEXT_LINE_COUNT: u32 = 20;const EDIT_HISTORY_CONTEXT_LINE_COUNT: u32 = 20;const GIT_LOG_CONTEXT_LINE_COUNT: u32 = 10000;const GIT_LOG_CONTEXT_FILE_COUNT: usize = 10;const ORACLE_SNIPPET_MIN_CONTEXT_LINE_COUNT: u32 = 10;const ORACLE_SNIPPET_MAX_CONTEXT_LINE_COUNT: u32 = 40;/// How far excerpt boundaries may be nudged to land on a natural block/// boundary, mirroring `zeta_prompt::multi_region`'s marker placement.const BOUNDARY_SNAP_LINE_COUNT: u32 = 5;/// Maximum number of rows between two excerpts of the same buffer that get/// bridged into one contiguous excerpt instead of rendering an elision/// marker between them.const BRIDGED_GAP_LINE_COUNT: u32 = 3; type RangesByBuffer = HashMap<EntityId, (Entity<Buffer>, Vec<EditableContextRange>)>; #[derive(Clone)]pub struct EditHistoryContextEntry { pub buffer: Entity<Buffer>, pub edited_range: Range<Anchor>,} /// A file known (from expected patches) to be edited next, used by the/// oracle context sources when generating training data.#[derive(Clone, Debug)]pub struct OracleTarget { pub path: Arc<Path>, /// 0-based, end-exclusive row ranges of the expected edit hunks. /// Only used by `ContextSource::OracleSnippet`. pub row_ranges: Vec<Range<u32>>,} struct EditableContextRange { range: Range<Anchor>, order: usize, context_source: ContextSource,} struct ResolvedEditableContextRange { range: Range<Point>, order: usize, context_source: ContextSource,} pub async fn collect_editable_context( project: Entity<Project>, active_buffer: Entity<Buffer>, cursor_position: Anchor, edit_history: Vec<EditHistoryContextEntry>, oracle_targets: Vec<OracleTarget>, context_sources: Vec<ContextSource>, cx: &mut AsyncApp,) -> anyhow::Result<Vec<RelatedFile>> { let mut ranges_by_buffer = RangesByBuffer::default(); if context_sources.contains(&ContextSource::CursorExcerpt) { collect_cursor_excerpt_context( &mut ranges_by_buffer, active_buffer.clone(), cursor_position, cx, ); } if context_sources.contains(&ContextSource::CurrentFile) { collect_current_file_context(&mut ranges_by_buffer, active_buffer.clone(), cx); } if context_sources.contains(&ContextSource::EditHistory) { collect_edit_history_context(&mut ranges_by_buffer, &edit_history, cx); } if context_sources.contains(&ContextSource::EditHistoryFile) { collect_edit_history_file_context(&mut ranges_by_buffer, &edit_history, cx); } if context_sources.contains(&ContextSource::GitLog) { collect_git_log_context( &mut ranges_by_buffer, project.clone(), active_buffer.clone(), cx, ) .await; } // Collected before bm25 so that, under a related-files byte budget, the // small snippets containing the expected edits are never trimmed away in // favor of bm25 excerpts. if context_sources.contains(&ContextSource::OracleSnippet) { collect_oracle_snippet_context(&mut ranges_by_buffer, project.clone(), &oracle_targets, cx) .await; } if context_sources.contains(&ContextSource::Bm25) { collect_bm25_context_ranges( &mut ranges_by_buffer, project.clone(), active_buffer, cursor_position, &edit_history, cx, ) .await; } if context_sources.contains(&ContextSource::OracleFile) { collect_oracle_file_context(&mut ranges_by_buffer, project.clone(), &oracle_targets, cx) .await; } Ok(cx.update(|cx| { let project = project.read(cx); let mut related_files = ranges_by_buffer .into_values() .filter_map(|(buffer, ranges)| related_file_for_ranges(&project, &buffer, ranges, cx)) .collect::<Vec<_>>(); related_files.sort_by_key(|file| { file.excerpts .iter() .map(|excerpt| excerpt.order) .min() .unwrap_or(usize::MAX) }); related_files }))} pub fn limit_retrieved_context_to_bytes( related_files: &[RelatedFile], max_bytes: usize,) -> Vec<RelatedFile> { struct ExcerptCandidate { file_index: usize, excerpt_index: usize, order: usize, } let mut candidates = related_files .iter() .enumerate() .flat_map(|(file_index, file)| { file.excerpts .iter() .enumerate() .map(move |(excerpt_index, excerpt)| ExcerptCandidate { file_index, excerpt_index, order: excerpt.order, }) }) .collect::<Vec<_>>(); candidates.sort_by_key(|candidate| { ( candidate.order, candidate.file_index, candidate.excerpt_index, ) }); let mut selected_excerpts = related_files .iter() .map(|file| vec![false; file.excerpts.len()]) .collect::<Vec<_>>(); let mut covered_ranges_by_file = vec![Vec::<Range<u32>>::new(); related_files.len()]; let mut selected_bytes: usize = 0; for candidate in candidates { let file = &related_files[candidate.file_index]; let excerpt = &file.excerpts[candidate.excerpt_index]; let added_bytes = uncovered_excerpt_bytes(excerpt, &covered_ranges_by_file[candidate.file_index]); if added_bytes == 0 || selected_bytes.saturating_add(added_bytes) > max_bytes { continue; } selected_bytes += added_bytes; selected_excerpts[candidate.file_index][candidate.excerpt_index] = true; push_covered_range( &mut covered_ranges_by_file[candidate.file_index], excerpt.row_range.clone(), ); } related_files .iter() .enumerate() .filter_map(|(file_index, file)| { let excerpts = file .excerpts .iter() .enumerate() .filter_map(|(excerpt_index, excerpt)| { selected_excerpts[file_index][excerpt_index].then(|| excerpt.clone()) }) .collect::<Vec<_>>(); if excerpts.is_empty() { return None; } Some(RelatedFile { path: file.path.clone(), max_row: file.max_row, excerpts, in_open_source_repo: file.in_open_source_repo, }) }) .collect()} fn uncovered_excerpt_bytes(excerpt: &RelatedExcerpt, covered_ranges: &[Range<u32>]) -> usize { let mut bytes = 0; for (row, line) in (excerpt.row_range.start..).zip(excerpt.text.split_inclusive('\n')) { if row >= excerpt.row_range.end { break; } if !covered_ranges .iter() .any(|covered_range| covered_range.contains(&row)) { bytes += line.len(); } } bytes} fn push_covered_range(covered_ranges: &mut Vec<Range<u32>>, range: Range<u32>) { covered_ranges.push(range); covered_ranges.sort_by_key(|range| (range.start, range.end)); let mut merged_ranges: Vec<Range<u32>> = Vec::new(); for range in covered_ranges.drain(..) { if let Some(last_range) = merged_ranges.last_mut() && range.start <= last_range.end { last_range.end = last_range.end.max(range.end); continue; } merged_ranges.push(range); } *covered_ranges = merged_ranges;} fn collect_cursor_excerpt_context( ranges_by_buffer: &mut RangesByBuffer, active_buffer: Entity<Buffer>, cursor_position: Anchor, cx: &mut AsyncApp,) { let cursor_range = active_buffer.read_with(cx, |buffer, _cx| { let snapshot = buffer.snapshot(); expanded_anchor_range( &snapshot, cursor_position..cursor_position, CURSOR_CONTEXT_LINE_COUNT, ) }); push_context_range( ranges_by_buffer, active_buffer, cursor_range, 0, ContextSource::CursorExcerpt, );} fn collect_current_file_context( ranges_by_buffer: &mut RangesByBuffer, active_buffer: Entity<Buffer>, cx: &mut AsyncApp,) { collect_full_buffer_context( ranges_by_buffer, active_buffer, 0, ContextSource::CurrentFile, cx, );} fn collect_edit_history_context( ranges_by_buffer: &mut RangesByBuffer, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp,) { for (index, entry) in edit_history.iter().enumerate() { let edit_history_range = entry.buffer.read_with(cx, |buffer, _cx| { expanded_anchor_range( &buffer.snapshot(), entry.edited_range.clone(), EDIT_HISTORY_CONTEXT_LINE_COUNT, ) }); push_context_range( ranges_by_buffer, entry.buffer.clone(), edit_history_range, index + 1, ContextSource::EditHistory, ); }} fn collect_edit_history_file_context( ranges_by_buffer: &mut RangesByBuffer, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp,) { let next_order = next_context_order(ranges_by_buffer); let mut seen_buffers = HashSet::default(); let mut index = 0; for entry in edit_history { if !seen_buffers.insert(entry.buffer.entity_id()) { continue; } collect_full_buffer_context( ranges_by_buffer, entry.buffer.clone(), next_order + index, ContextSource::EditHistoryFile, cx, ); index += 1; }} async fn collect_bm25_context_ranges( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, active_buffer: Entity<Buffer>, cursor_position: Anchor, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp,) { let next_order = next_context_order(ranges_by_buffer); let candidates = collect_bm25_context( project.clone(), active_buffer, cursor_position, edit_history, next_order, cx, ) .await; for candidate in candidates { collect_bm25_candidate_context(ranges_by_buffer, &project, candidate, cx).await; }} async fn collect_bm25_candidate_context( ranges_by_buffer: &mut RangesByBuffer, project: &Entity<Project>, candidate: Bm25ContextCandidate, cx: &mut AsyncApp,) { let buffer = match open_buffer_for_path(project, &candidate.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!( "failed to find BM25 context path: {}", candidate.path.display() ); return; } Err(error) => { log::debug!( "failed to open BM25 context path {}: {error:#}", candidate.path.display() ); return; } }; let Some(range) = buffer.read_with(cx, |buffer, _cx| { anchor_range_for_row_range(&buffer.snapshot(), candidate.row_range.clone()) }) else { return; }; push_context_range( ranges_by_buffer, buffer, range, candidate.order, ContextSource::Bm25, );} fn anchor_range_for_row_range( snapshot: &BufferSnapshot, row_range: Range<u32>,) -> Option<Range<Anchor>> { if row_range.start >= row_range.end || row_range.start > snapshot.max_point().row { return None; } let max_point = snapshot.max_point(); let start = snapshot.anchor_before(Point::new(row_range.start, 0)); let end_point = if row_range.end > max_point.row { max_point } else { Point::new(row_range.end, 0) }; let end = snapshot.anchor_after(end_point); Some(start..end)} async fn collect_oracle_file_context( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, oracle_targets: &[OracleTarget], cx: &mut AsyncApp,) { let next_order = next_context_order(ranges_by_buffer); let mut seen_buffers = HashSet::default(); let mut index = 0; for target in oracle_targets { let buffer = match open_buffer_for_path(&project, &target.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!("failed to find oracle file path: {}", target.path.display()); continue; } Err(error) => { log::debug!( "failed to open oracle file path {}: {error:#}", target.path.display() ); continue; } }; if !seen_buffers.insert(buffer.entity_id()) { continue; } collect_full_buffer_context( ranges_by_buffer, buffer, next_order + index, ContextSource::OracleFile, cx, ); index += 1; }} async fn collect_oracle_snippet_context( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, oracle_targets: &[OracleTarget], cx: &mut AsyncApp,) { let next_order = next_context_order(ranges_by_buffer); let mut index = 0; for target in oracle_targets { if target.row_ranges.is_empty() { continue; } let buffer = match open_buffer_for_path(&project, &target.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!( "failed to find oracle snippet path: {}", target.path.display() ); continue; } Err(error) => { log::debug!( "failed to open oracle snippet path {}: {error:#}", target.path.display()Lines 1–500 of 1238. Load more
use collections::{HashMap, HashSet}; use gpui::{App, AppContext as _, AsyncApp, Entity, EntityId}; use language::{Buffer, BufferSnapshot, Point, ToPoint as _}; use project::{Project, ProjectPath}; use std::{ ops::Range, path::{Path, PathBuf}, sync::Arc, }; use text::Anchor; use util::{paths::PathStyle, rel_path::RelPath}; use zeta_prompt::{ContextSource, RelatedExcerpt, RelatedFile, multi_region::is_good_block_start}; use crate::{ bm25_context::{Bm25ContextCandidate, collect_bm25_context}, git_log_context::build_git_log_index, }; /// This module contains collectors for editable context: /// excerpts or full files that are likely to be edited. const CURSOR_CONTEXT_LINE_COUNT: u32 = 20; const EDIT_HISTORY_CONTEXT_LINE_COUNT: u32 = 20; const GIT_LOG_CONTEXT_LINE_COUNT: u32 = 10000; const GIT_LOG_CONTEXT_FILE_COUNT: usize = 10; const ORACLE_SNIPPET_MIN_CONTEXT_LINE_COUNT: u32 = 10; const ORACLE_SNIPPET_MAX_CONTEXT_LINE_COUNT: u32 = 40; /// How far excerpt boundaries may be nudged to land on a natural block /// boundary, mirroring `zeta_prompt::multi_region`'s marker placement. const BOUNDARY_SNAP_LINE_COUNT: u32 = 5; /// Maximum number of rows between two excerpts of the same buffer that get /// bridged into one contiguous excerpt instead of rendering an elision /// marker between them. const BRIDGED_GAP_LINE_COUNT: u32 = 3; type RangesByBuffer = HashMap<EntityId, (Entity<Buffer>, Vec<EditableContextRange>)>; #[derive(Clone)] pub struct EditHistoryContextEntry { pub buffer: Entity<Buffer>, pub edited_range: Range<Anchor>, } /// A file known (from expected patches) to be edited next, used by the /// oracle context sources when generating training data. #[derive(Clone, Debug)] pub struct OracleTarget { pub path: Arc<Path>, /// 0-based, end-exclusive row ranges of the expected edit hunks. /// Only used by `ContextSource::OracleSnippet`. pub row_ranges: Vec<Range<u32>>, } struct EditableContextRange { range: Range<Anchor>, order: usize, context_source: ContextSource, } struct ResolvedEditableContextRange { range: Range<Point>, order: usize, context_source: ContextSource, } pub async fn collect_editable_context( project: Entity<Project>, active_buffer: Entity<Buffer>, cursor_position: Anchor, edit_history: Vec<EditHistoryContextEntry>, oracle_targets: Vec<OracleTarget>, context_sources: Vec<ContextSource>, cx: &mut AsyncApp, ) -> anyhow::Result<Vec<RelatedFile>> { let mut ranges_by_buffer = RangesByBuffer::default(); if context_sources.contains(&ContextSource::CursorExcerpt) { collect_cursor_excerpt_context( &mut ranges_by_buffer, active_buffer.clone(), cursor_position, cx, ); } if context_sources.contains(&ContextSource::CurrentFile) { collect_current_file_context(&mut ranges_by_buffer, active_buffer.clone(), cx); } if context_sources.contains(&ContextSource::EditHistory) { collect_edit_history_context(&mut ranges_by_buffer, &edit_history, cx); } if context_sources.contains(&ContextSource::EditHistoryFile) { collect_edit_history_file_context(&mut ranges_by_buffer, &edit_history, cx); } if context_sources.contains(&ContextSource::GitLog) { collect_git_log_context( &mut ranges_by_buffer, project.clone(), active_buffer.clone(), cx, ) .await; } // Collected before bm25 so that, under a related-files byte budget, the // small snippets containing the expected edits are never trimmed away in // favor of bm25 excerpts. if context_sources.contains(&ContextSource::OracleSnippet) { collect_oracle_snippet_context(&mut ranges_by_buffer, project.clone(), &oracle_targets, cx) .await; } if context_sources.contains(&ContextSource::Bm25) { collect_bm25_context_ranges( &mut ranges_by_buffer, project.clone(), active_buffer, cursor_position, &edit_history, cx, ) .await; } if context_sources.contains(&ContextSource::OracleFile) { collect_oracle_file_context(&mut ranges_by_buffer, project.clone(), &oracle_targets, cx) .await; } Ok(cx.update(|cx| { let project = project.read(cx); let mut related_files = ranges_by_buffer .into_values() .filter_map(|(buffer, ranges)| related_file_for_ranges(&project, &buffer, ranges, cx)) .collect::<Vec<_>>(); related_files.sort_by_key(|file| { file.excerpts .iter() .map(|excerpt| excerpt.order) .min() .unwrap_or(usize::MAX) }); related_files })) } pub fn limit_retrieved_context_to_bytes( related_files: &[RelatedFile], max_bytes: usize, ) -> Vec<RelatedFile> { struct ExcerptCandidate { file_index: usize, excerpt_index: usize, order: usize, } let mut candidates = related_files .iter() .enumerate() .flat_map(|(file_index, file)| { file.excerpts .iter() .enumerate() .map(move |(excerpt_index, excerpt)| ExcerptCandidate { file_index, excerpt_index, order: excerpt.order, }) }) .collect::<Vec<_>>(); candidates.sort_by_key(|candidate| { ( candidate.order, candidate.file_index, candidate.excerpt_index, ) }); let mut selected_excerpts = related_files .iter() .map(|file| vec![false; file.excerpts.len()]) .collect::<Vec<_>>(); let mut covered_ranges_by_file = vec![Vec::<Range<u32>>::new(); related_files.len()]; let mut selected_bytes: usize = 0; for candidate in candidates { let file = &related_files[candidate.file_index]; let excerpt = &file.excerpts[candidate.excerpt_index]; let added_bytes = uncovered_excerpt_bytes(excerpt, &covered_ranges_by_file[candidate.file_index]); if added_bytes == 0 || selected_bytes.saturating_add(added_bytes) > max_bytes { continue; } selected_bytes += added_bytes; selected_excerpts[candidate.file_index][candidate.excerpt_index] = true; push_covered_range( &mut covered_ranges_by_file[candidate.file_index], excerpt.row_range.clone(), ); } related_files .iter() .enumerate() .filter_map(|(file_index, file)| { let excerpts = file .excerpts .iter() .enumerate() .filter_map(|(excerpt_index, excerpt)| { selected_excerpts[file_index][excerpt_index].then(|| excerpt.clone()) }) .collect::<Vec<_>>(); if excerpts.is_empty() { return None; } Some(RelatedFile { path: file.path.clone(), max_row: file.max_row, excerpts, in_open_source_repo: file.in_open_source_repo, }) }) .collect() } fn uncovered_excerpt_bytes(excerpt: &RelatedExcerpt, covered_ranges: &[Range<u32>]) -> usize { let mut bytes = 0; for (row, line) in (excerpt.row_range.start..).zip(excerpt.text.split_inclusive('\n')) { if row >= excerpt.row_range.end { break; } if !covered_ranges .iter() .any(|covered_range| covered_range.contains(&row)) { bytes += line.len(); } } bytes } fn push_covered_range(covered_ranges: &mut Vec<Range<u32>>, range: Range<u32>) { covered_ranges.push(range); covered_ranges.sort_by_key(|range| (range.start, range.end)); let mut merged_ranges: Vec<Range<u32>> = Vec::new(); for range in covered_ranges.drain(..) { if let Some(last_range) = merged_ranges.last_mut() && range.start <= last_range.end { last_range.end = last_range.end.max(range.end); continue; } merged_ranges.push(range); } *covered_ranges = merged_ranges; } fn collect_cursor_excerpt_context( ranges_by_buffer: &mut RangesByBuffer, active_buffer: Entity<Buffer>, cursor_position: Anchor, cx: &mut AsyncApp, ) { let cursor_range = active_buffer.read_with(cx, |buffer, _cx| { let snapshot = buffer.snapshot(); expanded_anchor_range( &snapshot, cursor_position..cursor_position, CURSOR_CONTEXT_LINE_COUNT, ) }); push_context_range( ranges_by_buffer, active_buffer, cursor_range, 0, ContextSource::CursorExcerpt, ); } fn collect_current_file_context( ranges_by_buffer: &mut RangesByBuffer, active_buffer: Entity<Buffer>, cx: &mut AsyncApp, ) { collect_full_buffer_context( ranges_by_buffer, active_buffer, 0, ContextSource::CurrentFile, cx, ); } fn collect_edit_history_context( ranges_by_buffer: &mut RangesByBuffer, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp, ) { for (index, entry) in edit_history.iter().enumerate() { let edit_history_range = entry.buffer.read_with(cx, |buffer, _cx| { expanded_anchor_range( &buffer.snapshot(), entry.edited_range.clone(), EDIT_HISTORY_CONTEXT_LINE_COUNT, ) }); push_context_range( ranges_by_buffer, entry.buffer.clone(), edit_history_range, index + 1, ContextSource::EditHistory, ); } } fn collect_edit_history_file_context( ranges_by_buffer: &mut RangesByBuffer, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp, ) { let next_order = next_context_order(ranges_by_buffer); let mut seen_buffers = HashSet::default(); let mut index = 0; for entry in edit_history { if !seen_buffers.insert(entry.buffer.entity_id()) { continue; } collect_full_buffer_context( ranges_by_buffer, entry.buffer.clone(), next_order + index, ContextSource::EditHistoryFile, cx, ); index += 1; } } async fn collect_bm25_context_ranges( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, active_buffer: Entity<Buffer>, cursor_position: Anchor, edit_history: &[EditHistoryContextEntry], cx: &mut AsyncApp, ) { let next_order = next_context_order(ranges_by_buffer); let candidates = collect_bm25_context( project.clone(), active_buffer, cursor_position, edit_history, next_order, cx, ) .await; for candidate in candidates { collect_bm25_candidate_context(ranges_by_buffer, &project, candidate, cx).await; } } async fn collect_bm25_candidate_context( ranges_by_buffer: &mut RangesByBuffer, project: &Entity<Project>, candidate: Bm25ContextCandidate, cx: &mut AsyncApp, ) { let buffer = match open_buffer_for_path(project, &candidate.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!( "failed to find BM25 context path: {}", candidate.path.display() ); return; } Err(error) => { log::debug!( "failed to open BM25 context path {}: {error:#}", candidate.path.display() ); return; } }; let Some(range) = buffer.read_with(cx, |buffer, _cx| { anchor_range_for_row_range(&buffer.snapshot(), candidate.row_range.clone()) }) else { return; }; push_context_range( ranges_by_buffer, buffer, range, candidate.order, ContextSource::Bm25, ); } fn anchor_range_for_row_range( snapshot: &BufferSnapshot, row_range: Range<u32>, ) -> Option<Range<Anchor>> { if row_range.start >= row_range.end || row_range.start > snapshot.max_point().row { return None; } let max_point = snapshot.max_point(); let start = snapshot.anchor_before(Point::new(row_range.start, 0)); let end_point = if row_range.end > max_point.row { max_point } else { Point::new(row_range.end, 0) }; let end = snapshot.anchor_after(end_point); Some(start..end) } async fn collect_oracle_file_context( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, oracle_targets: &[OracleTarget], cx: &mut AsyncApp, ) { let next_order = next_context_order(ranges_by_buffer); let mut seen_buffers = HashSet::default(); let mut index = 0; for target in oracle_targets { let buffer = match open_buffer_for_path(&project, &target.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!("failed to find oracle file path: {}", target.path.display()); continue; } Err(error) => { log::debug!( "failed to open oracle file path {}: {error:#}", target.path.display() ); continue; } }; if !seen_buffers.insert(buffer.entity_id()) { continue; } collect_full_buffer_context( ranges_by_buffer, buffer, next_order + index, ContextSource::OracleFile, cx, ); index += 1; } } async fn collect_oracle_snippet_context( ranges_by_buffer: &mut RangesByBuffer, project: Entity<Project>, oracle_targets: &[OracleTarget], cx: &mut AsyncApp, ) { let next_order = next_context_order(ranges_by_buffer); let mut index = 0; for target in oracle_targets { if target.row_ranges.is_empty() { continue; } let buffer = match open_buffer_for_path(&project, &target.path, cx).await { Ok(Some(buffer)) => buffer, Ok(None) => { log::debug!( "failed to find oracle snippet path: {}", target.path.display() ); continue; } Err(error) => { log::debug!( "failed to open oracle snippet path {}: {error:#}", target.path.display()