7030: Support labels in reference search r=matklad a=Veykril

Implements general navigation for labels, goto def, rename and gives labels their own semantic highlighting class.

Fixes #6966

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2020-12-24 14:46:41 +00:00 committed by GitHub
commit 581419fd78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 142 additions and 14 deletions

View file

@ -5,7 +5,7 @@ use std::fmt;
use either::Either;
use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource};
use ide_db::{
base_db::{FileId, SourceDatabase},
base_db::{FileId, FileRange, SourceDatabase},
symbol_index::FileSymbolKind,
};
use ide_db::{defs::Definition, RootDatabase};
@ -28,6 +28,7 @@ pub enum SymbolKind {
ValueParam,
SelfParam,
Local,
Label,
Function,
Const,
Static,
@ -223,6 +224,7 @@ impl TryToNav for Definition {
Definition::Local(it) => Some(it.to_nav(db)),
Definition::TypeParam(it) => Some(it.to_nav(db)),
Definition::LifetimeParam(it) => Some(it.to_nav(db)),
Definition::Label(it) => Some(it.to_nav(db)),
}
}
}
@ -421,6 +423,27 @@ impl ToNav for hir::Local {
}
}
impl ToNav for hir::Label {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
let src = self.source(db);
let node = src.value.syntax();
let FileRange { file_id, range } = src.with_value(node).original_file_range(db);
let focus_range =
src.value.lifetime().and_then(|lt| lt.lifetime_ident_token()).map(|lt| lt.text_range());
let name = self.name(db).to_string().into();
NavigationTarget {
file_id,
name,
kind: Some(SymbolKind::Label),
full_range: range,
focus_range,
container_name: None,
description: None,
docs: None,
}
}
}
impl ToNav for hir::TypeParam {
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
let src = self.source(db);

View file

@ -193,7 +193,8 @@ fn rewrite_intra_doc_link(
Definition::SelfType(_)
| Definition::Local(_)
| Definition::TypeParam(_)
| Definition::LifetimeParam(_) => return None,
| Definition::LifetimeParam(_)
| Definition::Label(_) => return None,
}?;
let krate = resolved.module(db)?.krate();
let canonical_path = resolved.canonical_path(db)?;

View file

@ -1130,4 +1130,19 @@ fn foo<T>() where T: for<'a> Foo<&'a<|> (u8, u16)>, {}
"#,
);
}
#[test]
fn goto_label() {
check(
r#"
fn foo<'foo>(_: &'foo ()) {
'foo: {
//^^^^
'bar: loop {
break 'foo<|>;
}
}
}"#,
)
}
}

View file

@ -370,7 +370,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
Adt::Enum(it) => from_def_source(db, it, mod_path),
})
}
Definition::TypeParam(_) | Definition::LifetimeParam(_) => {
Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => {
// FIXME: Hover for generic param
None
}

View file

@ -130,7 +130,7 @@ pub(crate) fn find_all_refs(
kind = ReferenceKind::FieldShorthandForLocal;
}
}
} else if let Definition::LifetimeParam(_) = def {
} else if matches!(def, Definition::LifetimeParam(_) | Definition::Label(_)) {
kind = ReferenceKind::Lifetime;
};
@ -1122,4 +1122,26 @@ fn main() {
"#]],
);
}
#[test]
fn test_find_labels() {
check(
r#"
fn foo<'a>() -> &'a () {
'a: loop {
'b: loop {
continue 'a<|>;
}
break 'a;
}
}
"#,
expect![[r#"
'a Label FileId(0) 29..32 29..31 Lifetime
FileId(0) 80..82 Lifetime
FileId(0) 108..110 Lifetime
"#]],
);
}
}

View file

@ -1540,4 +1540,29 @@ fn main() {
}"#,
);
}
#[test]
fn test_rename_label() {
check(
"'foo",
r#"
fn foo<'a>() -> &'a () {
'a: {
'b: loop {
break 'a<|>;
}
}
}
"#,
r#"
fn foo<'a>() -> &'a () {
'foo: {
'b: loop {
break 'foo;
}
}
}
"#,
)
}
}

View file

@ -560,10 +560,20 @@ fn highlight_element(
CHAR => HighlightTag::CharLiteral.into(),
QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
LIFETIME => {
let h = Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam));
match element.parent().map(|it| it.kind()) {
Some(LIFETIME_PARAM) | Some(LABEL) => h | HighlightModifier::Definition,
_ => h,
let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
match NameClass::classify_lifetime(sema, &lifetime) {
Some(NameClass::Definition(def)) => {
highlight_def(db, def) | HighlightModifier::Definition
}
None => match NameRefClass::classify_lifetime(sema, &lifetime) {
Some(NameRefClass::Definition(def)) => highlight_def(db, def),
_ => Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam)),
},
_ => {
Highlight::new(HighlightTag::Symbol(SymbolKind::LifetimeParam))
| HighlightModifier::Definition
}
}
}
p if p.is_punct() => match p {
@ -825,6 +835,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
return h;
}
Definition::LifetimeParam(_) => HighlightTag::Symbol(SymbolKind::LifetimeParam),
Definition::Label(_) => HighlightTag::Symbol(SymbolKind::Label),
}
.into()
}

