mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 06:11:35 +00:00
Added new inlay hint kind and rules for method chaining
This commit is contained in:
parent
fae627174a
commit
a197abbc7a
5 changed files with 98 additions and 5 deletions
|
@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase;
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
|
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
|
||||||
match_ast, SmolStr, TextRange,
|
match_ast, SmolStr, TextRange, NodeOrToken, SyntaxKind, Direction
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{FileId, FunctionSignature};
|
use crate::{FileId, FunctionSignature};
|
||||||
|
@ -14,12 +14,18 @@ use crate::{FileId, FunctionSignature};
|
||||||
pub struct InlayHintsOptions {
|
pub struct InlayHintsOptions {
|
||||||
pub type_hints: bool,
|
pub type_hints: bool,
|
||||||
pub parameter_hints: bool,
|
pub parameter_hints: bool,
|
||||||
|
pub chaining_hints: bool,
|
||||||
pub max_length: Option<usize>,
|
pub max_length: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for InlayHintsOptions {
|
impl Default for InlayHintsOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { type_hints: true, parameter_hints: true, max_length: None }
|
Self {
|
||||||
|
type_hints: true,
|
||||||
|
parameter_hints: true,
|
||||||
|
chaining_hints: true,
|
||||||
|
max_length: None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +33,7 @@ impl Default for InlayHintsOptions {
|
||||||
pub enum InlayKind {
|
pub enum InlayKind {
|
||||||
TypeHint,
|
TypeHint,
|
||||||
ParameterHint,
|
ParameterHint,
|
||||||
|
ChainingHint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -47,6 +54,10 @@ pub(crate) fn inlay_hints(
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for node in file.syntax().descendants() {
|
for node in file.syntax().descendants() {
|
||||||
|
if let Some(expr) = ast::Expr::cast(node.clone()) {
|
||||||
|
get_chaining_hints(&mut res, &sema, options, expr);
|
||||||
|
}
|
||||||
|
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
|
ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
|
||||||
|
@ -222,6 +233,45 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_chaining_hints(
|
||||||
|
acc: &mut Vec<InlayHint>,
|
||||||
|
sema: &Semantics<RootDatabase>,
|
||||||
|
options: &InlayHintsOptions,
|
||||||
|
expr: ast::Expr,
|
||||||
|
) -> Option<()> {
|
||||||
|
if !options.chaining_hints {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty = sema.type_of_expr(&expr)?;
|
||||||
|
let label = ty.display_truncated(sema.db, options.max_length).to_string();
|
||||||
|
if ty.is_unknown() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tokens = expr.syntax()
|
||||||
|
.siblings_with_tokens(Direction::Next)
|
||||||
|
.filter_map(NodeOrToken::into_token)
|
||||||
|
.filter(|t| match t.kind() {
|
||||||
|
SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
|
||||||
|
SyntaxKind::COMMENT => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chaining can be defined as an expression whose next sibling tokens are newline and dot
|
||||||
|
// Ignoring extra whitespace and comments
|
||||||
|
let next = tokens.next()?.kind();
|
||||||
|
let next_next = tokens.next()?.kind();
|
||||||
|
if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
|
||||||
|
acc.push(InlayHint {
|
||||||
|
range: expr.syntax().text_range(),
|
||||||
|
kind: InlayKind::ChainingHint,
|
||||||
|
label: label.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::inlay_hints::InlayHintsOptions;
|
use crate::inlay_hints::InlayHintsOptions;
|
||||||
|
@ -229,6 +279,43 @@ mod tests {
|
||||||
|
|
||||||
use crate::mock_analysis::single_file;
|
use crate::mock_analysis::single_file;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_chaining_hints() {
|
||||||
|
let (analysis, file_id) = single_file(
|
||||||
|
r#"
|
||||||
|
struct A<T>(T);
|
||||||
|
struct B<T>(T);
|
||||||
|
struct C<T>(T);
|
||||||
|
struct X<T,R>(T, R);
|
||||||
|
|
||||||
|
impl<T> A<T> {
|
||||||
|
fn new(t: T) -> Self { A(t) }
|
||||||
|
fn into_b(self) -> B<T> { B(self.0) }
|
||||||
|
}
|
||||||
|
impl<T> B<T> {
|
||||||
|
fn into_c(self) -> C<T> { C(self.0) }
|
||||||
|
}
|
||||||
|
fn test() {
|
||||||
|
let c = A::new(X(42, true))
|
||||||
|
.into_b() // All the from A -> B -> C
|
||||||
|
.into_c();
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
|
||||||
|
[
|
||||||
|
InlayHint {
|
||||||
|
range: [416; 465),
|
||||||
|
kind: ChainingHint,
|
||||||
|
label: "B<X<i32, bool>>",
|
||||||
|
},
|
||||||
|
InlayHint {
|
||||||
|
range: [416; 435),
|
||||||
|
kind: ChainingHint,
|
||||||
|
label: "A<X<i32, bool>>",
|
||||||
|
},
|
||||||
|
]"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn param_hints_only() {
|
fn param_hints_only() {
|
||||||
let (analysis, file_id) = single_file(
|
let (analysis, file_id) = single_file(
|
||||||
|
@ -238,7 +325,7 @@ mod tests {
|
||||||
let _x = foo(4, 4);
|
let _x = foo(4, 4);
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###"
|
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
|
||||||
[
|
[
|
||||||
InlayHint {
|
InlayHint {
|
||||||
range: [106; 107),
|
range: [106; 107),
|
||||||
|
@ -262,7 +349,7 @@ mod tests {
|
||||||
let _x = foo(4, 4);
|
let _x = foo(4, 4);
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###);
|
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -274,7 +361,7 @@ mod tests {
|
||||||
let _x = foo(4, 4);
|
let _x = foo(4, 4);
|
||||||
}"#,
|
}"#,
|
||||||
);
|
);
|
||||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###"
|
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
|
||||||
[
|
[
|
||||||
InlayHint {
|
InlayHint {
|
||||||
range: [97; 99),
|
range: [97; 99),
|
||||||
|
|
|
@ -34,6 +34,8 @@ pub struct ServerConfig {
|
||||||
pub inlay_hints_type: bool,
|
pub inlay_hints_type: bool,
|
||||||
#[serde(deserialize_with = "nullable_bool_true")]
|
#[serde(deserialize_with = "nullable_bool_true")]
|
||||||
pub inlay_hints_parameter: bool,
|
pub inlay_hints_parameter: bool,
|
||||||
|
#[serde(deserialize_with = "nullable_bool_true")]
|
||||||
|
pub inlay_hints_chaining: bool,
|
||||||
pub inlay_hints_max_length: Option<usize>,
|
pub inlay_hints_max_length: Option<usize>,
|
||||||
|
|
||||||
pub cargo_watch_enable: bool,
|
pub cargo_watch_enable: bool,
|
||||||
|
@ -66,6 +68,7 @@ impl Default for ServerConfig {
|
||||||
lru_capacity: None,
|
lru_capacity: None,
|
||||||
inlay_hints_type: true,
|
inlay_hints_type: true,
|
||||||
inlay_hints_parameter: true,
|
inlay_hints_parameter: true,
|
||||||
|
inlay_hints_chaining: true,
|
||||||
inlay_hints_max_length: None,
|
inlay_hints_max_length: None,
|
||||||
cargo_watch_enable: true,
|
cargo_watch_enable: true,
|
||||||
cargo_watch_args: Vec::new(),
|
cargo_watch_args: Vec::new(),
|
||||||
|
|
|
@ -332,6 +332,7 @@ impl ConvWith<&LineIndex> for InlayHint {
|
||||||
kind: match self.kind {
|
kind: match self.kind {
|
||||||
InlayKind::ParameterHint => req::InlayKind::ParameterHint,
|
InlayKind::ParameterHint => req::InlayKind::ParameterHint,
|
||||||
InlayKind::TypeHint => req::InlayKind::TypeHint,
|
InlayKind::TypeHint => req::InlayKind::TypeHint,
|
||||||
|
InlayKind::ChainingHint => req::InlayKind::ChainingHint,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,6 +183,7 @@ pub fn main_loop(
|
||||||
inlay_hints: InlayHintsOptions {
|
inlay_hints: InlayHintsOptions {
|
||||||
type_hints: config.inlay_hints_type,
|
type_hints: config.inlay_hints_type,
|
||||||
parameter_hints: config.inlay_hints_parameter,
|
parameter_hints: config.inlay_hints_parameter,
|
||||||
|
chaining_hints: config.inlay_hints_chaining,
|
||||||
max_length: config.inlay_hints_max_length,
|
max_length: config.inlay_hints_max_length,
|
||||||
},
|
},
|
||||||
cargo_watch: CheckOptions {
|
cargo_watch: CheckOptions {
|
||||||
|
|
|
@ -200,6 +200,7 @@ pub struct InlayHintsParams {
|
||||||
pub enum InlayKind {
|
pub enum InlayKind {
|
||||||
TypeHint,
|
TypeHint,
|
||||||
ParameterHint,
|
ParameterHint,
|
||||||
|
ChainingHint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue