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

Format ranges#346

Merged
nrc merged 8 commits intorust-lang:masterfrom
kogai:format-ranges
Jun 20, 2017
Merged

Format ranges#346
nrc merged 8 commits intorust-lang:masterfrom
kogai:format-ranges

Conversation

@kogai
Copy link
Copy Markdown
Contributor

@kogai kogai commented Jun 13, 2017

Implements #333

This PR request Rustfmt to format a range of text.

However, with the return value from Rustfmt as a factor, in response to LanguageClient, the text of the whole file is sent.

@kogai kogai changed the title WIP: Format ranges Format ranges Jun 13, 2017
};
let input = match self.vfs.load_file(path) {
Ok(FileContents::Text(s)) => FmtInput::Text(s),
Ok(FileContents::Text(_)) => FmtInput::File(path.clone()),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an incorrect change - it will mean any changes in memory are ignored in favour of the old version on disk

"file": "{}",
"range": [{}, {}]
}}]"#, path.to_str().unwrap(), range.start.line + 1, range.end.line + 1);
config.set().file_lines(file_lines.parse().unwrap());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we need a new API for rustfmt to take an optional range over a text input, rather than using this file/range approach

let file_lines = format!(r#"[{{
"file": "{}",
"range": [{}, {}]
}}]"#, path.to_str().unwrap(), range.start.line + 1, range.end.line + 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this range is from rls-span, then you should use the to_one_indexed/to_zero_indexed API rather than manually adding 1. If it is not, then you should convert the range to an rls-span range in server.rs


// If Rustfmt returns range of text that changed,
// we will be able to pass only range of changed text to the client.
let range = ls_util::range_from_vfs_file(&self.vfs, path);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could move and rename this variable and only make one call to range_from_vfs_file

@kogai
Copy link
Copy Markdown
Contributor Author

kogai commented Jun 14, 2017

Thank you for reviewing! and I fixed up some codes.

Copy link
Copy Markdown
Member

@nrc nrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, I have a few more comments from re-review.

"file": "stdin",
"range": [{}, {}]
}}]"#, range_rls.row_start.0, range_rls.row_end.0);
config.set().file_lines(file_lines.parse().unwrap());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

COuld we move all of this stuff inside the match/Some branch (and the decl of config before the match)? I'd like to avoid using the file_lines change if no range was given.

}

