diff --git a/.noir-sync-commit b/.noir-sync-commit index 5d1206097e6..c29458064f4 100644 --- a/.noir-sync-commit +++ b/.noir-sync-commit @@ -1 +1 @@ -5bbce7977f72b07336bc8ef09f6acff687f1644a +1fabcde195f3965c6b8701eb4e1fed49ec1bde4b diff --git a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs index 87cc7990753..057daa2bdde 100644 --- a/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs +++ b/noir/noir-repo/compiler/noirc_frontend/src/ast/expression.rs @@ -800,12 +800,8 @@ impl FunctionDefinition { return_visibility: Visibility::Private, } } -} - -impl Display for FunctionDefinition { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - writeln!(f, "{:?}", self.attributes)?; + pub fn signature(&self) -> String { let parameters = vecmap(&self.parameters, |Param { visibility, pattern, typ, span: _ }| { if *visibility == Visibility::Public { format!("{pattern}: {visibility} {typ}") @@ -827,15 +823,14 @@ impl Display for FunctionDefinition { format!(" -> {}", self.return_type) }; - write!( - f, - "fn {}({}){}{} {}", - self.name, - parameters.join(", "), - return_type, - where_clause_str, - self.body - ) + format!("fn {}({}){}{}", self.name, parameters.join(", "), return_type, where_clause_str) + } +} + +impl Display for FunctionDefinition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{:?}", self.attributes)?; + write!(f, "fn {} {}", self.signature(), self.body) } } diff --git a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr index 6b70b6ddef0..9324802c2f8 100644 --- a/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr +++ b/noir/noir-repo/noir_stdlib/src/embedded_curve_ops.nr @@ -105,6 +105,9 @@ fn multi_scalar_mul_array_return(points: [EmbeddedCurvePoint; N], sc #[foreign(multi_scalar_mul)] pub(crate) fn multi_scalar_mul_slice(points: [EmbeddedCurvePoint], scalars: [EmbeddedCurveScalar]) -> [Field; 3] {} +#[foreign(multi_scalar_mul)] +pub(crate) fn multi_scalar_mul_slice(points: [EmbeddedCurvePoint], scalars: [EmbeddedCurveScalar]) -> [Field; 3] {} + // docs:start:fixed_base_scalar_mul pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint // docs:end:fixed_base_scalar_mul diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index bc9b352c10f..2a729f45a36 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -33,20 +33,7 @@ pub fn pedersen_commitment(input: [Field; N]) -> EmbeddedCurvePoint pedersen_commitment_with_separator(input, 0) } -fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field { - __pedersen_hash_with_separator(input, separator) -} - fn pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { - let value = __pedersen_commitment_with_separator(input, separator); - if (value[0] == 0) & (value[1] == 0) { - EmbeddedCurvePoint { x: 0, y: 0, is_infinite: true } - } else { - EmbeddedCurvePoint { x: value[0], y: value[1], is_infinite: false } - } -} - -fn pedersen_commitment_with_separator_noir(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N]; for i in 0..N { // we use the unsafe version because the multi_scalar_mul will constraint the scalars. @@ -56,7 +43,7 @@ fn pedersen_commitment_with_separator_noir(input: [Field; N], separa multi_scalar_mul(generators, points) } -fn pedersen_hash_with_separator_noir(input: [Field; N], separator: u32) -> Field { +fn pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field { let mut scalars: Vec = Vec::from_slice([EmbeddedCurveScalar { lo: 0, hi: 0 }; N].as_slice()); //Vec::new(); for i in 0..N { @@ -84,12 +71,6 @@ fn derive_generators(domain_separator_bytes: [u8; M], st __derive_generators(domain_separator_bytes, starting_index) } -#[foreign(pedersen_hash)] -pub fn __pedersen_hash_with_separator(input: [Field; N], separator: u32) -> Field {} - -#[foreign(pedersen_commitment)] -fn __pedersen_commitment_with_separator(input: [Field; N], separator: u32) -> [Field; 2] {} - #[builtin(derive_pedersen_generators)] #[field(bn254)] fn __derive_generators( diff --git a/noir/noir-repo/tooling/lsp/src/lib.rs b/noir/noir-repo/tooling/lsp/src/lib.rs index ae4268a3280..80c4573138c 100644 --- a/noir/noir-repo/tooling/lsp/src/lib.rs +++ b/noir/noir-repo/tooling/lsp/src/lib.rs @@ -21,7 +21,10 @@ use async_lsp::{ use fm::{codespan_files as files, FileManager}; use fxhash::FxHashSet; use lsp_types::{ - request::{HoverRequest, InlayHintRequest, PrepareRenameRequest, References, Rename}, + request::{ + DocumentSymbolRequest, HoverRequest, InlayHintRequest, PrepareRenameRequest, References, + Rename, + }, CodeLens, }; use nargo::{ @@ -45,10 +48,10 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_lens_request, on_formatting, on_goto_declaration_request, on_goto_definition_request, - on_goto_type_definition_request, on_hover_request, on_initialize, on_inlay_hint_request, - on_prepare_rename_request, on_profile_run_request, on_references_request, on_rename_request, - on_shutdown, on_test_run_request, on_tests_request, + on_code_lens_request, on_document_symbol_request, on_formatting, on_goto_declaration_request, + on_goto_definition_request, on_goto_type_definition_request, on_hover_request, on_initialize, + on_inlay_hint_request, on_prepare_rename_request, on_profile_run_request, + on_references_request, on_rename_request, on_shutdown, on_test_run_request, on_tests_request, }; use serde_json::Value as JsonValue; use thiserror::Error; @@ -126,6 +129,7 @@ impl NargoLspService { .request::(on_goto_definition_request) .request::(on_goto_declaration_request) .request::(on_goto_type_definition_request) + .request::(on_document_symbol_request) .request::(on_references_request) .request::(on_prepare_rename_request) .request::(on_rename_request) diff --git a/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs new file mode 100644 index 00000000000..67e2505d8fd --- /dev/null +++ b/noir/noir-repo/tooling/lsp/src/requests/document_symbol.rs @@ -0,0 +1,746 @@ +use std::future::{self, Future}; + +use async_lsp::ResponseError; +use fm::{FileId, FileMap, PathString}; +use lsp_types::{ + DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Location, Position, SymbolKind, + TextDocumentPositionParams, +}; +use noirc_errors::Span; +use noirc_frontend::{ + ast::{ + Expression, FunctionReturnType, Ident, LetStatement, NoirFunction, NoirStruct, NoirTrait, + NoirTraitImpl, TraitImplItem, TraitItem, TypeImpl, UnresolvedType, UnresolvedTypeData, + }, + parser::{Item, ItemKind, ParsedSubModule}, + ParsedModule, +}; + +use crate::LspState; + +use super::process_request; + +pub(crate) fn on_document_symbol_request( + state: &mut LspState, + params: DocumentSymbolParams, +) -> impl Future, ResponseError>> { + let Ok(file_path) = params.text_document.uri.to_file_path() else { + return future::ready(Ok(None)); + }; + + let text_document_position_params = TextDocumentPositionParams { + text_document: params.text_document.clone(), + position: Position { line: 0, character: 0 }, + }; + + let result = process_request(state, text_document_position_params, |args| { + args.files.get_file_id(&PathString::from_path(file_path)).map(|file_id| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source); + + let mut collector = DocumentSymbolCollector::new(file_id, args.files); + let mut symbols = Vec::new(); + collector.collect_in_parsed_module(&parsed_module, &mut symbols); + DocumentSymbolResponse::Nested(symbols) + }) + }); + + future::ready(result) +} + +struct DocumentSymbolCollector<'a> { + file_id: FileId, + files: &'a FileMap, +} + +impl<'a> DocumentSymbolCollector<'a> { + fn new(file_id: FileId, files: &'a FileMap) -> Self { + Self { file_id, files } + } + + fn collect_in_parsed_module( + &mut self, + parsed_module: &ParsedModule, + symbols: &mut Vec, + ) { + for item in &parsed_module.items { + self.collect_in_item(item, symbols); + } + } + + fn collect_in_item(&mut self, item: &Item, symbols: &mut Vec) { + match &item.kind { + ItemKind::Function(noir_function) => { + self.collect_in_noir_function(noir_function, item.span, symbols); + } + ItemKind::Struct(noir_struct) => { + self.collect_in_noir_struct(noir_struct, item.span, symbols); + } + ItemKind::Trait(noir_trait) => { + self.collect_in_noir_trait(noir_trait, item.span, symbols); + } + ItemKind::TraitImpl(noir_trait_impl) => { + self.collect_in_noir_trait_impl(noir_trait_impl, item.span, symbols); + } + ItemKind::Impl(type_impl) => { + self.collect_in_type_impl(type_impl, item.span, symbols); + } + ItemKind::Submodules(parsed_sub_module) => { + self.collect_in_parsed_sub_module(parsed_sub_module, item.span, symbols); + } + ItemKind::Global(let_statement) => { + self.collect_in_global(let_statement, item.span, symbols); + } + ItemKind::Import(..) | ItemKind::TypeAlias(..) | ItemKind::ModuleDecl(..) => (), + } + } + + fn collect_in_noir_function( + &mut self, + noir_function: &NoirFunction, + span: Span, + symbols: &mut Vec, + ) { + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let Some(selection_location) = self.to_lsp_location(noir_function.name_ident().span()) + else { + return; + }; + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: noir_function.name().to_string(), + detail: Some(noir_function.def.signature()), + kind: SymbolKind::FUNCTION, + tags: None, + deprecated: None, + range: location.range, + selection_range: selection_location.range, + children: None, + }); + } + + fn collect_in_noir_struct( + &mut self, + noir_struct: &NoirStruct, + span: Span, + symbols: &mut Vec, + ) { + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let Some(selection_location) = self.to_lsp_location(noir_struct.name.span()) else { + return; + }; + + let mut children = Vec::new(); + for (field_name, typ) in &noir_struct.fields { + let span = if let Some(typ) = typ.span { + Span::from(field_name.span().start()..typ.end()) + } else { + field_name.span() + }; + + let Some(field_location) = self.to_lsp_location(span) else { + continue; + }; + + let Some(field_name_location) = self.to_lsp_location(field_name.span()) else { + continue; + }; + + #[allow(deprecated)] + children.push(DocumentSymbol { + name: field_name.to_string(), + detail: None, + kind: SymbolKind::FIELD, + tags: None, + deprecated: None, + range: field_location.range, + selection_range: field_name_location.range, + children: None, + }); + } + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: noir_struct.name.to_string(), + detail: None, + kind: SymbolKind::STRUCT, + tags: None, + deprecated: None, + range: location.range, + selection_range: selection_location.range, + children: Some(children), + }); + } + + fn collect_in_noir_trait( + &mut self, + noir_trait: &NoirTrait, + span: Span, + symbols: &mut Vec, + ) { + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let Some(selection_location) = self.to_lsp_location(noir_trait.name.span()) else { + return; + }; + + let mut children = Vec::new(); + for item in &noir_trait.items { + self.collect_in_noir_trait_item(item, &mut children); + } + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: noir_trait.name.to_string(), + detail: None, + kind: SymbolKind::INTERFACE, + tags: None, + deprecated: None, + range: location.range, + selection_range: selection_location.range, + children: Some(children), + }); + } + + fn collect_in_noir_trait_item( + &mut self, + trait_item: &TraitItem, + symbols: &mut Vec, + ) { + // Ideally `TraitItem` has a `span` for the entire definition, and we'd use that + // for the `range` property. For now we do our best to find a reasonable span. + match trait_item { + TraitItem::Function { name, parameters, return_type, body, .. } => { + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let mut span = name.span(); + + // If there are parameters, extend the span to include the last parameter. + if let Some((param_name, _param_type)) = parameters.last() { + span = Span::from(span.start()..param_name.span().end()); + } + + // If there's a return type, extend the span to include it + match return_type { + FunctionReturnType::Default(return_type_span) => { + span = Span::from(span.start()..return_type_span.end()); + } + FunctionReturnType::Ty(typ) => { + if let Some(type_span) = typ.span { + span = Span::from(span.start()..type_span.end()); + } + } + } + + // If there's a body, extend the span to include it + if let Some(body) = body { + if let Some(statement) = body.statements.last() { + span = Span::from(span.start()..statement.span.end()); + } + } + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::METHOD, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); + } + TraitItem::Constant { name, typ, default_value } => { + self.collect_in_constant(name, typ, default_value.as_ref(), symbols); + } + TraitItem::Type { name } => { + self.collect_in_type(name, None, symbols); + } + } + } + + fn collect_in_constant( + &mut self, + name: &Ident, + typ: &UnresolvedType, + default_value: Option<&Expression>, + symbols: &mut Vec, + ) { + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let mut span = name.span(); + + // If there's a type span, extend the span to include it + if let Some(type_span) = typ.span { + span = Span::from(span.start()..type_span.end()); + } + + // If there's a default value, extend the span to include it + if let Some(default_value) = default_value { + span = Span::from(span.start()..default_value.span.end()); + } + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::CONSTANT, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); + } + + fn collect_in_type( + &mut self, + name: &Ident, + typ: Option<&UnresolvedType>, + symbols: &mut Vec, + ) { + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let span = if let Some(type_span) = typ.and_then(|typ| typ.span) { + Span::from(name.span().start()..type_span.end()) + } else { + name.span() + }; + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::TYPE_PARAMETER, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); + } + + fn collect_in_noir_trait_impl( + &mut self, + noir_trait_impl: &NoirTraitImpl, + span: Span, + symbols: &mut Vec, + ) { + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let Some(name_location) = self.to_lsp_location(noir_trait_impl.trait_name.span) else { + return; + }; + + let mut trait_name = String::new(); + trait_name.push_str(&noir_trait_impl.trait_name.to_string()); + if !noir_trait_impl.trait_generics.is_empty() { + trait_name.push('<'); + for (index, generic) in noir_trait_impl.trait_generics.iter().enumerate() { + if index > 0 { + trait_name.push_str(", "); + } + trait_name.push_str(&generic.to_string()); + } + trait_name.push('>'); + } + + let mut children = Vec::new(); + for trait_impl_item in &noir_trait_impl.items { + self.collect_in_trait_impl_item(trait_impl_item, &mut children); + } + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: format!("impl {} for {}", trait_name, noir_trait_impl.object_type), + detail: None, + kind: SymbolKind::NAMESPACE, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: Some(children), + }); + } + + fn collect_in_trait_impl_item( + &mut self, + trait_impl_item: &TraitImplItem, + symbols: &mut Vec, + ) { + match trait_impl_item { + TraitImplItem::Function(noir_function) => { + let span = Span::from( + noir_function.name_ident().span().start()..noir_function.span().end(), + ); + self.collect_in_noir_function(noir_function, span, symbols); + } + TraitImplItem::Constant(name, typ, default_value) => { + self.collect_in_constant(name, typ, Some(default_value), symbols); + } + TraitImplItem::Type { name, alias } => self.collect_in_type(name, Some(alias), symbols), + } + } + + fn collect_in_type_impl( + &mut self, + type_impl: &TypeImpl, + span: Span, + symbols: &mut Vec, + ) { + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let UnresolvedTypeData::Named(name_path, ..) = &type_impl.object_type.typ else { + return; + }; + + let name = name_path.last_segment(); + + let Some(name_location) = self.to_lsp_location(name.span()) else { + return; + }; + + let mut children = Vec::new(); + for (noir_function, noir_function_span) in &type_impl.methods { + self.collect_in_noir_function(noir_function, *noir_function_span, &mut children); + } + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: name.to_string(), + detail: None, + kind: SymbolKind::NAMESPACE, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: Some(children), + }); + } + + fn collect_in_parsed_sub_module( + &mut self, + parsed_sub_module: &ParsedSubModule, + span: Span, + symbols: &mut Vec, + ) { + let Some(name_location) = self.to_lsp_location(parsed_sub_module.name.span()) else { + return; + }; + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + let mut children = Vec::new(); + for item in &parsed_sub_module.contents.items { + self.collect_in_item(item, &mut children); + } + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: parsed_sub_module.name.to_string(), + detail: None, + kind: SymbolKind::MODULE, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: Some(children), + }); + } + + fn collect_in_global( + &mut self, + global: &LetStatement, + span: Span, + symbols: &mut Vec, + ) { + let Some(name_location) = self.to_lsp_location(global.pattern.span()) else { + return; + }; + + let Some(location) = self.to_lsp_location(span) else { + return; + }; + + #[allow(deprecated)] + symbols.push(DocumentSymbol { + name: global.pattern.to_string(), + detail: None, + kind: SymbolKind::CONSTANT, + tags: None, + deprecated: None, + range: location.range, + selection_range: name_location.range, + children: None, + }); + } + + fn to_lsp_location(&self, span: Span) -> Option { + super::to_lsp_location(self.files, self.file_id, span) + } +} + +#[cfg(test)] +mod document_symbol_tests { + use crate::test_utils; + + use super::*; + use lsp_types::{ + PartialResultParams, Range, SymbolKind, TextDocumentIdentifier, WorkDoneProgressParams, + }; + use tokio::test; + + #[test] + async fn test_document_symbol() { + let (mut state, noir_text_document) = test_utils::init_lsp_server("document_symbol").await; + + let response = on_document_symbol_request( + &mut state, + DocumentSymbolParams { + text_document: TextDocumentIdentifier { uri: noir_text_document }, + work_done_progress_params: WorkDoneProgressParams { work_done_token: None }, + partial_result_params: PartialResultParams { partial_result_token: None }, + }, + ) + .await + .expect("Could not execute on_document_symbol_request") + .unwrap(); + + let DocumentSymbolResponse::Nested(symbols) = response else { + panic!("Expected response to be nested"); + }; + + assert_eq!( + symbols, + vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "foo".to_string(), + detail: Some("fn foo(_x: i32)".to_string()), + kind: SymbolKind::FUNCTION, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 0, character: 0 }, + end: Position { line: 2, character: 1 }, + }, + selection_range: Range { + start: Position { line: 0, character: 3 }, + end: Position { line: 0, character: 6 }, + }, + children: None, + }, + #[allow(deprecated)] + DocumentSymbol { + name: "SomeStruct".to_string(), + detail: None, + kind: SymbolKind::STRUCT, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 4, character: 0 }, + end: Position { line: 6, character: 1 }, + }, + selection_range: Range { + start: Position { line: 4, character: 7 }, + end: Position { line: 4, character: 17 }, + }, + children: Some(vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "field".to_string(), + detail: None, + kind: SymbolKind::FIELD, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 5, character: 4 }, + end: Position { line: 5, character: 14 }, + }, + selection_range: Range { + start: Position { line: 5, character: 4 }, + end: Position { line: 5, character: 9 }, + }, + children: None, + }, + ],), + }, + #[allow(deprecated)] + DocumentSymbol { + name: "SomeStruct".to_string(), + detail: None, + kind: SymbolKind::NAMESPACE, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 8, character: 0 }, + end: Position { line: 12, character: 1 }, + }, + selection_range: Range { + start: Position { line: 8, character: 5 }, + end: Position { line: 8, character: 15 }, + }, + children: Some(vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "new".to_string(), + detail: Some("fn new() -> SomeStruct".to_string()), + kind: SymbolKind::FUNCTION, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 9, character: 4 }, + end: Position { line: 11, character: 5 }, + }, + selection_range: Range { + start: Position { line: 9, character: 7 }, + end: Position { line: 9, character: 10 }, + }, + children: None, + }, + ],), + }, + #[allow(deprecated)] + DocumentSymbol { + name: "SomeTrait".to_string(), + detail: None, + kind: SymbolKind::INTERFACE, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 14, character: 0 }, + end: Position { line: 16, character: 1 }, + }, + selection_range: Range { + start: Position { line: 14, character: 6 }, + end: Position { line: 14, character: 15 }, + }, + children: Some(vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "some_method".to_string(), + detail: None, + kind: SymbolKind::METHOD, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 15, character: 7 }, + end: Position { line: 15, character: 25 }, + }, + selection_range: Range { + start: Position { line: 15, character: 7 }, + end: Position { line: 15, character: 18 }, + }, + children: None, + }, + ],), + }, + #[allow(deprecated)] + DocumentSymbol { + name: "impl SomeTrait for SomeStruct".to_string(), + detail: None, + kind: SymbolKind::NAMESPACE, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 18, character: 0 }, + end: Position { line: 21, character: 1 }, + }, + selection_range: Range { + start: Position { line: 18, character: 5 }, + end: Position { line: 18, character: 14 }, + }, + children: Some(vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "some_method".to_string(), + detail: Some("fn some_method(_x: i32)".to_string()), + kind: SymbolKind::FUNCTION, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 19, character: 7 }, + end: Position { line: 20, character: 5 }, + }, + selection_range: Range { + start: Position { line: 19, character: 7 }, + end: Position { line: 19, character: 18 }, + }, + children: None, + }, + ],), + }, + #[allow(deprecated)] + DocumentSymbol { + name: "submodule".to_string(), + detail: None, + kind: SymbolKind::MODULE, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 23, character: 0 }, + end: Position { line: 25, character: 1 }, + }, + selection_range: Range { + start: Position { line: 23, character: 4 }, + end: Position { line: 23, character: 13 }, + }, + children: Some(vec![ + #[allow(deprecated)] + DocumentSymbol { + name: "SOME_GLOBAL".to_string(), + detail: None, + kind: SymbolKind::CONSTANT, + tags: None, + deprecated: None, + range: Range { + start: Position { line: 24, character: 4 }, + end: Position { line: 24, character: 27 } + }, + selection_range: Range { + start: Position { line: 24, character: 11 }, + end: Position { line: 24, character: 22 } + }, + children: None + } + ]), + }, + ] + ); + } +} diff --git a/noir/noir-repo/tooling/lsp/src/requests/mod.rs b/noir/noir-repo/tooling/lsp/src/requests/mod.rs index 6b2673d7795..5e2250ff248 100644 --- a/noir/noir-repo/tooling/lsp/src/requests/mod.rs +++ b/noir/noir-repo/tooling/lsp/src/requests/mod.rs @@ -33,6 +33,7 @@ use crate::{ // and params passed in. mod code_lens_request; +mod document_symbol; mod goto_declaration; mod goto_definition; mod hover; @@ -45,11 +46,12 @@ 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, - goto_definition::on_goto_type_definition_request, hover::on_hover_request, - inlay_hint::on_inlay_hint_request, profile_run::on_profile_run_request, - references::on_references_request, rename::on_prepare_rename_request, - rename::on_rename_request, test_run::on_test_run_request, tests::on_tests_request, + document_symbol::on_document_symbol_request, goto_declaration::on_goto_declaration_request, + goto_definition::on_goto_definition_request, goto_definition::on_goto_type_definition_request, + hover::on_hover_request, inlay_hint::on_inlay_hint_request, + profile_run::on_profile_run_request, references::on_references_request, + rename::on_prepare_rename_request, rename::on_rename_request, test_run::on_test_run_request, + tests::on_tests_request, }; /// LSP client will send initialization request after the server has started. @@ -141,6 +143,14 @@ pub(crate) fn on_initialize( }, resolve_provider: None, })), + document_symbol_provider: Some(lsp_types::OneOf::Right( + lsp_types::DocumentSymbolOptions { + work_done_progress_options: WorkDoneProgressOptions { + work_done_progress: None, + }, + label: Some("Noir".to_string()), + }, + )), }, server_info: None, }) diff --git a/noir/noir-repo/tooling/lsp/src/types.rs b/noir/noir-repo/tooling/lsp/src/types.rs index 3d1c8cc6edc..fa3234cf3bb 100644 --- a/noir/noir-repo/tooling/lsp/src/types.rs +++ b/noir/noir-repo/tooling/lsp/src/types.rs @@ -1,7 +1,7 @@ use fm::FileId; use lsp_types::{ - DeclarationCapability, DefinitionOptions, HoverOptions, InlayHintOptions, OneOf, - ReferencesOptions, RenameOptions, TypeDefinitionProviderCapability, + DeclarationCapability, DefinitionOptions, DocumentSymbolOptions, HoverOptions, + InlayHintOptions, OneOf, ReferencesOptions, RenameOptions, TypeDefinitionProviderCapability, }; use noirc_driver::DebugFile; use noirc_errors::{debug_info::OpCodesCount, Location}; @@ -152,6 +152,10 @@ pub(crate) struct ServerCapabilities { /// The server provides inlay hints support. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) inlay_hint_provider: Option>, + + /// The server provides document symbol support. + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) document_symbol_provider: Option>, } #[derive(Debug, PartialEq, Clone, Default, Deserialize, Serialize)] diff --git a/noir/noir-repo/tooling/lsp/test_programs/document_symbol/Nargo.toml b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/Nargo.toml new file mode 100644 index 00000000000..367b145f045 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "document_symbol" +type = "bin" +authors = [""] + +[dependencies] diff --git a/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr new file mode 100644 index 00000000000..39b2c7fff12 --- /dev/null +++ b/noir/noir-repo/tooling/lsp/test_programs/document_symbol/src/main.nr @@ -0,0 +1,26 @@ +fn foo(_x: i32) { + let _ = 1; +} + +struct SomeStruct { + field: i32, +} + +impl SomeStruct { + fn new() -> SomeStruct { + SomeStruct { field: 0 } + } +} + +trait SomeTrait { + fn some_method(x: U); +} + +impl SomeTrait for SomeStruct { + fn some_method(_x: i32) { + } +} + +mod submodule { + global SOME_GLOBAL = 1; +}