1use std::mem;
6
7use hir::Mutability;
8use ide_db::famous_defs::FamousDefs;
9
10use ide_db::text_edit::TextEditBuilder;
11use syntax::ast::{self, AstNode};
12
13use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
14
15pub(super) fn hints(
16 acc: &mut Vec<InlayHint>,
17 FamousDefs(sema, _): &FamousDefs<'_, '_>,
18 config: &InlayHintsConfig<'_>,
19 pat: &ast::Pat,
20) -> Option<()> {
21 if !config.binding_mode_hints {
22 return None;
23 }
24
25 let outer_paren_pat = pat.syntax().ancestors().skip(1).map_while(ast::ParenPat::cast).last();
26 let range = outer_paren_pat.as_ref().map_or_else(
27 || match pat {
28 ast::Pat::IdentPat(it) => {
31 it.pat().map_or_else(|| it.syntax().text_range(), |it| it.syntax().text_range())
32 }
33 it => it.syntax().text_range(),
34 },
35 |it| it.syntax().text_range(),
36 );
37 let mut hint = InlayHint {
38 range,
39 kind: InlayKind::BindingMode,
40 label: InlayHintLabel::default(),
41 text_edit: None,
42 position: InlayHintPosition::Before,
43 pad_left: false,
44 pad_right: false,
45 resolve_parent: Some(pat.syntax().text_range()),
46 };
47 let pattern_adjustments = sema.pattern_adjustments(pat);
48 let mut was_mut_last = false;
49 pattern_adjustments.iter().for_each(|ty| {
50 let reference = ty.is_reference();
51 let mut_reference = ty.is_mutable_reference();
52 let r = match (reference, mut_reference) {
53 (true, true) => "&mut",
54 (true, false) => "&",
55 _ => return,
56 };
57 if mem::replace(&mut was_mut_last, mut_reference) {
58 hint.label.append_str(" ");
59 }
60 hint.label.append_str(r);
61 });
62 let acc_base = acc.len();
63 match pat {
64 ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
65 let bm = sema.binding_mode_of_pat(pat)?;
66 let bm = match bm {
67 hir::BindingMode::Move => None,
68 hir::BindingMode::Ref(Mutability::Mut) => Some("ref mut"),
69 hir::BindingMode::Ref(Mutability::Shared) => Some("ref"),
70 };
71 if let Some(bm) = bm {
72 acc.push(InlayHint {
73 range: pat.syntax().text_range(),
74 kind: InlayKind::BindingMode,
75 label: bm.into(),
76 text_edit: None,
77 position: InlayHintPosition::Before,
78 pad_left: false,
79 pad_right: true,
80 resolve_parent: Some(pat.syntax().text_range()),
81 });
82 }
83 }
84 ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
85 hint.label.append_str("(");
86 was_mut_last = false;
87 acc.push(InlayHint::closing_paren_after(
88 InlayKind::BindingMode,
89 pat.syntax().text_range(),
90 ));
91 }
92 _ => (),
93 }
94 if !hint.label.parts.is_empty() {
95 hint.pad_right = was_mut_last;
96 acc.push(hint);
97 }
98
99 if let hints @ [_, ..] = &mut acc[acc_base..] {
100 let edit = config.lazy_text_edit(|| {
101 let mut edit = TextEditBuilder::default();
102 for h in &mut *hints {
103 edit.insert(
104 match h.position {
105 InlayHintPosition::Before => h.range.start(),
106 InlayHintPosition::After => h.range.end(),
107 },
108 h.label
109 .parts
110 .iter()
111 .map(|p| &*p.text)
112 .chain(h.pad_right.then_some(" "))
113 .collect(),
114 );
115 }
116 edit.finish()
117 });
118 hints.iter_mut().for_each(|h| h.text_edit = Some(edit.clone()));
119 }
120
121 Some(())
122}
123
124#[cfg(test)]
125mod tests {
126 use expect_test::expect;
127
128 use crate::{
129 InlayHintsConfig,
130 inlay_hints::tests::{DISABLED_CONFIG, check_edit, check_with_config},
131 };
132
133 #[test]
134 fn hints_binding_modes() {
135 check_with_config(
136 InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
137 r#"
138fn __(
139 (x,): (u32,),
140 (x,): &(u32,),
141 //^^^^&
142 //^ ref
143 (x,): &mut (u32,)
144 //^^^^&mut
145 //^ ref mut
146 (x,): &mut &mut (u32,)
147 //^^^^&mut &mut
148 //^ ref mut
149 (x,): &&(u32,)
150 //^^^^&&
151 //^ ref
152
153) {
154 let (x,) = (0,);
155 let (x,) = &(0,);
156 //^^^^ &
157 //^ ref
158 let (x,) = &mut (0,);
159 //^^^^ &mut
160 //^ ref mut
161 let &mut (x,) = &mut (0,);
162 let (ref mut x,) = &mut (0,);
163 //^^^^^^^^^^^^ &mut
164 let &mut (ref mut x,) = &mut (0,);
165 let (mut x,) = &mut (0,);
166 //^^^^^^^^ &mut
167 match (0,) {
168 (x,) => ()
169 }
170 match &(0,) {
171 (x,) | (x,) => (),
172 //^^^^^^^^^^^)
173 //^^^^^^^^^^^&(
174 //^ ref
175 //^ ref
176 ((x,) | (x,)) => (),
177 //^^^^^^^^^^^^^&
178 //^ ref
179 //^ ref
180 }
181 match &mut (0,) {
182 (x,) => ()
183 //^^^^ &mut
184 //^ ref mut
185 }
186}"#,
187 );
188 }
189
190 #[test]
191 fn hints_binding_modes_complex_ident_pat() {
192 check_with_config(
193 InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
194 r#"
195struct Struct {
196 field: &'static str,
197}
198fn foo(s @ Struct { field, .. }: &Struct) {}
199 //^^^^^^^^^^^^^^^^^^^^&
200 //^^^^^ref
201"#,
202 );
203 }
204
205 #[test]
206 fn edits() {
207 check_edit(
208 InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
209 r#"
210fn main() {
211 match &(0,) {
212 (x,) | (x,) => (),
213 ((x,) | (x,)) => (),
214 }
215}
216"#,
217 expect![[r#"
218 fn main() {
219 match &(0,) {
220 &(&((ref x,) | (ref x,))) => (),
221 &((ref x,) | (ref x,)) => (),
222 }
223 }
224 "#]],
225 );
226 }
227}