use cloud_llm_client::predict_edits_v3::DeclarationScoreComponents;use collections::HashMap;use language::BufferSnapshot;use ordered_float::OrderedFloat;use project::ProjectEntryId;use serde::Serialize;use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};use strum::EnumIter;use text::{Point, ToPoint};use util::RangeExt as _; use crate::{ CachedDeclarationPath, Declaration, EditPredictionExcerpt, Identifier, imports::{Import, Imports, Module}, reference::{Reference, ReferenceRegion}, syntax_index::SyntaxIndexState, text_similarity::{Occurrences, jaccard_similarity, weighted_overlap_coefficient},}; const MAX_IDENTIFIER_DECLARATION_COUNT: usize = 16; #[derive(Clone, Debug, PartialEq, Eq)]pub struct EditPredictionScoreOptions { pub omit_excerpt_overlaps: bool,} #[derive(Clone, Debug)]pub struct ScoredDeclaration { /// identifier used by the local reference pub identifier: Identifier, pub declaration: Declaration, pub components: DeclarationScoreComponents,} #[derive(EnumIter, Clone, Copy, PartialEq, Eq, Hash, Debug)]pub enum DeclarationStyle { Signature, Declaration,} #[derive(Clone, Debug, Serialize, Default)]pub struct DeclarationScores { pub signature: f32, pub declaration: f32, pub retrieval: f32,} impl ScoredDeclaration { /// Returns the score for this declaration with the specified style. pub fn score(&self, style: DeclarationStyle) -> f32 { // TODO: handle truncation // Score related to how likely this is the correct declaration, range 0 to 1 let retrieval = self.retrieval_score(); // Score related to the distance between the reference and cursor, range 0 to 1 let distance_score = if self.components.is_referenced_nearby { 1.0 / (1.0 + self.components.reference_line_distance as f32 / 10.0).powf(2.0) } else { // same score as ~14 lines away, rationale is to not overly penalize references from parent signatures 0.5 }; // For now instead of linear combination, the scores are just multiplied together. let combined_score = 10.0 * retrieval * distance_score; match style { DeclarationStyle::Signature => { combined_score * self.components.excerpt_vs_signature_weighted_overlap } DeclarationStyle::Declaration => { 2.0 * combined_score * self.components.excerpt_vs_item_weighted_overlap } } } pub fn retrieval_score(&self) -> f32 { let mut score = if self.components.is_same_file { 10.0 / self.components.same_file_declaration_count as f32 } else if self.components.path_import_match_count > 0 { 3.0 } else if self.components.wildcard_path_import_match_count > 0 { 1.0 } else if self.components.normalized_import_similarity > 0.0 { self.components.normalized_import_similarity } else if self.components.normalized_wildcard_import_similarity > 0.0 { 0.5 * self.components.normalized_wildcard_import_similarity } else { 1.0 / self.components.declaration_count as f32 }; score *= 1. + self.components.included_by_others as f32 / 2.; score *= 1. + self.components.includes_others as f32 / 4.; score } pub fn size(&self, style: DeclarationStyle) -> usize { match &self.declaration { Declaration::File { declaration, .. } => match style { DeclarationStyle::Signature => declaration.signature_range.len(), DeclarationStyle::Declaration => declaration.text.len(), }, Declaration::Buffer { declaration, .. } => match style { DeclarationStyle::Signature => declaration.signature_range.len(), DeclarationStyle::Declaration => declaration.item_range.len(), }, } } pub fn score_density(&self, style: DeclarationStyle) -> f32 { self.score(style) / self.size(style) as f32 }} pub fn scored_declarations( options: &EditPredictionScoreOptions, index: &SyntaxIndexState, excerpt: &EditPredictionExcerpt, excerpt_occurrences: &Occurrences, adjacent_occurrences: &Occurrences, imports: &Imports, identifier_to_references: HashMap<Identifier, Vec<Reference>>, cursor_offset: usize, current_buffer: &BufferSnapshot,) -> Vec<ScoredDeclaration> { let cursor_point = cursor_offset.to_point(¤t_buffer); let mut wildcard_import_occurrences = Vec::new(); let mut wildcard_import_paths = Vec::new(); for wildcard_import in imports.wildcard_modules.iter() { match wildcard_import { Module::Namespace(namespace) => { wildcard_import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => wildcard_import_paths.push(path), Module::SourceFuzzy(path) => { wildcard_import_occurrences.push(Occurrences::from_path(&path)) } } } let mut scored_declarations = Vec::new(); let mut project_entry_id_to_outline_ranges: HashMap<ProjectEntryId, Vec<Range<usize>>> = HashMap::default(); for (identifier, references) in identifier_to_references { let mut import_occurrences = Vec::new(); let mut import_paths = Vec::new(); let mut found_external_identifier: Option<&Identifier> = None; if let Some(imports) = imports.identifier_to_imports.get(&identifier) { // only use alias when it's the only import, could be generalized if some language // has overlapping aliases // // TODO: when an aliased declaration is included in the prompt, should include the // aliasing in the prompt. // // TODO: For SourceFuzzy consider having componentwise comparison that pays // attention to ordering. if let [ Import::Alias { module, external_identifier, }, ] = imports.as_slice() { match module { Module::Namespace(namespace) => { import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => import_paths.push(path), Module::SourceFuzzy(path) => { import_occurrences.push(Occurrences::from_path(&path)) } } found_external_identifier = Some(&external_identifier); } else { for import in imports { match import { Import::Direct { module } => match module { Module::Namespace(namespace) => { import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => import_paths.push(path), Module::SourceFuzzy(path) => { import_occurrences.push(Occurrences::from_path(&path)) } }, Import::Alias { .. } => {} } } } } let identifier_to_lookup = found_external_identifier.unwrap_or(&identifier); // TODO: update this to be able to return more declarations? Especially if there is the // ability to quickly filter a large list (based on imports) let identifier_declarations = index .declarations_for_identifier::<MAX_IDENTIFIER_DECLARATION_COUNT>(&identifier_to_lookup); let declaration_count = identifier_declarations.len(); if declaration_count == 0 { continue; } // TODO: option to filter out other candidates when same file / import match let mut checked_declarations = Vec::with_capacity(declaration_count); for (declaration_id, declaration) in identifier_declarations { match declaration { Declaration::Buffer { buffer_id, declaration: buffer_declaration, .. } => { if buffer_id == ¤t_buffer.remote_id() { let already_included_in_prompt = range_intersection(&buffer_declaration.item_range, &excerpt.range) .is_some() || excerpt .parent_declarations .iter() .any(|(excerpt_parent, _)| excerpt_parent == &declaration_id); if !options.omit_excerpt_overlaps || !already_included_in_prompt { let declaration_line = buffer_declaration .item_range .start .to_point(current_buffer) .row; let declaration_line_distance = (cursor_point.row as i32 - declaration_line as i32).unsigned_abs(); checked_declarations.push(CheckedDeclaration { declaration, same_file_line_distance: Some(declaration_line_distance), path_import_match_count: 0, wildcard_path_import_match_count: 0, }); } continue; } else { } } Declaration::File { .. } => {} } let declaration_path = declaration.cached_path(); let path_import_match_count = import_paths .iter() .filter(|import_path| { declaration_path_matches_import(&declaration_path, import_path) }) .count(); let wildcard_path_import_match_count = wildcard_import_paths .iter() .filter(|import_path| { declaration_path_matches_import(&declaration_path, import_path) }) .count(); checked_declarations.push(CheckedDeclaration { declaration, same_file_line_distance: None, path_import_match_count, wildcard_path_import_match_count, }); } let mut max_import_similarity = 0.0; let mut max_wildcard_import_similarity = 0.0; let mut scored_declarations_for_identifier = Vec::with_capacity(checked_declarations.len()); for checked_declaration in checked_declarations { let same_file_declaration_count = index.file_declaration_count(checked_declaration.declaration); let declaration = score_declaration( &identifier, &references, checked_declaration, same_file_declaration_count, declaration_count, &excerpt_occurrences, &adjacent_occurrences, &import_occurrences, &wildcard_import_occurrences, cursor_point, current_buffer, ); if declaration.components.import_similarity > max_import_similarity { max_import_similarity = declaration.components.import_similarity; } if declaration.components.wildcard_import_similarity > max_wildcard_import_similarity { max_wildcard_import_similarity = declaration.components.wildcard_import_similarity; } project_entry_id_to_outline_ranges .entry(declaration.declaration.project_entry_id()) .or_default() .push(declaration.declaration.item_range()); scored_declarations_for_identifier.push(declaration); } if max_import_similarity > 0.0 || max_wildcard_import_similarity > 0.0 { for declaration in scored_declarations_for_identifier.iter_mut() { if max_import_similarity > 0.0 { declaration.components.max_import_similarity = max_import_similarity; declaration.components.normalized_import_similarity = declaration.components.import_similarity / max_import_similarity; } if max_wildcard_import_similarity > 0.0 { declaration.components.normalized_wildcard_import_similarity = declaration.components.wildcard_import_similarity / max_wildcard_import_similarity; } } } scored_declarations.extend(scored_declarations_for_identifier); } // TODO: Inform this via import / retrieval scores of outline items // TODO: Consider using a sweepline for scored_declaration in scored_declarations.iter_mut() { let project_entry_id = scored_declaration.declaration.project_entry_id(); let Some(ranges) = project_entry_id_to_outline_ranges.get(&project_entry_id) else { continue; }; for range in ranges { if range.contains_inclusive(&scored_declaration.declaration.item_range()) { scored_declaration.components.included_by_others += 1 } else if scored_declaration .declaration .item_range() .contains_inclusive(range) { scored_declaration.components.includes_others += 1 } } } scored_declarations.sort_unstable_by_key(|declaration| { Reverse(OrderedFloat( declaration.score(DeclarationStyle::Declaration), )) }); scored_declarations} struct CheckedDeclaration<'a> { declaration: &'a Declaration, same_file_line_distance: Option<u32>, path_import_match_count: usize, wildcard_path_import_match_count: usize,} fn declaration_path_matches_import( declaration_path: &CachedDeclarationPath, import_path: &Arc<Path>,) -> bool { if import_path.is_absolute() { declaration_path.equals_absolute_path(import_path) } else { declaration_path.ends_with_posix_path(import_path) }} fn range_intersection<T: Ord + Clone>(a: &Range<T>, b: &Range<T>) -> Option<Range<T>> { let start = a.start.clone().max(b.start.clone()); let end = a.end.clone().min(b.end.clone()); if start < end { Some(Range { start, end }) } else { None }} fn score_declaration( identifier: &Identifier, references: &[Reference], checked_declaration: CheckedDeclaration, same_file_declaration_count: usize, declaration_count: usize, excerpt_occurrences: &Occurrences, adjacent_occurrences: &Occurrences, import_occurrences: &[Occurrences], wildcard_import_occurrences: &[Occurrences], cursor: Point, current_buffer: &BufferSnapshot,) -> ScoredDeclaration { let CheckedDeclaration { declaration, same_file_line_distance, path_import_match_count, wildcard_path_import_match_count, } = checked_declaration; let is_referenced_nearby = references .iter() .any(|r| r.region == ReferenceRegion::Nearby); let is_referenced_in_breadcrumb = references .iter() .any(|r| r.region == ReferenceRegion::Breadcrumb); let reference_count = references.len(); let reference_line_distance = references .iter() .map(|r| { let reference_line = r.range.start.to_point(current_buffer).row as i32; (cursor.row as i32 - reference_line).unsigned_abs() }) .min() .unwrap(); let is_same_file = same_file_line_distance.is_some(); let declaration_line_distance = same_file_line_distance.unwrap_or(u32::MAX); let item_source_occurrences = Occurrences::within_string(&declaration.item_text().0); let item_signature_occurrences = Occurrences::within_string(&declaration.signature_text().0); let excerpt_vs_item_jaccard = jaccard_similarity(excerpt_occurrences, &item_source_occurrences); let excerpt_vs_signature_jaccard = jaccard_similarity(excerpt_occurrences, &item_signature_occurrences); let adjacent_vs_item_jaccard = jaccard_similarity(adjacent_occurrences, &item_source_occurrences); let adjacent_vs_signature_jaccard = jaccard_similarity(adjacent_occurrences, &item_signature_occurrences); let excerpt_vs_item_weighted_overlap = weighted_overlap_coefficient(excerpt_occurrences, &item_source_occurrences); let excerpt_vs_signature_weighted_overlap = weighted_overlap_coefficient(excerpt_occurrences, &item_signature_occurrences); let adjacent_vs_item_weighted_overlap = weighted_overlap_coefficient(adjacent_occurrences, &item_source_occurrences); let adjacent_vs_signature_weighted_overlap = weighted_overlap_coefficient(adjacent_occurrences, &item_signature_occurrences); let mut import_similarity = 0f32; let mut wildcard_import_similarity = 0f32; if !import_occurrences.is_empty() || !wildcard_import_occurrences.is_empty() { let cached_path = declaration.cached_path(); let path_occurrences = Occurrences::from_worktree_path( cached_path .worktree_abs_path .file_name() .map(|f| f.to_string_lossy()), &cached_path.rel_path, ); import_similarity = import_occurrences .iter() .map(|namespace_occurrences| { OrderedFloat(jaccard_similarity(namespace_occurrences, &path_occurrences)) }) .max() .map(|similarity| similarity.into_inner()) .unwrap_or_default(); // TODO: Consider something other than max wildcard_import_similarity = wildcard_import_occurrences .iter() .map(|namespace_occurrences| { OrderedFloat(jaccard_similarity(namespace_occurrences, &path_occurrences)) }) .max() .map(|similarity| similarity.into_inner()) .unwrap_or_default(); } // TODO: Consider adding declaration_file_count let score_components = DeclarationScoreComponents { is_same_file, is_referenced_nearby, is_referenced_in_breadcrumb, reference_line_distance, declaration_line_distance, reference_count, same_file_declaration_count, declaration_count, excerpt_vs_item_jaccard, excerpt_vs_signature_jaccard, adjacent_vs_item_jaccard, adjacent_vs_signature_jaccard, excerpt_vs_item_weighted_overlap, excerpt_vs_signature_weighted_overlap, adjacent_vs_item_weighted_overlap, adjacent_vs_signature_weighted_overlap, path_import_match_count, wildcard_path_import_match_count, import_similarity, max_import_similarity: 0.0, normalized_import_similarity: 0.0, wildcard_import_similarity, normalized_wildcard_import_similarity: 0.0, included_by_others: 0, includes_others: 0, }; ScoredDeclaration { identifier: identifier.clone(), declaration: declaration.clone(), components: score_components, }} #[cfg(test)]Lines 1–500 of 539. Load more
use cloud_llm_client::predict_edits_v3::DeclarationScoreComponents; use collections::HashMap; use language::BufferSnapshot; use ordered_float::OrderedFloat; use project::ProjectEntryId; use serde::Serialize; use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; use strum::EnumIter; use text::{Point, ToPoint}; use util::RangeExt as _; use crate::{ CachedDeclarationPath, Declaration, EditPredictionExcerpt, Identifier, imports::{Import, Imports, Module}, reference::{Reference, ReferenceRegion}, syntax_index::SyntaxIndexState, text_similarity::{Occurrences, jaccard_similarity, weighted_overlap_coefficient}, }; const MAX_IDENTIFIER_DECLARATION_COUNT: usize = 16; #[derive(Clone, Debug, PartialEq, Eq)] pub struct EditPredictionScoreOptions { pub omit_excerpt_overlaps: bool, } #[derive(Clone, Debug)] pub struct ScoredDeclaration { /// identifier used by the local reference pub identifier: Identifier, pub declaration: Declaration, pub components: DeclarationScoreComponents, } #[derive(EnumIter, Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum DeclarationStyle { Signature, Declaration, } #[derive(Clone, Debug, Serialize, Default)] pub struct DeclarationScores { pub signature: f32, pub declaration: f32, pub retrieval: f32, } impl ScoredDeclaration { /// Returns the score for this declaration with the specified style. pub fn score(&self, style: DeclarationStyle) -> f32 { // TODO: handle truncation // Score related to how likely this is the correct declaration, range 0 to 1 let retrieval = self.retrieval_score(); // Score related to the distance between the reference and cursor, range 0 to 1 let distance_score = if self.components.is_referenced_nearby { 1.0 / (1.0 + self.components.reference_line_distance as f32 / 10.0).powf(2.0) } else { // same score as ~14 lines away, rationale is to not overly penalize references from parent signatures 0.5 }; // For now instead of linear combination, the scores are just multiplied together. let combined_score = 10.0 * retrieval * distance_score; match style { DeclarationStyle::Signature => { combined_score * self.components.excerpt_vs_signature_weighted_overlap } DeclarationStyle::Declaration => { 2.0 * combined_score * self.components.excerpt_vs_item_weighted_overlap } } } pub fn retrieval_score(&self) -> f32 { let mut score = if self.components.is_same_file { 10.0 / self.components.same_file_declaration_count as f32 } else if self.components.path_import_match_count > 0 { 3.0 } else if self.components.wildcard_path_import_match_count > 0 { 1.0 } else if self.components.normalized_import_similarity > 0.0 { self.components.normalized_import_similarity } else if self.components.normalized_wildcard_import_similarity > 0.0 { 0.5 * self.components.normalized_wildcard_import_similarity } else { 1.0 / self.components.declaration_count as f32 }; score *= 1. + self.components.included_by_others as f32 / 2.; score *= 1. + self.components.includes_others as f32 / 4.; score } pub fn size(&self, style: DeclarationStyle) -> usize { match &self.declaration { Declaration::File { declaration, .. } => match style { DeclarationStyle::Signature => declaration.signature_range.len(), DeclarationStyle::Declaration => declaration.text.len(), }, Declaration::Buffer { declaration, .. } => match style { DeclarationStyle::Signature => declaration.signature_range.len(), DeclarationStyle::Declaration => declaration.item_range.len(), }, } } pub fn score_density(&self, style: DeclarationStyle) -> f32 { self.score(style) / self.size(style) as f32 } } pub fn scored_declarations( options: &EditPredictionScoreOptions, index: &SyntaxIndexState, excerpt: &EditPredictionExcerpt, excerpt_occurrences: &Occurrences, adjacent_occurrences: &Occurrences, imports: &Imports, identifier_to_references: HashMap<Identifier, Vec<Reference>>, cursor_offset: usize, current_buffer: &BufferSnapshot, ) -> Vec<ScoredDeclaration> { let cursor_point = cursor_offset.to_point(¤t_buffer); let mut wildcard_import_occurrences = Vec::new(); let mut wildcard_import_paths = Vec::new(); for wildcard_import in imports.wildcard_modules.iter() { match wildcard_import { Module::Namespace(namespace) => { wildcard_import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => wildcard_import_paths.push(path), Module::SourceFuzzy(path) => { wildcard_import_occurrences.push(Occurrences::from_path(&path)) } } } let mut scored_declarations = Vec::new(); let mut project_entry_id_to_outline_ranges: HashMap<ProjectEntryId, Vec<Range<usize>>> = HashMap::default(); for (identifier, references) in identifier_to_references { let mut import_occurrences = Vec::new(); let mut import_paths = Vec::new(); let mut found_external_identifier: Option<&Identifier> = None; if let Some(imports) = imports.identifier_to_imports.get(&identifier) { // only use alias when it's the only import, could be generalized if some language // has overlapping aliases // // TODO: when an aliased declaration is included in the prompt, should include the // aliasing in the prompt. // // TODO: For SourceFuzzy consider having componentwise comparison that pays // attention to ordering. if let [ Import::Alias { module, external_identifier, }, ] = imports.as_slice() { match module { Module::Namespace(namespace) => { import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => import_paths.push(path), Module::SourceFuzzy(path) => { import_occurrences.push(Occurrences::from_path(&path)) } } found_external_identifier = Some(&external_identifier); } else { for import in imports { match import { Import::Direct { module } => match module { Module::Namespace(namespace) => { import_occurrences.push(namespace.occurrences()) } Module::SourceExact(path) => import_paths.push(path), Module::SourceFuzzy(path) => { import_occurrences.push(Occurrences::from_path(&path)) } }, Import::Alias { .. } => {} } } } } let identifier_to_lookup = found_external_identifier.unwrap_or(&identifier); // TODO: update this to be able to return more declarations? Especially if there is the // ability to quickly filter a large list (based on imports) let identifier_declarations = index .declarations_for_identifier::<MAX_IDENTIFIER_DECLARATION_COUNT>(&identifier_to_lookup); let declaration_count = identifier_declarations.len(); if declaration_count == 0 { continue; } // TODO: option to filter out other candidates when same file / import match let mut checked_declarations = Vec::with_capacity(declaration_count); for (declaration_id, declaration) in identifier_declarations { match declaration { Declaration::Buffer { buffer_id, declaration: buffer_declaration, .. } => { if buffer_id == ¤t_buffer.remote_id() { let already_included_in_prompt = range_intersection(&buffer_declaration.item_range, &excerpt.range) .is_some() || excerpt .parent_declarations .iter() .any(|(excerpt_parent, _)| excerpt_parent == &declaration_id); if !options.omit_excerpt_overlaps || !already_included_in_prompt { let declaration_line = buffer_declaration .item_range .start .to_point(current_buffer) .row; let declaration_line_distance = (cursor_point.row as i32 - declaration_line as i32).unsigned_abs(); checked_declarations.push(CheckedDeclaration { declaration, same_file_line_distance: Some(declaration_line_distance), path_import_match_count: 0, wildcard_path_import_match_count: 0, }); } continue; } else { } } Declaration::File { .. } => {} } let declaration_path = declaration.cached_path(); let path_import_match_count = import_paths .iter() .filter(|import_path| { declaration_path_matches_import(&declaration_path, import_path) }) .count(); let wildcard_path_import_match_count = wildcard_import_paths .iter() .filter(|import_path| { declaration_path_matches_import(&declaration_path, import_path) }) .count(); checked_declarations.push(CheckedDeclaration { declaration, same_file_line_distance: None, path_import_match_count, wildcard_path_import_match_count, }); } let mut max_import_similarity = 0.0; let mut max_wildcard_import_similarity = 0.0; let mut scored_declarations_for_identifier = Vec::with_capacity(checked_declarations.len()); for checked_declaration in checked_declarations { let same_file_declaration_count = index.file_declaration_count(checked_declaration.declaration); let declaration = score_declaration( &identifier, &references, checked_declaration, same_file_declaration_count, declaration_count, &excerpt_occurrences, &adjacent_occurrences, &import_occurrences, &wildcard_import_occurrences, cursor_point, current_buffer, ); if declaration.components.import_similarity > max_import_similarity { max_import_similarity = declaration.components.import_similarity; } if declaration.components.wildcard_import_similarity > max_wildcard_import_similarity { max_wildcard_import_similarity = declaration.components.wildcard_import_similarity; } project_entry_id_to_outline_ranges .entry(declaration.declaration.project_entry_id()) .or_default() .push(declaration.declaration.item_range()); scored_declarations_for_identifier.push(declaration); } if max_import_similarity > 0.0 || max_wildcard_import_similarity > 0.0 { for declaration in scored_declarations_for_identifier.iter_mut() { if max_import_similarity > 0.0 { declaration.components.max_import_similarity = max_import_similarity; declaration.components.normalized_import_similarity = declaration.components.import_similarity / max_import_similarity; } if max_wildcard_import_similarity > 0.0 { declaration.components.normalized_wildcard_import_similarity = declaration.components.wildcard_import_similarity / max_wildcard_import_similarity; } } } scored_declarations.extend(scored_declarations_for_identifier); } // TODO: Inform this via import / retrieval scores of outline items // TODO: Consider using a sweepline for scored_declaration in scored_declarations.iter_mut() { let project_entry_id = scored_declaration.declaration.project_entry_id(); let Some(ranges) = project_entry_id_to_outline_ranges.get(&project_entry_id) else { continue; }; for range in ranges { if range.contains_inclusive(&scored_declaration.declaration.item_range()) { scored_declaration.components.included_by_others += 1 } else if scored_declaration .declaration .item_range() .contains_inclusive(range) { scored_declaration.components.includes_others += 1 } } } scored_declarations.sort_unstable_by_key(|declaration| { Reverse(OrderedFloat( declaration.score(DeclarationStyle::Declaration), )) }); scored_declarations } struct CheckedDeclaration<'a> { declaration: &'a Declaration, same_file_line_distance: Option<u32>, path_import_match_count: usize, wildcard_path_import_match_count: usize, } fn declaration_path_matches_import( declaration_path: &CachedDeclarationPath, import_path: &Arc<Path>, ) -> bool { if import_path.is_absolute() { declaration_path.equals_absolute_path(import_path) } else { declaration_path.ends_with_posix_path(import_path) } } fn range_intersection<T: Ord + Clone>(a: &Range<T>, b: &Range<T>) -> Option<Range<T>> { let start = a.start.clone().max(b.start.clone()); let end = a.end.clone().min(b.end.clone()); if start < end { Some(Range { start, end }) } else { None } } fn score_declaration( identifier: &Identifier, references: &[Reference], checked_declaration: CheckedDeclaration, same_file_declaration_count: usize, declaration_count: usize, excerpt_occurrences: &Occurrences, adjacent_occurrences: &Occurrences, import_occurrences: &[Occurrences], wildcard_import_occurrences: &[Occurrences], cursor: Point, current_buffer: &BufferSnapshot, ) -> ScoredDeclaration { let CheckedDeclaration { declaration, same_file_line_distance, path_import_match_count, wildcard_path_import_match_count, } = checked_declaration; let is_referenced_nearby = references .iter() .any(|r| r.region == ReferenceRegion::Nearby); let is_referenced_in_breadcrumb = references .iter() .any(|r| r.region == ReferenceRegion::Breadcrumb); let reference_count = references.len(); let reference_line_distance = references .iter() .map(|r| { let reference_line = r.range.start.to_point(current_buffer).row as i32; (cursor.row as i32 - reference_line).unsigned_abs() }) .min() .unwrap(); let is_same_file = same_file_line_distance.is_some(); let declaration_line_distance = same_file_line_distance.unwrap_or(u32::MAX); let item_source_occurrences = Occurrences::within_string(&declaration.item_text().0); let item_signature_occurrences = Occurrences::within_string(&declaration.signature_text().0); let excerpt_vs_item_jaccard = jaccard_similarity(excerpt_occurrences, &item_source_occurrences); let excerpt_vs_signature_jaccard = jaccard_similarity(excerpt_occurrences, &item_signature_occurrences); let adjacent_vs_item_jaccard = jaccard_similarity(adjacent_occurrences, &item_source_occurrences); let adjacent_vs_signature_jaccard = jaccard_similarity(adjacent_occurrences, &item_signature_occurrences); let excerpt_vs_item_weighted_overlap = weighted_overlap_coefficient(excerpt_occurrences, &item_source_occurrences); let excerpt_vs_signature_weighted_overlap = weighted_overlap_coefficient(excerpt_occurrences, &item_signature_occurrences); let adjacent_vs_item_weighted_overlap = weighted_overlap_coefficient(adjacent_occurrences, &item_source_occurrences); let adjacent_vs_signature_weighted_overlap = weighted_overlap_coefficient(adjacent_occurrences, &item_signature_occurrences); let mut import_similarity = 0f32; let mut wildcard_import_similarity = 0f32; if !import_occurrences.is_empty() || !wildcard_import_occurrences.is_empty() { let cached_path = declaration.cached_path(); let path_occurrences = Occurrences::from_worktree_path( cached_path .worktree_abs_path .file_name() .map(|f| f.to_string_lossy()), &cached_path.rel_path, ); import_similarity = import_occurrences .iter() .map(|namespace_occurrences| { OrderedFloat(jaccard_similarity(namespace_occurrences, &path_occurrences)) }) .max() .map(|similarity| similarity.into_inner()) .unwrap_or_default(); // TODO: Consider something other than max wildcard_import_similarity = wildcard_import_occurrences .iter() .map(|namespace_occurrences| { OrderedFloat(jaccard_similarity(namespace_occurrences, &path_occurrences)) }) .max() .map(|similarity| similarity.into_inner()) .unwrap_or_default(); } // TODO: Consider adding declaration_file_count let score_components = DeclarationScoreComponents { is_same_file, is_referenced_nearby, is_referenced_in_breadcrumb, reference_line_distance, declaration_line_distance, reference_count, same_file_declaration_count, declaration_count, excerpt_vs_item_jaccard, excerpt_vs_signature_jaccard, adjacent_vs_item_jaccard, adjacent_vs_signature_jaccard, excerpt_vs_item_weighted_overlap, excerpt_vs_signature_weighted_overlap, adjacent_vs_item_weighted_overlap, adjacent_vs_signature_weighted_overlap, path_import_match_count, wildcard_path_import_match_count, import_similarity, max_import_similarity: 0.0, normalized_import_similarity: 0.0, wildcard_import_similarity, normalized_wildcard_import_similarity: 0.0, included_by_others: 0, includes_others: 0, }; ScoredDeclaration { identifier: identifier.clone(), declaration: declaration.clone(), components: score_components, } } #[cfg(test)]