Factor out pick_best_token ide pattern into ide_db

This commit is contained in:
Lukas Wirth 2021-06-22 17:28:07 +02:00
parent 4e2ec914f4
commit f615efdfc3
9 changed files with 62 additions and 101 deletions

View file

@ -3,8 +3,7 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use hir::Semantics; use hir::Semantics;
use ide_db::call_info::FnCallNode; use ide_db::{call_info::FnCallNode, RootDatabase};
use ide_db::RootDatabase;
use syntax::{ast, AstNode, TextRange}; use syntax::{ast, AstNode, TextRange};
use crate::{ use crate::{

View file

@ -16,11 +16,10 @@ use hir::{
}; };
use ide_db::{ use ide_db::{
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
RootDatabase, RootDatabase,
}; };
use syntax::{ use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
ast, match_ast, AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TokenAtOffset, T,
};
use crate::{FilePosition, Semantics}; use crate::{FilePosition, Semantics};
@ -102,7 +101,12 @@ pub(crate) fn external_docs(
) -> Option<DocumentationLink> { ) -> Option<DocumentationLink> {
let sema = Semantics::new(db); let sema = Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone(); let file = sema.parse(position.file_id).syntax().clone();
let token = pick_best(file.token_at_offset(position.offset))?; let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
IDENT | INT_NUMBER => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
let token = sema.descend_into_macros(token); let token = sema.descend_into_macros(token);
let node = token.parent()?; let node = token.parent()?;
@ -522,18 +526,6 @@ fn get_symbol_fragment(db: &dyn HirDatabase, field_or_assoc: &FieldOrAssocItem)
}) })
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};

View file

