Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(lsp): goto type definition #4029

Merged
merged 5 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions compiler/noirc_frontend/src/resolve_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,21 @@ impl NodeInterner {

/// Returns the [Location] of the definition of the given Ident found at [Span] of the given [FileId].
/// Returns [None] when definition is not found.
pub fn get_definition_location_from(&self, location: Location) -> Option<Location> {
pub fn get_definition_location_from(
&self,
location: Location,
return_type_location_instead: bool,
) -> Option<Location> {
self.find_location_index(location)
.and_then(|index| self.resolve_location(index))
.and_then(|index| self.resolve_location(index, return_type_location_instead))
.or_else(|| self.try_resolve_trait_impl_location(location))
.or_else(|| self.try_resolve_trait_method_declaration(location))
}

pub fn get_declaration_location_from(&self, location: Location) -> Option<Location> {
self.try_resolve_trait_method_declaration(location).or_else(|| {
self.find_location_index(location)
.and_then(|index| self.resolve_location(index))
.and_then(|index| self.resolve_location(index, false))
.and_then(|found_impl_location| {
self.try_resolve_trait_method_declaration(found_impl_location)
})
Expand All @@ -53,20 +57,43 @@ impl NodeInterner {
/// For a given [Index] we return [Location] to which we resolved to
/// We currently return None for features not yet implemented
/// TODO(#3659): LSP goto def should error when Ident at Location could not resolve
fn resolve_location(&self, index: impl Into<Index>) -> Option<Location> {
fn resolve_location(
&self,
index: impl Into<Index>,
return_type_location_instead: bool,
) -> Option<Location> {
kobyhallx marked this conversation as resolved.
Show resolved Hide resolved
if return_type_location_instead {
return self.get_type_location_from_index(index);
}

let node = self.nodes.get(index.into())?;

match node {
Node::Function(func) => self.resolve_location(func.as_expr()),
Node::Expression(expression) => self.resolve_expression_location(expression),
Node::Function(func) => {
self.resolve_location(func.as_expr(), return_type_location_instead)
}
Node::Expression(expression) => {
self.resolve_expression_location(expression, return_type_location_instead)
}
_ => None,
}
}

fn get_type_location_from_index(&self, index: impl Into<Index>) -> Option<Location> {
match self.id_type(index.into()) {
Type::Struct(struct_type, _) => Some(struct_type.borrow().location),
_ => None,
}
}

/// Resolves the [Location] of the definition for a given [HirExpression]
///
/// Note: current the code returns None because some expressions are not yet implemented.
fn resolve_expression_location(&self, expression: &HirExpression) -> Option<Location> {
fn resolve_expression_location(
&self,
expression: &HirExpression,
return_type_location_instead: bool,
) -> Option<Location> {
match expression {
HirExpression::Ident(ident) => {
let definition_info = self.definition(ident.id);
Expand All @@ -88,7 +115,7 @@ impl NodeInterner {
}
HirExpression::Call(expr_call) => {
let func = expr_call.func;
self.resolve_location(func)
self.resolve_location(func, return_type_location_instead)
}

_ => None,
Expand Down
4 changes: 3 additions & 1 deletion tooling/lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ use notifications::{
};
use requests::{
on_code_lens_request, on_formatting, on_goto_declaration_request, on_goto_definition_request,
on_initialize, on_profile_run_request, on_shutdown, on_test_run_request, on_tests_request,
on_goto_type_definition_request, on_initialize, on_profile_run_request, on_shutdown,
on_test_run_request, on_tests_request,
};
use serde_json::Value as JsonValue;
use thiserror::Error;
Expand Down Expand Up @@ -98,6 +99,7 @@ impl NargoLspService {
.request::<request::NargoProfileRun, _>(on_profile_run_request)
.request::<request::GotoDefinition, _>(on_goto_definition_request)
.request::<request::GotoDeclaration, _>(on_goto_declaration_request)
.request::<request::GotoTypeDefinition, _>(on_goto_type_definition_request)
.notification::<notification::Initialized>(on_initialized)
.notification::<notification::DidChangeConfiguration>(on_did_change_configuration)
.notification::<notification::DidOpenTextDocument>(on_did_open_text_document)
Expand Down
17 changes: 14 additions & 3 deletions tooling/lsp/src/requests/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::resolve_workspace_for_source_path;
use crate::{types::GotoDefinitionResult, LspState};
use async_lsp::{ErrorCode, ResponseError};

use lsp_types::request::GotoTypeDefinitionParams;
use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse};
use nargo::insert_all_files_for_workspace_into_file_manager;
use noirc_driver::file_manager_with_stdlib;
Expand All @@ -14,13 +15,22 @@ pub(crate) fn on_goto_definition_request(
state: &mut LspState,
params: GotoDefinitionParams,
) -> impl Future<Output = Result<GotoDefinitionResult, ResponseError>> {
let result = on_goto_definition_inner(state, params);
let result = on_goto_definition_inner(state, params, false);
future::ready(result)
}

pub(crate) fn on_goto_type_definition_request(
state: &mut LspState,
params: GotoTypeDefinitionParams,
) -> impl Future<Output = Result<GotoDefinitionResult, ResponseError>> {
let result = on_goto_definition_inner(state, params, true);
future::ready(result)
}

fn on_goto_definition_inner(
_state: &mut LspState,
params: GotoDefinitionParams,
return_type_location_instead: bool,
) -> Result<GotoDefinitionResult, ResponseError> {
let file_path =
params.text_document_position_params.text_document.uri.to_file_path().map_err(|_| {
Expand Down Expand Up @@ -65,8 +75,9 @@ fn on_goto_definition_inner(
span: noirc_errors::Span::single_char(byte_index as u32),
};

let goto_definition_response =
interner.get_definition_location_from(search_for_location).and_then(|found_location| {
let goto_definition_response = interner
.get_definition_location_from(search_for_location, return_type_location_instead)
.and_then(|found_location| {
let file_id = found_location.file;
let definition_position = to_lsp_location(files, file_id, found_location.span)?;
let response: GotoDefinitionResponse =
Expand Down
6 changes: 4 additions & 2 deletions tooling/lsp/src/requests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use async_lsp::ResponseError;
use fm::codespan_files::Error;
use lsp_types::{
DeclarationCapability, Location, Position, TextDocumentSyncCapability, TextDocumentSyncKind,
Url,
TypeDefinitionProviderCapability, Url,
};
use nargo_fmt::Config;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -35,7 +35,8 @@ mod tests;
pub(crate) use {
code_lens_request::collect_lenses_for_package, code_lens_request::on_code_lens_request,
goto_declaration::on_goto_declaration_request, goto_definition::on_goto_definition_request,
profile_run::on_profile_run_request, test_run::on_test_run_request, tests::on_tests_request,
goto_definition::on_goto_type_definition_request, profile_run::on_profile_run_request,
test_run::on_test_run_request, tests::on_tests_request,
};

/// LSP client will send initialization request after the server has started.
Expand Down Expand Up @@ -94,6 +95,7 @@ pub(crate) fn on_initialize(
nargo: Some(nargo),
definition_provider: Some(lsp_types::OneOf::Left(true)),
declaration_provider: Some(DeclarationCapability::Simple(true)),
type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
},
server_info: None,
})
Expand Down
11 changes: 9 additions & 2 deletions tooling/lsp/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use fm::FileId;
use lsp_types::{DeclarationCapability, DefinitionOptions, OneOf};
use lsp_types::{
DeclarationCapability, DefinitionOptions, OneOf, TypeDefinitionProviderCapability,
};
use noirc_driver::DebugFile;
use noirc_errors::{debug_info::OpCodesCount, Location};
use noirc_frontend::graph::CrateName;
Expand All @@ -25,7 +27,8 @@ pub(crate) mod request {

// Re-providing lsp_types that we don't need to override
pub(crate) use lsp_types::request::{
CodeLensRequest as CodeLens, Formatting, GotoDeclaration, GotoDefinition, Shutdown,
CodeLensRequest as CodeLens, Formatting, GotoDeclaration, GotoDefinition,
GotoTypeDefinition, Shutdown,
};

#[derive(Debug)]
Expand Down Expand Up @@ -118,6 +121,10 @@ pub(crate) struct ServerCapabilities {
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) definition_provider: Option<OneOf<bool, DefinitionOptions>>,

/// The server provides goto type definition support.
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) type_definition_provider: Option<TypeDefinitionProviderCapability>,

/// The server provides code lens.
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) code_lens_provider: Option<CodeLensOptions>,
Expand Down
Loading