From 4c8dae1b2cd845f688f3770912a1c68dc5638f4a Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Mon, 14 Aug 2023 10:31:45 -0700 Subject: [PATCH 1/5] feat(lsp): Add `Compile` code lens for `main` function and contracts --- crates/lsp/src/lib.rs | 74 +++++++++++++++++++- crates/noirc_frontend/src/hir/def_map/mod.rs | 3 +- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index 7af00fa15a8..f5a7ae6ff29 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -29,6 +29,8 @@ use tower::Service; const TEST_COMMAND: &str = "nargo.test"; const TEST_CODELENS_TITLE: &str = "▶\u{fe0e} Run Test"; +const COMPILE_COMMAND: &str = "nargo.compile"; +const COMPILE_CODELENS_TITLE: &str = "▶\u{fe0e} Compile"; // State for the LSP gets implemented on this struct and is internal to the implementation pub struct LspState { @@ -185,7 +187,7 @@ fn on_code_lens_request( for package in &workspace { let (mut context, crate_id) = prepare_package(package); - // We ignore the warnings and errors produced by compilation for producing codelenses + // We ignore the warnings and errors produced by compilation for producing code lenses // because we can still get the test functions even if compilation fails let _ = check_crate(&mut context, crate_id, false); @@ -226,6 +228,73 @@ fn on_code_lens_request( lenses.push(lens); } + + if package.is_binary() { + if let Some(main_func_id) = context.get_main_function(&crate_id) { + let location = context.function_meta(&main_func_id).name.location; + let file_id = location.file; + + // Ignore diagnostics for any file that wasn't the file we saved + // TODO: In the future, we could create "related" diagnostics for these files + // TODO: This currently just appends the `.nr` file extension that we store as a constant, + // but that won't work if we accept other extensions + if fm.path(file_id).with_extension(FILE_EXTENSION) != file_path { + continue; + } + + let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()) + .unwrap_or_default(); + + let command = Command { + title: COMPILE_CODELENS_TITLE.into(), + command: COMPILE_COMMAND.into(), + arguments: Some(vec![ + "--program-dir".into(), + format!("{}", workspace.root_dir.display()).into(), + "--package".into(), + format!("{}", package.name).into(), + ]), + }; + + let lens = CodeLens { range, command: command.into(), data: None }; + + lenses.push(lens); + } + } + + if package.is_contract() { + // Currently not looking to dedupe this since we don't have a clear decision on if the Contract stuff is staying + for contract in context.get_all_contracts(&crate_id) { + let location = contract.location; + let file_id = location.file; + + // Ignore diagnostics for any file that wasn't the file we saved + // TODO: In the future, we could create "related" diagnostics for these files + // TODO: This currently just appends the `.nr` file extension that we store as a constant, + // but that won't work if we accept other extensions + if fm.path(file_id).with_extension(FILE_EXTENSION) != file_path { + continue; + } + + let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()) + .unwrap_or_default(); + + let command = Command { + title: COMPILE_CODELENS_TITLE.into(), + command: COMPILE_COMMAND.into(), + arguments: Some(vec![ + "--program-dir".into(), + format!("{}", workspace.root_dir.display()).into(), + "--package".into(), + format!("{}", package.name).into(), + ]), + }; + + let lens = CodeLens { range, command: command.into(), data: None }; + + lenses.push(lens); + } + } } let res = if lenses.is_empty() { Ok(None) } else { Ok(Some(lenses)) }; @@ -365,6 +434,9 @@ fn on_did_save_text_document( } } + // We need to refresh lenses when we compile since that's the only time they can be accurately reflected + let _ = state.client.code_lens_refresh(()); + let _ = state.client.publish_diagnostics(PublishDiagnosticsParams { uri: params.text_document.uri, version: None, diff --git a/crates/noirc_frontend/src/hir/def_map/mod.rs b/crates/noirc_frontend/src/hir/def_map/mod.rs index f264d3f40b8..2dc8c5ec96f 100644 --- a/crates/noirc_frontend/src/hir/def_map/mod.rs +++ b/crates/noirc_frontend/src/hir/def_map/mod.rs @@ -148,7 +148,7 @@ impl CrateDefMap { let functions = module.value_definitions().filter_map(|id| id.as_function()).collect(); let name = self.get_module_path(id, module.parent); - Some(Contract { name, functions }) + Some(Contract { name, location: module.location, functions }) } else { None } @@ -194,6 +194,7 @@ impl CrateDefMap { pub struct Contract { /// To keep `name` semi-unique, it is prefixed with the names of parent modules via CrateDefMap::get_module_path pub name: String, + pub location: Location, pub functions: Vec, } From 9911ff936b35c570e6d477383ecfd4796e6f7949 Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Tue, 15 Aug 2023 13:05:06 -0700 Subject: [PATCH 2/5] better titles --- crates/lsp/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index f5a7ae6ff29..40d4f02418f 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -27,10 +27,11 @@ use noirc_frontend::hir::FunctionNameMatch; use serde_json::Value as JsonValue; use tower::Service; +const ARROW: &str = "▶\u{fe0e}"; const TEST_COMMAND: &str = "nargo.test"; -const TEST_CODELENS_TITLE: &str = "▶\u{fe0e} Run Test"; +const TEST_CODELENS_TITLE: &str = "Run Test"; const COMPILE_COMMAND: &str = "nargo.compile"; -const COMPILE_CODELENS_TITLE: &str = "▶\u{fe0e} Compile"; +const COMPILE_CODELENS_TITLE: &str = "Compile"; // State for the LSP gets implemented on this struct and is internal to the implementation pub struct LspState { @@ -212,7 +213,7 @@ fn on_code_lens_request( .unwrap_or_default(); let command = Command { - title: TEST_CODELENS_TITLE.into(), + title: format!("{ARROW} {TEST_CODELENS_TITLE}"), command: TEST_COMMAND.into(), arguments: Some(vec![ "--program-dir".into(), @@ -246,7 +247,7 @@ fn on_code_lens_request( .unwrap_or_default(); let command = Command { - title: COMPILE_CODELENS_TITLE.into(), + title: format!("{ARROW} {COMPILE_CODELENS_TITLE}"), command: COMPILE_COMMAND.into(), arguments: Some(vec![ "--program-dir".into(), @@ -280,7 +281,7 @@ fn on_code_lens_request( .unwrap_or_default(); let command = Command { - title: COMPILE_CODELENS_TITLE.into(), + title: format!("{ARROW} {COMPILE_CODELENS_TITLE}"), command: COMPILE_COMMAND.into(), arguments: Some(vec![ "--program-dir".into(), From cfd85638cde868115ea90fd62fb2816ac51ec39b Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Tue, 15 Aug 2023 13:05:21 -0700 Subject: [PATCH 3/5] cspell --- crates/lsp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index 40d4f02418f..b4f88cb77eb 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -264,7 +264,7 @@ fn on_code_lens_request( } if package.is_contract() { - // Currently not looking to dedupe this since we don't have a clear decision on if the Contract stuff is staying + // Currently not looking to deduplicate this since we don't have a clear decision on if the Contract stuff is staying for contract in context.get_all_contracts(&crate_id) { let location = contract.location; let file_id = location.file; From 86ff9572c93f1226bcf8fa08453a8d6166ccc65e Mon Sep 17 00:00:00 2001 From: Blaine Bublitz Date: Tue, 15 Aug 2023 13:31:41 -0700 Subject: [PATCH 4/5] feat(lsp): Add `Execute` code lens for `main` functions --- crates/lsp/src/lib.rs | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index b4f88cb77eb..f3cdd88be1f 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -32,6 +32,8 @@ const TEST_COMMAND: &str = "nargo.test"; const TEST_CODELENS_TITLE: &str = "Run Test"; const COMPILE_COMMAND: &str = "nargo.compile"; const COMPILE_CODELENS_TITLE: &str = "Compile"; +const EXECUTE_COMMAND: &str = "nargo.execute"; +const EXECUTE_CODELENS_TITLE: &str = "Execute"; // State for the LSP gets implemented on this struct and is internal to the implementation pub struct LspState { @@ -212,7 +214,7 @@ fn on_code_lens_request( let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()) .unwrap_or_default(); - let command = Command { + let test_command = Command { title: format!("{ARROW} {TEST_CODELENS_TITLE}"), command: TEST_COMMAND.into(), arguments: Some(vec![ @@ -225,9 +227,9 @@ fn on_code_lens_request( ]), }; - let lens = CodeLens { range, command: command.into(), data: None }; + let test_lens = CodeLens { range, command: Some(test_command), data: None }; - lenses.push(lens); + lenses.push(test_lens); } if package.is_binary() { @@ -246,7 +248,7 @@ fn on_code_lens_request( let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()) .unwrap_or_default(); - let command = Command { + let compile_command = Command { title: format!("{ARROW} {COMPILE_CODELENS_TITLE}"), command: COMPILE_COMMAND.into(), arguments: Some(vec![ @@ -257,9 +259,24 @@ fn on_code_lens_request( ]), }; - let lens = CodeLens { range, command: command.into(), data: None }; + let compile_lens = CodeLens { range, command: Some(compile_command), data: None }; - lenses.push(lens); + lenses.push(compile_lens); + + let execute_command = Command { + title: format!("{EXECUTE_CODELENS_TITLE}"), + command: EXECUTE_COMMAND.into(), + arguments: Some(vec![ + "--program-dir".into(), + format!("{}", workspace.root_dir.display()).into(), + "--package".into(), + format!("{}", package.name).into(), + ]), + }; + + let execute_lens = CodeLens { range, command: Some(execute_command), data: None }; + + lenses.push(execute_lens); } } @@ -280,7 +297,7 @@ fn on_code_lens_request( let range = byte_span_to_range(files, file_id.as_usize(), location.span.into()) .unwrap_or_default(); - let command = Command { + let compile_command = Command { title: format!("{ARROW} {COMPILE_CODELENS_TITLE}"), command: COMPILE_COMMAND.into(), arguments: Some(vec![ @@ -291,9 +308,9 @@ fn on_code_lens_request( ]), }; - let lens = CodeLens { range, command: command.into(), data: None }; + let compile_lens = CodeLens { range, command: Some(compile_command), data: None }; - lenses.push(lens); + lenses.push(compile_lens); } } } From 7972981584066928c7c8e5e6529a77368fe9aa09 Mon Sep 17 00:00:00 2001 From: Tom French <15848336+TomAFrench@users.noreply.github.com> Date: Wed, 16 Aug 2023 19:14:23 +0100 Subject: [PATCH 5/5] Update crates/lsp/src/lib.rs --- crates/lsp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/lsp/src/lib.rs b/crates/lsp/src/lib.rs index f3cdd88be1f..104470480b8 100644 --- a/crates/lsp/src/lib.rs +++ b/crates/lsp/src/lib.rs @@ -264,7 +264,7 @@ fn on_code_lens_request( lenses.push(compile_lens); let execute_command = Command { - title: format!("{EXECUTE_CODELENS_TITLE}"), + title: EXECUTE_CODELENS_TITLE.to_string(), command: EXECUTE_COMMAND.into(), arguments: Some(vec![ "--program-dir".into(),