Skip to content

Commit fe93675

Browse files
committed
New HirDisplay method for displaying sourcecode
1 parent d3eb9d8 commit fe93675

File tree

4 files changed

+184
-41
lines changed

4 files changed

+184
-41
lines changed

crates/ra_hir/src/code_model.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ use hir_expand::{
2222
MacroDefId, MacroDefKind,
2323
};
2424
use hir_ty::{
25-
autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
26-
Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
25+
autoderef,
26+
display::{HirDisplayError, HirFormatter},
27+
expr::ExprValidator,
28+
method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty,
29+
TyDefId, TypeCtor,
2730
};
2831
use ra_db::{CrateId, CrateName, Edition, FileId};
2932
use ra_prof::profile;
@@ -1319,7 +1322,7 @@ impl Type {
13191322
}
13201323

13211324
impl HirDisplay for Type {
1322-
fn hir_fmt(&self, f: &mut HirFormatter) -> std::fmt::Result {
1325+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
13231326
self.ty.value.hir_fmt(f)
13241327
}
13251328
}

crates/ra_hir_ty/src/display.rs

Lines changed: 140 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,42 @@ use crate::{
66
db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate,
77
Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
88
};
9-
use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup};
9+
use hir_def::{
10+
find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId,
11+
Lookup, ModuleId,
12+
};
1013
use hir_expand::name::Name;
1114

12-
pub struct HirFormatter<'a, 'b> {
15+
pub struct HirFormatter<'a> {
1316
pub db: &'a dyn HirDatabase,
14-
fmt: &'a mut fmt::Formatter<'b>,
17+
fmt: &'a mut dyn fmt::Write,
1518
buf: String,
1619
curr_size: usize,
1720
pub(crate) max_size: Option<usize>,
1821
omit_verbose_types: bool,
22+
display_target: DisplayTarget,
1923
}
2024

2125
pub trait HirDisplay {
22-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result;
26+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>;
2327

28+
/// Returns a `Display`able type that is human-readable.
29+
/// Use this for showing types to the user (e.g. diagnostics)
2430
fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self>
2531
where
2632
Self: Sized,
2733
{
28-
HirDisplayWrapper(db, self, None, false)
34+
HirDisplayWrapper {
35+
db,
36+
t: self,
37+
max_size: None,
38+
omit_verbose_types: false,
39+
display_target: DisplayTarget::Diagnostics,
40+
}
2941
}
3042

43+
/// Returns a `Display`able type that is human-readable and tries to be succinct.
44+
/// Use this for showing types to the user where space is constrained (e.g. doc popups)
3145
fn display_truncated<'a>(
3246
&'a self,
3347
db: &'a dyn HirDatabase,
@@ -36,16 +50,46 @@ pub trait HirDisplay {
3650
where
3751
Self: Sized,
3852
{
39-
HirDisplayWrapper(db, self, max_size, true)
53+
HirDisplayWrapper {
54+
db,
55+
t: self,
56+
max_size,
57+
omit_verbose_types: true,
58+
display_target: DisplayTarget::Diagnostics,
59+
}
60+
}
61+
62+
/// Returns a String representation of `self` that can be inserted into the given module.
63+
/// Use this when generating code (e.g. assists)
64+
fn display_source_code<'a>(
65+
&'a self,
66+
db: &'a dyn HirDatabase,
67+
module_id: ModuleId,
68+
) -> Result<String, DisplaySourceCodeError> {
69+
let mut result = String::new();
70+
match self.hir_fmt(&mut HirFormatter {
71+
db,
72+
fmt: &mut result,
73+
buf: String::with_capacity(20),
74+
curr_size: 0,
75+
max_size: None,
76+
omit_verbose_types: false,
77+
display_target: DisplayTarget::SourceCode { module_id },
78+
}) {
79+
Ok(()) => {}
80+
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
81+
Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e),
82+
};
83+
Ok(result)
4084
}
4185
}
4286

