use crate::convert;
use rite_resolver::SpanMap;
use tower_lsp_server::ls_types::{Location, Position, Uri};
pub fn find_references_at(
span_map: &SpanMap,
text: &str,
pos: Position,
uri: &Uri,
include_declaration: bool,
) -> Vec<Location> {
let line_1 = pos.line as usize + 1;
let col_1 = pos.character as usize + 1;
let target = if let Some(t) = span_map.find_target_at(line_1, col_1) {
t.clone()
} else {
let word = crate::convert::word_at_position(text, pos).unwrap_or_default();
if word.is_empty() {
return vec![];
}
let Some(target) = span_map.declaration_target_for_word(&word) else {
return vec![];
};
target
};
let mut locs: Vec<Location> = Vec::new();
if include_declaration && let Some(decl_span) = span_map.declaration_span(&target) {
locs.push(Location {
uri: uri.clone(),
range: convert::point_range(convert::span_to_position(decl_span)),
});
}
locs.extend(span_map.references_for_target(&target).map(|e| Location {
uri: uri.clone(),
range: convert::span_to_range(e.span),
}));
locs
}
#[cfg(test)]
mod tests {
use super::*;
use rite_model::{ArtifactId, SectionId, StepId};
use rite_resolver::{ReferenceContext, ReferenceEntry, ReferenceTarget, Span};
fn make_uri() -> Uri {
"file:///test.yaml".parse().unwrap()
}
fn span(line: usize, column: usize) -> Span {
Span {
line,
column,
length: None,
}
}
#[test]
fn finds_references_when_cursor_on_reference_value() {
let mut span_map = SpanMap::default();
span_map.sections.insert(SectionId::new("main"), span(5, 7));
span_map.references.push(ReferenceEntry {
span: Span {
length: Some(4),
..span(10, 14)
},
target: ReferenceTarget::Section(SectionId::new("main")),
context: ReferenceContext::Step(StepId::new("test")),
value: "main".to_string(),
});
span_map.references.push(ReferenceEntry {
span: Span {
length: Some(4),
..span(20, 14)
},
target: ReferenceTarget::Section(SectionId::new("main")),
context: ReferenceContext::Step(StepId::new("test")),
value: "main".to_string(),
});
span_map.references.push(ReferenceEntry {
span: Span {
length: Some(5),
..span(30, 14)
},
target: ReferenceTarget::Section(SectionId::new("other")),
context: ReferenceContext::Step(StepId::new("test")),
value: "other".to_string(),
});
let pos = Position {
line: 9,
character: 14,
};
let locs = find_references_at(&span_map, "", pos, &make_uri(), false);
assert_eq!(locs.len(), 2, "should find both 'main' references");
assert_eq!(locs[0].range.start.line, 9); assert_eq!(locs[1].range.start.line, 19);
}
#[test]
fn finds_references_when_cursor_on_declaration() {
let mut span_map = SpanMap::default();
span_map
.sections
.insert(SectionId::new("setup"), span(3, 7));
span_map.references.push(ReferenceEntry {
span: Span {
length: Some(5),
..span(15, 14)
},
target: ReferenceTarget::Section(SectionId::new("setup")),
context: ReferenceContext::Step(StepId::new("test")),
value: "setup".to_string(),
});
let text = " - id: setup\n";
let pos = Position {
line: 0,
character: 11,
};
let locs = find_references_at(&span_map, text, pos, &make_uri(), false);
assert_eq!(locs.len(), 1);
assert_eq!(locs[0].range.start.line, 14); }
#[test]
fn returns_empty_when_no_target() {
let span_map = SpanMap::default();
let text = " action: confirm\n";
let pos = Position {
line: 0,
character: 12,
};
assert!(find_references_at(&span_map, text, pos, &make_uri(), false).is_empty());
}
#[test]
fn include_declaration_prepends_decl_site() {
let mut span_map = SpanMap::default();
span_map.sections.insert(SectionId::new("main"), span(5, 7));
span_map.references.push(ReferenceEntry {
span: Span {
length: Some(4),
..span(10, 14)
},
target: ReferenceTarget::Section(SectionId::new("main")),
context: ReferenceContext::Step(StepId::new("test")),
value: "main".to_string(),
});
let pos = Position {
line: 9,
character: 14,
};
let locs = find_references_at(&span_map, "", pos, &make_uri(), true);
assert_eq!(locs.len(), 2);
assert_eq!(locs[0].range.start.line, 4);
assert_eq!(locs[1].range.start.line, 9);
}
#[test]
fn finds_artifact_references_from_creates_or_reads() {
let mut span_map = SpanMap::default();
let creates_span = Span {
line: 5,
column: 7,
length: Some(20),
};
span_map
.artifacts
.insert(ArtifactId::new("keypair"), creates_span);
span_map.references.push(ReferenceEntry {
span: creates_span,
target: ReferenceTarget::Artifact(ArtifactId::new("keypair")),
context: ReferenceContext::Step(StepId::new("gen_step")),
value: "${artifact.keypair}".to_string(),
});
span_map.references.push(ReferenceEntry {
span: Span {
line: 12,
column: 14,
length: Some(20),
},
target: ReferenceTarget::Artifact(ArtifactId::new("keypair")),
context: ReferenceContext::Step(StepId::new("use_step")),
value: "${artifact.keypair}".to_string(),
});
let pos = Position {
line: 11,
character: 14,
};
let locs = find_references_at(&span_map, "", pos, &make_uri(), true);
assert_eq!(locs.len(), 3);
assert_eq!(locs[0].range.start.line, 4); }
}