diff options
-rw-r--r-- | crates/libeditor/src/code_actions.rs | 68 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 10 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 25 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 3 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 4 |
5 files changed, 85 insertions, 25 deletions
diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 1e20c0f48..6c41923dd 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs | |||
@@ -1,4 +1,7 @@ | |||
1 | use {TextUnit, EditBuilder, Edit}; | 1 | use std::{ |
2 | fmt::{self, Write}, | ||
3 | }; | ||
4 | |||
2 | use libsyntax2::{ | 5 | use libsyntax2::{ |
3 | ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner, ParsedFile}, | 6 | ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner, ParsedFile}, |
4 | SyntaxKind::COMMA, | 7 | SyntaxKind::COMMA, |
@@ -9,6 +12,8 @@ use libsyntax2::{ | |||
9 | }, | 12 | }, |
10 | }; | 13 | }; |
11 | 14 | ||
15 | use {TextUnit, EditBuilder, Edit}; | ||
16 | |||
12 | pub struct ActionResult { | 17 | pub struct ActionResult { |
13 | pub edit: Edit, | 18 | pub edit: Edit, |
14 | pub cursor_position: Option<TextUnit>, | 19 | pub cursor_position: Option<TextUnit>, |
@@ -63,27 +68,31 @@ pub fn add_impl<'a>(file: &'a ParsedFile, offset: TextUnit) -> Option<impl FnOnc | |||
63 | let name = nominal.name()?; | 68 | let name = nominal.name()?; |
64 | 69 | ||
65 | Some(move || { | 70 | Some(move || { |
66 | // let type_params = nominal.type_param_list(); | 71 | let type_params = nominal.type_param_list(); |
67 | // let type_args = match type_params { | ||
68 | // None => String::new(), | ||
69 | // Some(params) => { | ||
70 | // let mut buf = String::new(); | ||
71 | // } | ||
72 | // }; | ||
73 | let mut edit = EditBuilder::new(); | 72 | let mut edit = EditBuilder::new(); |
74 | let start_offset = nominal.syntax().range().end(); | 73 | let start_offset = nominal.syntax().range().end(); |
75 | edit.insert( | 74 | let mut buf = String::new(); |
76 | start_offset, | 75 | buf.push_str("\n\nimpl"); |
77 | format!( | 76 | if let Some(type_params) = type_params { |
78 | "\n\nimpl {} {{\n\n}}", | 77 | buf.push_display(&type_params.syntax().text()); |
79 | name.text(), | 78 | } |
80 | ) | 79 | buf.push_str(" "); |
81 | ); | 80 | buf.push_str(name.text().as_str()); |
81 | if let Some(type_params) = type_params { | ||
82 | comma_list( | ||
83 | &mut buf, "<", ">", | ||
84 | type_params.type_params() | ||
85 | .filter_map(|it| it.name()) | ||
86 | .map(|it| it.text()) | ||
87 | ); | ||
88 | } | ||
89 | buf.push_str(" {\n"); | ||
90 | let offset = start_offset + TextUnit::of_str(&buf); | ||
91 | buf.push_str("\n}"); | ||
92 | edit.insert(start_offset, buf); | ||
82 | ActionResult { | 93 | ActionResult { |
83 | edit: edit.finish(), | 94 | edit: edit.finish(), |
84 | cursor_position: Some( | 95 | cursor_position: Some(offset), |
85 | start_offset + TextUnit::of_str("\n\nimpl {\n") + name.syntax().range().len() | ||
86 | ), | ||
87 | } | 96 | } |
88 | }) | 97 | }) |
89 | } | 98 | } |
@@ -104,3 +113,26 @@ pub fn find_node<'a, N: AstNode<'a>>(syntax: SyntaxNodeRef<'a>, offset: TextUnit | |||
104 | .next() | 113 | .next() |
105 | } | 114 | } |
106 | 115 | ||
116 | fn comma_list(buf: &mut String, bra: &str, ket: &str, items: impl Iterator<Item=impl fmt::Display>) { | ||
117 | buf.push_str(bra); | ||
118 | let mut first = true; | ||
119 | for item in items { | ||
120 | if !first { | ||
121 | first = false; | ||
122 | buf.push_str(", "); | ||
123 | } | ||
124 | write!(buf, "{}", item).unwrap(); | ||
125 | } | ||
126 | buf.push_str(ket); | ||
127 | } | ||
128 | |||
129 | trait PushDisplay { | ||
130 | fn push_display<T: fmt::Display>(&mut self, item: &T); | ||
131 | } | ||
132 | |||
133 | impl PushDisplay for String { | ||
134 | fn push_display<T: fmt::Display>(&mut self, item: &T) { | ||
135 | use std::fmt::Write; | ||
136 | write!(self, "{}", item).unwrap() | ||
137 | } | ||
138 | } | ||
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 3b0ec78eb..42926ffc8 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs | |||
@@ -151,11 +151,11 @@ fn test_add_impl() { | |||
151 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", | 151 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", |
152 | |file, off| add_impl(file, off).map(|f| f()), | 152 | |file, off| add_impl(file, off).map(|f| f()), |
153 | ); | 153 | ); |
154 | // check_action( | 154 | check_action( |
155 | // "struct Foo<T: Clone> {<|>}", | 155 | "struct Foo<T: Clone> {<|>}", |
156 | // "struct Foo<T: Clone> {}\nimpl<T: Clone> Foo<T> {\n<|>\n}", | 156 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", |
157 | // |file, off| add_impl(file, off).map(|f| f()), | 157 | |file, off| add_impl(file, off).map(|f| f()), |
158 | // ); | 158 | ); |
159 | } | 159 | } |
160 | 160 | ||
161 | #[test] | 161 | #[test] |
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 6df1d2311..5edb9faaa 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs | |||
@@ -580,6 +580,25 @@ impl<'a> ast::TypeParamsOwner<'a> for TypeDef<'a> {} | |||
580 | impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {} | 580 | impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {} |
581 | impl<'a> TypeDef<'a> {} | 581 | impl<'a> TypeDef<'a> {} |
582 | 582 | ||
583 | // TypeParam | ||
584 | #[derive(Debug, Clone, Copy)] | ||
585 | pub struct TypeParam<'a> { | ||
586 | syntax: SyntaxNodeRef<'a>, | ||
587 | } | ||
588 | |||
589 | impl<'a> AstNode<'a> for TypeParam<'a> { | ||
590 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
591 | match syntax.kind() { | ||
592 | TYPE_PARAM => Some(TypeParam { syntax }), | ||
593 | _ => None, | ||
594 | } | ||
595 | } | ||
596 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
597 | } | ||
598 | |||
599 | impl<'a> ast::NameOwner<'a> for TypeParam<'a> {} | ||
600 | impl<'a> TypeParam<'a> {} | ||
601 | |||
583 | // TypeParamList | 602 | // TypeParamList |
584 | #[derive(Debug, Clone, Copy)] | 603 | #[derive(Debug, Clone, Copy)] |
585 | pub struct TypeParamList<'a> { | 604 | pub struct TypeParamList<'a> { |
@@ -596,7 +615,11 @@ impl<'a> AstNode<'a> for TypeParamList<'a> { | |||
596 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | 615 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } |
597 | } | 616 | } |
598 | 617 | ||
599 | impl<'a> TypeParamList<'a> {} | 618 | impl<'a> TypeParamList<'a> { |
619 | pub fn type_params(self) -> impl Iterator<Item = TypeParam<'a>> + 'a { | ||
620 | super::children(self) | ||
621 | } | ||
622 | } | ||
600 | 623 | ||
601 | // TypeRef | 624 | // TypeRef |
602 | #[derive(Debug, Clone, Copy)] | 625 | #[derive(Debug, Clone, Copy)] |
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index e56496be1..daf80dde3 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron | |||
@@ -310,7 +310,8 @@ Grammar( | |||
310 | "NameRef": (), | 310 | "NameRef": (), |
311 | "Attr": ( options: [ ["value", "TokenTree"] ] ), | 311 | "Attr": ( options: [ ["value", "TokenTree"] ] ), |
312 | "TokenTree": (), | 312 | "TokenTree": (), |
313 | "TypeParamList": (), | 313 | "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), |
314 | "TypeParam": ( traits: ["NameOwner"]), | ||
314 | "WhereClause": (), | 315 | "WhereClause": (), |
315 | }, | 316 | }, |
316 | ) | 317 | ) |
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 9ff821a8b..b47cbc0fc 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs | |||
@@ -110,6 +110,7 @@ pub fn handle_code_action( | |||
110 | let actions = &[ | 110 | let actions = &[ |
111 | (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), | 111 | (ActionId::FlipComma, libeditor::flip_comma(&file, offset).is_some()), |
112 | (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), | 112 | (ActionId::AddDerive, libeditor::add_derive(&file, offset).is_some()), |
113 | (ActionId::AddImpl, libeditor::add_impl(&file, offset).is_some()), | ||
113 | ]; | 114 | ]; |
114 | 115 | ||
115 | for (id, edit) in actions { | 116 | for (id, edit) in actions { |
@@ -218,6 +219,7 @@ pub fn handle_execute_command( | |||
218 | let action_result = match arg.id { | 219 | let action_result = match arg.id { |
219 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), | 220 | ActionId::FlipComma => libeditor::flip_comma(&file, arg.offset).map(|f| f()), |
220 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), | 221 | ActionId::AddDerive => libeditor::add_derive(&file, arg.offset).map(|f| f()), |
222 | ActionId::AddImpl => libeditor::add_impl(&file, arg.offset).map(|f| f()), | ||
221 | }.ok_or_else(|| format_err!("command not applicable"))?; | 223 | }.ok_or_else(|| format_err!("command not applicable"))?; |
222 | let line_index = world.analysis().file_line_index(file_id)?; | 224 | let line_index = world.analysis().file_line_index(file_id)?; |
223 | let mut changes = HashMap::new(); | 225 | let mut changes = HashMap::new(); |
@@ -259,6 +261,7 @@ fn apply_code_action_cmd(id: ActionId, doc: TextDocumentIdentifier, offset: Text | |||
259 | enum ActionId { | 261 | enum ActionId { |
260 | FlipComma, | 262 | FlipComma, |
261 | AddDerive, | 263 | AddDerive, |
264 | AddImpl, | ||
262 | } | 265 | } |
263 | 266 | ||
264 | impl ActionId { | 267 | impl ActionId { |
@@ -266,6 +269,7 @@ impl ActionId { | |||
266 | match *self { | 269 | match *self { |
267 | ActionId::FlipComma => "Flip `,`", | 270 | ActionId::FlipComma => "Flip `,`", |
268 | ActionId::AddDerive => "Add `#[derive]`", | 271 | ActionId::AddDerive => "Add `#[derive]`", |
272 | ActionId::AddImpl => "Add impl", | ||
269 | } | 273 | } |
270 | } | 274 | } |
271 | } | 275 | } |