1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! Find-references provider for pytest fixtures.
use super::Backend;
use tower_lsp_server::jsonrpc::Result;
use tower_lsp_server::ls_types::*;
use tracing::{debug, info};
impl Backend {
/// Handle references request
pub async fn handle_references(
&self,
params: ReferenceParams,
) -> Result<Option<Vec<Location>>> {
let uri = params.text_document_position.text_document.uri;
let position = params.text_document_position.position;
info!(
"references request: uri={:?}, line={}, char={}",
uri, position.line, position.character
);
if let Some(file_path) = self.uri_to_path(&uri) {
info!(
"Looking for fixture references at {:?}:{}:{}",
file_path, position.line, position.character
);
// First, find which fixture we're looking at (definition or usage)
if let Some(fixture_name) = self.fixture_db.find_fixture_at_position(
&file_path,
position.line,
position.character,
) {
info!(
"Found fixture: {}, determining which definition to use",
fixture_name
);
let current_line = Self::lsp_line_to_internal(position.line);
info!(
"Current cursor position: line {} (1-indexed), char {}",
current_line, position.character
);
// Determine which specific definition the user is referring to
// This could be a usage (resolve to definition) or clicking on a definition itself
let target_definition = self.fixture_db.find_fixture_definition(
&file_path,
position.line,
position.character,
);
let (references, definition_to_include) = if let Some(definition) =
target_definition
{
info!(
"Found definition via usage at {:?}:{}, finding references that resolve to it",
definition.file_path, definition.line
);
// Find only references that resolve to this specific definition
let refs = self.fixture_db.find_references_for_definition(&definition);
(refs, Some(definition))
} else {
// find_fixture_definition returns None if cursor is on a definition line (not a usage)
// Check if we're on a fixture definition line
let target_line = Self::lsp_line_to_internal(position.line);
if let Some(definition_at_line) = self.fixture_db.get_definition_at_line(
&file_path,
target_line,
&fixture_name,
) {
info!(
"Found definition at cursor position {:?}:{}, finding references that resolve to it",
file_path, target_line
);
let refs = self
.fixture_db
.find_references_for_definition(&definition_at_line);
(refs, Some(definition_at_line))
} else {
info!(
"No specific definition found at cursor, finding all references by name"
);
// Fallback to finding all references by name (shouldn't normally happen)
(self.fixture_db.find_fixture_references(&fixture_name), None)
}
};
if references.is_empty() && definition_to_include.is_none() {
info!("No references found for fixture: {}", fixture_name);
return Ok(None);
}
info!(
"Found {} references for fixture: {}",
references.len(),
fixture_name
);
// Log all references to help debug
for (i, r) in references.iter().enumerate() {
debug!(
" Reference {}: {:?}:{} (chars {}-{})",
i,
r.file_path.file_name(),
r.line,
r.start_char,
r.end_char
);
}
// Check if current position is in the references
let has_current_position = references
.iter()
.any(|r| r.file_path == file_path && r.line == current_line);
info!(
"Current position (line {}) in references: {}",
current_line, has_current_position
);
// Convert references to LSP Locations
let mut locations = Vec::new();
// First, add the definition if we have one (LSP spec: includeDeclaration)
if let Some(ref def) = definition_to_include {
let Some(def_uri) = self.path_to_uri(&def.file_path) else {
return Ok(None);
};
let def_line = Self::internal_line_to_lsp(def.line);
let def_location = Location {
uri: def_uri,
range: Self::create_point_range(def_line, 0),
};
locations.push(def_location);
}
// Then add all the usage references
// Skip references that are on the same line as the definition (to avoid duplicates)
let mut skipped_count = 0;
for reference in &references {
// Check if this reference is the same location as the definition
if let Some(ref def) = definition_to_include {
if reference.file_path == def.file_path && reference.line == def.line {
debug!(
"Skipping reference at {:?}:{} (same as definition location)",
reference.file_path, reference.line
);
skipped_count += 1;
continue;
}
}
let Some(ref_uri) = self.path_to_uri(&reference.file_path) else {
continue;
};
let ref_line = Self::internal_line_to_lsp(reference.line);
let location = Location {
uri: ref_uri,
range: Self::create_range(
ref_line,
reference.start_char as u32,
ref_line,
reference.end_char as u32,
),
};
debug!(
"Adding reference location: {:?}:{} (chars {}-{})",
reference.file_path.file_name(),
reference.line,
reference.start_char,
reference.end_char
);
locations.push(location);
}
info!(
"Returning {} locations (definition: {}, references: {}/{}, skipped: {})",
locations.len(),
if definition_to_include.is_some() {
1
} else {
0
},
references.len() - skipped_count,
references.len(),
skipped_count
);
return Ok(Some(locations));
} else {
info!("No fixture found at this position");
}
}
Ok(None)
}
}