diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index 3402b8c458e..3c5a9a5cf2d 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -41,6 +41,7 @@ pub use contract::{CompiledContract, CompiledContractOutputs, ContractFunction}; pub use debug::DebugFile; pub use noirc_frontend::graph::{CrateId, CrateName}; pub use program::CompiledProgram; +pub use stdlib::stdlib_paths_with_source; const STD_CRATE_NAME: &str = "std"; const DEBUG_CRATE_NAME: &str = "__debug"; @@ -336,7 +337,7 @@ fn add_stdlib_source_to_file_manager(file_manager: &mut FileManager) { // on the stdlib. For other dependencies, we read the package.Dependencies file to add their file // contents to the file manager. However since the dependency on the stdlib is implicit, we need // to manually add it here. - let stdlib_paths_with_source = stdlib::stdlib_paths_with_source(); + let stdlib_paths_with_source = stdlib_paths_with_source(); for (path, source) in stdlib_paths_with_source { file_manager.add_file_with_source_canonical_path(Path::new(&path), source); } diff --git a/compiler/noirc_driver/src/stdlib.rs b/compiler/noirc_driver/src/stdlib.rs index 5a91e3f45b5..479a14ed209 100644 --- a/compiler/noirc_driver/src/stdlib.rs +++ b/compiler/noirc_driver/src/stdlib.rs @@ -12,7 +12,7 @@ struct StdLibAssets; // This is needed because when we preload the file manager, it needs to know where // the source code for the stdlib is. The stdlib is treated special because it comes with // the compiler and is never included as a dependency like other user defined crates. -pub(crate) fn stdlib_paths_with_source() -> Vec<(String, String)> { +pub fn stdlib_paths_with_source() -> Vec<(String, String)> { StdLibAssets::iter() .map(|path| { let source = std::str::from_utf8(StdLibAssets::get(&path).unwrap().data.as_ref()) diff --git a/tooling/lsp/src/lib.rs b/tooling/lsp/src/lib.rs index e1c7bd364fb..8c08492850d 100644 --- a/tooling/lsp/src/lib.rs +++ b/tooling/lsp/src/lib.rs @@ -77,7 +77,10 @@ use solver::WrapperSolver; use types::{NargoTest, NargoTestId, Position, Range, Url, notification, request}; use with_file::parsed_module_with_file; -use crate::{requests::on_expand_request, types::request::NargoExpand}; +use crate::{ + requests::{on_expand_request, on_std_source_code_request}, + types::request::{NargoExpand, NargoStdSourceCode}, +}; #[derive(Debug, Error)] pub enum LspError { @@ -173,6 +176,7 @@ impl NargoLspService { .request::(on_code_action_request) .request::(on_workspace_symbol_request) .request::(on_expand_request) + .request::(on_std_source_code_request) .notification::(on_initialized) .notification::(on_did_change_configuration) .notification::(on_did_open_text_document) @@ -424,8 +428,9 @@ pub fn insert_all_files_for_workspace_into_file_manager( pub fn source_code_overrides(input_files: &HashMap) -> HashMap { let mut overrides: HashMap = HashMap::new(); for (path, source) in input_files { - let path = path.strip_prefix("file://").unwrap(); - overrides.insert(PathBuf::from_str(path).unwrap(), source); + if let Some(path) = path.strip_prefix("file://") { + overrides.insert(PathBuf::from_str(path).unwrap(), source); + } } overrides } diff --git a/tooling/lsp/src/notifications/mod.rs b/tooling/lsp/src/notifications/mod.rs index 45ca05a8154..d23ccbef718 100644 --- a/tooling/lsp/src/notifications/mod.rs +++ b/tooling/lsp/src/notifications/mod.rs @@ -1,6 +1,8 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; +use std::collections::{BTreeMap, HashSet}; use std::ops::ControlFlow; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; +use std::str::FromStr as _; use crate::{ PackageCacheData, WorkspaceCacheData, insert_all_files_for_workspace_into_file_manager, @@ -9,8 +11,10 @@ use async_lsp::lsp_types; use async_lsp::lsp_types::{DiagnosticRelatedInformation, DiagnosticTag, Url}; use async_lsp::{ErrorCode, LanguageClient, ResponseError}; use fm::{FileManager, FileMap}; +use nargo::package::{Package, PackageType}; use nargo::workspace::Workspace; use noirc_driver::check_crate; +use noirc_driver::{CrateName, NOIR_ARTIFACT_VERSION_STRING}; use noirc_errors::reporter::CustomLabel; use noirc_errors::{CustomDiagnostic, DiagnosticKind, Location}; @@ -98,7 +102,7 @@ pub(super) fn on_did_save_text_document( // Process any pending changes if state.workspaces_to_process.remove(&workspace.root_dir) { - let _ = process_workspace_for_noir_document(state, &workspace.root_dir, false); + let _ = process_workspace(state, &workspace, false); } // Cached data should be here but, if it doesn't, we'll just type-check and output diagnostics @@ -107,11 +111,7 @@ pub(super) fn on_did_save_text_document( state.package_cache.get(&workspace.root_dir), ) else { let output_diagnostics = true; - return match process_workspace_for_noir_document( - state, - &workspace.root_dir, - output_diagnostics, - ) { + return match process_workspace(state, &workspace, output_diagnostics) { Ok(_) => ControlFlow::Continue(()), Err(err) => return ControlFlow::Break(Err(err)), }; @@ -122,11 +122,7 @@ pub(super) fn on_did_save_text_document( // so here we force a type-check just in case. if package_cache.diagnostics_just_published { let output_diagnostics = true; - return match process_workspace_for_noir_document( - state, - &workspace.root_dir, - output_diagnostics, - ) { + return match process_workspace(state, &workspace, output_diagnostics) { Ok(_) => ControlFlow::Continue(()), Err(err) => return ControlFlow::Break(Err(err)), }; @@ -164,41 +160,48 @@ fn handle_text_document_notification( // If it's the first time we see this package, show diagnostics. // This can happen for example when a user opens a Noir file in a package for the first time. let output_diagnostics = true; - process_workspace_for_noir_document(state, &workspace.root_dir, output_diagnostics) + process_workspace(state, &workspace, output_diagnostics) } } -fn workspace_from_document_uri(document_uri: Url) -> Result { - let file_path = document_uri.to_file_path().map_err(|_| { - ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") - })?; +pub(crate) fn workspace_from_document_uri( + document_uri: Url, +) -> Result { + if document_uri.scheme() == "noir-std" { + Ok(fake_stdlib_workspace()) + } else { + let file_path = document_uri.to_file_path().map_err(|_| { + ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") + })?; - let workspace = resolve_workspace_for_source_path(&file_path).map_err(|lsp_error| { - ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) - })?; + let workspace = resolve_workspace_for_source_path(&file_path).map_err(|lsp_error| { + ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) + })?; - Ok(workspace) + Ok(workspace) + } } // Given a Noir document, find the workspace it's contained in (an assumed workspace is created if // it's only contained in a package), then type-checks the workspace's packages, -// caching code lenses and type definitions, and notifying about compilation errors. -pub(crate) fn process_workspace_for_noir_document( +// caching type definitions, and notifying about compilation errors if `output_diagnostics` is true. +pub(crate) fn process_workspace( state: &mut LspState, - file_path: &Path, + workspace: &Workspace, output_diagnostics: bool, ) -> Result<(), async_lsp::Error> { - let workspace = resolve_workspace_for_source_path(file_path).map_err(|lsp_error| { - ResponseError::new(ErrorCode::REQUEST_FAILED, lsp_error.to_string()) - })?; - let mut workspace_file_manager = workspace.new_file_manager(); - - insert_all_files_for_workspace_into_file_manager( - state, - &workspace, - &mut workspace_file_manager, - ); + if workspace.is_assumed { + let package = workspace.members.first().unwrap(); + workspace_file_manager + .add_file_with_source_canonical_path(&package.entry_path, String::new()); + } else { + insert_all_files_for_workspace_into_file_manager( + state, + workspace, + &mut workspace_file_manager, + ); + } let parsed_files = parse_diff(&workspace_file_manager, state); @@ -247,6 +250,27 @@ pub(crate) fn process_workspace_for_noir_document( Ok(()) } +pub(crate) fn fake_stdlib_workspace() -> Workspace { + let assumed_package = Package { + version: None, + compiler_required_version: Some(NOIR_ARTIFACT_VERSION_STRING.to_string()), + compiler_required_unstable_features: Vec::new(), + root_dir: PathBuf::from_str("std").unwrap(), + package_type: PackageType::Binary, + entry_path: PathBuf::from_str("fake_entry_path.nr").unwrap(), + name: CrateName::from_str("fake_std").unwrap(), + dependencies: BTreeMap::new(), + expression_width: None, + }; + Workspace { + root_dir: PathBuf::from_str("std").unwrap(), + members: vec![assumed_package], + selected_package_index: Some(0), + is_assumed: true, + target_dir: None, + } +} + fn publish_diagnostics( state: &mut LspState, package_root_dir: &PathBuf, diff --git a/tooling/lsp/src/requests/code_action.rs b/tooling/lsp/src/requests/code_action.rs index 74191dbf2f6..69e25fddce4 100644 --- a/tooling/lsp/src/requests/code_action.rs +++ b/tooling/lsp/src/requests/code_action.rs @@ -9,7 +9,7 @@ use async_lsp::lsp_types::{ CodeAction, CodeActionKind, CodeActionOrCommand, CodeActionParams, CodeActionResponse, TextDocumentPositionParams, TextEdit, Url, WorkspaceEdit, }; -use fm::{FileId, FileMap, PathString}; +use fm::{FileId, FileMap}; use noirc_errors::Span; use noirc_frontend::{ ParsedModule, @@ -49,27 +49,25 @@ pub(crate) fn on_code_action_request( TextDocumentPositionParams { text_document: params.text_document, position }; let result = process_request(state, text_document_position_params, |args| { - let path = PathString::from_path(uri.to_file_path().unwrap()); - args.files.get_file_id(&path).and_then(|file_id| { - utils::range_to_byte_span(args.files, file_id, ¶ms.range).and_then(|byte_range| { - let file = args.files.get_file(file_id).unwrap(); - let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); - - let mut finder = CodeActionFinder::new( - uri, - args.files, - file_id, - source, - byte_range, - args.crate_id, - args.def_maps, - args.dependencies(), - args.interner, - args.usage_tracker, - ); - finder.find(&parsed_module) - }) + let file_id = args.location.file; + utils::range_to_byte_span(args.files, file_id, ¶ms.range).and_then(|byte_range| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); + + let mut finder = CodeActionFinder::new( + uri, + args.files, + file_id, + source, + byte_range, + args.crate_id, + args.def_maps, + args.dependencies(), + args.interner, + args.usage_tracker, + ); + finder.find(&parsed_module) }) }); future::ready(result) diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 3df18d836ba..75cbfb427b8 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -13,7 +13,7 @@ use completion_items::{ trait_impl_method_completion_item, }; use convert_case::{Case, Casing}; -use fm::{FileId, FileMap, PathString}; +use fm::{FileId, FileMap}; use iter_extended::vecmap; use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems}; use noirc_errors::{Location, Span}; @@ -63,16 +63,9 @@ pub(crate) fn on_completion_request( state: &mut LspState, params: CompletionParams, ) -> impl Future, ResponseError>> + use<> { - let uri = params.text_document_position.clone().text_document.uri; - let result = process_request(state, params.text_document_position.clone(), |args| { - let path = PathString::from_path(uri.to_file_path().unwrap()); - args.files.get_file_id(&path).and_then(|file_id| { - utils::position_to_byte_index( - args.files, - file_id, - ¶ms.text_document_position.position, - ) + let file_id = args.location.file; + utils::position_to_byte_index(args.files, file_id, ¶ms.text_document_position.position) .and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); @@ -92,7 +85,6 @@ pub(crate) fn on_completion_request( ); finder.find(&parsed_module) }) - }) }); future::ready(result) } diff --git a/tooling/lsp/src/requests/document_symbol.rs b/tooling/lsp/src/requests/document_symbol.rs index 6b66d55e6d0..7f81177d403 100644 --- a/tooling/lsp/src/requests/document_symbol.rs +++ b/tooling/lsp/src/requests/document_symbol.rs @@ -5,7 +5,7 @@ use async_lsp::lsp_types::{ DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Location, Position, SymbolKind, TextDocumentPositionParams, }; -use fm::{FileId, FileMap, PathString}; +use fm::{FileId, FileMap}; use noirc_errors::Span; use noirc_frontend::ast::TraitBound; use noirc_frontend::{ @@ -25,25 +25,20 @@ pub(crate) fn on_document_symbol_request( state: &mut LspState, params: DocumentSymbolParams, ) -> impl Future, ResponseError>> + use<> { - 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, file_id); - - let mut collector = DocumentSymbolCollector::new(file_id, args.files); - let symbols = collector.collect(&parsed_module); - DocumentSymbolResponse::Nested(symbols) - }) + let file_id = args.location.file; + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); + + let mut collector = DocumentSymbolCollector::new(file_id, args.files); + let symbols = collector.collect(&parsed_module); + Some(DocumentSymbolResponse::Nested(symbols)) }); future::ready(result) diff --git a/tooling/lsp/src/requests/expand.rs b/tooling/lsp/src/requests/expand.rs index 7b965913567..f48c7996a50 100644 --- a/tooling/lsp/src/requests/expand.rs +++ b/tooling/lsp/src/requests/expand.rs @@ -13,13 +13,16 @@ pub(crate) fn on_expand_request( state: &mut LspState, params: NargoExpandParams, ) -> impl Future> + use<> { + let is_stdlib = params.text_document.uri.scheme() == "noir-std"; + let text_document_position_params = TextDocumentPositionParams { text_document: params.text_document, position: params.position, }; let result = process_request(state, text_document_position_params, |args| { - get_expanded_crate(args.crate_id, args.crate_graph, args.def_maps, args.interner) + let crate_id = if is_stdlib { *args.crate_graph.stdlib_crate_id() } else { args.crate_id }; + get_expanded_crate(crate_id, args.crate_graph, args.def_maps, args.interner) }); future::ready(result) diff --git a/tooling/lsp/src/requests/goto_definition.rs b/tooling/lsp/src/requests/goto_definition.rs index 5757088db79..0e059c48fec 100644 --- a/tooling/lsp/src/requests/goto_definition.rs +++ b/tooling/lsp/src/requests/goto_definition.rs @@ -6,7 +6,6 @@ use crate::{LspState, types::GotoDefinitionResult}; use async_lsp::ResponseError; use async_lsp::lsp_types; -use fm::PathString; use lsp_types::request::GotoTypeDefinitionParams; use lsp_types::{GotoDefinitionParams, GotoDefinitionResponse}; @@ -33,11 +32,10 @@ fn on_goto_definition_inner( params: GotoDefinitionParams, return_type_location_instead: bool, ) -> Result { - let uri = params.text_document_position_params.text_document.uri.clone(); let position = params.text_document_position_params.position; process_request(state, params.text_document_position_params, |args| { - let path = PathString::from_path(uri.to_file_path().unwrap()); - let reference_id = args.files.get_file_id(&path).and_then(|file_id| { + let file_id = args.location.file; + let reference_id = utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { let file = args.files.get_file(file_id).unwrap(); let source = file.source(); @@ -50,8 +48,7 @@ fn on_goto_definition_inner( args.def_maps, ); finder.find(&parsed_module) - }) - }); + }); let location = if let Some(reference_id) = reference_id { Some(args.interner.reference_location(reference_id)) } else { diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 80a59dffd6e..93f985d1c09 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -2,7 +2,6 @@ use std::future::{self, Future}; use async_lsp::ResponseError; use async_lsp::lsp_types::{Hover, HoverParams}; -use fm::PathString; use from_reference::hover_from_reference; use from_visitor::hover_from_visitor; @@ -17,11 +16,9 @@ pub(crate) fn on_hover_request( state: &mut LspState, params: HoverParams, ) -> impl Future, ResponseError>> + use<> { - let uri = params.text_document_position_params.text_document.uri.clone(); let position = params.text_document_position_params.position; let result = process_request(state, params.text_document_position_params, |args| { - let path = PathString::from_path(uri.to_file_path().unwrap()); - let file_id = args.files.get_file_id(&path); + let file_id = args.location.file; hover_from_reference(file_id, position, &args) .or_else(|| hover_from_visitor(file_id, position, &args)) }); @@ -245,13 +242,16 @@ mod hover_tests { .expect("Could not resolve root path"); let workspace_on_src_lib_path = workspace_on_src_lib_path.to_string_lossy(); - assert_hover( - "workspace", - "two/src/lib.nr", - Position { line: 51, character: 8 }, - &format!(" let x: BoundedVec\n\nGo to [SubOneStruct](file://{workspace_on_src_lib_path}#L4,12-4,24)"), - ) - .await; + let hover_text = + get_hover_text("workspace", "two/src/lib.nr", Position { line: 51, character: 8 }) + .await; + assert!(hover_text.contains(" let x: BoundedVec")); + assert!(hover_text.contains("Go to [BoundedVec](noir-std:")); + assert!( + hover_text.contains(&format!( + "[SubOneStruct](file://{workspace_on_src_lib_path}#L4,12-4,24)" + )) + ); } #[test] diff --git a/tooling/lsp/src/requests/hover/from_reference.rs b/tooling/lsp/src/requests/hover/from_reference.rs index f78d08e14a9..40d88f237d3 100644 --- a/tooling/lsp/src/requests/hover/from_reference.rs +++ b/tooling/lsp/src/requests/hover/from_reference.rs @@ -27,25 +27,19 @@ use crate::{ }; pub(super) fn hover_from_reference( - file_id: Option, + file_id: FileId, position: Position, args: &ProcessRequestCallbackArgs, ) -> Option { - file_id - .and_then(|file_id| { - utils::position_to_byte_index(args.files, file_id, &position).and_then(|byte_index| { - let file = args.files.get_file(file_id).unwrap(); - let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); - - let mut finder = AttributeReferenceFinder::new( - file_id, - byte_index, - args.crate_id, - args.def_maps, - ); - finder.find(&parsed_module) - }) + utils::position_to_byte_index(args.files, file_id, &position) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); + + let mut finder = + AttributeReferenceFinder::new(file_id, byte_index, args.crate_id, args.def_maps); + finder.find(&parsed_module) }) .or_else(|| args.interner.reference_at_location(args.location)) .and_then(|reference| { diff --git a/tooling/lsp/src/requests/hover/from_visitor.rs b/tooling/lsp/src/requests/hover/from_visitor.rs index c2ea0724dbd..570c9582f08 100644 --- a/tooling/lsp/src/requests/hover/from_visitor.rs +++ b/tooling/lsp/src/requests/hover/from_visitor.rs @@ -15,11 +15,10 @@ use crate::{ }; pub(super) fn hover_from_visitor( - file_id: Option, + file_id: FileId, position: Position, args: &ProcessRequestCallbackArgs, ) -> Option { - let file_id = file_id?; let file = args.files.get_file(file_id)?; let source = file.source(); let (parsed_module, _errors) = parse_program(source, file_id); diff --git a/tooling/lsp/src/requests/inlay_hint.rs b/tooling/lsp/src/requests/inlay_hint.rs index e8773973735..e25ab456f66 100644 --- a/tooling/lsp/src/requests/inlay_hint.rs +++ b/tooling/lsp/src/requests/inlay_hint.rs @@ -6,7 +6,7 @@ use async_lsp::lsp_types::{ InlayHint, InlayHintKind, InlayHintLabel, InlayHintLabelPart, InlayHintParams, Position, Range, TextDocumentPositionParams, TextEdit, }; -use fm::{FileId, FileMap, PathString}; +use fm::{FileId, FileMap}; use noirc_errors::{Location, Span}; use noirc_frontend::{ self, Kind, Type, TypeBinding, TypeVariable, @@ -36,20 +36,18 @@ pub(crate) fn on_inlay_hint_request( let options = state.options.inlay_hints; let result = process_request(state, text_document_position_params, |args| { - let path = PathString::from_path(params.text_document.uri.to_file_path().unwrap()); - args.files.get_file_id(&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, file_id); - - let span = utils::range_to_byte_span(args.files, file_id, ¶ms.range) - .map(|range| Span::from(range.start as u32..range.end as u32)); - - let mut collector = - InlayHintCollector::new(args.files, file_id, args.interner, span, options); - parsed_module.accept(&mut collector); - collector.inlay_hints - }) + let file_id = args.location.file; + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); + + let span = utils::range_to_byte_span(args.files, file_id, ¶ms.range) + .map(|range| Span::from(range.start as u32..range.end as u32)); + + let mut collector = + InlayHintCollector::new(args.files, file_id, args.interner, span, options); + parsed_module.accept(&mut collector); + Some(collector.inlay_hints) }); future::ready(result) } diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index 32398847c1f..5bf47c163bc 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -1,8 +1,10 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::{collections::HashMap, future::Future}; -use crate::notifications::process_workspace_for_noir_document; +use crate::notifications::fake_stdlib_workspace; +use crate::notifications::process_workspace; use crate::{PackageCacheData, insert_all_files_for_workspace_into_file_manager, parse_diff}; use crate::{ resolve_workspace_for_source_path, @@ -60,6 +62,7 @@ mod inlay_hint; mod references; mod rename; mod signature_help; +mod std_source_code; mod test_run; mod tests; mod workspace_symbol; @@ -71,8 +74,9 @@ pub(crate) use { goto_definition::on_goto_definition_request, goto_definition::on_goto_type_definition_request, hover::on_hover_request, inlay_hint::on_inlay_hint_request, references::on_references_request, rename::on_prepare_rename_request, rename::on_rename_request, - signature_help::on_signature_help_request, test_run::on_test_run_request, - tests::on_tests_request, workspace_symbol::on_workspace_symbol_request, + signature_help::on_signature_help_request, std_source_code::on_std_source_code_request, + test_run::on_test_run_request, tests::on_tests_request, + workspace_symbol::on_workspace_symbol_request, }; /// LSP client will send initialization request after the server has started. @@ -444,8 +448,13 @@ pub(crate) fn to_lsp_location( let range = crate::byte_span_to_range(files, file_id, definition_span.into())?; let file_name = files.get_absolute_name(file_id).ok()?; let path = file_name.to_string(); - let uri = Url::from_file_path(path).ok()?; - Some(Location { uri, range }) + if let Ok(uri) = Url::from_file_path(path.clone()) { + Some(Location { uri, range }) + } else if path.starts_with("std/") { + Some(Location { uri: Url::from_str(&format!("noir-std://{path}")).unwrap(), range }) + } else { + None + } } pub(crate) fn on_shutdown( @@ -483,16 +492,25 @@ pub(crate) fn process_request( where F: FnOnce(ProcessRequestCallbackArgs) -> T, { - let file_path = - text_document_position_params.text_document.uri.to_file_path().map_err(|_| { + let uri = text_document_position_params.text_document.uri.clone(); + + let (file_path, workspace) = if uri.scheme() == "noir-std" { + let workspace = fake_stdlib_workspace(); + let file_path = + PathBuf::from_str(&format!("{}{}", uri.host().unwrap(), uri.path())).unwrap(); + (file_path, workspace) + } else { + let file_path = uri.to_file_path().map_err(|_| { ResponseError::new(ErrorCode::REQUEST_FAILED, "URI is not a valid file path") })?; - let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); + let workspace = resolve_workspace_for_source_path(file_path.as_path()).unwrap(); + (file_path, workspace) + }; // First type-check the workspace if needed if state.workspaces_to_process.remove(&workspace.root_dir) { - let _ = process_workspace_for_noir_document(state, &workspace.root_dir, false); + let _ = process_workspace(state, &workspace, false); } let package = crate::workspace_package_for_file(&workspace, &file_path).ok_or_else(|| { diff --git a/tooling/lsp/src/requests/references.rs b/tooling/lsp/src/requests/references.rs index 2d1a8166172..ddf6a1cb4cb 100644 --- a/tooling/lsp/src/requests/references.rs +++ b/tooling/lsp/src/requests/references.rs @@ -28,6 +28,7 @@ pub(crate) fn on_references_request( #[cfg(test)] mod references_tests { use super::*; + use crate::notifications::workspace_from_document_uri; use crate::test_utils::{self, search_in_file}; use crate::utils::get_cursor_line_and_column; use crate::{notifications, on_did_open_text_document}; @@ -113,13 +114,9 @@ mod references_tests { // We call this to open the document, so that the entire workspace is analyzed let output_diagnostics = true; + let workspace = workspace_from_document_uri(one_lib.clone()).unwrap(); - notifications::process_workspace_for_noir_document( - &mut state, - &one_lib.to_file_path().unwrap(), - output_diagnostics, - ) - .unwrap(); + notifications::process_workspace(&mut state, &workspace, output_diagnostics).unwrap(); let params = ReferenceParams { text_document_position: TextDocumentPositionParams { diff --git a/tooling/lsp/src/requests/signature_help.rs b/tooling/lsp/src/requests/signature_help.rs index 10cac787adc..2fb971f6fb2 100644 --- a/tooling/lsp/src/requests/signature_help.rs +++ b/tooling/lsp/src/requests/signature_help.rs @@ -4,7 +4,7 @@ use async_lsp::ResponseError; use async_lsp::lsp_types::{ ParameterInformation, ParameterLabel, SignatureHelp, SignatureHelpParams, SignatureInformation, }; -use fm::{FileId, PathString}; +use fm::FileId; use noirc_errors::{Location, Span}; use noirc_frontend::{ ParsedModule, Type, @@ -27,24 +27,20 @@ pub(crate) fn on_signature_help_request( state: &mut LspState, params: SignatureHelpParams, ) -> impl Future, ResponseError>> + use<> { - let uri = params.text_document_position_params.clone().text_document.uri; - let result = process_request(state, params.text_document_position_params.clone(), |args| { - let path = PathString::from_path(uri.to_file_path().unwrap()); - args.files.get_file_id(&path).and_then(|file_id| { - utils::position_to_byte_index( - args.files, - file_id, - ¶ms.text_document_position_params.position, - ) - .and_then(|byte_index| { - let file = args.files.get_file(file_id).unwrap(); - let source = file.source(); - let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); - - let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); - finder.find(&parsed_module) - }) + let file_id = args.location.file; + utils::position_to_byte_index( + args.files, + file_id, + ¶ms.text_document_position_params.position, + ) + .and_then(|byte_index| { + let file = args.files.get_file(file_id).unwrap(); + let source = file.source(); + let (parsed_module, _errors) = noirc_frontend::parse_program(source, file_id); + + let mut finder = SignatureFinder::new(file_id, byte_index, args.interner); + finder.find(&parsed_module) }) }); future::ready(result) diff --git a/tooling/lsp/src/requests/std_source_code.rs b/tooling/lsp/src/requests/std_source_code.rs new file mode 100644 index 00000000000..cf0723b2429 --- /dev/null +++ b/tooling/lsp/src/requests/std_source_code.rs @@ -0,0 +1,21 @@ +use std::future; + +use crate::{ + LspState, + types::{NargoStdSourceCodeParams, NargoStdSourceCodeResult}, +}; +use async_lsp::{ErrorCode, ResponseError}; +use noirc_driver::stdlib_paths_with_source; + +pub(crate) fn on_std_source_code_request( + _state: &mut LspState, + params: NargoStdSourceCodeParams, +) -> impl Future> + use<> { + let path = format!("{}{}", params.uri.host_str().unwrap(), params.uri.path()); + for (std_path, source) in stdlib_paths_with_source() { + if std_path == path { + return future::ready(Ok(source)); + } + } + future::ready(Err(ResponseError::new(ErrorCode::REQUEST_FAILED, "File not found".to_string()))) +} diff --git a/tooling/lsp/src/types.rs b/tooling/lsp/src/types.rs index 9a76cdfe1d4..b21e228e61a 100644 --- a/tooling/lsp/src/types.rs +++ b/tooling/lsp/src/types.rs @@ -18,7 +18,9 @@ pub(crate) use async_lsp::lsp_types::{ pub(crate) mod request { use async_lsp::lsp_types::{InitializeParams, request::Request}; - use crate::types::{NargoExpandParams, NargoExpandResult}; + use crate::types::{ + NargoExpandParams, NargoExpandResult, NargoStdSourceCodeParams, NargoStdSourceCodeResult, + }; use super::{ InitializeResult, NargoTestRunParams, NargoTestRunResult, NargoTestsParams, @@ -62,6 +64,14 @@ pub(crate) mod request { type Result = NargoExpandResult; const METHOD: &'static str = "nargo/expand"; } + + #[derive(Debug)] + pub(crate) struct NargoStdSourceCode; + impl Request for NargoStdSourceCode { + type Params = NargoStdSourceCodeParams; + type Result = NargoStdSourceCodeResult; + const METHOD: &'static str = "nargo/std-source-code"; + } } pub(crate) mod notification { @@ -265,6 +275,14 @@ pub(crate) struct NargoExpandParams { pub(crate) type NargoExpandResult = String; +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct NargoStdSourceCodeParams { + pub(crate) uri: Url, +} + +pub(crate) type NargoStdSourceCodeResult = String; + pub(crate) type CodeLensResult = Option>; pub(crate) type GotoDefinitionResult = Option; pub(crate) type GotoDeclarationResult =