#[test]
fn test_reformat_with_range() {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to duplicate the test_data directory so each test is pointing at its own directory (since tests run concurrently, they'll otherwise race)

src/test/mod.rs Outdated

assert_eq!(ls_server::LsService::handle_message(server.clone()),
ls_server::ServerStateChange::Continue);
expect_messages(results.clone(), &[ExpectedMessage::new(Some(42)).expect_contains(r#"{"start":{"line":0,"character":0},"end":{"line":11,"character":69}}"#)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the test ensure that code outside of the range (or at least the AST node that encloses the range) is not reformatted?

src/test/mod.rs Outdated
assert_eq!(ls_server::LsService::handle_message(server.clone()),
ls_server::ServerStateChange::Continue);
expect_messages(results.clone(), &[ExpectedMessage::new(Some(42)).expect_contains(r#"{"start":{"line":0,"character":0},"end":{"line":14,"character":5}}"#)
.expect_contains(r#"newText":"// Copyright 2017 The Rust Project Developers. See the COPYRIGHT\n// file at the top-level directory of this distribution and at\n// http://rust-lang.org/COPYRIGHT.\n//\n// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or\n// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license\n// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your\n// option. This file may not be copied, modified, or distributed\n// except according to those terms.\n\npub fn main() {\n let world1 = \"world\";\n println!(\"Hello, {}!\", world1);\nlet world2 = \"world\"; println!(\"Hello, {}!\", world2);\n}"#)]);
Copy link
Copy Markdown
Contributor Author

@kogai kogai Jun 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing the range to rustfmt, it format the line feed correctly only within the range,
but the indent seems to be modified across the entire text.
Is it intended behavior of rustfmt (or, am I doing something wrong)❓

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not intended. However... what Rustfmt does with the range is that it rounds it up to the nearest Item ast node (a function or module, etc) and reformats the entire item. I'm not sure if that is happening here, but it might explain why stuff outside the range is formatted.

Could you copy the before and after for what Rustfmt is doing please?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, here is copy of what Rustfmt do.

For simplicity, i execute rustfmt command like follows, instead of call from rls.
cargo fmt -- --file-lines '[{"file": "path/to/file", "range": [2,3]}]'

before

      pub fn   main()
{
let world1 = "world"; println!("Hello, {}!", world1);
      let   world2     = "world"; println!("Hello, {}!", world2);
       }

after

pub fn main() {
    let world1 = "world";
    println!("Hello, {}!", world1);
      let   world2     = "world"; println!("Hello, {}!", world2);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like the first 3 lines have been formatted and so has the last line, but the forth line has not. This seems like rustfmt is being a bit buggy, but that your stuff (the RLS end) is working fine. I can't actually explain why the forth line has been skipped.

Copy link
Copy Markdown
Contributor Author

@kogai kogai Jun 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood. And then, I modified a test to avoid that part. 965cd33

@kogai
Copy link
Copy Markdown
Contributor Author

kogai commented Jun 15, 2017

Thank you a lot for reviewing ❗️
I fixed up some codes and could you please review again 🙏

@kogai kogai force-pushed the format-ranges branch 2 times, most recently from aec86f8 to 965cd33 Compare June 18, 2017 01:06
assert_eq!(ls_server::LsService::handle_message(server.clone()),
ls_server::ServerStateChange::Continue);
expect_messages(results.clone(), &[ExpectedMessage::new(Some(42)).expect_contains(r#"{"start":{"line":0,"character":0},"end":{"line":11,"character":69}}"#)
expect_messages(results.clone(), &[ExpectedMessage::new(Some(42)).expect_contains(r#"{"start":{"line":0,"character":0},"end":{"line":12,"character":0}}"#)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed that test failed when rebasing current master.
It seems to be able to fix #350.

Copy link
Copy Markdown
Member

@nrc nrc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, and sorry to make you rebase. I just had two minor things left to address.

pub fn reformat(&self, id: usize, doc: TextDocumentIdentifier, out: &Output, opts: &FormattingOptions) {
trace!("Reformat: {} {:?} {} {}", id, doc, opts.tab_size, opts.insert_spaces);
pub fn reformat(&self, id: usize, doc: TextDocumentIdentifier, selection: Option<Range>, out: &Output, opts: &FormattingOptions) {
trace!("Reformat: {} {:?}", id, doc);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you keep the options in this trace!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh..., I apologize for failing to rebase 🙇

let file_lines = format!(r#"[{{
"file": "stdin",
"range": [{}, {}]
}}]"#, range.row_start.0, range.row_end.0);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use a struct and serialise it, rather than using format! here please? (Ideally, you could use a struct from Rustfmt, but it's ok to create your own here if that is not possible).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I create Adopter of Rustfmt::FileLines and avoid to serialize FileLines in reformat method directory, daaac8e but if it does not reflect what you pointed out, please tell me 🙏

@nrc
Copy link
Copy Markdown
Member

nrc commented Jun 18, 2017

I create Adopter of Rustfmt::FileLines and avoid to serialize FileLines in reformat method directory, daaac8e but if it does not reflect what you pointed out, please tell me

I think that to create the FileLines, rather than use the parse method, you can create the object directly

@kogai
Copy link
Copy Markdown
Contributor Author

kogai commented Jun 19, 2017

I am sorry if I misunderstood something, but it doesn't seem to be able to create FileLines directly because Rustfmt doesn't export it's own Range struct as a public one that depended by FileLines struct 🤔

@nrc
Copy link
Copy Markdown
Member

nrc commented Jun 19, 2017

Ah, you are right, I fixed it - rust-lang/rustfmt@34fa428 I'll make a release of Rustfmt with that change later today.

@kogai
Copy link
Copy Markdown
Contributor Author

kogai commented Jun 19, 2017

Thank you so much for responding.
But for this purpose, it seems necessary to expose several constructors like this.
May I send this patch to Rustfmt?

@nrc
Copy link
Copy Markdown
Member

nrc commented Jun 20, 2017

May I send this patch to Rustfmt?

Yes please!

@kogai
Copy link
Copy Markdown
Contributor Author

kogai commented Jun 20, 2017

I have created a PR.
Could you review it, please?
rust-lang/rustfmt#1735

ranges.insert("stdin".to_owned(), vec![range]);
let file_lines = FileLines::from_ranges(ranges);
config.set().file_lines(file_lines);
};
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix up to create FileLines directly instead of using format!.
At present, it destroys the build, until this patch is released 🙏

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Released 0.1.5 with that patch

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you'll need to cargo update and commit the Cargo.lock to get the latest rustfmt version

Copy link
Copy Markdown
Contributor Author

@kogai kogai Jun 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated Cargo.lock, and pushed it ❗️

@nrc nrc merged commit 4e359ad into rust-lang:master Jun 20, 2017
@nrc
Copy link
Copy Markdown
Member

nrc commented Jun 20, 2017

Thank you!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants