mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-29 11:37:39 +00:00
Add basic lifetime completion
This commit is contained in:
parent
5cc8ad0c4a
commit
3c000c6364
@ -2,25 +2,27 @@
|
|||||||
|
|
||||||
pub(crate) mod attribute;
|
pub(crate) mod attribute;
|
||||||
pub(crate) mod dot;
|
pub(crate) mod dot;
|
||||||
pub(crate) mod record;
|
pub(crate) mod flyimport;
|
||||||
pub(crate) mod pattern;
|
|
||||||
pub(crate) mod fn_param;
|
pub(crate) mod fn_param;
|
||||||
pub(crate) mod keyword;
|
pub(crate) mod keyword;
|
||||||
pub(crate) mod snippet;
|
pub(crate) mod lifetime;
|
||||||
pub(crate) mod qualified_path;
|
|
||||||
pub(crate) mod unqualified_path;
|
|
||||||
pub(crate) mod postfix;
|
|
||||||
pub(crate) mod macro_in_item_position;
|
pub(crate) mod macro_in_item_position;
|
||||||
pub(crate) mod trait_impl;
|
|
||||||
pub(crate) mod mod_;
|
pub(crate) mod mod_;
|
||||||
pub(crate) mod flyimport;
|
pub(crate) mod pattern;
|
||||||
|
pub(crate) mod postfix;
|
||||||
|
pub(crate) mod qualified_path;
|
||||||
|
pub(crate) mod record;
|
||||||
|
pub(crate) mod snippet;
|
||||||
|
pub(crate) mod trait_impl;
|
||||||
|
pub(crate) mod unqualified_path;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use hir::{known, ModPath, ScopeDef, Type};
|
use hir::{known, ModPath, ScopeDef, Type};
|
||||||
|
use ide_db::SymbolKind;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
item::Builder,
|
item::{Builder, CompletionKind},
|
||||||
render::{
|
render::{
|
||||||
const_::render_const,
|
const_::render_const,
|
||||||
enum_variant::render_variant,
|
enum_variant::render_variant,
|
||||||
@ -31,7 +33,7 @@ use crate::{
|
|||||||
type_alias::render_type_alias,
|
type_alias::render_type_alias,
|
||||||
RenderContext,
|
RenderContext,
|
||||||
},
|
},
|
||||||
CompletionContext, CompletionItem,
|
CompletionContext, CompletionItem, CompletionItemKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Represents an in-progress set of completions being built.
|
/// Represents an in-progress set of completions being built.
|
||||||
@ -77,6 +79,13 @@ impl Completions {
|
|||||||
self.add(item);
|
self.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
|
||||||
|
let mut item =
|
||||||
|
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
|
||||||
|
item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
|
||||||
|
self.add(item.build());
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn add_resolution(
|
pub(crate) fn add_resolution(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
181
crates/ide_completion/src/completions/lifetime.rs
Normal file
181
crates/ide_completion/src/completions/lifetime.rs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
//! Completes lifetimes.
|
||||||
|
use hir::ScopeDef;
|
||||||
|
|
||||||
|
use crate::{completions::Completions, context::CompletionContext};
|
||||||
|
|
||||||
|
/// Completes lifetimes.
|
||||||
|
pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
|
if !ctx.lifetime_allowed {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let param_lifetime = match (
|
||||||
|
&ctx.lifetime_syntax,
|
||||||
|
ctx.lifetime_param_syntax.as_ref().and_then(|lp| lp.lifetime()),
|
||||||
|
) {
|
||||||
|
(Some(lt), Some(lp)) if lp == lt.clone() => return,
|
||||||
|
(Some(_), Some(lp)) => Some(lp.to_string()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.scope.process_all_names(&mut |name, res| {
|
||||||
|
if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
|
||||||
|
if param_lifetime != Some(name.to_string()) {
|
||||||
|
acc.add_resolution(ctx, name.to_string(), &res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if param_lifetime.is_none() {
|
||||||
|
acc.add_static_lifetime(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use expect_test::{expect, Expect};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
test_utils::{check_edit, completion_list_with_config, TEST_CONFIG},
|
||||||
|
CompletionConfig, CompletionKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
|
check_with_config(TEST_CONFIG, ra_fixture, expect);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) {
|
||||||
|
let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference);
|
||||||
|
expect.assert_eq(&actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_lifetime_edit() {
|
||||||
|
check_edit(
|
||||||
|
"'lifetime",
|
||||||
|
r#"
|
||||||
|
fn func<'lifetime>(foo: &'li$0) {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn func<'lifetime>(foo: &'lifetime) {}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_ref() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo<'lifetime>(foo: &'a$0 usize) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_ref_missing_ty() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo<'lifetime>(foo: &'a$0) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_self_ref() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl<'impl> Foo {
|
||||||
|
fn foo<'func>(&'a$0 self) {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'func
|
||||||
|
lt 'impl
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_arg_list() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<'lt>;
|
||||||
|
fn foo<'lifetime>(_: Foo<'a$0>) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_where_pred() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo2<'lifetime, T>() where 'a$0 {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_ty_bound() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo2<'lifetime, T>() where T: 'a$0 {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo2<'lifetime, T>() where T: Trait<'a$0> {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'lifetime
|
||||||
|
lt 'static
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dont_complete_lifetime_in_assoc_ty_bound() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo2<'lifetime, T>() where T: Trait<Item = 'a$0> {}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn complete_lifetime_in_param_list() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo<'a$0>() {}
|
||||||
|
"#,
|
||||||
|
expect![[r#""#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo<'footime, 'lifetime: 'a$0>() {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
lt 'footime
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
//! Completes constats and paths in patterns.
|
//! Completes constants and paths in patterns.
|
||||||
|
|
||||||
use crate::{CompletionContext, Completions};
|
use crate::{CompletionContext, Completions};
|
||||||
|
|
||||||
|
@ -38,12 +38,15 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
pub(super) expected_name: Option<String>,
|
pub(super) expected_name: Option<String>,
|
||||||
pub(super) expected_type: Option<Type>,
|
pub(super) expected_type: Option<Type>,
|
||||||
pub(super) name_ref_syntax: Option<ast::NameRef>,
|
pub(super) name_ref_syntax: Option<ast::NameRef>,
|
||||||
|
pub(super) lifetime_syntax: Option<ast::Lifetime>,
|
||||||
|
pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
|
||||||
pub(super) function_syntax: Option<ast::Fn>,
|
pub(super) function_syntax: Option<ast::Fn>,
|
||||||
pub(super) use_item_syntax: Option<ast::Use>,
|
pub(super) use_item_syntax: Option<ast::Use>,
|
||||||
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
||||||
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
||||||
pub(super) record_field_syntax: Option<ast::RecordExprField>,
|
pub(super) record_field_syntax: Option<ast::RecordExprField>,
|
||||||
pub(super) impl_def: Option<ast::Impl>,
|
pub(super) impl_def: Option<ast::Impl>,
|
||||||
|
pub(super) lifetime_allowed: bool,
|
||||||
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
|
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
|
||||||
pub(super) active_parameter: Option<ActiveParameter>,
|
pub(super) active_parameter: Option<ActiveParameter>,
|
||||||
pub(super) is_param: bool,
|
pub(super) is_param: bool,
|
||||||
@ -136,9 +139,12 @@ impl<'a> CompletionContext<'a> {
|
|||||||
original_token,
|
original_token,
|
||||||
token,
|
token,
|
||||||
krate,
|
krate,
|
||||||
|
lifetime_allowed: false,
|
||||||
expected_name: None,
|
expected_name: None,
|
||||||
expected_type: None,
|
expected_type: None,
|
||||||
name_ref_syntax: None,
|
name_ref_syntax: None,
|
||||||
|
lifetime_syntax: None,
|
||||||
|
lifetime_param_syntax: None,
|
||||||
function_syntax: None,
|
function_syntax: None,
|
||||||
use_item_syntax: None,
|
use_item_syntax: None,
|
||||||
record_lit_syntax: None,
|
record_lit_syntax: None,
|
||||||
@ -241,7 +247,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
pub(crate) fn source_range(&self) -> TextRange {
|
pub(crate) fn source_range(&self) -> TextRange {
|
||||||
// check kind of macro-expanded token, but use range of original token
|
// check kind of macro-expanded token, but use range of original token
|
||||||
let kind = self.token.kind();
|
let kind = self.token.kind();
|
||||||
if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() {
|
if kind == IDENT || kind == LIFETIME_IDENT || kind == UNDERSCORE || kind.is_keyword() {
|
||||||
cov_mark::hit!(completes_if_prefix_is_keyword);
|
cov_mark::hit!(completes_if_prefix_is_keyword);
|
||||||
self.original_token.text_range()
|
self.original_token.text_range()
|
||||||
} else {
|
} else {
|
||||||
@ -386,6 +392,11 @@ impl<'a> CompletionContext<'a> {
|
|||||||
self.expected_name = expected.1;
|
self.expected_name = expected.1;
|
||||||
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
|
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
|
||||||
|
|
||||||
|
if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
|
||||||
|
{
|
||||||
|
self.classify_lifetime(original_file, lifetime, offset);
|
||||||
|
}
|
||||||
|
|
||||||
// First, let's try to complete a reference to some declaration.
|
// First, let's try to complete a reference to some declaration.
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
|
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
|
||||||
// Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
|
// Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
|
||||||
@ -445,6 +456,23 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn classify_lifetime(
|
||||||
|
&mut self,
|
||||||
|
original_file: &SyntaxNode,
|
||||||
|
lifetime: ast::Lifetime,
|
||||||
|
offset: TextSize,
|
||||||
|
) {
|
||||||
|
self.lifetime_syntax =
|
||||||
|
find_node_at_offset(original_file, lifetime.syntax().text_range().start());
|
||||||
|
if lifetime.syntax().parent().map_or(false, |p| p.kind() != syntax::SyntaxKind::ERROR) {
|
||||||
|
self.lifetime_allowed = true;
|
||||||
|
}
|
||||||
|
if let Some(_) = lifetime.syntax().parent().and_then(ast::LifetimeParam::cast) {
|
||||||
|
self.lifetime_param_syntax =
|
||||||
|
self.sema.find_node_at_offset_with_macros(original_file, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn classify_name_ref(
|
fn classify_name_ref(
|
||||||
&mut self,
|
&mut self,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
@ -452,11 +480,11 @@ impl<'a> CompletionContext<'a> {
|
|||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) {
|
) {
|
||||||
self.name_ref_syntax =
|
self.name_ref_syntax =
|
||||||
find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
|
find_node_at_offset(original_file, name_ref.syntax().text_range().start());
|
||||||
let name_range = name_ref.syntax().text_range();
|
let name_range = name_ref.syntax().text_range();
|
||||||
if ast::RecordExprField::for_field_name(&name_ref).is_some() {
|
if ast::RecordExprField::for_field_name(&name_ref).is_some() {
|
||||||
self.record_lit_syntax =
|
self.record_lit_syntax =
|
||||||
self.sema.find_node_at_offset_with_macros(&original_file, offset);
|
self.sema.find_node_at_offset_with_macros(original_file, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fill_impl_def();
|
self.fill_impl_def();
|
||||||
|
@ -130,6 +130,7 @@ pub fn completions(
|
|||||||
completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
|
completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
|
||||||
completions::mod_::complete_mod(&mut acc, &ctx);
|
completions::mod_::complete_mod(&mut acc, &ctx);
|
||||||
completions::flyimport::import_on_the_fly(&mut acc, &ctx);
|
completions::flyimport::import_on_the_fly(&mut acc, &ctx);
|
||||||
|
completions::lifetime::complete_lifetime(&mut acc, &ctx);
|
||||||
|
|
||||||
Some(acc)
|
Some(acc)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user