mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Skip trival token in original_range
This commit is contained in:
parent
2dee0779e9
commit
553254973e
4 changed files with 54 additions and 23 deletions
|
@ -8,8 +8,9 @@ use hir_def::{
|
||||||
};
|
};
|
||||||
use ra_db::{FileId, FileRange};
|
use ra_db::{FileId, FileRange};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::find_covering_element, ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxNode,
|
algo::{find_covering_element, skip_trivia_token},
|
||||||
SyntaxToken, TextRange, TextUnit,
|
ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
|
||||||
|
TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
|
@ -384,11 +385,12 @@ fn original_range_and_origin(
|
||||||
};
|
};
|
||||||
|
|
||||||
// the input node has only one token ?
|
// the input node has only one token ?
|
||||||
let single = node.value.first_token()? == node.value.last_token()?;
|
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
|
||||||
|
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;
|
||||||
|
|
||||||
return Some(node.value.descendants().find_map(|it| {
|
return Some(node.value.descendants().find_map(|it| {
|
||||||
let first = it.first_token()?;
|
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
|
||||||
let last = it.last_token()?;
|
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
|
||||||
|
|
||||||
if !single && first == last {
|
if !single && first == last {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::iter::successors;
|
||||||
use hir::Semantics;
|
use hir::Semantics;
|
||||||
use ra_ide_db::RootDatabase;
|
use ra_ide_db::RootDatabase;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::{self, find_covering_element},
|
algo::{self, find_covering_element, skip_trivia_token},
|
||||||
ast::{self, AstNode, AstToken},
|
ast::{self, AstNode, AstToken},
|
||||||
Direction, NodeOrToken,
|
Direction, NodeOrToken,
|
||||||
SyntaxKind::{self, *},
|
SyntaxKind::{self, *},
|
||||||
|
@ -118,14 +118,14 @@ fn extend_tokens_from_range(
|
||||||
NodeOrToken::Token(it) => (it.clone(), it),
|
NodeOrToken::Token(it) => (it.clone(), it),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut first_token = skip_whitespace(first_token, Direction::Next)?;
|
let mut first_token = skip_trivia_token(first_token, Direction::Next)?;
|
||||||
let mut last_token = skip_whitespace(last_token, Direction::Prev)?;
|
let mut last_token = skip_trivia_token(last_token, Direction::Prev)?;
|
||||||
|
|
||||||
while !first_token.text_range().is_subrange(&original_range) {
|
while !first_token.text_range().is_subrange(&original_range) {
|
||||||
first_token = skip_whitespace(first_token.next_token()?, Direction::Next)?;
|
first_token = skip_trivia_token(first_token.next_token()?, Direction::Next)?;
|
||||||
}
|
}
|
||||||
while !last_token.text_range().is_subrange(&original_range) {
|
while !last_token.text_range().is_subrange(&original_range) {
|
||||||
last_token = skip_whitespace(last_token.prev_token()?, Direction::Prev)?;
|
last_token = skip_trivia_token(last_token.prev_token()?, Direction::Prev)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute original mapped token range
|
// compute original mapped token range
|
||||||
|
@ -149,14 +149,14 @@ fn extend_tokens_from_range(
|
||||||
// Find the first and last text range under expanded parent
|
// Find the first and last text range under expanded parent
|
||||||
let first = successors(Some(first_token), |token| {
|
let first = successors(Some(first_token), |token| {
|
||||||
let token = token.prev_token()?;
|
let token = token.prev_token()?;
|
||||||
skip_whitespace(token, Direction::Prev)
|
skip_trivia_token(token, Direction::Prev)
|
||||||
})
|
})
|
||||||
.take_while(validate)
|
.take_while(validate)
|
||||||
.last()?;
|
.last()?;
|
||||||
|
|
||||||
let last = successors(Some(last_token), |token| {
|
let last = successors(Some(last_token), |token| {
|
||||||
let token = token.next_token()?;
|
let token = token.next_token()?;
|
||||||
skip_whitespace(token, Direction::Next)
|
skip_trivia_token(token, Direction::Next)
|
||||||
})
|
})
|
||||||
.take_while(validate)
|
.take_while(validate)
|
||||||
.last()?;
|
.last()?;
|
||||||
|
@ -169,16 +169,6 @@ fn extend_tokens_from_range(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn skip_whitespace(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
|
|
||||||
while token.kind() == WHITESPACE {
|
|
||||||
token = match direction {
|
|
||||||
Direction::Next => token.next_token()?,
|
|
||||||
Direction::Prev => token.prev_token()?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union_range(range: TextRange, r: TextRange) -> TextRange {
|
fn union_range(range: TextRange, r: TextRange) -> TextRange {
|
||||||
let start = range.start().min(r.start());
|
let start = range.start().min(r.start());
|
||||||
let end = range.end().max(r.end());
|
let end = range.end().max(r.end());
|
||||||
|
|
|
@ -174,6 +174,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
|
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
|
||||||
|
|
||||||
|
// FIXME: Currently `hover::typeof` do not work inside
|
||||||
|
// macro expansion such that if the hover range is pointing to
|
||||||
|
// a string literal, the following type_of will return None.
|
||||||
|
// See also `test_hover_through_literal_string_in_macro`
|
||||||
let frange = sema.original_range(&node);
|
let frange = sema.original_range(&node);
|
||||||
res.extend(type_of(db, frange).map(rust_code_markup));
|
res.extend(type_of(db, frange).map(rust_code_markup));
|
||||||
if res.is_empty() {
|
if res.is_empty() {
|
||||||
|
@ -250,6 +254,11 @@ mod tests {
|
||||||
content[hover.range].to_string()
|
content[hover.range].to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_hover_no_result(fixture: &str) {
|
||||||
|
let (analysis, position) = analysis_and_position(fixture);
|
||||||
|
assert!(analysis.hover(position).unwrap().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_shows_type_of_an_expression() {
|
fn hover_shows_type_of_an_expression() {
|
||||||
let (analysis, position) = single_file_with_position(
|
let (analysis, position) = single_file_with_position(
|
||||||
|
@ -774,6 +783,24 @@ fn func(foo: i32) { if true { <|>foo; }; }
|
||||||
assert_eq!(hover_on, "bar")
|
assert_eq!(hover_on, "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hover_through_literal_string_in_macro() {
|
||||||
|
// FIXME: Currently `hover::type_of` do not work inside
|
||||||
|
// macro expansion
|
||||||
|
check_hover_no_result(
|
||||||
|
r#"
|
||||||
|
//- /lib.rs
|
||||||
|
macro_rules! arr {
|
||||||
|
($($tt:tt)*) => { [$($tt)*)] }
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
let mastered_for_itunes = "";
|
||||||
|
let _ = arr!("Tr<|>acks", &mastered_for_itunes);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_hover_non_ascii_space_doc() {
|
fn test_hover_non_ascii_space_doc() {
|
||||||
check_hover_result(
|
check_hover_result(
|
||||||
|
|
|
@ -7,7 +7,8 @@ use ra_text_edit::TextEditBuilder;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, TextRange, TextUnit,
|
AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
|
||||||
|
TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns ancestors of the node at the offset, sorted by length. This should
|
/// Returns ancestors of the node at the offset, sorted by length. This should
|
||||||
|
@ -37,6 +38,17 @@ pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) ->
|
||||||
ancestors_at_offset(syntax, offset).find_map(N::cast)
|
ancestors_at_offset(syntax, offset).find_map(N::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Skip to next non `trivia` token
|
||||||
|
pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> {
|
||||||
|
while token.kind().is_trivia() {
|
||||||
|
token = match direction {
|
||||||
|
Direction::Next => token.next_token()?,
|
||||||
|
Direction::Prev => token.prev_token()?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(token)
|
||||||
|
}
|
||||||
|
|
||||||
/// Finds the first sibling in the given direction which is not `trivia`
|
/// Finds the first sibling in the given direction which is not `trivia`
|
||||||
pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> {
|
pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> {
|
||||||
return match element {
|
return match element {
|
||||||
|
|
Loading…
Reference in a new issue