View file

@ -64,6 +64,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -80,6 +80,7 @@ impl HighlightTag {
SymbolKind::LifetimeParam => "lifetime",
SymbolKind::Macro => "macro",
SymbolKind::Local => "variable",
SymbolKind::Label => "label",
SymbolKind::ValueParam => "value_param",
SymbolKind::SelfParam => "self_keyword",
SymbolKind::Impl => "self_type",

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }
@ -194,6 +195,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="punctuation">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="punctuation">;</span>
<span class="label declaration">'foo</span><span class="punctuation">:</span> <span class="keyword control">loop</span> <span class="punctuation">{</span>
<span class="keyword control">break</span> <span class="label">'foo</span><span class="punctuation">;</span>
<span class="keyword control">continue</span> <span class="label">'foo</span><span class="punctuation">;</span>
<span class="punctuation">}</span>
<span class="punctuation">}</span>
<span class="keyword">enum</span> <span class="enum declaration">Option</span><span class="punctuation">&lt;</span><span class="type_param declaration">T</span><span class="punctuation">&gt;</span> <span class="punctuation">{</span>

View file

@ -4,6 +4,7 @@ body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.injected { opacity: 0.65 ; }

View file

@ -168,6 +168,11 @@ fn main() {
let baz = -baz;
let _ = !true;
'foo: loop {
break 'foo;
continue 'foo;
}
}
enum Option<T> {

View file

@ -6,8 +6,8 @@
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
use hir::{
db::HirDatabase, Crate, Field, HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module,
ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
};
use syntax::{
ast::{self, AstNode},
@ -26,7 +26,7 @@ pub enum Definition {
Local(Local),
TypeParam(TypeParam),
LifetimeParam(LifetimeParam),
// FIXME: Label
Label(Label),
}
impl Definition {
@ -39,6 +39,7 @@ impl Definition {
Definition::Local(it) => Some(it.module(db)),
Definition::TypeParam(it) => Some(it.module(db)),
Definition::LifetimeParam(it) => Some(it.module(db)),
Definition::Label(it) => Some(it.module(db)),
}
}
@ -51,6 +52,7 @@ impl Definition {
Definition::Local(_) => None,
Definition::TypeParam(_) => None,
Definition::LifetimeParam(_) => None,
Definition::Label(_) => None,
}
}
@ -77,6 +79,7 @@ impl Definition {
Definition::Local(it) => it.name(db)?,
Definition::TypeParam(it) => it.name(db),
Definition::LifetimeParam(it) => it.name(db),
Definition::Label(it) => it.name(db),
};
Some(name)
}
@ -248,7 +251,10 @@ impl NameClass {
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::LifetimeParam(def)))
},
ast::Label(_it) => None,
ast::Label(it) => {
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::Label(def)))
},
_ => None,
}
}
@ -370,6 +376,9 @@ impl NameRefClass {
let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
let parent = lifetime.syntax().parent()?;
match parent.kind() {
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
}
SyntaxKind::LIFETIME_ARG
| SyntaxKind::SELF_PARAM
| SyntaxKind::TYPE_BOUND
@ -387,7 +396,6 @@ impl NameRefClass {
.map(Definition::LifetimeParam)
.map(NameRefClass::Definition)
}
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None,
_ => None,
}
}

View file

@ -45,6 +45,7 @@ define_semantic_token_types![
(FORMAT_SPECIFIER, "formatSpecifier"),
(GENERIC, "generic"),
(LIFETIME, "lifetime"),
(LABEL, "label"),
(PUNCTUATION, "punctuation"),
(SELF_KEYWORD, "selfKeyword"),
(TYPE_ALIAS, "typeAlias"),

View file

@ -46,7 +46,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
SymbolKind::Local
| SymbolKind::SelfParam
| SymbolKind::LifetimeParam
| SymbolKind::ValueParam => lsp_types::SymbolKind::Variable,
| SymbolKind::ValueParam
| SymbolKind::Label => lsp_types::SymbolKind::Variable,
SymbolKind::Union => lsp_types::SymbolKind::Struct,
}
}
@ -378,6 +379,7 @@ fn semantic_token_type_and_modifiers(
SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
SymbolKind::Label => semantic_tokens::LABEL,
SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
SymbolKind::SelfParam => semantic_tokens::SELF_KEYWORD,
SymbolKind::Local => lsp_types::SemanticTokenType::VARIABLE,