@ -1,11 +1,8 @@
use std::iter; use std::iter;
use hir::Semantics; use hir::Semantics;
use ide_db::RootDatabase; use ide_db::{helpers::pick_best_token, RootDatabase};
use syntax::{ use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, SyntaxToken,
TokenAtOffset, WalkEvent, T,
};
use crate::FilePosition; use crate::FilePosition;
@ -29,7 +26,10 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
let sema = Semantics::new(db); let sema = Semantics::new(db);
let file = sema.parse(position.file_id); let file = sema.parse(position.file_id);
let tok = pick_best(file.syntax().token_at_offset(position.offset))?; let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
SyntaxKind::IDENT => 1,
_ => 0,
})?;
let mut expanded = None; let mut expanded = None;
let mut name = None; let mut name = None;
for node in tok.ancestors() { for node in tok.ancestors() {
@ -57,16 +57,6 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
Some(ExpandedMacro { name: name?, expansion }) Some(ExpandedMacro { name: name?, expansion })
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT => 1,
_ => 0,
}
}
}
fn expand_macro_recur( fn expand_macro_recur(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
macro_call: &ast::MacroCall, macro_call: &ast::MacroCall,

View file

@ -5,11 +5,10 @@ use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
use ide_db::{ use ide_db::{
base_db::{AnchoredPath, FileId, FileLoader}, base_db::{AnchoredPath, FileId, FileLoader},
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
RootDatabase, RootDatabase,
}; };
use syntax::{ use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T,
};
use crate::{ use crate::{
display::TryToNav, display::TryToNav,
@ -34,7 +33,12 @@ pub(crate) fn goto_definition(
) -> Option<RangeInfo<Vec<NavigationTarget>>> { ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db); let sema = Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone(); let file = sema.parse(position.file_id).syntax().clone();
let original_token = pick_best(file.token_at_offset(position.offset))?; let original_token =
pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
let token = sema.descend_into_macros(original_token.clone()); let token = sema.descend_into_macros(original_token.clone());
let parent = token.parent()?; let parent = token.parent()?;
if let Some(_) = ast::Comment::cast(token.clone()) { if let Some(_) = ast::Comment::cast(token.clone()) {
@ -128,17 +132,6 @@ fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | COMMENT => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
}
}
pub(crate) fn reference_definition( pub(crate) fn reference_definition(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
name_ref: Either<&ast::Lifetime, &ast::NameRef>, name_ref: Either<&ast::Lifetime, &ast::NameRef>,

View file

@ -1,6 +1,7 @@
use ide_db::base_db::Upcast; use ide_db::base_db::Upcast;
use ide_db::helpers::pick_best_token;
use ide_db::RootDatabase; use ide_db::RootDatabase;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, T};
use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
@ -22,7 +23,12 @@ pub(crate) fn goto_type_definition(
let sema = hir::Semantics::new(db); let sema = hir::Semantics::new(db);
let file: ast::SourceFile = sema.parse(position.file_id); let file: ast::SourceFile = sema.parse(position.file_id);
let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; let token: SyntaxToken =
pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
IDENT | INT_NUMBER | T![self] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
let token: SyntaxToken = sema.descend_into_macros(token); let token: SyntaxToken = sema.descend_into_macros(token);
let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| { let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
@ -56,17 +62,6 @@ pub(crate) fn goto_type_definition(
Some(RangeInfo::new(node.text_range(), vec![nav])) Some(RangeInfo::new(node.text_range(), vec![nav]))
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER | T![self] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ide_db::base_db::FileRange; use ide_db::base_db::FileRange;

View file

@ -5,16 +5,13 @@ use ide_db::{
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
helpers::{ helpers::{
generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
FamousDefs, pick_best_token, FamousDefs,
}, },
RootDatabase, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
use stdx::format_to; use stdx::format_to;
use syntax::{ use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, T};
algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxKind::*, SyntaxToken, TokenAtOffset,
T,
};
use crate::{ use crate::{
display::{macro_label, TryToNav}, display::{macro_label, TryToNav},
@ -80,7 +77,12 @@ pub(crate) fn hover(
) -> Option<RangeInfo<HoverResult>> { ) -> Option<RangeInfo<HoverResult>> {
let sema = hir::Semantics::new(db); let sema = hir::Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone(); let file = sema.parse(position.file_id).syntax().clone();
let token = pick_best(file.token_at_offset(position.offset))?; let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
let token = sema.descend_into_macros(token); let token = sema.descend_into_macros(token);
let mut res = HoverResult::default(); let mut res = HoverResult::default();
@ -519,19 +521,6 @@ fn find_std_module(famous_defs: &FamousDefs, name: &str) -> Option<hir::Module>
.find(|module| module.name(db).map_or(false, |module| module.to_string() == name)) .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};

View file

@ -439,7 +439,7 @@ impl Analysis {
self.with_db(|db| call_hierarchy::incoming_calls(db, position)) self.with_db(|db| call_hierarchy::incoming_calls(db, position))
} }
/// Computes incoming calls for the given file position. /// Computes outgoing calls for the given file position.
pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> { pub fn outgoing_calls(&self, position: FilePosition) -> Cancellable<Option<Vec<CallItem>>> {
self.with_db(|db| call_hierarchy::outgoing_calls(db, position)) self.with_db(|db| call_hierarchy::outgoing_calls(db, position))
} }

View file

@ -1,12 +1,9 @@
use std::{iter::once, mem}; use std::{iter::once, mem};
use hir::Semantics; use hir::Semantics;
use ide_db::{base_db::FileRange, RootDatabase}; use ide_db::{base_db::FileRange, helpers::pick_best_token, RootDatabase};
use itertools::Itertools; use itertools::Itertools;
use syntax::{ use syntax::{algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange};
algo, ast, match_ast, AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange,
TokenAtOffset,
};
use text_edit::{TextEdit, TextEditBuilder}; use text_edit::{TextEdit, TextEditBuilder};
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -36,7 +33,14 @@ pub(crate) fn move_item(
let file = sema.parse(range.file_id); let file = sema.parse(range.file_id);
let item = if range.range.is_empty() { let item = if range.range.is_empty() {
SyntaxElement::Token(pick_best(file.syntax().token_at_offset(range.range.start()))?) SyntaxElement::Token(pick_best_token(
file.syntax().token_at_offset(range.range.start()),
|kind| match kind {
SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
kind if kind.is_trivia() => 0,
_ => 1,
},
)?)
} else { } else {
file.syntax().covering_element(range.range) file.syntax().covering_element(range.range)
}; };
@ -170,18 +174,6 @@ fn replace_nodes<'a>(
edit.finish() edit.finish()
} }
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
match n.kind() {
SyntaxKind::IDENT | SyntaxKind::LIFETIME_IDENT => 2,
kind if kind.is_trivia() => 0,
_ => 1,
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::fixture; use crate::fixture;

View file

@ -10,7 +10,10 @@ use std::collections::VecDeque;
use base_db::FileId; use base_db::FileId;
use either::Either; use either::Either;
use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
use syntax::ast::{self, make}; use syntax::{
ast::{self, make},
SyntaxKind, SyntaxToken, TokenAtOffset,
};
use crate::RootDatabase; use crate::RootDatabase;
@ -22,6 +25,14 @@ pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
} }
} }
/// Picks the token with the highest rank returned by the passed in function.
pub fn pick_best_token(
tokens: TokenAtOffset<SyntaxToken>,
f: impl Fn(SyntaxKind) -> usize,
) -> Option<SyntaxToken> {
tokens.max_by_key(move |t| f(t.kind()))
}
/// Converts the mod path struct into its ast representation. /// Converts the mod path struct into its ast representation.
pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
let _p = profile::span("mod_path_to_ast"); let _p = profile::span("mod_path_to_ast");