Skip to content

Commit

Permalink
feat(lsp): goto type definition (#4029)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

feat(lsp): goto struct definition from local reference #3720

## Summary\*

Go to definition of type.

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[Exceptional Case]** Documentation to be submitted in a separate
PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
kobyhallx authored Jan 17, 2024
1 parent 418ffa6 commit 8bb4ddf
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 16 deletions.
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> {
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

0 comments on commit 8bb4ddf

Please sign in to comment.