mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Add some assist ranges
This commit is contained in:
parent
12e3b4c70b
commit
a3622eb629
6 changed files with 60 additions and 10 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1,3 +1,5 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "0.6.9"
|
version = "0.6.9"
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
}
|
}
|
||||||
Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
|
Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'),
|
||||||
};
|
};
|
||||||
|
edit.target(nominal.syntax().range());
|
||||||
edit.set_cursor(offset)
|
edit.set_cursor(offset)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub(crate) enum Assist {
|
||||||
|
|
||||||
/// `AssistCtx` allows to apply an assist or check if it could be applied.
|
/// `AssistCtx` allows to apply an assist or check if it could be applied.
|
||||||
///
|
///
|
||||||
/// Assists use a somewhat overengineered approach, given the current needs. The
|
/// Assists use a somewhat over-engineered approach, given the current needs. The
|
||||||
/// assists workflow consists of two phases. In the first phase, a user asks for
|
/// assists workflow consists of two phases. In the first phase, a user asks for
|
||||||
/// the list of available assists. In the second phase, the user picks a
|
/// the list of available assists. In the second phase, the user picks a
|
||||||
/// particular assist and it gets applied.
|
/// particular assist and it gets applied.
|
||||||
|
@ -106,6 +106,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
|
||||||
pub(crate) struct AssistBuilder {
|
pub(crate) struct AssistBuilder {
|
||||||
edit: TextEditBuilder,
|
edit: TextEditBuilder,
|
||||||
cursor_position: Option<TextUnit>,
|
cursor_position: Option<TextUnit>,
|
||||||
|
target: Option<TextRange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AssistBuilder {
|
impl AssistBuilder {
|
||||||
|
@ -138,7 +139,15 @@ impl AssistBuilder {
|
||||||
self.cursor_position = Some(offset)
|
self.cursor_position = Some(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn target(&mut self, target: TextRange) {
|
||||||
|
self.target = Some(target)
|
||||||
|
}
|
||||||
|
|
||||||
fn build(self) -> AssistAction {
|
fn build(self) -> AssistAction {
|
||||||
AssistAction { edit: self.edit.finish(), cursor_position: self.cursor_position }
|
AssistAction {
|
||||||
|
edit: self.edit.finish(),
|
||||||
|
cursor_position: self.cursor_position,
|
||||||
|
target: self.target,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
_ => false,
|
_ => false,
|
||||||
});
|
});
|
||||||
|
|
||||||
let offset = if let Some(keyword) = item_keyword {
|
let (offset, target) = if let Some(keyword) = item_keyword {
|
||||||
let parent = keyword.parent()?;
|
let parent = keyword.parent()?;
|
||||||
let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
|
let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
|
||||||
// Parent is not a definition, can't add visibility
|
// Parent is not a definition, can't add visibility
|
||||||
|
@ -31,17 +31,18 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
vis_offset(parent)
|
(vis_offset(parent), parent.range())
|
||||||
} else {
|
} else {
|
||||||
let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
||||||
let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
|
let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
|
||||||
if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
|
if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
vis_offset(field.syntax())
|
(vis_offset(field.syntax()), field.syntax().range())
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.build("make pub(crate)", |edit| {
|
ctx.build("make pub(crate)", |edit| {
|
||||||
|
edit.target(target);
|
||||||
edit.insert(offset, "pub(crate) ");
|
edit.insert(offset, "pub(crate) ");
|
||||||
edit.set_cursor(offset);
|
edit.set_cursor(offset);
|
||||||
})
|
})
|
||||||
|
@ -60,13 +61,15 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
|
||||||
|
|
||||||
fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
|
fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
|
||||||
if vis.syntax().text() == "pub" {
|
if vis.syntax().text() == "pub" {
|
||||||
return ctx.build("chage to pub(crate)", |edit| {
|
return ctx.build("change to pub(crate)", |edit| {
|
||||||
|
edit.target(vis.syntax().range());
|
||||||
edit.replace(vis.syntax().range(), "pub(crate)");
|
edit.replace(vis.syntax().range(), "pub(crate)");
|
||||||
edit.set_cursor(vis.syntax().range().start());
|
edit.set_cursor(vis.syntax().range().start());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if vis.syntax().text() == "pub(crate)" {
|
if vis.syntax().text() == "pub(crate)" {
|
||||||
return ctx.build("chage to pub", |edit| {
|
return ctx.build("change to pub", |edit| {
|
||||||
|
edit.target(vis.syntax().range());
|
||||||
edit.replace(vis.syntax().range(), "pub");
|
edit.replace(vis.syntax().range(), "pub");
|
||||||
edit.set_cursor(vis.syntax().range().start());
|
edit.set_cursor(vis.syntax().range().start());
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
mod assist_ctx;
|
mod assist_ctx;
|
||||||
|
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use ra_syntax::{TextUnit, SyntaxNode, Direction};
|
use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction};
|
||||||
use ra_db::FileRange;
|
use ra_db::FileRange;
|
||||||
use hir::db::HirDatabase;
|
use hir::db::HirDatabase;
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ pub struct AssistLabel {
|
||||||
pub struct AssistAction {
|
pub struct AssistAction {
|
||||||
pub edit: TextEdit,
|
pub edit: TextEdit,
|
||||||
pub cursor_position: Option<TextUnit>,
|
pub cursor_position: Option<TextUnit>,
|
||||||
|
pub target: Option<TextRange>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return all the assists applicable at the given position.
|
/// Return all the assists applicable at the given position.
|
||||||
|
@ -53,15 +54,26 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
|
||||||
where
|
where
|
||||||
H: HirDatabase + 'static,
|
H: HirDatabase + 'static,
|
||||||
{
|
{
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
AssistCtx::with_ctx(db, range, true, |ctx| {
|
AssistCtx::with_ctx(db, range, true, |ctx| {
|
||||||
all_assists()
|
let mut a = all_assists()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|f| f(ctx.clone()))
|
.filter_map(|f| f(ctx.clone()))
|
||||||
.map(|a| match a {
|
.map(|a| match a {
|
||||||
Assist::Resolved(label, action) => (label, action),
|
Assist::Resolved(label, action) => (label, action),
|
||||||
Assist::Unresolved(..) => unreachable!(),
|
Assist::Unresolved(..) => unreachable!(),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect::<Vec<(AssistLabel, AssistAction)>>();
|
||||||
|
a.sort_unstable_by(|a, b| match a {
|
||||||
|
// Some(y) < Some(x) < None for y < x
|
||||||
|
(_, AssistAction { target: Some(a), .. }) => match b {
|
||||||
|
(_, AssistAction { target: Some(b), .. }) => a.len().cmp(&b.len()),
|
||||||
|
_ => Ordering::Less,
|
||||||
|
},
|
||||||
|
_ => Ordering::Greater,
|
||||||
|
});
|
||||||
|
a
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,5 +174,27 @@ mod helpers {
|
||||||
let assist = AssistCtx::with_ctx(&db, frange, true, assist);
|
let assist = AssistCtx::with_ctx(&db, frange, true, assist);
|
||||||
assert!(assist.is_none());
|
assert!(assist.is_none());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use hir::mock::MockDatabase;
|
||||||
|
use ra_syntax::TextRange;
|
||||||
|
use ra_db::FileRange;
|
||||||
|
use test_utils::extract_offset;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assist_order() {
|
||||||
|
let before = "struct Foo { <|>bar: u32 }";
|
||||||
|
let (before_cursor_pos, before) = extract_offset(before);
|
||||||
|
let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
|
||||||
|
let frange =
|
||||||
|
FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
|
||||||
|
let assists = super::assists(&db, frange);
|
||||||
|
let mut assists = assists.iter();
|
||||||
|
|
||||||
|
assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
|
||||||
|
assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.build("split import", |edit| {
|
ctx.build("split import", |edit| {
|
||||||
|
edit.target(colon_colon.range());
|
||||||
edit.insert(l_curly, "{");
|
edit.insert(l_curly, "{");
|
||||||
edit.insert(r_curly, "}");
|
edit.insert(r_curly, "}");
|
||||||
edit.set_cursor(l_curly + TextUnit::of_str("{"));
|
edit.set_cursor(l_curly + TextUnit::of_str("{"));
|
||||||
|
|
Loading…
Reference in a new issue