From 8b24f158f75e4496cfc7f8edf9aa41b10dbac9b3 Mon Sep 17 00:00:00 2001 From: Aleksander Vognild Burkow Date: Sat, 29 Dec 2018 20:09:42 +0100 Subject: Add support for formatting entire document with rustfmt Attempting to format a document when rustfmt isn't installed will result in an error being returned to the frontend. An alternative implementation would be returning zero replacements. --- crates/ra_analysis/src/imp.rs | 3 ++ crates/ra_analysis/src/lib.rs | 3 ++ crates/ra_lsp_server/src/caps.rs | 2 +- crates/ra_lsp_server/src/main_loop.rs | 1 + crates/ra_lsp_server/src/main_loop/handlers.rs | 33 +++++++++++++++ crates/ra_lsp_server/tests/heavy_tests/main.rs | 56 +++++++++++++++++++++++++- 6 files changed, 95 insertions(+), 3 deletions(-) (limited to 'crates') diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index fcb4cd957..bff2e00c9 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs @@ -140,6 +140,9 @@ impl fmt::Debug for AnalysisImpl { } impl AnalysisImpl { + pub fn file_text(&self, file_id: FileId) -> Arc { + self.db.file_text(file_id) + } pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { self.db.source_file(file_id) } diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 67b1c1482..9f5e9f358 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs @@ -274,6 +274,9 @@ pub struct Analysis { } impl Analysis { + pub fn file_text(&self, file_id: FileId) -> Arc { + self.imp.file_text(file_id) + } pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { self.imp.file_syntax(file_id).clone() } diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 5f7038f63..8d508a3ba 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs @@ -33,7 +33,7 @@ pub fn server_capabilities() -> ServerCapabilities { workspace_symbol_provider: Some(true), code_action_provider: Some(CodeActionProviderCapability::Simple(true)), code_lens_provider: None, - document_formatting_provider: None, + document_formatting_provider: Some(true), document_range_formatting_provider: None, document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { first_trigger_character: "=".to_string(), diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 1edb9fae4..97c1be778 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs @@ -295,6 +295,7 @@ fn on_request( .on::(handlers::handle_prepare_rename)? .on::(handlers::handle_rename)? .on::(handlers::handle_references)? + .on::(handlers::handle_formatting)? .finish(); match req { Ok(id) => { diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index d6f3dbe28..07579be12 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -6,13 +6,16 @@ use languageserver_types::{ DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, + Range, WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, + DocumentFormattingParams, }; use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; use ra_syntax::{TextUnit, text_utils::intersect}; use ra_text_edit::text_utils::contains_offset_nonstrict; use rustc_hash::FxHashMap; use serde_json::to_value; +use std::io::Write; use crate::{ conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith}, @@ -601,6 +604,36 @@ pub fn handle_references( )) } +pub fn handle_formatting( + world: ServerWorld, + params: DocumentFormattingParams, +) -> Result>> { + let file_id = params.text_document.try_conv_with(&world)?; + let file = world.analysis().file_text(file_id); + + let file_line_index = world.analysis().file_line_index(file_id); + let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); + + use std::process; + let mut rustfmt = process::Command::new("rustfmt") + .stdin(process::Stdio::piped()) + .stdout(process::Stdio::piped()) + .spawn()?; + + rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; + + let output = rustfmt.wait_with_output()?; + let captured_stdout = String::from_utf8(output.stdout)?; + if !output.status.success() { + return Err(failure::err_msg(captured_stdout)); + } + + Ok(Some(vec![TextEdit { + range: Range::new(Position::new(0, 0), end_position), + new_text: captured_stdout, + }])) +} + pub fn handle_code_action( world: ServerWorld, params: req::CodeActionParams, diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 1f5cc5e8b..e9f02a6e4 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -1,8 +1,8 @@ mod support; use serde_json::json; -use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams}; -use languageserver_types::{Position, Range, CodeActionContext}; +use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting}; +use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions}; use crate::support::project; @@ -118,6 +118,58 @@ fn test_eggs() {} ); } +use std::collections::HashMap; +#[test] +fn test_format_document() { + let server = project( + r#" +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +mod bar; + +fn main() { +} + +pub use std::collections::HashMap; +"#, + ); + server.wait_for_feedback("workspace loaded"); + + server.request::( + DocumentFormattingParams { + text_document: server.doc_id("src/lib.rs"), + options: FormattingOptions { + tab_size: 4, + insert_spaces: false, + properties: HashMap::new(), + }, + }, + json!([ + { + "newText": r#"mod bar; + +fn main() {} + +pub use std::collections::HashMap; +"#, + "range": { + "end": { + "character": 0, + "line": 6 + }, + "start": { + "character": 0, + "line": 0 + } + } + } + ]), + ); +} + #[test] fn test_missing_module_code_action() { let server = project( -- cgit v1.2.3