Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 13 | ||||
| -rw-r--r-- | crates/ide/src/inlay_hints/bind_pat.rs | 95 | ||||
| -rw-r--r-- | crates/ide/src/inlay_hints/chaining.rs | 107 | ||||
| -rw-r--r-- | crates/ide/src/lib.rs | 2 | ||||
| -rw-r--r-- | crates/ide/src/static_index.rs | 3 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/cli/analysis_stats.rs | 1 | ||||
| -rw-r--r-- | crates/rust-analyzer/src/config.rs | 22 | ||||
| -rw-r--r-- | docs/book/src/configuration_generated.md | 7 | ||||
| -rw-r--r-- | editors/code/package.json | 18 |
9 files changed, 250 insertions, 18 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 83f2660433..f51d7f5207 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -303,6 +303,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, @@ -332,6 +333,12 @@ pub struct InlayHintsConfig<'a> { pub ra_fixture: RaFixtureConfig<'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 { @@ -908,12 +915,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, @@ -947,6 +957,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 caf7cc714d..b901c6b67d 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,6 +196,7 @@ 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_expect, check_no_edit, check_with_config, @@ -205,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 2f89efd33f..93b455eb8e 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 2f63aa0d8c..bcc41d426e 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -17,7 +17,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, }; @@ -168,6 +168,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 19d5e8fdce..e56727d39d 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1367,6 +1367,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 eb390f024f..1bc164b157 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -314,6 +314,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, @@ -2016,6 +2019,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 @@ -3015,6 +3022,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")] @@ -3924,6 +3938,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 31ef4fddea..aff2e32b63 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -1156,6 +1156,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 a1a414b4f4..484c2700ae 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2512,6 +2512,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": { |