-//! Completes lifetimes.
+//! Completes lifetimes and labels.
use hir::ScopeDef;
use crate::{completions::Completions, context::CompletionContext};
}
}
+/// Completes labels.
+pub(crate) fn complete_label(acc: &mut Completions, ctx: &CompletionContext) {
+ if !ctx.is_label_ref {
+ return;
+ }
+ ctx.scope.process_all_names(&mut |name, res| {
+ if let ScopeDef::Label(_) = res {
+ acc.add_resolution(ctx, name.to_string(), &res);
+ }
+ });
+}
+
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
"#]],
);
}
+
+ #[test]
+ fn complete_label_in_loop() {
+ check(
+ r#"
+fn foo() {
+ 'foop: loop {
+ break '$0
+ }
+}
+"#,
+ expect![[r#"
+ lb 'foop
+ "#]],
+ );
+ check(
+ r#"
+fn foo() {
+ 'foop: loop {
+ continue '$0
+ }
+}
+"#,
+ expect![[r#"
+ lb 'foop
+ "#]],
+ );
+ }
+
+ #[test]
+ fn complete_label_in_block_nested() {
+ check(
+ r#"
+fn foo() {
+ 'foop: {
+ 'baap: {
+ break '$0
+ }
+ }
+}
+"#,
+ expect![[r#"
+ lb 'baap
+ lb 'foop
+ "#]],
+ );
+ }
+
+ #[test]
+ fn complete_label_in_loop_with_value() {
+ check(
+ r#"
+fn foo() {
+ 'foop: loop {
+ break '$0 i32;
+ }
+}
+"#,
+ expect![[r#"
+ lb 'foop
+ "#]],
+ );
+ }
}
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
pub(super) active_parameter: Option<ActiveParameter>,
pub(super) is_param: bool,
+ pub(super) is_label_ref: bool,
/// If a name-binding or reference to a const in a pattern.
/// Irrefutable patterns (like let) are excluded.
pub(super) is_pat_binding_or_const: bool,
record_field_syntax: None,
impl_def: None,
active_parameter: ActiveParameter::at(db, position),
+ is_label_ref: false,
is_param: false,
is_pat_binding_or_const: false,
is_irrefutable_pat_binding: false,
) {
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);
+ if let Some(parent) = lifetime.syntax().parent() {
+ if parent.kind() == syntax::SyntaxKind::ERROR {
+ return;
+ }
+
+ if parent.kind() != syntax::SyntaxKind::LABEL {
+ match_ast! {
+ match parent {
+ ast::LifetimeParam(_it) => {
+ self.lifetime_allowed = true;
+ self.lifetime_param_syntax =
+ self.sema.find_node_at_offset_with_macros(original_file, offset);
+ },
+ ast::BreakExpr(_it) => self.is_label_ref = true,
+ ast::ContinueExpr(_it) => self.is_label_ref = true,
+ ast::Label(_it) => (),
+ _ => self.lifetime_allowed = true,
+ }
+ }
+ }
}
}
hir::GenericParam::ConstParam(_) => SymbolKind::ConstParam,
}),
ScopeDef::Local(..) => CompletionItemKind::SymbolKind(SymbolKind::Local),
+ ScopeDef::Label(..) => CompletionItemKind::SymbolKind(SymbolKind::Label),
ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => {
CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
}