Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide/src/inlay_hints.rs13
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs98
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs107
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/static_index.rs3
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs22
-rw-r--r--docs/book/src/configuration_generated.md7
-rw-r--r--editors/code/package.json18
9 files changed, 252 insertions, 19 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index f57f2883b1..69b3db6446 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -302,6 +302,7 @@ fn hints(
pub struct InlayHintsConfig<'a> {
pub render_colons: bool,
pub type_hints: bool,
+ pub type_hints_placement: TypeHintsPlacement,
pub sized_bound: bool,
pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool,
@@ -331,6 +332,12 @@ pub struct InlayHintsConfig<'a> {
pub minicore: MiniCore<'a>,
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum TypeHintsPlacement {
+ Inline,
+ EndOfLine,
+}
+
impl InlayHintsConfig<'_> {
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
if self.fields_to_resolve.resolve_text_edits {
@@ -876,12 +883,15 @@ mod tests {
use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode};
use crate::{LifetimeElisionHints, fixture, inlay_hints::InlayHintsConfig};
- use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve};
+ use super::{
+ ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve, TypeHintsPlacement,
+ };
pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never,
render_colons: false,
type_hints: false,
+ type_hints_placement: TypeHintsPlacement::Inline,
parameter_hints: false,
parameter_hints_for_missing_arguments: false,
sized_bound: false,
@@ -915,6 +925,7 @@ mod tests {
};
pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
type_hints: true,
+ type_hints_placement: TypeHintsPlacement::Inline,
parameter_hints: true,
chaining_hints: true,
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index de207c7821..5e16be332e 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -8,10 +8,12 @@ use ide_db::{RootDatabase, famous_defs::FamousDefs};
use itertools::Itertools;
use syntax::{
+ TextRange,
ast::{self, AstNode, HasGenericArgs, HasName},
match_ast,
};
+use super::TypeHintsPlacement;
use crate::{
InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
@@ -29,6 +31,7 @@ pub(super) fn hints(
}
let parent = pat.syntax().parent()?;
+ let mut enclosing_let_stmt = None;
let type_ascriptable = match_ast! {
match parent {
ast::Param(it) => {
@@ -41,6 +44,7 @@ pub(super) fn hints(
Some(it.colon_token())
},
ast::LetStmt(it) => {
+ enclosing_let_stmt = Some(it.clone());
if config.hide_closure_initialization_hints
&& let Some(ast::Expr::ClosureExpr(closure)) = it.initializer()
&& closure_has_block_body(&closure) {
@@ -101,16 +105,26 @@ pub(super) fn hints(
Some(name) => name.syntax().text_range(),
None => pat.syntax().text_range(),
};
+ let mut range = match type_ascriptable {
+ Some(Some(t)) => text_range.cover(t.text_range()),
+ _ => text_range,
+ };
+
+ let mut pad_left = !render_colons;
+ if matches!(config.type_hints_placement, TypeHintsPlacement::EndOfLine)
+ && let Some(let_stmt) = enclosing_let_stmt
+ {
+ let stmt_range = let_stmt.syntax().text_range();
+ range = TextRange::new(range.start(), stmt_range.end());
+ pad_left = true;
+ }
acc.push(InlayHint {
- range: match type_ascriptable {
- Some(Some(t)) => text_range.cover(t.text_range()),
- _ => text_range,
- },
+ range,
kind: InlayKind::Type,
label,
text_edit,
position: InlayHintPosition::After,
- pad_left: !render_colons,
+ pad_left,
pad_right: false,
resolve_parent: Some(pat.syntax().text_range()),
});
@@ -182,8 +196,10 @@ mod tests {
use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig};
+ use super::TypeHintsPlacement;
use crate::inlay_hints::tests::{
- DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config,
+ DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit,
+ check_with_config,
};
#[track_caller]
@@ -204,6 +220,76 @@ fn main() {
}
#[test]
+ fn type_hints_end_of_line_placement() {
+ let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG };
+ config.type_hints_placement = TypeHintsPlacement::EndOfLine;
+ check_expect(
+ config,
+ r#"
+fn main() {
+ let foo = 92_i32;
+}
+ "#,
+ expect![[r#"
+ [
+ (
+ 20..33,
+ [
+ "i32",
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn type_hints_end_of_line_placement_chain_expr() {
+ let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG };
+ config.type_hints_placement = TypeHintsPlacement::EndOfLine;
+ check_expect(
+ config,
+ r#"
+fn main() {
+ struct Builder;
+ impl Builder {
+ fn iter(self) -> Builder { Builder }
+ fn map(self) -> Builder { Builder }
+ }
+ fn make() -> Builder { Builder }
+
+ let foo = make()
+ .iter()
+ .map();
+}
+"#,
+ expect![[r#"
+ [
+ (
+ 192..236,
+ [
+ InlayHintLabelPart {
+ text: "Builder",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 23..30,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
fn type_hints_bindings_after_at() {
check_types(
r#"
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index cf3149c946..4b06f83971 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -2,13 +2,13 @@
use hir::DisplayTarget;
use ide_db::famous_defs::FamousDefs;
use syntax::{
- Direction, NodeOrToken, SyntaxKind, T,
+ Direction, NodeOrToken, SyntaxKind, T, TextRange,
ast::{self, AstNode},
};
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
-use super::label_of_ty;
+use super::{TypeHintsPlacement, label_of_ty};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -40,13 +40,14 @@ pub(super) fn hints(
// 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();
- if next == SyntaxKind::WHITESPACE {
- let mut next_next = tokens.next()?.kind();
- while next_next == SyntaxKind::WHITESPACE {
- next_next = tokens.next()?.kind();
+ let next_token = tokens.next()?;
+ if next_token.kind() == SyntaxKind::WHITESPACE {
+ let newline_token = next_token;
+ let mut next_next = tokens.next()?;
+ while next_next.kind() == SyntaxKind::WHITESPACE {
+ next_next = tokens.next()?;
}
- if next_next == T![.] {
+ if next_next.kind() == T![.] {
let ty = sema.type_of_expr(desc_expr)?.original;
if ty.is_unknown() {
return None;
@@ -58,8 +59,18 @@ pub(super) fn hints(
return None;
}
let label = label_of_ty(famous_defs, config, &ty, display_target)?;
+ let range = {
+ let mut range = expr.syntax().text_range();
+ if config.type_hints_placement == TypeHintsPlacement::EndOfLine {
+ range = TextRange::new(
+ range.start(),
+ newline_token.text_range().start().max(range.end()),
+ );
+ }
+ range
+ };
acc.push(InlayHint {
- range: expr.syntax().text_range(),
+ range,
kind: InlayKind::Chaining,
label,
text_edit: None,
@@ -79,7 +90,7 @@ mod tests {
use ide_db::text_edit::{TextRange, TextSize};
use crate::{
- InlayHintsConfig, fixture,
+ InlayHintsConfig, TypeHintsPlacement, fixture,
inlay_hints::{
LazyProperty,
tests::{DISABLED_CONFIG, TEST_CONFIG, check_expect, check_with_config},
@@ -686,4 +697,80 @@ fn main() {
"#]],
);
}
+
+ #[test]
+ fn chaining_hints_end_of_line_placement() {
+ check_expect(
+ InlayHintsConfig {
+ chaining_hints: true,
+ type_hints_placement: TypeHintsPlacement::EndOfLine,
+ ..DISABLED_CONFIG
+ },
+ r#"
+fn main() {
+ let baz = make()
+ .into_bar()
+ .into_baz();
+}
+
+struct Foo;
+struct Bar;
+struct Baz;
+
+impl Foo {
+ fn into_bar(self) -> Bar { Bar }
+}
+
+impl Bar {
+ fn into_baz(self) -> Baz { Baz }
+}
+
+fn make() -> Foo {
+ Foo
+}
+"#,
+ expect![[r#"
+ [
+ (
+ 26..52,
+ [
+ InlayHintLabelPart {
+ text: "Bar",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 96..99,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ (
+ 26..32,
+ [
+ InlayHintLabelPart {
+ text: "Foo",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 84..87,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 5e4d930393..da2a0aa1f2 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -96,7 +96,7 @@ pub use crate::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
- LifetimeElisionHints,
+ LifetimeElisionHints, TypeHintsPlacement,
},
join_lines::JoinLinesConfig,
markup::Markup,
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index aba6b64f97..14af86f050 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -16,7 +16,7 @@ use crate::navigation_target::UpmappingResult;
use crate::{
Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
hover::{SubstTyLen, hover_for_definition},
- inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve},
+ inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve, TypeHintsPlacement},
moniker::{MonikerResult, SymbolInformationKind, def_to_kind, def_to_moniker},
parent_module::crates_for,
};
@@ -167,6 +167,7 @@ impl StaticIndex<'_> {
render_colons: true,
discriminant_hints: crate::DiscriminantHints::Fieldless,
type_hints: true,
+ type_hints_placement: TypeHintsPlacement::Inline,
sized_bound: false,
parameter_hints: true,
parameter_hints_for_missing_arguments: false,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 76256b0a22..75d6710b5b 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1204,6 +1204,7 @@ impl flags::AnalysisStats {
&InlayHintsConfig {
render_colons: false,
type_hints: true,
+ type_hints_placement: ide::TypeHintsPlacement::Inline,
sized_bound: false,
discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 2371f7a656..010cbd9e4c 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -320,6 +320,9 @@ config_data! {
/// Hide inlay type hints for constructors.
inlayHints_typeHints_hideNamedConstructor: bool = false,
+ /// Where to render type hints relative to their binding pattern.
+ inlayHints_typeHints_location: TypeHintsLocation = TypeHintsLocation::Inline,
+
/// Enable the experimental support for interpreting tests.
interpret_tests: bool = false,
@@ -1926,6 +1929,10 @@ impl Config {
InlayHintsConfig {
render_colons: self.inlayHints_renderColons().to_owned(),
type_hints: self.inlayHints_typeHints_enable().to_owned(),
+ type_hints_placement: match self.inlayHints_typeHints_location() {
+ TypeHintsLocation::Inline => ide::TypeHintsPlacement::Inline,
+ TypeHintsLocation::EndOfLine => ide::TypeHintsPlacement::EndOfLine,
+ },
sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
parameter_hints_for_missing_arguments: self
@@ -2910,6 +2917,13 @@ enum ClosureStyle {
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
+enum TypeHintsLocation {
+ Inline,
+ EndOfLine,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
enum ReborrowHintsDef {
Mutable,
#[serde(with = "true_or_always")]
@@ -3811,6 +3825,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"`hide`: Shows `...` for every closure type",
],
},
+ "TypeHintsLocation" => set! {
+ "type": "string",
+ "enum": ["inline", "end_of_line"],
+ "enumDescriptions": [
+ "Render type hints directly after the binding identifier.",
+ "Render type hints after the end of the containing `let` statement when possible.",
+ ],
+ },
"Option<MemoryLayoutHoverRenderKindDef>" => set! {
"anyOf": [
{
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 6b7ef04964..78281ec635 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -1150,6 +1150,13 @@ Default: `false`
Hide inlay type hints for constructors.
+## rust-analyzer.inlayHints.typeHints.location {#inlayHints.typeHints.location}
+
+Default: `"inline"`
+
+Where to render type hints relative to their binding pattern.
+
+
## rust-analyzer.interpret.tests {#interpret.tests}
Default: `false`
diff --git a/editors/code/package.json b/editors/code/package.json
index d0410c70da..d3d2ea8d19 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -2513,6 +2513,24 @@
}
},
{
+ "title": "Inlay Hints",
+ "properties": {
+ "rust-analyzer.inlayHints.typeHints.location": {
+ "markdownDescription": "Where to render type hints relative to their binding pattern.",
+ "default": "inline",
+ "type": "string",
+ "enum": [
+ "inline",
+ "end_of_line"
+ ],
+ "enumDescriptions": [
+ "Render type hints directly after the binding identifier.",
+ "Render type hints after the end of the containing `let` statement when possible."
+ ]
+ }
+ }
+ },
+ {
"title": "Interpret",
"properties": {
"rust-analyzer.interpret.tests": {