mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 04:23:25 +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]]
|
||||
name = "aho-corasick"
|
||||
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(')'),
|
||||
};
|
||||
edit.target(nominal.syntax().range());
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
/// the list of available assists. In the second phase, the user picks a
|
||||
/// particular assist and it gets applied.
|
||||
|
@ -106,6 +106,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
|
|||
pub(crate) struct AssistBuilder {
|
||||
edit: TextEditBuilder,
|
||||
cursor_position: Option<TextUnit>,
|
||||
target: Option<TextRange>,
|
||||
}
|
||||
|
||||
impl AssistBuilder {
|
||||
|
@ -138,7 +139,15 @@ impl AssistBuilder {
|
|||
self.cursor_position = Some(offset)
|
||||
}
|
||||
|
||||
pub(crate) fn target(&mut self, target: TextRange) {
|
||||
self.target = Some(target)
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
let offset = if let Some(keyword) = item_keyword {
|
||||
let (offset, target) = if let Some(keyword) = item_keyword {
|
||||
let parent = keyword.parent()?;
|
||||
let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF];
|
||||
// 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) {
|
||||
return None;
|
||||
}
|
||||
vis_offset(parent)
|
||||
(vis_offset(parent), parent.range())
|
||||
} else {
|
||||
let ident = ctx.leaf_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
||||
let field = ident.ancestors().find_map(ast::NamedFieldDef::cast)?;
|
||||
if field.name()?.syntax().range() != ident.range() && field.visibility().is_some() {
|
||||
return None;
|
||||
}
|
||||
vis_offset(field.syntax())
|
||||
(vis_offset(field.syntax()), field.syntax().range())
|
||||
};
|
||||
|
||||
ctx.build("make pub(crate)", |edit| {
|
||||
edit.target(target);
|
||||
edit.insert(offset, "pub(crate) ");
|
||||
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> {
|
||||
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.set_cursor(vis.syntax().range().start());
|
||||
});
|
||||
}
|
||||
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.set_cursor(vis.syntax().range().start());
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
mod assist_ctx;
|
||||
|
||||
use ra_text_edit::TextEdit;
|
||||
use ra_syntax::{TextUnit, SyntaxNode, Direction};
|
||||
use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction};
|
||||
use ra_db::FileRange;
|
||||
use hir::db::HirDatabase;
|
||||
|
||||
|
@ -23,6 +23,7 @@ pub struct AssistLabel {
|
|||
pub struct AssistAction {
|
||||
pub edit: TextEdit,
|
||||
pub cursor_position: Option<TextUnit>,
|
||||
pub target: Option<TextRange>,
|
||||
}
|
||||
|
||||
/// 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
|
||||
H: HirDatabase + 'static,
|
||||
{
|
||||
use std::cmp::Ordering;
|
||||
|
||||
AssistCtx::with_ctx(db, range, true, |ctx| {
|
||||
all_assists()
|
||||
let mut a = all_assists()
|
||||
.iter()
|
||||
.filter_map(|f| f(ctx.clone()))
|
||||
.map(|a| match a {
|
||||
Assist::Resolved(label, action) => (label, action),
|
||||
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);
|
||||
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| {
|
||||
edit.target(colon_colon.range());
|
||||
edit.insert(l_curly, "{");
|
||||
edit.insert(r_curly, "}");
|
||||
edit.set_cursor(l_curly + TextUnit::of_str("{"));
|
||||
|
|
Loading…
Reference in a new issue