introduce variable
This commit is contained in:
parent
47e8b80e9b
commit
bb64edf8ba
@ -256,12 +256,14 @@ pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
|
||||
pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> {
|
||||
let file = self.file_syntax(file_id);
|
||||
let offset = range.start();
|
||||
let actions = vec![
|
||||
("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())),
|
||||
("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())),
|
||||
("add impl", libeditor::add_impl(&file, offset).map(|f| f())),
|
||||
("introduce variable", libeditor::introduce_variable(&file, range).map(|f| f())),
|
||||
];
|
||||
actions.into_iter()
|
||||
.filter_map(|(name, local_edit)| {
|
||||
|
@ -209,8 +209,8 @@ pub fn completions(&self, file_id: FileId, offset: TextUnit) -> Option<Vec<Compl
|
||||
let file = self.file_syntax(file_id);
|
||||
libeditor::scope_completion(&file, offset)
|
||||
}
|
||||
pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> {
|
||||
self.imp.assists(file_id, offset)
|
||||
pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> {
|
||||
self.imp.assists(file_id, range)
|
||||
}
|
||||
pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> {
|
||||
self.imp.diagnostics(file_id)
|
||||
|
@ -1,13 +1,15 @@
|
||||
use join_to_string::join;
|
||||
|
||||
use libsyntax2::{
|
||||
File, TextUnit,
|
||||
File, TextUnit, TextRange,
|
||||
ast::{self, AstNode, AttrsOwner, TypeParamsOwner, NameOwner},
|
||||
SyntaxKind::COMMA,
|
||||
SyntaxKind::{COMMA, WHITESPACE},
|
||||
SyntaxNodeRef,
|
||||
algo::{
|
||||
Direction, siblings,
|
||||
find_leaf_at_offset,
|
||||
find_covering_node,
|
||||
ancestors,
|
||||
},
|
||||
};
|
||||
|
||||
@ -97,6 +99,31 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() ->
|
||||
})
|
||||
}
|
||||
|
||||
pub fn introduce_variable<'a>(file: &'a File, range: TextRange) -> Option<impl FnOnce() -> LocalEdit + 'a> {
|
||||
let node = find_covering_node(file.syntax(), range);
|
||||
let expr = ancestors(node).filter_map(ast::Expr::cast).next()?;
|
||||
let anchor_stmt = ancestors(expr.syntax()).filter_map(ast::Stmt::cast).next()?;
|
||||
let indent = anchor_stmt.syntax().prev_sibling()?;
|
||||
if indent.kind() != WHITESPACE {
|
||||
return None;
|
||||
}
|
||||
Some(move || {
|
||||
let mut buf = String::new();
|
||||
buf.push_str("let var_name = ");
|
||||
expr.syntax().text().push_to(&mut buf);
|
||||
buf.push_str(";");
|
||||
indent.text().push_to(&mut buf);
|
||||
|
||||
let mut edit = EditBuilder::new();
|
||||
edit.replace(expr.syntax().range(), "var_name".to_string());
|
||||
edit.insert(anchor_stmt.syntax().range().start(), buf);
|
||||
LocalEdit {
|
||||
edit: edit.finish(),
|
||||
cursor_position: Some(anchor_stmt.syntax().range().start() + TextUnit::of_str("let ")),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> {
|
||||
siblings(node, direction)
|
||||
.skip(1)
|
||||
@ -106,7 +133,7 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_utils::check_action;
|
||||
use test_utils::{check_action, check_action_range};
|
||||
|
||||
#[test]
|
||||
fn test_swap_comma() {
|
||||
@ -155,4 +182,19 @@ fn test_add_impl() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_intrdoduce_var() {
|
||||
check_action_range(
|
||||
"
|
||||
fn foo() {
|
||||
foo(<|>1 + 1<|>);
|
||||
}", "
|
||||
fn foo() {
|
||||
let <|>var_name = 1 + 1;
|
||||
foo(var_name);
|
||||
}",
|
||||
|file, range| introduce_variable(file, range).map(|f| f()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
code_actions::{
|
||||
LocalEdit,
|
||||
flip_comma, add_derive, add_impl,
|
||||
introduce_variable,
|
||||
},
|
||||
typing::{join_lines, on_eq_typed},
|
||||
completion::{scope_completion, CompletionItem},
|
||||
|
@ -1,4 +1,4 @@
|
||||
use libsyntax2::{File, TextUnit};
|
||||
use libsyntax2::{File, TextUnit, TextRange};
|
||||
pub use _test_utils::*;
|
||||
use LocalEdit;
|
||||
|
||||
@ -18,3 +18,20 @@ pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> (
|
||||
let actual = add_cursor(&actual, actual_cursor_pos);
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
|
||||
pub fn check_action_range<F: Fn(&File, TextRange) -> Option<LocalEdit>> (
|
||||
before: &str,
|
||||
after: &str,
|
||||
f: F,
|
||||
) {
|
||||
let (range, before) = extract_range(before);
|
||||
let file = File::parse(&before);
|
||||
let result = f(&file, range).expect("code action is not applicable");
|
||||
let actual = result.edit.apply(&before);
|
||||
let actual_cursor_pos = match result.cursor_position {
|
||||
None => result.edit.apply_to_offset(range.start()).unwrap(),
|
||||
Some(off) => off,
|
||||
};
|
||||
let actual = add_cursor(&actual, actual_cursor_pos);
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
|
@ -372,12 +372,12 @@ pub fn handle_code_action(
|
||||
) -> Result<Option<Vec<Command>>> {
|
||||
let file_id = params.text_document.try_conv_with(&world)?;
|
||||
let line_index = world.analysis().file_line_index(file_id);
|
||||
let offset = params.range.conv_with(&line_index).start();
|
||||
let range = params.range.conv_with(&line_index);
|
||||
|
||||
let assists = world.analysis().assists(file_id, offset).into_iter();
|
||||
let assists = world.analysis().assists(file_id, range).into_iter();
|
||||
let fixes = world.analysis().diagnostics(file_id).into_iter()
|
||||
.filter_map(|d| Some((d.range, d.fix?)))
|
||||
.filter(|(range, _fix)| contains_offset_nonstrict(*range, offset))
|
||||
.filter(|(range, _fix)| contains_offset_nonstrict(*range, range.start()))
|
||||
.map(|(_range, fix)| fix);
|
||||
|
||||
let mut res = Vec::new();
|
||||
|
Loading…
Reference in New Issue
Block a user