Load hints for part of the file only

This commit is contained in:
Kirill Bulatov 2022-02-12 00:48:01 +02:00 committed by Laurențiu Nicola
parent 9c0c199e96
commit b1d8dae930
5 changed files with 107 additions and 20 deletions

View File

@ -5,7 +5,7 @@
use stdx::to_lower_snake_case;
use syntax::{
ast::{self, AstNode, HasArgList, HasName, UnaryOp},
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, TextRange, T,
};
use crate::FileId;
@ -58,6 +58,7 @@ pub struct InlayHint {
pub(crate) fn inlay_hints(
db: &RootDatabase,
file_id: FileId,
range_limit: Option<FileRange>,
config: &InlayHintsConfig,
) -> Vec<InlayHint> {
let _p = profile::span("inlay_hints");
@ -65,25 +66,50 @@ pub(crate) fn inlay_hints(
let file = sema.parse(file_id);
let file = file.syntax();
let mut res = Vec::new();
let mut hints = Vec::new();
for node in file.descendants() {
if let Some(expr) = ast::Expr::cast(node.clone()) {
get_chaining_hints(&mut res, &sema, config, &expr);
match expr {
ast::Expr::CallExpr(it) => {
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
if let Some(range_limit) = range_limit {
let range_limit = range_limit.range;
match file.covering_element(range_limit) {
NodeOrToken::Token(_) => return hints,
NodeOrToken::Node(n) => {
for node in n
.descendants()
.filter(|descendant| range_limit.contains_range(descendant.text_range()))
{
get_hints(&mut hints, &sema, config, node);
}
ast::Expr::MethodCallExpr(it) => {
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
}
_ => (),
}
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
get_bind_pat_hints(&mut res, &sema, config, &it);
}
} else {
for node in file.descendants() {
get_hints(&mut hints, &sema, config, node);
}
}
res
hints
}
fn get_hints(
hints: &mut Vec<InlayHint>,
sema: &Semantics<RootDatabase>,
config: &InlayHintsConfig,
node: SyntaxNode,
) {
if let Some(expr) = ast::Expr::cast(node.clone()) {
get_chaining_hints(hints, sema, config, &expr);
match expr {
ast::Expr::CallExpr(it) => {
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
}
ast::Expr::MethodCallExpr(it) => {
get_param_name_hints(hints, sema, config, ast::Expr::from(it));
}
_ => (),
}
} else if let Some(it) = ast::IdentPat::cast(node) {
get_bind_pat_hints(hints, sema, config, &it);
}
}
fn get_chaining_hints(
@ -541,6 +567,8 @@ fn get_callable(
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
use ide_db::base_db::FileRange;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
use crate::{fixture, inlay_hints::InlayHintsConfig};
@ -604,7 +632,7 @@ fn check_chains(ra_fixture: &str) {
fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
let (analysis, file_id) = fixture::file(ra_fixture);
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let actual =
inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
@ -613,7 +641,7 @@ fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
#[track_caller]
fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
let (analysis, file_id) = fixture::file(ra_fixture);
let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
expect.assert_debug_eq(&inlay_hints)
}
@ -1045,6 +1073,55 @@ fn main() {
)
}
#[test]
fn check_hint_range_limit() {
let fixture = r#"
//- minicore: fn, sized
fn foo() -> impl Fn() { loop {} }
fn foo1() -> impl Fn(f64) { loop {} }
fn foo2() -> impl Fn(f64, f64) { loop {} }
fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
fn main() {
let foo = foo();
let foo = foo1();
let foo = foo2();
let foo = foo3();
// ^^^ impl Fn(f64, f64) -> u32
let foo = foo4();
// ^^^ &dyn Fn(f64, f64) -> u32
let foo = foo5();
let foo = foo6();
let foo = foo7();
}
"#;
let (analysis, file_id) = fixture::file(fixture);
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
let inlay_hints = analysis
.inlay_hints(
&InlayHintsConfig {
parameter_hints: false,
type_hints: true,
chaining_hints: false,
hide_named_constructor_hints: false,
max_length: None,
},
file_id,
Some(FileRange {
file_id,
range: TextRange::new(TextSize::from(500), TextSize::from(600)),
}),
)
.unwrap();
let actual =
inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
}
#[test]
fn fn_hints_ptr_rpit_fn_parentheses() {
check_types(

View File

@ -358,8 +358,9 @@ pub fn inlay_hints(
&self,
config: &InlayHintsConfig,
file_id: FileId,
range: Option<FileRange>,
) -> Cancellable<Vec<InlayHint>> {
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config))
}
/// Returns the set of folding ranges.

View File

@ -112,6 +112,7 @@ fn add_file(&mut self, file_id: FileId) {
max_length: Some(25),
},
file_id,
None,
)
.unwrap();
// hovers

View File

@ -1318,11 +1318,18 @@ pub(crate) fn handle_inlay_hints(
params: InlayHintsParams,
) -> Result<Vec<InlayHint>> {
let _p = profile::span("handle_inlay_hints");
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
let document_uri = &params.text_document.uri;
let file_id = from_proto::file_id(&snap, document_uri)?;
let line_index = snap.file_line_index(file_id)?;
let range = params
.range
.map(|range| {
from_proto::file_range(&snap, TextDocumentIdentifier::new(document_uri.to_owned()), range)
})
.transpose()?;
Ok(snap
.analysis
.inlay_hints(&snap.config.inlay_hints(), file_id)?
.inlay_hints(&snap.config.inlay_hints(), file_id, range)?
.into_iter()
.map(|it| to_proto::inlay_hint(&line_index, it))
.collect())

View File

@ -240,6 +240,7 @@ impl Request for InlayHints {
#[serde(rename_all = "camelCase")]
pub struct InlayHintsParams {
pub text_document: TextDocumentIdentifier,
pub range: Option<lsp_types::Range>,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]