Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit ecae8bb

Browse files
committed
Return only partial text edits on formatting request
1 parent 1ad894b commit ecae8bb

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

rls/src/actions/format.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use std::process::{Command, Stdio};
99
use std::string::FromUtf8Error;
1010

1111
use log::debug;
12+
use lsp_types::{Position, Range, TextEdit};
1213
use rand::{distributions, thread_rng, Rng};
13-
use rustfmt_nightly::{Config, Input, Session};
14+
use rustfmt_nightly::{Config, Input, ModifiedLines, NewlineStyle, Session};
1415
use serde_json;
1516

1617
/// Specifies which `rustfmt` to use.
@@ -66,6 +67,41 @@ impl Rustfmt {
6667
Rustfmt::External { path, cwd } => format_external(path, cwd, input, cfg),
6768
}
6869
}
70+
71+
pub fn calc_text_edits(&self, input: String, mut cfg: Config) -> Result<Vec<TextEdit>, Error> {
72+
cfg.set().emit_mode(rustfmt_nightly::EmitMode::ModifiedLines);
73+
74+
let native = if cfg!(windows) { "\r\n" } else { "\n" };
75+
let newline = match cfg.newline_style() {
76+
NewlineStyle::Windows => "\r\n",
77+
NewlineStyle::Unix | NewlineStyle::Auto => "\n",
78+
NewlineStyle::Native => native,
79+
};
80+
81+
let output = self.format(input, cfg)?;
82+
let ModifiedLines { chunks } = output.parse().map_err(|_| Error::Failed)?;
83+
84+
Ok(chunks
85+
.into_iter()
86+
.map(|item| {
87+
// Rustfmt's line indices are 1-based
88+
let start_line = u64::from(item.line_number_orig) - 1;
89+
let removed = u64::from(item.lines_removed);
90+
// If there is only one line and we add them, we may underflow.
91+
let removed = if removed == 0 { 0 } else { removed - 1 };
92+
TextEdit {
93+
range: Range {
94+
start: Position::new(start_line, 0),
95+
// We don't extend the range past the last line because
96+
// sometimes it may not exist, skewing the diff and
97+
// making us add an invalid additional trailing newline.
98+
end: Position::new(start_line + removed, u64::max_value()),
99+
},
100+
new_text: item.lines.join(newline),
101+
}
102+
})
103+
.collect())
104+
}
69105
}
70106

71107
fn format_external(

rls/src/actions/requests.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ impl RequestAction for CodeAction {
620620
}
621621

622622
impl RequestAction for Formatting {
623-
type Response = [TextEdit; 1];
623+
type Response = Vec<TextEdit>;
624624

625625
fn fallback_response() -> Result<Self::Response, ResponseError> {
626626
Err(ResponseError::Message(
@@ -638,7 +638,7 @@ impl RequestAction for Formatting {
638638
}
639639

640640
impl RequestAction for RangeFormatting {
641-
type Response = [TextEdit; 1];
641+
type Response = Vec<TextEdit>;
642642

643643
fn fallback_response() -> Result<Self::Response, ResponseError> {
644644
Err(ResponseError::Message(
@@ -660,7 +660,7 @@ fn reformat(
660660
selection: Option<Range>,
661661
opts: &FormattingOptions,
662662
ctx: &InitActionContext,
663-
) -> Result<[TextEdit; 1], ResponseError> {
663+
) -> Result<Vec<TextEdit>, ResponseError> {
664664
ctx.quiescent.store(true, Ordering::SeqCst);
665665
trace!("Reformat: {:?} {:?} {} {}", doc, selection, opts.tab_size, opts.insert_spaces);
666666
let path = parse_file_path!(&doc.uri, "reformat")?;
@@ -683,7 +683,6 @@ fn reformat(
683683
}
684684
};
685685

686-
let range_whole_file = ls_util::range_from_file_string(&input);
687686
let mut config = ctx.fmt_config().get_rustfmt_config().clone();
688687
if !config.was_set().hard_tabs() {
689688
config.set().hard_tabs(!opts.insert_spaces);
@@ -722,9 +721,9 @@ fn reformat(
722721
config.set().file_lines(file_lines);
723722
};
724723

725-
let formatted_text = ctx
724+
let text_edits = ctx
726725
.formatter()
727-
.format(input, config)
726+
.calc_text_edits(input, config)
728727
.map_err(|msg| ResponseError::Message(ErrorCode::InternalError, msg.to_string()))?;
729728

730729
// Note that we don't need to update the VFS, the client echos back the
@@ -737,9 +736,7 @@ fn reformat(
737736
));
738737
}
739738

740-
// If Rustfmt returns range of text that changed,
741-
// we will be able to pass only range of changed text to the client.
742-
Ok([TextEdit { range: range_whole_file, new_text: formatted_text }])
739+
Ok(text_edits)
743740
}
744741

745742
impl RequestAction for ResolveCompletion {

tests/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1758,7 +1758,7 @@ fn client_reformat() {
17581758
start: Position { line: 0, character: 0 },
17591759
end: Position { line: 2, character: 0 },
17601760
},
1761-
new_text: "pub mod foo;\npub fn main() {\n let world = \"world\";\n println!(\"Hello, {}!\", world);\n}\n".to_string(),
1761+
new_text: "pub mod foo;\npub fn main() {\n let world = \"world\";\n println!(\"Hello, {}!\", world);\n}".to_string(),
17621762
});
17631763
}
17641764

0 commit comments

Comments
 (0)