]> git.lizzy.rs Git - rust.git/commitdiff
Add label completion
authorLukas Wirth <lukastw97@gmail.com>
Sun, 21 Mar 2021 00:00:09 +0000 (01:00 +0100)
committerLukas Wirth <lukastw97@gmail.com>
Sun, 21 Mar 2021 00:02:22 +0000 (01:02 +0100)
crates/ide_completion/src/completions/lifetime.rs
crates/ide_completion/src/context.rs
crates/ide_completion/src/lib.rs
crates/ide_completion/src/render.rs

index 74eb233602a89ab100f5aa86d278286ca4d1e955..07be28e9c41f72003d9508e6a33a5965a97f73b9 100644 (file)
@@ -1,4 +1,4 @@
-//! Completes lifetimes.
+//! Completes lifetimes and labels.
 use hir::ScopeDef;
 
 use crate::{completions::Completions, context::CompletionContext};
@@ -29,6 +29,18 @@ pub(crate) fn complete_lifetime(acc: &mut Completions, ctx: &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};
@@ -178,4 +190,67 @@ fn foo<'footime, 'lifetime: 'a$0>() {}
             "#]],
         );
     }
+
+    #[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
+            "#]],
+        );
+    }
 }
index 4c2b31084d66fdb39dcd317edeac0ca84aa2fbae..6cb7e526454a935ab3cc3ebcd0820907abcf53a1 100644 (file)
@@ -53,6 +53,7 @@ pub(crate) struct CompletionContext<'a> {
     /// 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,
@@ -155,6 +156,7 @@ pub(super) fn new(
             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,
@@ -468,12 +470,26 @@ fn classify_lifetime(
     ) {
         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,
+                    }
+                }
+            }
         }
     }
 
index 7a0eb6a96623298ec3a6658146971723ca9edfdd..995970fcab8cc5c8be15b966c776b4c7300390b5 100644 (file)
@@ -131,6 +131,7 @@ pub fn completions(
     completions::mod_::complete_mod(&mut acc, &ctx);
     completions::flyimport::import_on_the_fly(&mut acc, &ctx);
     completions::lifetime::complete_lifetime(&mut acc, &ctx);
+    completions::lifetime::complete_label(&mut acc, &ctx);
 
     Some(acc)
 }
index 2b6e9ebd1f84f92f4803f120be655212b0d4c741..23e00aa471a359c3a390b1a8393fa1d5570eedcc 100644 (file)
@@ -219,6 +219,7 @@ fn render_resolution(
                 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)
             }