mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
4207: Add unwrap block assist #4156 r=matklad a=bnjjj close issue #4156 4253: Remove `workspaceLoaded` setting r=matklad a=eminence The `workspaceLoaded` notification setting was originally designed to control the display of a popup message that said: "workspace loaded, {} rust packages" This popup was removed and replaced by a much sleeker message in the VSCode status bar that provides a real-time status while loading: rust-analyzer: {}/{} packages This was done as part of #3587 The change in this PR simply renames this setting from `workspaceLoaded` to `progress` to better describe what it actually controls. At the moment, the only type of progress message that is controlled by this setting is the initial load messages, but in theory other messages could also be controlled by this setting. Reviewer notes: * If we didn't like the idea of causing minor breaking to user's config, we could keep the setting name as `workspaceLoaded` * I think we can now close both #2719 and #3176 since the notification dialog in question no longer exists (actually I think you can close those issues even if you reject this PR 😄 ) Co-authored-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Co-authored-by: Andrew Chin <achin@eminence32.net>
This commit is contained in:
commit
89e1f97515
8 changed files with 390 additions and 14 deletions
|
@ -728,3 +728,22 @@ use std::{collections::HashMap};
|
|||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_unwrap_block() {
|
||||
check(
|
||||
"unwrap_block",
|
||||
r#####"
|
||||
fn foo() {
|
||||
if true {<|>
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
fn foo() {
|
||||
println!("foo");
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
|
348
crates/ra_assists/src/handlers/unwrap_block.rs
Normal file
348
crates/ra_assists/src/handlers/unwrap_block.rs
Normal file
|
@ -0,0 +1,348 @@
|
|||
use crate::{Assist, AssistCtx, AssistId};
|
||||
|
||||
use ast::{BlockExpr, Expr, ForExpr, IfExpr, LoopBodyOwner, LoopExpr, WhileExpr};
|
||||
use ra_fmt::unwrap_trivial_block;
|
||||
use ra_syntax::{ast, AstNode, TextRange, T};
|
||||
|
||||
// Assist: unwrap_block
|
||||
//
|
||||
// This assist removes if...else, for, while and loop control statements to just keep the body.
|
||||
//
|
||||
// ```
|
||||
// fn foo() {
|
||||
// if true {<|>
|
||||
// println!("foo");
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn foo() {
|
||||
// println!("foo");
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
|
||||
let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
|
||||
|
||||
let res = if let Some(if_expr) = l_curly_token.ancestors().find_map(IfExpr::cast) {
|
||||
// if expression
|
||||
let expr_to_unwrap = if_expr.blocks().find_map(|expr| extract_expr(ctx.frange.range, expr));
|
||||
let expr_to_unwrap = expr_to_unwrap?;
|
||||
// Find if we are in a else if block
|
||||
let ancestor = if_expr.syntax().ancestors().skip(1).find_map(ast::IfExpr::cast);
|
||||
|
||||
if let Some(ancestor) = ancestor {
|
||||
Some((ast::Expr::IfExpr(ancestor), expr_to_unwrap))
|
||||
} else {
|
||||
Some((ast::Expr::IfExpr(if_expr), expr_to_unwrap))
|
||||
}
|
||||
} else if let Some(for_expr) = l_curly_token.ancestors().find_map(ForExpr::cast) {
|
||||
// for expression
|
||||
let block_expr = for_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::ForExpr(for_expr), expr_to_unwrap))
|
||||
} else if let Some(while_expr) = l_curly_token.ancestors().find_map(WhileExpr::cast) {
|
||||
// while expression
|
||||
let block_expr = while_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::WhileExpr(while_expr), expr_to_unwrap))
|
||||
} else if let Some(loop_expr) = l_curly_token.ancestors().find_map(LoopExpr::cast) {
|
||||
// loop expression
|
||||
let block_expr = loop_expr.loop_body()?;
|
||||
extract_expr(ctx.frange.range, block_expr)
|
||||
.map(|expr_to_unwrap| (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (expr, expr_to_unwrap) = res?;
|
||||
ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| {
|
||||
edit.set_cursor(expr.syntax().text_range().start());
|
||||
edit.target(expr_to_unwrap.syntax().text_range());
|
||||
|
||||
let pat_start: &[_] = &[' ', '{', '\n'];
|
||||
let expr_to_unwrap = expr_to_unwrap.to_string();
|
||||
let expr_string = expr_to_unwrap.trim_start_matches(pat_start);
|
||||
let mut expr_string_lines: Vec<&str> = expr_string.lines().collect();
|
||||
expr_string_lines.pop(); // Delete last line
|
||||
|
||||
let expr_string = expr_string_lines
|
||||
.into_iter()
|
||||
.map(|line| line.replacen(" ", "", 1)) // Delete indentation
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n");
|
||||
|
||||
edit.replace(expr.syntax().text_range(), expr_string);
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_expr(cursor_range: TextRange, block: BlockExpr) -> Option<Expr> {
|
||||
let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range);
|
||||
|
||||
if cursor_in_range {
|
||||
Some(unwrap_trivial_block(block))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::helpers::{check_assist, check_assist_not_applicable};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn simple_if() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
if true {<|>
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
<|>foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_else() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {<|>
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();
|
||||
<|>println!("bar");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_else_if() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
//bar();
|
||||
if true {
|
||||
println!("true");
|
||||
|
||||
//comment
|
||||
//bar();
|
||||
} else if false {<|>
|
||||
println!("bar");
|
||||
} else {
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
//bar();
|
||||
<|>println!("bar");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_bad_cursor_position() {
|
||||
check_assist_not_applicable(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
bar();<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_for() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_in_for() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {
|
||||
if true {<|>
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
for i in 0..5 {
|
||||
<|>foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_loop() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
loop {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_while() {
|
||||
check_assist(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
while true {<|>
|
||||
if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
fn main() {
|
||||
<|>if true {
|
||||
foo();
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_if_in_while_bad_cursor_position() {
|
||||
check_assist_not_applicable(
|
||||
unwrap_block,
|
||||
r#"
|
||||
fn main() {
|
||||
while true {
|
||||
if true {
|
||||
foo();<|>
|
||||
|
||||
//comment
|
||||
bar();
|
||||
} else {
|
||||
println!("bar");
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -143,6 +143,7 @@ mod handlers {
|
|||
mod split_import;
|
||||
mod add_from_impl_for_enum;
|
||||
mod reorder_fields;
|
||||
mod unwrap_block;
|
||||
|
||||
pub(crate) fn all() -> &'static [AssistHandler] {
|
||||
&[
|
||||
|
@ -181,6 +182,7 @@ mod handlers {
|
|||
replace_unwrap_with_match::replace_unwrap_with_match,
|
||||
split_import::split_import,
|
||||
add_from_impl_for_enum::add_from_impl_for_enum,
|
||||
unwrap_block::unwrap_block,
|
||||
// These are manually sorted for better priorities
|
||||
add_missing_impl_members::add_missing_impl_members,
|
||||
add_missing_impl_members::add_missing_default_members,
|
||||
|
|
|
@ -43,7 +43,7 @@ impl ast::IfExpr {
|
|||
Some(res)
|
||||
}
|
||||
|
||||
fn blocks(&self) -> AstChildren<ast::BlockExpr> {
|
||||
pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
|
||||
support::children(self.syntax())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ pub enum FilesWatcher {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NotificationsConfig {
|
||||
pub workspace_loaded: bool,
|
||||
pub cargo_toml_not_found: bool,
|
||||
}
|
||||
|
||||
|
@ -83,10 +82,7 @@ impl Default for Config {
|
|||
lru_capacity: None,
|
||||
proc_macro_srv: None,
|
||||
files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
|
||||
notifications: NotificationsConfig {
|
||||
workspace_loaded: true,
|
||||
cargo_toml_not_found: true,
|
||||
},
|
||||
notifications: NotificationsConfig { cargo_toml_not_found: true },
|
||||
|
||||
cargo: CargoConfig::default(),
|
||||
rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
|
||||
|
@ -129,7 +125,6 @@ impl Config {
|
|||
Some("client") => FilesWatcher::Client,
|
||||
Some("notify") | _ => FilesWatcher::Notify
|
||||
};
|
||||
set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
|
||||
set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
|
||||
|
||||
set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
|
||||
|
|
|
@ -415,8 +415,7 @@ fn loop_turn(
|
|||
});
|
||||
}
|
||||
|
||||
let show_progress =
|
||||
!loop_state.workspace_loaded && world_state.config.notifications.workspace_loaded;
|
||||
let show_progress = !loop_state.workspace_loaded;
|
||||
|
||||
if !loop_state.workspace_loaded
|
||||
&& loop_state.roots_scanned == loop_state.roots_total
|
||||
|
|
|
@ -697,3 +697,21 @@ use std::┃collections::HashMap;
|
|||
// AFTER
|
||||
use std::{collections::HashMap};
|
||||
```
|
||||
|
||||
## `unwrap_block`
|
||||
|
||||
This assist removes if...else, for, while and loop control statements to just keep the body.
|
||||
|
||||
```rust
|
||||
// BEFORE
|
||||
fn foo() {
|
||||
if true {┃
|
||||
println!("foo");
|
||||
}
|
||||
}
|
||||
|
||||
// AFTER
|
||||
fn foo() {
|
||||
println!("foo");
|
||||
}
|
||||
```
|
||||
|
|
|
@ -205,11 +205,6 @@
|
|||
"default": [],
|
||||
"description": "Paths to exclude from analysis."
|
||||
},
|
||||
"rust-analyzer.notifications.workspaceLoaded": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Whether to show `workspace loaded` message."
|
||||
},
|
||||
"rust-analyzer.notifications.cargoTomlNotFound": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
|
Loading…
Reference in a new issue