aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libeditor/src/code_actions.rs68
-rw-r--r--crates/libeditor/tests/test.rs10
-rw-r--r--crates/libsyntax2/src/ast/generated.rs25
-rw-r--r--crates/libsyntax2/src/grammar.ron3
-rw-r--r--crates/server/src/main_loop/handlers.rs4
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 @@
1use {TextUnit, EditBuilder, Edit}; 1use std::{
2 fmt::{self, Write},
3};
4
2use libsyntax2::{ 5use 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
15use {TextUnit, EditBuilder, Edit};
16
12pub struct ActionResult { 17pub 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
116fn 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
129trait PushDisplay {
130 fn push_display<T: fmt::Display>(&mut self, item: &T);
131}
132
133impl 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> {}
580impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {} 580impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {}
581impl<'a> TypeDef<'a> {} 581impl<'a> TypeDef<'a> {}
582 582
583// TypeParam
584#[derive(Debug, Clone, Copy)]
585pub struct TypeParam<'a> {
586 syntax: SyntaxNodeRef<'a>,
587}
588
589impl<'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
599impl<'a> ast::NameOwner<'a> for TypeParam<'a> {}
600impl<'a> TypeParam<'a> {}
601
583// TypeParamList 602// TypeParamList
584#[derive(Debug, Clone, Copy)] 603#[derive(Debug, Clone, Copy)]
585pub struct TypeParamList<'a> { 604pub 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
599impl<'a> TypeParamList<'a> {} 618impl<'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
259enum ActionId { 261enum ActionId {
260 FlipComma, 262 FlipComma,
261 AddDerive, 263 AddDerive,
264 AddImpl,
262} 265}
263 266
264impl ActionId { 267impl 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}