Skip to main content

pytest_language_server/providers/
implementation.rs

1//! Go-to-implementation provider for pytest fixtures.
2//!
3//! For generator fixtures (those with yield), "implementation" refers to
4//! the yield statement where the fixture value is produced.
5
6use super::Backend;
7use tower_lsp_server::jsonrpc::Result;
8use tower_lsp_server::ls_types::request::{GotoImplementationParams, GotoImplementationResponse};
9use tower_lsp_server::ls_types::*;
10use tracing::info;
11
12impl Backend {
13    /// Handle goto_implementation request.
14    ///
15    /// For fixtures, "implementation" is the yield statement (if present).
16    /// This allows jumping to where the fixture value is actually produced
17    /// in generator fixtures.
18    pub async fn handle_goto_implementation(
19        &self,
20        params: GotoImplementationParams,
21    ) -> Result<Option<GotoImplementationResponse>> {
22        let uri = params.text_document_position_params.text_document.uri;
23        let position = params.text_document_position_params.position;
24
25        info!(
26            "goto_implementation request: uri={:?}, line={}, char={}",
27            uri, position.line, position.character
28        );
29
30        if let Some(file_path) = self.uri_to_path(&uri) {
31            info!(
32                "Looking for fixture implementation at {:?}:{}:{}",
33                file_path, position.line, position.character
34            );
35
36            // First find the fixture definition (works on both definitions and usages)
37            if let Some(definition) = self.fixture_db.find_fixture_or_definition_at_position(
38                &file_path,
39                position.line,
40                position.character,
41            ) {
42                info!("Found definition: {:?}", definition);
43
44                // Check if the fixture has a yield line (generator fixture)
45                if let Some(yield_line) = definition.yield_line {
46                    let Some(def_uri) = self.path_to_uri(&definition.file_path) else {
47                        return Ok(None);
48                    };
49
50                    // Convert to LSP line (0-based)
51                    let lsp_line = Self::internal_line_to_lsp(yield_line);
52                    let location = Location {
53                        uri: def_uri.clone(),
54                        range: Self::create_point_range(lsp_line, 0),
55                    };
56                    info!("Returning yield location: {:?}", location);
57                    return Ok(Some(GotoImplementationResponse::Scalar(location)));
58                } else {
59                    info!("Fixture has no yield statement (not a generator fixture)");
60                    // For non-generator fixtures, fall back to definition
61                    let Some(def_uri) = self.path_to_uri(&definition.file_path) else {
62                        return Ok(None);
63                    };
64
65                    let def_line = Self::internal_line_to_lsp(definition.line);
66                    let location = Location {
67                        uri: def_uri.clone(),
68                        range: Self::create_point_range(def_line, 0),
69                    };
70                    info!("Returning definition location (no yield): {:?}", location);
71                    return Ok(Some(GotoImplementationResponse::Scalar(location)));
72                }
73            } else {
74                info!("No fixture definition found");
75            }
76        }
77
78        Ok(None)
79    }
80}