mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 04:19:13 +00:00
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.
This commit is contained in:
parent
2aac6b0e34
commit
8b24f158f7
6 changed files with 95 additions and 3 deletions
|
@ -140,6 +140,9 @@ impl fmt::Debug for AnalysisImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnalysisImpl {
|
impl AnalysisImpl {
|
||||||
|
pub fn file_text(&self, file_id: FileId) -> Arc<String> {
|
||||||
|
self.db.file_text(file_id)
|
||||||
|
}
|
||||||
pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
|
pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
|
||||||
self.db.source_file(file_id)
|
self.db.source_file(file_id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,6 +274,9 @@ pub struct Analysis {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Analysis {
|
impl Analysis {
|
||||||
|
pub fn file_text(&self, file_id: FileId) -> Arc<String> {
|
||||||
|
self.imp.file_text(file_id)
|
||||||
|
}
|
||||||
pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
|
pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
|
||||||
self.imp.file_syntax(file_id).clone()
|
self.imp.file_syntax(file_id).clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub fn server_capabilities() -> ServerCapabilities {
|
||||||
workspace_symbol_provider: Some(true),
|
workspace_symbol_provider: Some(true),
|
||||||
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
|
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
|
||||||
code_lens_provider: None,
|
code_lens_provider: None,
|
||||||
document_formatting_provider: None,
|
document_formatting_provider: Some(true),
|
||||||
document_range_formatting_provider: None,
|
document_range_formatting_provider: None,
|
||||||
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
|
||||||
first_trigger_character: "=".to_string(),
|
first_trigger_character: "=".to_string(),
|
||||||
|
|
|
@ -295,6 +295,7 @@ fn on_request(
|
||||||
.on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
|
.on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
|
||||||
.on::<req::Rename>(handlers::handle_rename)?
|
.on::<req::Rename>(handlers::handle_rename)?
|
||||||
.on::<req::References>(handlers::handle_references)?
|
.on::<req::References>(handlers::handle_references)?
|
||||||
|
.on::<req::Formatting>(handlers::handle_formatting)?
|
||||||
.finish();
|
.finish();
|
||||||
match req {
|
match req {
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
|
|
|
@ -6,13 +6,16 @@ use languageserver_types::{
|
||||||
DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
|
DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
|
||||||
FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
|
FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
|
||||||
PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
|
PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
|
||||||
|
Range,
|
||||||
WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
|
WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
|
||||||
|
DocumentFormattingParams,
|
||||||
};
|
};
|
||||||
use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
|
use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
|
||||||
use ra_syntax::{TextUnit, text_utils::intersect};
|
use ra_syntax::{TextUnit, text_utils::intersect};
|
||||||
use ra_text_edit::text_utils::contains_offset_nonstrict;
|
use ra_text_edit::text_utils::contains_offset_nonstrict;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use serde_json::to_value;
|
use serde_json::to_value;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
|
conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
|
||||||
|
@ -601,6 +604,36 @@ pub fn handle_references(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_formatting(
|
||||||
|
world: ServerWorld,
|
||||||
|
params: DocumentFormattingParams,
|
||||||
|
) -> Result<Option<Vec<TextEdit>>> {
|
||||||
|
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(
|
pub fn handle_code_action(
|
||||||
world: ServerWorld,
|
world: ServerWorld,
|
||||||
params: req::CodeActionParams,
|
params: req::CodeActionParams,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams};
|
use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting};
|
||||||
use languageserver_types::{Position, Range, CodeActionContext};
|
use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions};
|
||||||
|
|
||||||
use crate::support::project;
|
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::<Formatting>(
|
||||||
|
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]
|
#[test]
|
||||||
fn test_missing_module_code_action() {
|
fn test_missing_module_code_action() {
|
||||||
let server = project(
|
let server = project(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue