mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Move type inlay hint truncation to language server
This commit implements a general truncation framework for HirFormatter that keeps track of how much has been output so far. This information can then be used to perform truncation inside the language server, instead of relying on the client. Initial support is implemented for truncating types hints using the maxInlayHintLength server config option. The existing solution in the VSCode extension has been removed in favor of letting the server truncate type hints.
This commit is contained in:
parent
c24ee09904
commit
dadad36bb9
@ -800,6 +800,10 @@ impl HirDisplay for &Ty {
|
||||
|
||||
impl HirDisplay for ApplicationTy {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "…");
|
||||
}
|
||||
|
||||
match self.ctor {
|
||||
TypeCtor::Bool => write!(f, "bool")?,
|
||||
TypeCtor::Char => write!(f, "char")?,
|
||||
@ -901,6 +905,10 @@ impl HirDisplay for ApplicationTy {
|
||||
|
||||
impl HirDisplay for ProjectionTy {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "…");
|
||||
}
|
||||
|
||||
let trait_name = self
|
||||
.associated_ty
|
||||
.parent_trait(f.db)
|
||||
@ -919,6 +927,10 @@ impl HirDisplay for ProjectionTy {
|
||||
|
||||
impl HirDisplay for Ty {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "…");
|
||||
}
|
||||
|
||||
match self {
|
||||
Ty::Apply(a_ty) => a_ty.hir_fmt(f)?,
|
||||
Ty::Projection(p_ty) => p_ty.hir_fmt(f)?,
|
||||
@ -1001,6 +1013,10 @@ impl HirDisplay for Ty {
|
||||
|
||||
impl TraitRef {
|
||||
fn hir_fmt_ext(&self, f: &mut HirFormatter<impl HirDatabase>, use_as: bool) -> fmt::Result {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "…");
|
||||
}
|
||||
|
||||
self.substs[0].hir_fmt(f)?;
|
||||
if use_as {
|
||||
write!(f, " as ")?;
|
||||
@ -1031,6 +1047,10 @@ impl HirDisplay for &GenericPredicate {
|
||||
|
||||
impl HirDisplay for GenericPredicate {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result {
|
||||
if f.should_truncate() {
|
||||
return write!(f, "…");
|
||||
}
|
||||
|
||||
match self {
|
||||
GenericPredicate::Implemented(trait_ref) => trait_ref.hir_fmt(f)?,
|
||||
GenericPredicate::Projection(projection_pred) => {
|
||||
|
@ -7,15 +7,30 @@ use crate::db::HirDatabase;
|
||||
pub struct HirFormatter<'a, 'b, DB> {
|
||||
pub db: &'a DB,
|
||||
fmt: &'a mut fmt::Formatter<'b>,
|
||||
buf: String,
|
||||
curr_size: usize,
|
||||
max_size: Option<usize>,
|
||||
}
|
||||
|
||||
pub trait HirDisplay {
|
||||
fn hir_fmt(&self, f: &mut HirFormatter<impl HirDatabase>) -> fmt::Result;
|
||||
|
||||
fn display<'a, DB>(&'a self, db: &'a DB) -> HirDisplayWrapper<'a, DB, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper(db, self)
|
||||
HirDisplayWrapper(db, self, None)
|
||||
}
|
||||
|
||||
fn display_truncated<'a, DB>(
|
||||
&'a self,
|
||||
db: &'a DB,
|
||||
max_size: Option<usize>,
|
||||
) -> HirDisplayWrapper<'a, DB, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
HirDisplayWrapper(db, self, max_size)
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,11 +56,25 @@ where
|
||||
|
||||
/// This allows using the `write!` macro directly with a `HirFormatter`.
|
||||
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
||||
fmt::write(self.fmt, args)
|
||||
// We write to a buffer first to track output size
|
||||
self.buf.clear();
|
||||
fmt::write(&mut self.buf, args)?;
|
||||
self.curr_size += self.buf.len();
|
||||
|
||||
// Then we write to the internal formatter from the buffer
|
||||
self.fmt.write_str(&self.buf)
|
||||
}
|
||||
|
||||
pub fn should_truncate(&self) -> bool {
|
||||
if let Some(max_size) = self.max_size {
|
||||
self.curr_size >= max_size
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HirDisplayWrapper<'a, DB, T>(&'a DB, &'a T);
|
||||
pub struct HirDisplayWrapper<'a, DB, T>(&'a DB, &'a T, Option<usize>);
|
||||
|
||||
impl<'a, DB, T> fmt::Display for HirDisplayWrapper<'a, DB, T>
|
||||
where
|
||||
@ -53,6 +82,12 @@ where
|
||||
T: HirDisplay,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.1.hir_fmt(&mut HirFormatter { db: self.0, fmt: f })
|
||||
self.1.hir_fmt(&mut HirFormatter {
|
||||
db: self.0,
|
||||
fmt: f,
|
||||
buf: String::with_capacity(20),
|
||||
curr_size: 0,
|
||||
max_size: self.2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,15 @@ pub struct InlayHint {
|
||||
pub label: SmolStr,
|
||||
}
|
||||
|
||||
pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec<InlayHint> {
|
||||
pub(crate) fn inlay_hints(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
file: &SourceFile,
|
||||
max_inlay_hint_length: Option<usize>,
|
||||
) -> Vec<InlayHint> {
|
||||
file.syntax()
|
||||
.descendants()
|
||||
.map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default())
|
||||
.map(|node| get_inlay_hints(db, file_id, &node, max_inlay_hint_length).unwrap_or_default())
|
||||
.flatten()
|
||||
.collect()
|
||||
}
|
||||
@ -31,6 +36,7 @@ fn get_inlay_hints(
|
||||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
node: &SyntaxNode,
|
||||
max_inlay_hint_length: Option<usize>,
|
||||
) -> Option<Vec<InlayHint>> {
|
||||
let analyzer = SourceAnalyzer::new(db, hir::Source::new(file_id.into(), node), None);
|
||||
match_ast! {
|
||||
@ -40,7 +46,7 @@ fn get_inlay_hints(
|
||||
return None;
|
||||
}
|
||||
let pat = it.pat()?;
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false, max_inlay_hint_length))
|
||||
},
|
||||
ast::LambdaExpr(it) => {
|
||||
it.param_list().map(|param_list| {
|
||||
@ -48,22 +54,22 @@ fn get_inlay_hints(
|
||||
.params()
|
||||
.filter(|closure_param| closure_param.ascribed_type().is_none())
|
||||
.filter_map(|closure_param| closure_param.pat())
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false, max_inlay_hint_length))
|
||||
.flatten()
|
||||
.collect()
|
||||
})
|
||||
},
|
||||
ast::ForExpr(it) => {
|
||||
let pat = it.pat()?;
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false, max_inlay_hint_length))
|
||||
},
|
||||
ast::IfExpr(it) => {
|
||||
let pat = it.condition()?.pat()?;
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true, max_inlay_hint_length))
|
||||
},
|
||||
ast::WhileExpr(it) => {
|
||||
let pat = it.condition()?.pat()?;
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true, max_inlay_hint_length))
|
||||
},
|
||||
ast::MatchArmList(it) => {
|
||||
Some(
|
||||
@ -71,7 +77,7 @@ fn get_inlay_hints(
|
||||
.arms()
|
||||
.map(|match_arm| match_arm.pats())
|
||||
.flatten()
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true, max_inlay_hint_length))
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
@ -86,6 +92,7 @@ fn get_pat_type_hints(
|
||||
analyzer: &SourceAnalyzer,
|
||||
root_pat: ast::Pat,
|
||||
skip_root_pat_hint: bool,
|
||||
max_inlay_hint_length: Option<usize>,
|
||||
) -> Vec<InlayHint> {
|
||||
let original_pat = &root_pat.clone();
|
||||
|
||||
@ -99,7 +106,7 @@ fn get_pat_type_hints(
|
||||
.map(|(range, pat_type)| InlayHint {
|
||||
range,
|
||||
kind: InlayKind::TypeHint,
|
||||
label: pat_type.display(db).to_string().into(),
|
||||
label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@ -209,7 +216,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [193; 197),
|
||||
@ -278,7 +285,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [21; 30),
|
||||
@ -307,7 +314,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [21; 30),
|
||||
@ -355,7 +362,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [166; 170),
|
||||
@ -418,7 +425,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [166; 170),
|
||||
@ -481,7 +488,7 @@ fn main() {
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id).unwrap(), @r###"
|
||||
assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###"
|
||||
[
|
||||
InlayHint {
|
||||
range: [311; 315),
|
||||
|
@ -338,8 +338,14 @@ impl Analysis {
|
||||
}
|
||||
|
||||
/// Returns a list of the places in the file where type hints can be displayed.
|
||||
pub fn inlay_hints(&self, file_id: FileId) -> Cancelable<Vec<InlayHint>> {
|
||||
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree()))
|
||||
pub fn inlay_hints(
|
||||
&self,
|
||||
file_id: FileId,
|
||||
max_inlay_hint_length: Option<usize>,
|
||||
) -> Cancelable<Vec<InlayHint>> {
|
||||
self.with_db(|db| {
|
||||
inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree(), max_inlay_hint_length)
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the set of folding ranges.
|
||||
|
@ -29,6 +29,8 @@ pub struct ServerConfig {
|
||||
|
||||
pub lru_capacity: Option<usize>,
|
||||
|
||||
pub max_inlay_hint_length: Option<usize>,
|
||||
|
||||
/// For internal usage to make integrated tests faster.
|
||||
#[serde(deserialize_with = "nullable_bool_true")]
|
||||
pub with_sysroot: bool,
|
||||
@ -44,6 +46,7 @@ impl Default for ServerConfig {
|
||||
exclude_globs: Vec::new(),
|
||||
use_client_watching: false,
|
||||
lru_capacity: None,
|
||||
max_inlay_hint_length: None,
|
||||
with_sysroot: true,
|
||||
feature_flags: FxHashMap::default(),
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ pub fn main_loop(
|
||||
.and_then(|it| it.folding_range.as_ref())
|
||||
.and_then(|it| it.line_folding_only)
|
||||
.unwrap_or(false),
|
||||
max_inlay_hint_length: config.max_inlay_hint_length,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -870,7 +870,7 @@ pub fn handle_inlay_hints(
|
||||
let analysis = world.analysis();
|
||||
let line_index = analysis.file_line_index(file_id)?;
|
||||
Ok(analysis
|
||||
.inlay_hints(file_id)?
|
||||
.inlay_hints(file_id, world.options.max_inlay_hint_length)?
|
||||
.into_iter()
|
||||
.map(|api_type| InlayHint {
|
||||
label: api_type.label.to_string(),
|
||||
|
@ -28,6 +28,7 @@ pub struct Options {
|
||||
pub publish_decorations: bool,
|
||||
pub supports_location_link: bool,
|
||||
pub line_folding_only: bool,
|
||||
pub max_inlay_hint_length: Option<usize>,
|
||||
}
|
||||
|
||||
/// `WorldState` is the primary mutable state of the language server
|
||||
|
@ -87,7 +87,7 @@ export class HintsUpdater {
|
||||
range: hint.range,
|
||||
renderOptions: {
|
||||
after: {
|
||||
contentText: `: ${this.truncateHint(hint.label)}`
|
||||
contentText: `: ${hint.label}`
|
||||
}
|
||||
}
|
||||
}));
|
||||
@ -98,18 +98,6 @@ export class HintsUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
private truncateHint(label: string): string {
|
||||
if (!Server.config.maxInlayHintLength) {
|
||||
return label;
|
||||
}
|
||||
|
||||
let newLabel = label.substring(0, Server.config.maxInlayHintLength);
|
||||
if (label.length > Server.config.maxInlayHintLength) {
|
||||
newLabel += '…';
|
||||
}
|
||||
return newLabel;
|
||||
}
|
||||
|
||||
private async queryHints(documentUri: string): Promise<InlayHint[] | null> {
|
||||
const request: InlayHintsParams = {
|
||||
textDocument: { uri: documentUri }
|
||||
|
@ -43,6 +43,7 @@ export class Server {
|
||||
initializationOptions: {
|
||||
publishDecorations: true,
|
||||
lruCapacity: Server.config.lruCapacity,
|
||||
maxInlayHintLength: Server.config.maxInlayHintLength,
|
||||
excludeGlobs: Server.config.excludeGlobs,
|
||||
useClientWatching: Server.config.useClientWatching,
|
||||
featureFlags: Server.config.featureFlags
|
||||
|
Loading…
Reference in New Issue
Block a user