reference.rs

  1use language::BufferSnapshot;
  2use std::collections::HashMap;
  3use std::ops::Range;
  4
  5use crate::{
  6    excerpt::{EditPredictionExcerpt, EditPredictionExcerptText},
  7    outline::Identifier,
  8};
  9
 10#[derive(Debug)]
 11pub struct Reference {
 12    pub identifier: Identifier,
 13    pub range: Range<usize>,
 14    pub region: ReferenceRegion,
 15}
 16
 17#[derive(Copy, Clone, Debug, Eq, PartialEq)]
 18pub enum ReferenceRegion {
 19    Breadcrumb,
 20    Nearby,
 21}
 22
 23pub fn references_in_excerpt(
 24    excerpt: &EditPredictionExcerpt,
 25    excerpt_text: &EditPredictionExcerptText,
 26    snapshot: &BufferSnapshot,
 27) -> HashMap<Identifier, Vec<Reference>> {
 28    let mut references = identifiers_in_range(
 29        excerpt.range.clone(),
 30        excerpt_text.body.as_str(),
 31        ReferenceRegion::Nearby,
 32        snapshot,
 33    );
 34
 35    for (range, text) in excerpt
 36        .parent_signature_ranges
 37        .iter()
 38        .zip(excerpt_text.parent_signatures.iter())
 39    {
 40        references.extend(identifiers_in_range(
 41            range.clone(),
 42            text.as_str(),
 43            ReferenceRegion::Breadcrumb,
 44            snapshot,
 45        ));
 46    }
 47
 48    let mut identifier_to_references: HashMap<Identifier, Vec<Reference>> = HashMap::new();
 49    for reference in references {
 50        identifier_to_references
 51            .entry(reference.identifier.clone())
 52            .or_insert_with(Vec::new)
 53            .push(reference);
 54    }
 55    identifier_to_references
 56}
 57
 58/// Finds all nodes which have a "variable" match from the highlights query within the offset range.
 59pub fn identifiers_in_range(
 60    range: Range<usize>,
 61    range_text: &str,
 62    reference_region: ReferenceRegion,
 63    buffer: &BufferSnapshot,
 64) -> Vec<Reference> {
 65    let mut matches = buffer
 66        .syntax
 67        .matches(range.clone(), &buffer.text, |grammar| {
 68            grammar
 69                .highlights_config
 70                .as_ref()
 71                .map(|config| &config.query)
 72        });
 73
 74    let mut references = Vec::new();
 75    let mut last_added_range = None;
 76    while let Some(mat) = matches.peek() {
 77        let config = matches.grammars()[mat.grammar_index]
 78            .highlights_config
 79            .as_ref();
 80
 81        for capture in mat.captures {
 82            if let Some(config) = config {
 83                if config.identifier_capture_indices.contains(&capture.index) {
 84                    let node_range = capture.node.byte_range();
 85
 86                    // sometimes multiple highlight queries match - this deduplicates them
 87                    if Some(node_range.clone()) == last_added_range {
 88                        continue;
 89                    }
 90
 91                    let identifier_text =
 92                        &range_text[node_range.start - range.start..node_range.end - range.start];
 93                    references.push(Reference {
 94                        identifier: Identifier {
 95                            name: identifier_text.into(),
 96                            language_id: mat.language.id(),
 97                        },
 98                        range: node_range.clone(),
 99                        region: reference_region,
100                    });
101                    last_added_range = Some(node_range);
102                }
103            }
104        }
105
106        matches.advance();
107    }
108    references
109}