mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
fill match arm
This commit is contained in:
parent
581c97a5c3
commit
bfaefed3f6
89
crates/ra_ide_api/src/assits.rs
Normal file
89
crates/ra_ide_api/src/assits.rs
Normal file
@ -0,0 +1,89 @@
|
||||
mod fill_match_arm;
|
||||
|
||||
use ra_syntax::{
|
||||
TextRange, SourceFile, AstNode,
|
||||
algo::find_node_at_offset,
|
||||
};
|
||||
use ra_ide_api_light::{
|
||||
LocalEdit,
|
||||
assists::{
|
||||
Assist,
|
||||
AssistBuilder
|
||||
}
|
||||
};
|
||||
use crate::{
|
||||
db::RootDatabase,
|
||||
FileId
|
||||
};
|
||||
|
||||
/// Return all the assists applicable at the given position.
|
||||
pub(crate) fn assists(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
file: &SourceFile,
|
||||
range: TextRange,
|
||||
) -> Vec<LocalEdit> {
|
||||
let ctx = AssistCtx::new(db, file_id, file, range);
|
||||
[fill_match_arm::fill_match_arm]
|
||||
.iter()
|
||||
.filter_map(|&assist| ctx.clone().apply(assist))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AssistCtx<'a> {
|
||||
file_id: FileId,
|
||||
source_file: &'a SourceFile,
|
||||
db: &'a RootDatabase,
|
||||
range: TextRange,
|
||||
should_compute_edit: bool,
|
||||
}
|
||||
|
||||
impl<'a> AssistCtx<'a> {
|
||||
pub(crate) fn new(
|
||||
db: &'a RootDatabase,
|
||||
file_id: FileId,
|
||||
source_file: &'a SourceFile,
|
||||
range: TextRange,
|
||||
) -> AssistCtx<'a> {
|
||||
AssistCtx {
|
||||
source_file,
|
||||
file_id,
|
||||
db,
|
||||
range,
|
||||
should_compute_edit: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> Option<LocalEdit> {
|
||||
self.should_compute_edit = true;
|
||||
match assist(self) {
|
||||
None => None,
|
||||
Some(Assist::Edit(e)) => Some(e),
|
||||
Some(Assist::Applicable) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn check(mut self, assist: fn(AssistCtx) -> Option<Assist>) -> bool {
|
||||
self.should_compute_edit = false;
|
||||
match assist(self) {
|
||||
None => false,
|
||||
Some(Assist::Edit(_)) => unreachable!(),
|
||||
Some(Assist::Applicable) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn build(self, label: impl Into<String>, f: impl FnOnce(&mut AssistBuilder)) -> Option<Assist> {
|
||||
if !self.should_compute_edit {
|
||||
return Some(Assist::Applicable);
|
||||
}
|
||||
let mut edit = AssistBuilder::default();
|
||||
f(&mut edit);
|
||||
Some(edit.build(label))
|
||||
}
|
||||
|
||||
pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<&'a N> {
|
||||
find_node_at_offset(self.source_file.syntax(), self.range.start())
|
||||
}
|
||||
}
|
157
crates/ra_ide_api/src/assits/fill_match_arm.rs
Normal file
157
crates/ra_ide_api/src/assits/fill_match_arm.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use std::fmt::Write;
|
||||
use hir::{
|
||||
AdtDef,
|
||||
source_binder,
|
||||
Ty,
|
||||
FieldSource,
|
||||
};
|
||||
use ra_ide_api_light::{
|
||||
assists::{
|
||||
Assist,
|
||||
AssistBuilder
|
||||
}
|
||||
};
|
||||
use ra_syntax::{
|
||||
ast::{
|
||||
self,
|
||||
AstNode,
|
||||
}
|
||||
};
|
||||
|
||||
use crate::assits::AssistCtx;
|
||||
|
||||
pub fn fill_match_arm(ctx: AssistCtx) -> Option<Assist> {
|
||||
let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
|
||||
|
||||
// We already have some match arms, so we don't provide any assists.
|
||||
match match_expr.match_arm_list() {
|
||||
Some(arm_list) if arm_list.arms().count() > 0 => {
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let expr = match_expr.expr()?;
|
||||
let function = source_binder::function_from_child_node(ctx.db, ctx.file_id, expr.syntax())?;
|
||||
let infer_result = function.infer(ctx.db);
|
||||
let syntax_mapping = function.body_syntax_mapping(ctx.db);
|
||||
let node_expr = syntax_mapping.node_expr(expr)?;
|
||||
let match_expr_ty = infer_result[node_expr].clone();
|
||||
match match_expr_ty {
|
||||
Ty::Adt { def_id, .. } => match def_id {
|
||||
AdtDef::Enum(e) => {
|
||||
let mut buf = format!("match {} {{\n", expr.syntax().text().to_string());
|
||||
let variants = e.variants(ctx.db);
|
||||
for variant in variants {
|
||||
let name = variant.name(ctx.db)?;
|
||||
write!(
|
||||
&mut buf,
|
||||
" {}::{}",
|
||||
e.name(ctx.db)?.to_string(),
|
||||
name.to_string()
|
||||
)
|
||||
.expect("write fmt");
|
||||
|
||||
let pat = variant
|
||||
.fields(ctx.db)
|
||||
.into_iter()
|
||||
.map(|field| {
|
||||
let name = field.name(ctx.db).to_string();
|
||||
let (_, source) = field.source(ctx.db);
|
||||
match source {
|
||||
FieldSource::Named(_) => name,
|
||||
FieldSource::Pos(_) => "_".to_string(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
match pat.first().map(|s| s.as_str()) {
|
||||
Some("_") => write!(&mut buf, "({})", pat.join(", ")).expect("write fmt"),
|
||||
Some(_) => write!(&mut buf, "{{{}}}", pat.join(", ")).expect("write fmt"),
|
||||
None => (),
|
||||
};
|
||||
|
||||
buf.push_str(" => (),\n");
|
||||
}
|
||||
buf.push_str("}");
|
||||
ctx.build("fill match arms", |edit: &mut AssistBuilder| {
|
||||
edit.replace_node_and_indent(match_expr.syntax(), buf);
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot_matches;
|
||||
|
||||
use ra_syntax::{TextRange, TextUnit};
|
||||
|
||||
use crate::{
|
||||
FileRange,
|
||||
mock_analysis::{analysis_and_position, single_file_with_position}
|
||||
};
|
||||
use ra_db::SourceDatabase;
|
||||
|
||||
fn test_assit(name: &str, code: &str) {
|
||||
let (analysis, position) = if code.contains("//-") {
|
||||
analysis_and_position(code)
|
||||
} else {
|
||||
single_file_with_position(code)
|
||||
};
|
||||
let frange = FileRange {
|
||||
file_id: position.file_id,
|
||||
range: TextRange::offset_len(position.offset, TextUnit::from(1)),
|
||||
};
|
||||
let source_file = analysis
|
||||
.with_db(|db| db.parse(frange.file_id))
|
||||
.expect("source file");
|
||||
let ret = analysis
|
||||
.with_db(|db| crate::assits::assists(db, frange.file_id, &source_file, frange.range))
|
||||
.expect("assits");
|
||||
|
||||
assert_debug_snapshot_matches!(name, ret);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fill_match_arm() {
|
||||
test_assit(
|
||||
"fill_match_arm1",
|
||||
r#"
|
||||
enum A {
|
||||
As,
|
||||
Bs,
|
||||
Cs(String),
|
||||
Ds(String, String),
|
||||
Es{x: usize, y: usize}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A::As;
|
||||
match a<|>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
test_assit(
|
||||
"fill_match_arm2",
|
||||
r#"
|
||||
enum A {
|
||||
As,
|
||||
Bs,
|
||||
Cs(String),
|
||||
Ds(String, String),
|
||||
Es{x: usize, y: usize}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = A::As;
|
||||
match a<|> {}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
---
|
||||
created: "2019-02-03T15:38:46.094184+00:00"
|
||||
creator: insta@0.5.2
|
||||
expression: ret
|
||||
source: crates/ra_ide_api/src/assits/fill_match_arm.rs
|
||||
---
|
||||
[
|
||||
LocalEdit {
|
||||
label: "fill match arms",
|
||||
edit: TextEdit {
|
||||
atoms: [
|
||||
AtomTextEdit {
|
||||
delete: [211; 218),
|
||||
insert: "match a {\n A::As => (),\n A::Bs => (),\n A::Cs(_) => (),\n A::Ds(_, _) => (),\n A::Es{x, y} => (),\n }"
|
||||
}
|
||||
]
|
||||
},
|
||||
cursor_position: None
|
||||
}
|
||||
]
|
@ -0,0 +1,20 @@
|
||||
---
|
||||
created: "2019-02-03T15:41:34.640074+00:00"
|
||||
creator: insta@0.5.2
|
||||
expression: ret
|
||||
source: crates/ra_ide_api/src/assits/fill_match_arm.rs
|
||||
---
|
||||
[
|
||||
LocalEdit {
|
||||
label: "fill match arms",
|
||||
edit: TextEdit {
|
||||
atoms: [
|
||||
AtomTextEdit {
|
||||
delete: [211; 221),
|
||||
insert: "match a {\n A::As => (),\n A::Bs => (),\n A::Cs(_) => (),\n A::Ds(_, _) => (),\n A::Es{x, y} => (),\n }"
|
||||
}
|
||||
]
|
||||
},
|
||||
cursor_position: None
|
||||
}
|
||||
]
|
@ -10,7 +10,7 @@ use ra_db::{
|
||||
SourceDatabase, SourceRoot, SourceRootId,
|
||||
salsa::{Database, SweepStrategy},
|
||||
};
|
||||
use ra_ide_api_light::{self, assists, LocalEdit, Severity};
|
||||
use ra_ide_api_light::{self, LocalEdit, Severity};
|
||||
use ra_syntax::{
|
||||
algo::find_node_at_offset, ast::{self, NameOwner}, AstNode,
|
||||
SourceFile,
|
||||
@ -238,8 +238,9 @@ impl db::RootDatabase {
|
||||
|
||||
pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
|
||||
let file = self.parse(frange.file_id);
|
||||
assists::assists(&file, frange.range)
|
||||
ra_ide_api_light::assists::assists(&file, frange.range)
|
||||
.into_iter()
|
||||
.chain(crate::assits::assists(self, frange.file_id, &file, frange.range).into_iter())
|
||||
.map(|local_edit| SourceChange::from_local_edit(frange.file_id, local_edit))
|
||||
.collect()
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ mod syntax_highlighting;
|
||||
mod parent_module;
|
||||
mod rename;
|
||||
mod impls;
|
||||
mod assits;
|
||||
|
||||
#[cfg(test)]
|
||||
mod marks;
|
||||
|
@ -104,7 +104,7 @@ pub enum Assist {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AssistBuilder {
|
||||
pub struct AssistBuilder {
|
||||
edit: TextEditBuilder,
|
||||
cursor_position: Option<TextUnit>,
|
||||
}
|
||||
@ -142,11 +142,7 @@ impl<'a> AssistCtx<'a> {
|
||||
}
|
||||
let mut edit = AssistBuilder::default();
|
||||
f(&mut edit);
|
||||
Some(Assist::Edit(LocalEdit {
|
||||
label: label.into(),
|
||||
edit: edit.edit.finish(),
|
||||
cursor_position: edit.cursor_position,
|
||||
}))
|
||||
Some(edit.build(label))
|
||||
}
|
||||
|
||||
pub(crate) fn leaf_at_offset(&self) -> LeafAtOffset<&'a SyntaxNode> {
|
||||
@ -164,7 +160,7 @@ impl AssistBuilder {
|
||||
fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) {
|
||||
self.edit.replace(range, replace_with.into())
|
||||
}
|
||||
fn replace_node_and_indent(&mut self, node: &SyntaxNode, replace_with: impl Into<String>) {
|
||||
pub fn replace_node_and_indent(&mut self, node: &SyntaxNode, replace_with: impl Into<String>) {
|
||||
let mut replace_with = replace_with.into();
|
||||
if let Some(indent) = leading_indent(node) {
|
||||
replace_with = reindent(&replace_with, indent)
|
||||
@ -181,6 +177,13 @@ impl AssistBuilder {
|
||||
fn set_cursor(&mut self, offset: TextUnit) {
|
||||
self.cursor_position = Some(offset)
|
||||
}
|
||||
pub fn build(self, label: impl Into<String>) -> Assist {
|
||||
Assist::Edit(LocalEdit {
|
||||
label: label.into(),
|
||||
cursor_position: self.cursor_position,
|
||||
edit: self.edit.finish(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn reindent(text: &str, indent: &str) -> String {
|
||||
|
Loading…
Reference in New Issue
Block a user