aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-12-30 10:16:22 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-12-30 10:16:22 +0000
commit8d1df9834c96bd464c309383afdd8edea0576ae0 (patch)
tree1836353cc58c9aeede832bb18872c37af312f377
parent75acc25c5a5df2ac0a8978be2972187ee974a754 (diff)
parent0cb270e75d9501dff9ac6633354ae12d9c0f4260 (diff)
Merge #358
358: Add support for formatting entire document with rustfmt r=matklad a=aleksanb 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. Part of https://github.com/rust-analyzer/rust-analyzer/issues/160. Co-authored-by: Aleksander Vognild Burkow <[email protected]>
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_analysis/src/imp.rs3
-rw-r--r--crates/ra_analysis/src/lib.rs3
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/caps.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs37
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs58
-rw-r--r--crates/tools/src/lib.rs2
-rw-r--r--editors/README.md2
11 files changed, 107 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 76bc7bf57..68158e41c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ crates/*/target
4.idea/* 4.idea/*
5.vscode/* 5.vscode/*
6*.log 6*.log
7*.iml
diff --git a/Cargo.lock b/Cargo.lock
index 2fb993689..8e962d352 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -790,6 +790,7 @@ dependencies = [
790 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 790 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
791 "thread_worker 0.1.0", 791 "thread_worker 0.1.0",
792 "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 792 "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
793 "tools 0.1.0",
793 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 794 "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
794 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 795 "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
795] 796]
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 {
140} 140}
141 141
142impl AnalysisImpl { 142impl AnalysisImpl {
143 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
144 self.db.file_text(file_id)
145 }
143 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 146 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
144 self.db.source_file(file_id) 147 self.db.source_file(file_id)
145 } 148 }
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 {
274} 274}
275 275
276impl Analysis { 276impl Analysis {
277 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
278 self.imp.file_text(file_id)
279 }
277 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 280 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
278 self.imp.file_syntax(file_id).clone() 281 self.imp.file_syntax(file_id).clone()
279 } 282 }
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index c53106a62..f8f91e5e7 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -33,6 +33,7 @@ ra_text_edit = { path = "../ra_text_edit" }
33ra_analysis = { path = "../ra_analysis" } 33ra_analysis = { path = "../ra_analysis" }
34gen_lsp_server = { path = "../gen_lsp_server" } 34gen_lsp_server = { path = "../gen_lsp_server" }
35ra_vfs = { path = "../ra_vfs" } 35ra_vfs = { path = "../ra_vfs" }
36tools = { path = "../tools" }
36 37
37[dev-dependencies] 38[dev-dependencies]
38tempdir = "0.3.7" 39tempdir = "0.3.7"
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 {
33 workspace_symbol_provider: Some(true), 33 workspace_symbol_provider: Some(true),
34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
35 code_lens_provider: None, 35 code_lens_provider: None,
36 document_formatting_provider: None, 36 document_formatting_provider: Some(true),
37 document_range_formatting_provider: None, 37 document_range_formatting_provider: None,
38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
39 first_trigger_character: "=".to_string(), 39 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(
295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)? 295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
296 .on::<req::Rename>(handlers::handle_rename)? 296 .on::<req::Rename>(handlers::handle_rename)?
297 .on::<req::References>(handlers::handle_references)? 297 .on::<req::References>(handlers::handle_references)?
298 .on::<req::Formatting>(handlers::handle_formatting)?
298 .finish(); 299 .finish();
299 match req { 300 match req {
300 Ok(id) => { 301 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..a2c12a4c1 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::{
6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, 6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, 7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, 8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
9 Range,
9 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, 10 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
11 DocumentFormattingParams,
10}; 12};
11use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; 13use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
12use ra_syntax::{TextUnit, text_utils::intersect}; 14use ra_syntax::{TextUnit, text_utils::intersect};
13use ra_text_edit::text_utils::contains_offset_nonstrict; 15use ra_text_edit::text_utils::contains_offset_nonstrict;
14use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
15use serde_json::to_value; 17use serde_json::to_value;
18use std::io::Write;
16 19
17use crate::{ 20use crate::{
18 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith}, 21 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
@@ -601,6 +604,40 @@ pub fn handle_references(
601 )) 604 ))
602} 605}
603 606
607pub fn handle_formatting(
608 world: ServerWorld,
609 params: DocumentFormattingParams,
610) -> Result<Option<Vec<TextEdit>>> {
611 let file_id = params.text_document.try_conv_with(&world)?;
612 let file = world.analysis().file_text(file_id);
613
614 let file_line_index = world.analysis().file_line_index(file_id);
615 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
616
617 use std::process;
618 let mut rustfmt = process::Command::new("rustfmt")
619 .stdin(process::Stdio::piped())
620 .stdout(process::Stdio::piped())
621 .spawn()?;
622
623 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
624
625 let output = rustfmt.wait_with_output()?;
626 let captured_stdout = String::from_utf8(output.stdout)?;
627 if !output.status.success() {
628 failure::bail!(
629 "rustfmt exited with error code {}: {}.",
630 output.status,
631 captured_stdout,
632 );
633 }
634
635 Ok(Some(vec![TextEdit {
636 range: Range::new(Position::new(0, 0), end_position),
637 new_text: captured_stdout,
638 }]))
639}
640
604pub fn handle_code_action( 641pub fn handle_code_action(
605 world: ServerWorld, 642 world: ServerWorld,
606 params: req::CodeActionParams, 643 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..b0e1e65b6 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 @@
1mod support; 1mod support;
2 2
3use serde_json::json; 3use serde_json::json;
4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams}; 4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting};
5use languageserver_types::{Position, Range, CodeActionContext}; 5use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions};
6 6
7use crate::support::project; 7use crate::support::project;
8 8
@@ -118,6 +118,60 @@ fn test_eggs() {}
118 ); 118 );
119} 119}
120 120
121use std::collections::HashMap;
122#[test]
123fn test_format_document() {
124 tools::install_rustfmt().unwrap();
125
126 let server = project(
127 r#"
128[package]
129name = "foo"
130version = "0.0.0"
131
132//- src/lib.rs
133mod bar;
134
135fn main() {
136}
137
138pub use std::collections::HashMap;
139"#,
140 );
141 server.wait_for_feedback("workspace loaded");
142
143 server.request::<Formatting>(
144 DocumentFormattingParams {
145 text_document: server.doc_id("src/lib.rs"),
146 options: FormattingOptions {
147 tab_size: 4,
148 insert_spaces: false,
149 properties: HashMap::new(),
150 },
151 },
152 json!([
153 {
154 "newText": r#"mod bar;
155
156fn main() {}
157
158pub use std::collections::HashMap;
159"#,
160 "range": {
161 "end": {
162 "character": 0,
163 "line": 6
164 },
165 "start": {
166 "character": 0,
167 "line": 0
168 }
169 }
170 }
171 ]),
172 );
173}
174
121#[test] 175#[test]
122fn test_missing_module_code_action() { 176fn test_missing_module_code_action() {
123 let server = project( 177 let server = project(
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index 6f96b8120..e5b32c25c 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -117,7 +117,7 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
117 Ok(()) 117 Ok(())
118} 118}
119 119
120fn install_rustfmt() -> Result<()> { 120pub fn install_rustfmt() -> Result<()> {
121 run(&format!("rustup install {}", TOOLCHAIN), ".")?; 121 run(&format!("rustup install {}", TOOLCHAIN), ".")?;
122 run( 122 run(
123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), 123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN),
diff --git a/editors/README.md b/editors/README.md
index ad2ce1695..a63ced725 100644
--- a/editors/README.md
+++ b/editors/README.md
@@ -45,6 +45,8 @@ It's better to remove existing Rust plugins to avoid interference.
45 `#[test]`, this action runs this specific test. If the cursor is 45 `#[test]`, this action runs this specific test. If the cursor is
46 outside of the test function, this re-runs the last test. Do bind 46 outside of the test function, this re-runs the last test. Do bind
47 this to a shortcut! 47 this to a shortcut!
48 - **Format document**. Formats the current file with rustfmt.
49 Rustfmt must be installed separately with `rustup component add rustfmt`.
48 50
49* Typing assists 51* Typing assists
50 - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression. 52 - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression.