From dc4a4465ded5a9222fe152f8cfd89372a76af8ee Mon Sep 17 00:00:00 2001 From: f01dab1e Date: Mon, 6 Nov 2023 03:42:44 +0000 Subject: [PATCH 1/2] feat: enable lsp formatting --- Cargo.lock | 1 + tooling/lsp/Cargo.toml | 1 + tooling/lsp/src/lib.rs | 13 ++++++-- tooling/lsp/src/notifications/mod.rs | 16 ++++++---- tooling/lsp/src/requests/mod.rs | 46 +++++++++++++++++++++++++--- tooling/lsp/src/types.rs | 7 +++-- 6 files changed, 70 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad2f24ac2f2..0b2ac24a8b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2531,6 +2531,7 @@ dependencies = [ "codespan-reporting", "lsp-types 0.94.0", "nargo", + "nargo_fmt", "nargo_toml", "noirc_driver", "noirc_errors", diff --git a/tooling/lsp/Cargo.toml b/tooling/lsp/Cargo.toml index a1d58a0de49..a354eee5f08 100644 --- a/tooling/lsp/Cargo.toml +++ b/tooling/lsp/Cargo.toml @@ -14,6 +14,7 @@ codespan-lsp.workspace = true codespan-reporting.workspace = true lsp-types.workspace = true nargo.workspace = true +nargo_fmt.workspace = true nargo_toml.workspace = true noirc_driver.workspace = true noirc_errors.workspace = true diff --git a/tooling/lsp/src/lib.rs b/tooling/lsp/src/lib.rs index 6e71f3d642d..2ddc7faeefa 100644 --- a/tooling/lsp/src/lib.rs +++ b/tooling/lsp/src/lib.rs @@ -4,6 +4,7 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies, unused_extern_crates))] use std::{ + collections::HashMap, future::Future, ops::{self, ControlFlow}, path::{Path, PathBuf}, @@ -26,7 +27,8 @@ use notifications::{ on_did_open_text_document, on_did_save_text_document, on_exit, on_initialized, }; use requests::{ - on_code_lens_request, on_initialize, on_shutdown, on_test_run_request, on_tests_request, + on_code_lens_request, on_formatting, on_initialize, on_shutdown, on_test_run_request, + on_tests_request, }; use serde_json::Value as JsonValue; use tower::Service; @@ -44,11 +46,17 @@ pub struct LspState { root_path: Option, client: ClientSocket, solver: WrapperSolver, + input_files: HashMap, } impl LspState { fn new(client: &ClientSocket, solver: impl BlackBoxFunctionSolver + 'static) -> Self { - Self { client: client.clone(), root_path: None, solver: WrapperSolver(Box::new(solver)) } + Self { + client: client.clone(), + root_path: None, + solver: WrapperSolver(Box::new(solver)), + input_files: HashMap::new(), + } } } @@ -62,6 +70,7 @@ impl NargoLspService { let mut router = Router::new(state); router .request::(on_initialize) + .request::(on_formatting) .request::(on_shutdown) .request::(on_code_lens_request) .request::(on_tests_request) diff --git a/tooling/lsp/src/notifications/mod.rs b/tooling/lsp/src/notifications/mod.rs index 93fa8baf6ac..f6484f49d48 100644 --- a/tooling/lsp/src/notifications/mod.rs +++ b/tooling/lsp/src/notifications/mod.rs @@ -30,23 +30,27 @@ pub(super) fn on_did_change_configuration( } pub(super) fn on_did_open_text_document( - _state: &mut LspState, - _params: DidOpenTextDocumentParams, + state: &mut LspState, + params: DidOpenTextDocumentParams, ) -> ControlFlow> { + state.input_files.insert(params.text_document.uri.to_string(), params.text_document.text); ControlFlow::Continue(()) } pub(super) fn on_did_change_text_document( - _state: &mut LspState, - _params: DidChangeTextDocumentParams, + state: &mut LspState, + params: DidChangeTextDocumentParams, ) -> ControlFlow> { + let text = params.content_changes.into_iter().next().unwrap().text; + state.input_files.insert(params.text_document.uri.to_string(), text); ControlFlow::Continue(()) } pub(super) fn on_did_close_text_document( - _state: &mut LspState, - _params: DidCloseTextDocumentParams, + state: &mut LspState, + params: DidCloseTextDocumentParams, ) -> ControlFlow> { + state.input_files.remove(¶ms.text_document.uri.to_string()); ControlFlow::Continue(()) } diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index 166adb10b5a..adcd75b9292 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -1,7 +1,9 @@ use std::future::Future; -use crate::types::{CodeLensOptions, InitializeParams, TextDocumentSyncOptions}; +use crate::types::{CodeLensOptions, InitializeParams}; use async_lsp::ResponseError; +use lsp_types::{Position, TextDocumentSyncCapability, TextDocumentSyncKind}; +use nargo_fmt::Config; use crate::{ types::{InitializeResult, NargoCapability, NargoTestsOptions, ServerCapabilities}, @@ -33,8 +35,7 @@ pub(crate) fn on_initialize( state.root_path = params.root_uri.and_then(|root_uri| root_uri.to_file_path().ok()); async { - let text_document_sync = - TextDocumentSyncOptions { save: Some(true.into()), ..Default::default() }; + let text_document_sync = TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL); let code_lens = CodeLensOptions { resolve_provider: Some(false) }; @@ -48,8 +49,9 @@ pub(crate) fn on_initialize( Ok(InitializeResult { capabilities: ServerCapabilities { - text_document_sync: Some(text_document_sync.into()), + text_document_sync: Some(text_document_sync), code_lens_provider: Some(code_lens), + document_formatting_provider: true, nargo: Some(nargo), }, server_info: None, @@ -57,6 +59,42 @@ pub(crate) fn on_initialize( } } +pub(crate) fn on_formatting( + state: &mut LspState, + params: lsp_types::DocumentFormattingParams, +) -> impl Future>, ResponseError>> { + std::future::ready(on_formatting_inner(state, params)) +} + +fn on_formatting_inner( + state: &LspState, + params: lsp_types::DocumentFormattingParams, +) -> Result>, ResponseError> { + let path = params.text_document.uri.to_string(); + + if let Some(source) = state.input_files.get(&path) { + let (module, errors) = noirc_frontend::parse_program(source); + if !errors.is_empty() { + return Ok(None); + } + + let new_text = nargo_fmt::format(source, module, &Config::default()); + + let start_position = Position { line: 0, character: 0 }; + let end_position = Position { + line: source.lines().count() as u32, + character: source.chars().count() as u32, + }; + + Ok(Some(vec![lsp_types::TextEdit { + range: lsp_types::Range::new(start_position, end_position), + new_text, + }])) + } else { + Ok(None) + } +} + pub(crate) fn on_shutdown( _state: &mut LspState, _params: (), diff --git a/tooling/lsp/src/types.rs b/tooling/lsp/src/types.rs index 10f1764c63f..6778004e1f5 100644 --- a/tooling/lsp/src/types.rs +++ b/tooling/lsp/src/types.rs @@ -7,7 +7,7 @@ pub(crate) use lsp_types::{ DidChangeConfigurationParams, DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, InitializeParams, InitializedParams, LogMessageParams, MessageType, Position, PublishDiagnosticsParams, Range, ServerInfo, - TextDocumentSyncCapability, TextDocumentSyncOptions, Url, + TextDocumentSyncCapability, Url, }; pub(crate) mod request { @@ -19,7 +19,7 @@ pub(crate) mod request { }; // Re-providing lsp_types that we don't need to override - pub(crate) use lsp_types::request::{CodeLensRequest as CodeLens, Shutdown}; + pub(crate) use lsp_types::request::{CodeLensRequest as CodeLens, Formatting, Shutdown}; #[derive(Debug)] pub(crate) struct Initialize; @@ -99,6 +99,9 @@ pub(crate) struct ServerCapabilities { #[serde(skip_serializing_if = "Option::is_none")] pub(crate) code_lens_provider: Option, + /// The server provides document formatting. + pub(crate) document_formatting_provider: bool, + /// The server handles and provides custom nargo messages. #[serde(skip_serializing_if = "Option::is_none")] pub(crate) nargo: Option, From e9f17ec05d066abec687c103397ae2f4f47f9a75 Mon Sep 17 00:00:00 2001 From: f01dab1e Date: Mon, 6 Nov 2023 04:05:04 +0000 Subject: [PATCH 2/2] update test --- tooling/lsp/src/requests/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tooling/lsp/src/requests/mod.rs b/tooling/lsp/src/requests/mod.rs index adcd75b9292..54e44a8a98e 100644 --- a/tooling/lsp/src/requests/mod.rs +++ b/tooling/lsp/src/requests/mod.rs @@ -106,7 +106,7 @@ pub(crate) fn on_shutdown( mod initialization { use async_lsp::ClientSocket; use lsp_types::{ - CodeLensOptions, InitializeParams, TextDocumentSyncCapability, TextDocumentSyncOptions, + CodeLensOptions, InitializeParams, TextDocumentSyncCapability, TextDocumentSyncKind, }; use tokio::test; @@ -124,10 +124,11 @@ mod initialization { assert!(matches!( response.capabilities, ServerCapabilities { - text_document_sync: Some(TextDocumentSyncCapability::Options( - TextDocumentSyncOptions { save: Some(_), .. } + text_document_sync: Some(TextDocumentSyncCapability::Kind( + TextDocumentSyncKind::FULL )), code_lens_provider: Some(CodeLensOptions { resolve_provider: Some(false) }), + document_formatting_provider: true, .. } ));