43-
impl<'a, 'b> HirFormatter<'a, 'b> {
87+
impl<'a> HirFormatter<'a> {
4488
pub fn write_joined<T: HirDisplay>(
4589
&mut self,
4690
iter: impl IntoIterator<Item = T>,
4791
sep: &str,
48-
) -> fmt::Result {
92+
) -> Result<(), HirDisplayError> {
4993
let mut first = true;
5094
for e in iter {
5195
if !first {
@@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> {
58102
}
59103

60104
/// This allows using the `write!` macro directly with a `HirFormatter`.
61-
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
105+
pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> {
62106
// We write to a buffer first to track output size
63107
self.buf.clear();
64108
fmt::write(&mut self.buf, args)?;
65109
self.curr_size += self.buf.len();
66110

67111
// Then we write to the internal formatter from the buffer
68-
self.fmt.write_str(&self.buf)
112+
self.fmt.write_str(&self.buf).map_err(HirDisplayError::from)
69113
}
70114

71115
pub fn should_truncate(&self) -> bool {
@@ -81,34 +125,76 @@ impl<'a, 'b> HirFormatter<'a, 'b> {
81125
}
82126
}
83127

84-
pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option<usize>, bool);
128+
#[derive(Clone, Copy)]
129+
enum DisplayTarget {
130+
/// Display types for inlays, doc popups, autocompletion, etc...
131+
/// Showing `{unknown}` or not qualifying paths is fine here.
132+
/// There's no reason for this to fail.
133+
Diagnostics,
134+
/// Display types for inserting them in source files.
135+
/// The generated code should compile, so paths need to be qualified.
136+
SourceCode { module_id: ModuleId },
137+
}
138+
139+
#[derive(Debug)]
140+
pub enum DisplaySourceCodeError {
141+
PathNotFound,
142+
}
143+
144+
pub enum HirDisplayError {
145+
/// Errors that can occur when generating source code
146+
DisplaySourceCodeError(DisplaySourceCodeError),
147+
/// `FmtError` is required to be compatible with std::fmt::Display
148+
FmtError,
149+
}
150+
impl From<fmt::Error> for HirDisplayError {
151+
fn from(_: fmt::Error) -> Self {
152+
Self::FmtError
153+
}
154+
}
155+
156+
pub struct HirDisplayWrapper<'a, T> {
157+
db: &'a dyn HirDatabase,
158+
t: &'a T,
159+
max_size: Option<usize>,
160+
omit_verbose_types: bool,
161+
display_target: DisplayTarget,
162+
}
85163

86164
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
87165
where
88166
T: HirDisplay,
89167
{
90168
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91-
self.1.hir_fmt(&mut HirFormatter {
92-
db: self.0,
169+
match self.t.hir_fmt(&mut HirFormatter {
170+
db: self.db,
93171
fmt: f,
94172
buf: String::with_capacity(20),
95173
curr_size: 0,
96-
max_size: self.2,
97-
omit_verbose_types: self.3,
98-
})
174+
max_size: self.max_size,
175+
omit_verbose_types: self.omit_verbose_types,
176+
display_target: self.display_target,
177+
}) {
178+
Ok(()) => Ok(()),
179+
Err(HirDisplayError::FmtError) => Err(fmt::Error),
180+
Err(HirDisplayError::DisplaySourceCodeError(_)) => {
181+
// This should never happen
182+
panic!("HirDisplay failed when calling Display::fmt!")
183+
}
184+
}
99185
}
100186
}
101187

102188
const TYPE_HINT_TRUNCATION: &str = "…";
103189

104190
impl HirDisplay for &Ty {
105-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
191+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
106192
HirDisplay::hir_fmt(*self, f)
107193
}
108194
}
109195

110196
impl HirDisplay for ApplicationTy {
111-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
197+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
112198
if f.should_truncate() {
113199
return write!(f, "{}", TYPE_HINT_TRUNCATION);
114200
}
@@ -191,12 +277,30 @@ impl HirDisplay for ApplicationTy {
191277
}
192278
}
193279
TypeCtor::Adt(def_id) => {
194-
let name = match def_id {
195-
AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
196-
AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
197-
AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
198-
};
199-
write!(f, "{}", name)?;
280+
match f.display_target {
281+
DisplayTarget::Diagnostics => {
282+
let name = match def_id {
283+
AdtId::StructId(it) => f.db.struct_data(it).name.clone(),
284+
AdtId::UnionId(it) => f.db.union_data(it).name.clone(),
285+
AdtId::EnumId(it) => f.db.enum_data(it).name.clone(),
286+
};
287+
write!(f, "{}", name)?;
288+
}
289+
DisplayTarget::SourceCode { module_id } => {
290+
if let Some(path) = find_path::find_path(
291+
f.db.upcast(),
292+
ItemInNs::Types(def_id.into()),
293+
module_id,
294+
) {
295+
write!(f, "{}", path)?;
296+
} else {
297+
return Err(HirDisplayError::DisplaySourceCodeError(
298+
DisplaySourceCodeError::PathNotFound,
299+
));
300+
}
301+
}
302+
}
303+
200304
if self.parameters.len() > 0 {
201305
let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
202306
let parameters_to_write = if f.omit_verbose_types() {
@@ -269,7 +373,7 @@ impl HirDisplay for ApplicationTy {
269373
}
270374

271375
impl HirDisplay for ProjectionTy {
272-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
376+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
273377
if f.should_truncate() {
274378
return write!(f, "{}", TYPE_HINT_TRUNCATION);
275379
}
@@ -287,7 +391,7 @@ impl HirDisplay for ProjectionTy {
287391
}
288392

289393
impl HirDisplay for Ty {
290-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
394+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
291395
if f.should_truncate() {
292396
return write!(f, "{}", TYPE_HINT_TRUNCATION);
293397
}
@@ -332,7 +436,7 @@ impl HirDisplay for Ty {
332436
fn write_bounds_like_dyn_trait(
333437
predicates: &[GenericPredicate],
334438
f: &mut HirFormatter,
335-
) -> fmt::Result {
439+
) -> Result<(), HirDisplayError> {
336440
// Note: This code is written to produce nice results (i.e.
337441
// corresponding to surface Rust) for types that can occur in
338442
// actual Rust. It will have weird results if the predicates
@@ -394,7 +498,7 @@ fn write_bounds_like_dyn_trait(
394498
}
395499

396500
impl TraitRef {
397-
fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result {
501+
fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> {
398502
if f.should_truncate() {
399503
return write!(f, "{}", TYPE_HINT_TRUNCATION);
400504
}
@@ -416,19 +520,19 @@ impl TraitRef {
416520
}
417521

418522
impl HirDisplay for TraitRef {
419-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
523+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
420524
self.hir_fmt_ext(f, false)
421525
}
422526
}
423527

424528
impl HirDisplay for &GenericPredicate {
425-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
529+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
426530
HirDisplay::hir_fmt(*self, f)
427531
}
428532
}
429533

430534
impl HirDisplay for GenericPredicate {
431-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
535+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
432536
if f.should_truncate() {
433537
return write!(f, "{}", TYPE_HINT_TRUNCATION);
434538
}
@@ -452,15 +556,15 @@ impl HirDisplay for GenericPredicate {
452556
}
453557

454558
impl HirDisplay for Obligation {
455-
fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result {
456-
match self {
457-
Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)),
559+
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
560+
Ok(match self {
561+
Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?,
458562
Obligation::Projection(proj) => write!(
459563
f,
460564
"Normalize({} => {})",
461565
proj.projection_ty.display(f.db),
462566
proj.ty.display(f.db)
463-
),
464-
}
567+
)?,
568+
})
465569
}
466570
}

crates/ra_hir_ty/src/tests.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod patterns;
66
mod traits;
77
mod method_resolution;
88
mod macros;
9+
mod display_source_code;
910

1011
use std::sync::Arc;
1112

@@ -16,7 +17,7 @@ use hir_def::{
1617
item_scope::ItemScope,
1718
keys,
1819
nameres::CrateDefMap,
19-
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
20+
AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
2021
};
2122
use hir_expand::{db::AstDatabase, InFile};
2223
use insta::assert_snapshot;
@@ -37,6 +38,18 @@ use crate::{
3738
// update the snapshots.
3839

3940
fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
41+
type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string())
42+
}
43+
44+
fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String {
45+
type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap())
46+
}
47+
48+
fn type_at_pos_displayed(
49+
db: &TestDB,
50+
pos: FilePosition,
51+
display_fn: impl FnOnce(&Ty, ModuleId) -> String,
52+
) -> String {
4053
let file = db.parse(pos.file_id).ok().unwrap();
4154
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
4255
let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
@@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String {
4962
if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) {
5063
let infer = db.infer(func.into());
5164
let ty = &infer[expr_id];
52-
return ty.display(db).to_string();
65+
return display_fn(ty, module);
5366
}
5467
panic!("Can't find expression")
5568
}

0 commit comments

Comments
 (0)