Skip to content

Commit c54a827

Browse files
committed
Auto merge of #18075 - roife:fix-issue-17858, r=Veykril
feat: render patterns in params for hovering Fix #17858 This PR introduces an option to [hir-def/src/body/pretty.rs](https://github.com/rust-lang/rust-analyzer/blob/08c7bbc2dbe4dcc8968484f1a0e1e6fe7a1d4f6d/crates/hir-def/src/body/pretty.rs) to render the result as a single line, which is then reused for rendering patterns in parameters for hovering.
2 parents cfe8e37 + 5db510b commit c54a827

File tree

4 files changed

+303
-24
lines changed

4 files changed

+303
-24
lines changed

crates/hir-def/src/body.rs

+11
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,17 @@ impl Body {
227227
pretty::print_expr_hir(db, self, owner, expr, edition)
228228
}
229229

230+
pub fn pretty_print_pat(
231+
&self,
232+
db: &dyn DefDatabase,
233+
owner: DefWithBodyId,
234+
pat: PatId,
235+
oneline: bool,
236+
edition: Edition,
237+
) -> String {
238+
pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
239+
}
240+
230241
fn new(
231242
db: &dyn DefDatabase,
232243
owner: DefWithBodyId,

crates/hir-def/src/body/pretty.rs

+106-19
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ use crate::{
1616

1717
use super::*;
1818

19+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20+
pub(super) enum LineFormat {
21+
Oneline,
22+
Newline,
23+
Indentation,
24+
}
25+
1926
pub(super) fn print_body_hir(
2027
db: &dyn DefDatabase,
2128
body: &Body,
@@ -52,7 +59,14 @@ pub(super) fn print_body_hir(
5259
}
5360
};
5461

55-
let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition };
62+
let mut p = Printer {
63+
db,
64+
body,
65+
buf: header,
66+
indent_level: 0,
67+
line_format: LineFormat::Newline,
68+
edition,
69+
};
5670
if let DefWithBodyId::FunctionId(it) = owner {
5771
p.buf.push('(');
5872
let function_data = &db.function_data(it);
@@ -95,12 +109,38 @@ pub(super) fn print_expr_hir(
95109
expr: ExprId,
96110
edition: Edition,
97111
) -> String {
98-
let mut p =
99-
Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition };
112+
let mut p = Printer {
113+
db,
114+
body,
115+
buf: String::new(),
116+
indent_level: 0,
117+
line_format: LineFormat::Newline,
118+
edition,
119+
};
100120
p.print_expr(expr);
101121
p.buf
102122
}
103123

124+
pub(super) fn print_pat_hir(
125+
db: &dyn DefDatabase,
126+
body: &Body,
127+
_owner: DefWithBodyId,
128+
pat: PatId,
129+
oneline: bool,
130+
edition: Edition,
131+
) -> String {
132+
let mut p = Printer {
133+
db,
134+
body,
135+
buf: String::new(),
136+
indent_level: 0,
137+
line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline },
138+
edition,
139+
};
140+
p.print_pat(pat);
141+
p.buf
142+
}
143+
104144
macro_rules! w {
105145
($dst:expr, $($arg:tt)*) => {
106146
{ let _ = write!($dst, $($arg)*); }
@@ -109,10 +149,10 @@ macro_rules! w {
109149

110150
macro_rules! wln {
111151
($dst:expr) => {
112-
{ let _ = writeln!($dst); }
152+
{ $dst.newline(); }
113153
};
114154
($dst:expr, $($arg:tt)*) => {
115-
{ let _ = writeln!($dst, $($arg)*); }
155+
{ let _ = w!($dst, $($arg)*); $dst.newline(); }
116156
};
117157
}
118158

@@ -121,24 +161,30 @@ struct Printer<'a> {
121161
body: &'a Body,
122162
buf: String,
123163
indent_level: usize,
124-
needs_indent: bool,
164+
line_format: LineFormat,
125165
edition: Edition,
126166
}
127167

128168
impl Write for Printer<'_> {
129169
fn write_str(&mut self, s: &str) -> fmt::Result {
130170
for line in s.split_inclusive('\n') {
131-
if self.needs_indent {
171+
if matches!(self.line_format, LineFormat::Indentation) {
132172
match self.buf.chars().rev().find(|ch| *ch != ' ') {
133173
Some('\n') | None => {}
134174
_ => self.buf.push('\n'),
135175
}
136176
self.buf.push_str(&" ".repeat(self.indent_level));
137-
self.needs_indent = false;
138177
}
139178

140179
self.buf.push_str(line);
141-
self.needs_indent = line.ends_with('\n');
180+
181+
if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) {
182+
self.line_format = if line.ends_with('\n') {
183+
LineFormat::Indentation
184+
} else {
185+
LineFormat::Newline
186+
};
187+
}
142188
}
143189

144190
Ok(())
@@ -161,14 +207,28 @@ impl Printer<'_> {
161207
}
162208
}
163209

210+
// Add a newline if the current line is not empty.
211+
// If the current line is empty, add a space instead.
212+
//
213+
// Do not use [`writeln!()`] or [`wln!()`] here, which will result in
214+
// infinite recursive calls to this function.
164215
fn newline(&mut self) {
165-
match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
166-
Some((_, '\n')) | None => {}
167-
Some((idx, _)) => {
168-
if idx != 0 {
169-
self.buf.drain(self.buf.len() - idx..);
216+
if matches!(self.line_format, LineFormat::Oneline) {
217+
match self.buf.chars().last() {
218+
Some(' ') | None => {}
219+
Some(_) => {
220+
w!(self, " ");
221+
}
222+
}
223+
} else {
224+
match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
225+
Some((_, '\n')) | None => {}
226+
Some((idx, _)) => {
227+
if idx != 0 {
228+
self.buf.drain(self.buf.len() - idx..);
229+
}
230+
w!(self, "\n");
170231
}
171-
writeln!(self).unwrap()
172232
}
173233
}
174234
}
@@ -539,12 +599,14 @@ impl Printer<'_> {
539599
w!(self, ")");
540600
}
541601
Pat::Or(pats) => {
602+
w!(self, "(");
542603
for (i, pat) in pats.iter().enumerate() {
543604
if i != 0 {
544605
w!(self, " | ");
545606
}
546607
self.print_pat(*pat);
547608
}
609+
w!(self, ")");
548610
}
549611
Pat::Record { path, args, ellipsis } => {
550612
match path {
@@ -554,12 +616,37 @@ impl Printer<'_> {
554616

555617
w!(self, " {{");
556618
let edition = self.edition;
619+
let oneline = matches!(self.line_format, LineFormat::Oneline);
557620
self.indented(|p| {
558-
for arg in args.iter() {
559-
w!(p, "{}: ", arg.name.display(self.db.upcast(), edition));
560-
p.print_pat(arg.pat);
561-
wln!(p, ",");
621+
for (idx, arg) in args.iter().enumerate() {
622+
let field_name = arg.name.display(self.db.upcast(), edition).to_string();
623+
624+
let mut same_name = false;
625+
if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] {
626+
if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
627+
&self.body.bindings[*id]
628+
{
629+
if name.as_str() == field_name {
630+
same_name = true;
631+
}
632+
}
633+
}
634+
635+
w!(p, "{}", field_name);
636+
637+
if !same_name {
638+
w!(p, ": ");
639+
p.print_pat(arg.pat);
640+
}
641+
642+
// Do not print the extra comma if the line format is oneline
643+
if oneline && idx == args.len() - 1 {
644+
w!(p, " ");
645+
} else {
646+
wln!(p, ",");
647+
}
562648
}
649+
563650
if *ellipsis {
564651
wln!(p, "..");
565652
}

crates/hir/src/display.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,20 @@ impl HirDisplay for Function {
9999
}
100100

101101
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
102+
let body = db.body(self.id.into());
102103
for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
103-
let local = param.as_local(db).map(|it| it.name(db));
104104
if !first {
105105
f.write_str(", ")?;
106106
} else {
107107
first = false;
108108
}
109-
match local {
110-
Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?,
111-
None => f.write_str("_: ")?,
112-
}
109+
110+
let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
111+
let pat_str =
112+
body.pretty_print_pat(db.upcast(), self.id.into(), pat_id, true, f.edition());
113+
f.write_str(&pat_str)?;
114+
115+
f.write_str(": ")?;
113116
type_ref.hir_fmt(f)?;
114117
}
115118

0 commit comments

Comments
 (0)