@@ -197,6 +197,8 @@ use pyrefly_build::handle::Handle;
197197use pyrefly_config:: config:: ConfigSource ;
198198use pyrefly_config:: error_kind:: Severity ;
199199use pyrefly_python:: PYTHON_EXTENSIONS ;
200+ use pyrefly_python:: ast:: Ast ;
201+ use pyrefly_python:: module:: Module ;
200202use pyrefly_python:: module:: TextRangeWithModule ;
201203use pyrefly_python:: module_name:: ModuleName ;
202204use pyrefly_python:: module_name:: ModuleNameWithKind ;
@@ -314,9 +316,12 @@ use crate::lsp::wasm::provide_type::ProvideTypeResponse;
314316use crate :: lsp:: wasm:: provide_type:: provide_type;
315317use crate :: module:: bundled:: BundledStub ;
316318use crate :: state:: load:: LspFile ;
319+ use crate :: state:: loader:: LoaderFindCache ;
317320use crate :: state:: lsp:: DisplayTypeErrors ;
318321use crate :: state:: lsp:: FindDefinitionItemWithDocstring ;
319322use crate :: state:: lsp:: FindPreference ;
323+ use crate :: state:: lsp:: IdentifierContext ;
324+ use crate :: state:: lsp:: IdentifierWithContext ;
320325use crate :: state:: lsp:: ImportBehavior ;
321326use crate :: state:: lsp:: LocalRefactorCodeAction ;
322327use crate :: state:: notebook:: LspNotebook ;
@@ -1825,6 +1830,28 @@ impl Server {
18251830 return Ok ( ProcessEvent :: Continue ) ;
18261831 }
18271832
1833+ if let Some ( params) = as_request :: < GotoDefinition > ( & x) {
1834+ if let Some ( params) = self
1835+ . extract_request_params_or_send_err_response :: < GotoDefinition > (
1836+ params, & x. id ,
1837+ )
1838+ {
1839+ if let Some ( response) = self . goto_definition_import_fast_path ( & params) {
1840+ let response = match response {
1841+ Ok ( response) => response,
1842+ Err ( reason) => {
1843+ telemetry_event. set_empty_response_reason ( reason) ;
1844+ None
1845+ }
1846+ } ;
1847+ self . send_response ( new_response ( x. id , Ok ( response) ) ) ;
1848+ return Ok ( ProcessEvent :: Continue ) ;
1849+ }
1850+ } else {
1851+ return Ok ( ProcessEvent :: Continue ) ;
1852+ }
1853+ }
1854+
18281855 let mut transaction =
18291856 ide_transaction_manager. non_committable_transaction ( & self . state ) ;
18301857
@@ -3933,6 +3960,84 @@ impl Server {
39333960 . map ( |( handle, _) | handle)
39343961 }
39353962
3963+ fn module_for_uri_fast_path ( & self , uri : & Url , handle : & Handle ) -> Option < Module > {
3964+ let path = self . path_for_uri_or_notebook_cell ( uri) ?;
3965+ if let Some ( file) = self . open_files . read ( ) . get ( & path) . duped ( ) {
3966+ return Some ( match & * file {
3967+ LspFile :: Source ( contents) => {
3968+ Module :: new ( handle. module ( ) , handle. path ( ) . dupe ( ) , Arc :: clone ( contents) )
3969+ }
3970+ LspFile :: Notebook ( notebook) => Module :: new_notebook (
3971+ handle. module ( ) ,
3972+ handle. path ( ) . dupe ( ) ,
3973+ Arc :: clone ( notebook. ruff_notebook ( ) ) ,
3974+ ) ,
3975+ } ) ;
3976+ }
3977+ let contents = std:: fs:: read_to_string ( & path) . ok ( ) ?;
3978+ Some ( Module :: new (
3979+ handle. module ( ) ,
3980+ handle. path ( ) . dupe ( ) ,
3981+ Arc :: new ( contents) ,
3982+ ) )
3983+ }
3984+
3985+ fn goto_definition_import_fast_path (
3986+ & self ,
3987+ params : & GotoDefinitionParams ,
3988+ ) -> Option < Result < Option < GotoDefinitionResponse > , EmptyResponseReason > > {
3989+ let uri = & params. text_document_position_params . text_document . uri ;
3990+ let handle = match self . make_handle_if_enabled ( uri, Some ( GotoDefinition :: METHOD ) ) {
3991+ Ok ( handle) => handle,
3992+ Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
3993+ } ;
3994+ let module = match self . module_for_uri_fast_path ( uri, & handle) {
3995+ Some ( module) => module,
3996+ None => return Some ( Err ( EmptyResponseReason :: ModuleInfoNotFound ) ) ,
3997+ } ;
3998+ let position = module. from_lsp_position (
3999+ params. text_document_position_params . position ,
4000+ self . maybe_get_cell_index ( uri) ,
4001+ ) ;
4002+ let ast = Ast :: parse ( module. contents ( ) , module. source_type ( ) ) . 0 ;
4003+ let covering_nodes = Ast :: locate_node ( & ast, position) ;
4004+ let Some ( IdentifierWithContext {
4005+ identifier,
4006+ context : IdentifierContext :: ImportedModule { name, dots } ,
4007+ } ) = Transaction :: identifier_from_covering_nodes ( & covering_nodes)
4008+ else {
4009+ return None ;
4010+ } ;
4011+ let target_module =
4012+ Transaction :: target_imported_module_name ( & handle, & identifier, name, dots, position) ;
4013+ let config = self
4014+ . state
4015+ . config_finder ( )
4016+ . python_file ( handle. module_kind ( ) , handle. path ( ) ) ;
4017+ let loader = LoaderFindCache :: new ( config. dupe ( ) ) ;
4018+ let Some ( target_path) = loader
4019+ . find_import_prefer_executable ( target_module, Some ( handle. path ( ) ) )
4020+ . finding ( )
4021+ else {
4022+ return Some ( Err ( EmptyResponseReason :: ModuleNotFound ) ) ;
4023+ } ;
4024+ let Some ( path) = to_real_path ( & target_path) else {
4025+ return Some ( Err ( EmptyResponseReason :: ModuleInfoNotFound ) ) ;
4026+ } ;
4027+ let path = match & self . path_remapper {
4028+ Some ( remapper) => remapper ( & path) . into_owned ( ) ,
4029+ None => path,
4030+ } ;
4031+ let uri = match Url :: from_file_path ( path. absolutize ( ) ) {
4032+ Ok ( uri) => uri,
4033+ Err ( _) => return Some ( Err ( EmptyResponseReason :: NoFilePath ) ) ,
4034+ } ;
4035+ Some ( Ok ( Some ( GotoDefinitionResponse :: Scalar ( Location {
4036+ uri,
4037+ range : Range :: default ( ) ,
4038+ } ) ) ) )
4039+ }
4040+
39364041 fn goto_definition (
39374042 & self ,
39384043 transaction : & Transaction < ' _ > ,
0 commit comments