2021-09-13 13:43:13 +03:00
|
|
|
//! See [`complete_fn_param`].
|
2019-09-30 11:58:53 +03:00
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
use hir::HirDisplay;
|
2022-04-25 18:51:59 +02:00
|
|
|
use ide_db::FxHashMap;
|
2020-08-12 18:26:51 +02:00
|
|
|
use syntax::{
|
2022-01-31 11:56:42 +01:00
|
|
|
algo,
|
2021-09-27 12:54:24 +02:00
|
|
|
ast::{self, HasModuleItem},
|
2022-04-20 17:56:20 +09:00
|
|
|
match_ast, AstNode, Direction, SyntaxKind, TextRange,
|
2020-04-09 23:02:10 +02:00
|
|
|
};
|
2019-01-08 22:33:36 +03:00
|
|
|
|
2021-08-14 18:18:18 +02:00
|
|
|
use crate::{
|
2021-08-14 19:06:35 +02:00
|
|
|
context::{ParamKind, PatternContext},
|
2021-10-27 17:18:42 +02:00
|
|
|
CompletionContext, CompletionItem, CompletionItemKind, Completions,
|
2021-08-14 18:18:18 +02:00
|
|
|
};
|
2019-01-08 22:33:36 +03:00
|
|
|
|
2022-05-10 15:00:58 +02:00
|
|
|
// FIXME: Make this a submodule of [`pattern`]
|
2019-02-11 17:18:27 +01:00
|
|
|
/// Complete repeated parameters, both name and type. For example, if all
|
2019-01-08 22:33:36 +03:00
|
|
|
/// functions in a file have a `spam: &mut Spam` parameter, a completion with
|
2022-04-27 18:54:57 +09:00
|
|
|
/// `spam: &mut Spam` insert text/label will be suggested.
|
2022-01-31 11:56:42 +01:00
|
|
|
///
|
|
|
|
/// Also complete parameters for closure or local functions from the surrounding defined locals.
|
2021-07-10 15:12:16 +05:30
|
|
|
pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
|
2022-01-31 11:56:42 +01:00
|
|
|
let (param_list, _, param_kind) = match &ctx.pattern_ctx {
|
|
|
|
Some(PatternContext { param_ctx: Some(kind), .. }) => kind,
|
|
|
|
_ => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let comma_wrapper = comma_wrapper(ctx);
|
2022-04-27 18:54:57 +09:00
|
|
|
let mut add_new_item_to_acc = |label: &str| {
|
2022-04-20 17:56:20 +09:00
|
|
|
let mk_item = |label: &str, range: TextRange| {
|
|
|
|
CompletionItem::new(CompletionItemKind::Binding, range, label)
|
2022-01-31 11:56:42 +01:00
|
|
|
};
|
2022-04-22 00:07:42 +09:00
|
|
|
let item = match &comma_wrapper {
|
2022-04-27 18:54:57 +09:00
|
|
|
Some((fmt, range)) => mk_item(&fmt(label), *range),
|
|
|
|
None => mk_item(label, ctx.source_range()),
|
2022-01-31 11:56:42 +01:00
|
|
|
};
|
2022-04-27 18:54:57 +09:00
|
|
|
// Completion lookup is omitted intentionally here.
|
|
|
|
// See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073
|
2022-01-31 11:56:42 +01:00
|
|
|
item.add_to(acc)
|
|
|
|
};
|
2022-01-04 15:03:46 -03:00
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
match param_kind {
|
|
|
|
ParamKind::Function(function) => {
|
2022-03-12 13:04:13 +01:00
|
|
|
fill_fn_params(ctx, function, param_list, add_new_item_to_acc);
|
2022-01-31 11:56:42 +01:00
|
|
|
}
|
|
|
|
ParamKind::Closure(closure) => {
|
|
|
|
let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
|
|
|
|
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
|
2022-04-27 18:54:57 +09:00
|
|
|
add_new_item_to_acc(&format!("{name}: {ty}"));
|
2022-01-31 11:56:42 +01:00
|
|
|
});
|
|
|
|
}
|
2019-01-08 22:33:36 +03:00
|
|
|
}
|
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
Some(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fill_fn_params(
|
|
|
|
ctx: &CompletionContext,
|
|
|
|
function: &ast::Fn,
|
|
|
|
param_list: &ast::ParamList,
|
2022-04-27 18:54:57 +09:00
|
|
|
mut add_new_item_to_acc: impl FnMut(&str),
|
2022-01-31 11:56:42 +01:00
|
|
|
) {
|
2022-01-04 15:03:46 -03:00
|
|
|
let mut file_params = FxHashMap::default();
|
2020-07-30 11:42:51 +02:00
|
|
|
|
2022-01-04 15:03:46 -03:00
|
|
|
let mut extract_params = |f: ast::Fn| {
|
|
|
|
f.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
|
2021-03-03 21:58:48 +01:00
|
|
|
if let Some(pat) = param.pat() {
|
2021-11-05 12:30:39 +01:00
|
|
|
// FIXME: We should be able to turn these into SmolStr without having to allocate a String
|
2022-01-04 15:03:46 -03:00
|
|
|
let whole_param = param.syntax().text().to_string();
|
|
|
|
let binding = pat.syntax().text().to_string();
|
|
|
|
file_params.entry(whole_param).or_insert(binding);
|
2021-03-03 21:58:48 +01:00
|
|
|
}
|
|
|
|
});
|
2020-07-30 11:42:51 +02:00
|
|
|
};
|
|
|
|
|
2021-01-30 18:19:21 +03:00
|
|
|
for node in ctx.token.ancestors() {
|
2020-07-30 11:42:51 +02:00
|
|
|
match_ast! {
|
2019-10-05 17:03:03 +03:00
|
|
|
match node {
|
2020-07-30 11:42:51 +02:00
|
|
|
ast::SourceFile(it) => it.items().filter_map(|item| match item {
|
2020-07-30 14:51:08 +02:00
|
|
|
ast::Item::Fn(it) => Some(it),
|
2020-07-30 11:42:51 +02:00
|
|
|
_ => None,
|
2022-01-04 15:03:46 -03:00
|
|
|
}).for_each(&mut extract_params),
|
2020-07-30 11:42:51 +02:00
|
|
|
ast::ItemList(it) => it.items().filter_map(|item| match item {
|
2020-07-30 14:51:08 +02:00
|
|
|
ast::Item::Fn(it) => Some(it),
|
2020-07-30 11:42:51 +02:00
|
|
|
_ => None,
|
2022-01-04 15:03:46 -03:00
|
|
|
}).for_each(&mut extract_params),
|
2020-07-30 11:42:51 +02:00
|
|
|
ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
|
2020-07-30 14:51:08 +02:00
|
|
|
ast::AssocItem::Fn(it) => Some(it),
|
2020-07-30 11:42:51 +02:00
|
|
|
_ => None,
|
2022-01-04 15:03:46 -03:00
|
|
|
}).for_each(&mut extract_params),
|
2020-04-09 23:02:10 +02:00
|
|
|
_ => continue,
|
|
|
|
}
|
|
|
|
};
|
2019-01-08 22:33:36 +03:00
|
|
|
}
|
2020-07-10 16:29:14 +02:00
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) {
|
|
|
|
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
|
|
|
|
file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string());
|
|
|
|
});
|
|
|
|
}
|
2022-01-04 19:30:57 -03:00
|
|
|
remove_duplicated(&mut file_params, param_list.params());
|
2021-07-10 15:12:16 +05:30
|
|
|
let self_completion_items = ["self", "&self", "mut self", "&mut self"];
|
2022-01-04 15:03:46 -03:00
|
|
|
if should_add_self_completions(ctx, param_list) {
|
2022-04-27 18:54:57 +09:00
|
|
|
self_completion_items.into_iter().for_each(|self_item| add_new_item_to_acc(self_item));
|
2021-07-10 15:12:16 +05:30
|
|
|
}
|
|
|
|
|
2022-04-27 18:54:57 +09:00
|
|
|
file_params.keys().for_each(|whole_param| add_new_item_to_acc(whole_param));
|
2022-01-31 11:56:42 +01:00
|
|
|
}
|
2021-07-10 15:12:16 +05:30
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
fn params_from_stmt_list_scope(
|
|
|
|
ctx: &CompletionContext,
|
|
|
|
stmt_list: ast::StmtList,
|
|
|
|
mut cb: impl FnMut(hir::Name, String),
|
|
|
|
) {
|
|
|
|
let syntax_node = match stmt_list.syntax().last_child() {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return,
|
|
|
|
};
|
2022-03-31 11:12:08 +02:00
|
|
|
if let Some(scope) =
|
|
|
|
ctx.sema.scope_at_offset(stmt_list.syntax(), syntax_node.text_range().end())
|
|
|
|
{
|
|
|
|
let module = scope.module().into();
|
|
|
|
scope.process_all_names(&mut |name, def| {
|
|
|
|
if let hir::ScopeDef::Local(local) = def {
|
|
|
|
if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) {
|
|
|
|
cb(name, ty);
|
|
|
|
}
|
2022-01-31 11:56:42 +01:00
|
|
|
}
|
2022-03-31 11:12:08 +02:00
|
|
|
});
|
|
|
|
}
|
2021-07-10 15:12:16 +05:30
|
|
|
}
|
|
|
|
|
2022-01-04 15:03:46 -03:00
|
|
|
fn remove_duplicated(
|
|
|
|
file_params: &mut FxHashMap<String, String>,
|
2022-01-04 19:30:57 -03:00
|
|
|
fn_params: ast::AstChildren<ast::Param>,
|
|
|
|
) {
|
|
|
|
fn_params.for_each(|param| {
|
2022-01-04 15:03:46 -03:00
|
|
|
let whole_param = param.syntax().text().to_string();
|
|
|
|
file_params.remove(&whole_param);
|
|
|
|
|
2022-01-12 12:36:46 +01:00
|
|
|
match param.pat() {
|
|
|
|
// remove suggestions for patterns that already exist
|
|
|
|
// if the type is missing we are checking the current param to be completed
|
|
|
|
// in which case this would find itself removing the suggestions due to itself
|
|
|
|
Some(pattern) if param.ty().is_some() => {
|
|
|
|
let binding = pattern.syntax().text().to_string();
|
|
|
|
file_params.retain(|_, v| v != &binding);
|
|
|
|
}
|
|
|
|
_ => (),
|
2022-01-04 19:30:57 -03:00
|
|
|
}
|
2022-01-04 15:03:46 -03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
fn should_add_self_completions(ctx: &CompletionContext, param_list: &ast::ParamList) -> bool {
|
2022-01-04 15:03:46 -03:00
|
|
|
let inside_impl = ctx.impl_def.is_some();
|
|
|
|
let no_params = param_list.params().next().is_none() && param_list.self_param().is_none();
|
|
|
|
|
|
|
|
inside_impl && no_params
|
|
|
|
}
|
|
|
|
|
2022-04-27 18:54:57 +09:00
|
|
|
fn comma_wrapper(ctx: &CompletionContext) -> Option<(impl Fn(&str) -> String, TextRange)> {
|
2022-04-20 17:56:20 +09:00
|
|
|
let param = ctx.token.ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?;
|
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
let next_token_kind = {
|
2022-04-20 17:56:20 +09:00
|
|
|
let t = param.last_token()?.next_token()?;
|
2022-01-31 11:56:42 +01:00
|
|
|
let t = algo::skip_whitespace_token(t, Direction::Next)?;
|
|
|
|
t.kind()
|
2022-01-04 15:30:30 -03:00
|
|
|
};
|
2022-01-31 11:56:42 +01:00
|
|
|
let prev_token_kind = {
|
2022-04-20 17:56:20 +09:00
|
|
|
let t = param.first_token()?.prev_token()?;
|
2022-01-31 11:56:42 +01:00
|
|
|
let t = algo::skip_whitespace_token(t, Direction::Prev)?;
|
|
|
|
t.kind()
|
2022-01-04 15:03:46 -03:00
|
|
|
};
|
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
let has_trailing_comma =
|
|
|
|
matches!(next_token_kind, SyntaxKind::COMMA | SyntaxKind::R_PAREN | SyntaxKind::PIPE);
|
|
|
|
let trailing = if has_trailing_comma { "" } else { "," };
|
2022-01-04 15:03:46 -03:00
|
|
|
|
2022-01-31 11:56:42 +01:00
|
|
|
let has_leading_comma =
|
|
|
|
matches!(prev_token_kind, SyntaxKind::COMMA | SyntaxKind::L_PAREN | SyntaxKind::PIPE);
|
|
|
|
let leading = if has_leading_comma { "" } else { ", " };
|
2022-01-04 15:03:46 -03:00
|
|
|
|
2022-04-27 18:54:57 +09:00
|
|
|
Some((move |label: &_| (format!("{}{}{}", leading, label, trailing)), param.text_range()))
|
2019-01-08 22:33:36 +03:00
|
|
|
}
|