Auto merge of #17203 - kilpkonn:collapse_terms, r=Veykril

Fix OOM caused by term search

The issue came from multi Cartesian product for exprs with many (25+) arguments, each having multiple options.
The solution is two fold:
### Avoid blowing up in Cartesian product
**Before the logic was:**
    1. Find expressions for each argument/param - there may be many
    2. Take the Cartesian product (which blows up in some cases)
    4. If there are more than 2 options throw them away by squashing them to `Many`
**Now the logic is:**
    1. Find expressions for each argument/param and squash them to `Many` if there are more than 2 as otherwise we are guaranteed to also have more than 2 after taking the product which means squashing them anyway.
    2. Take the Cartesian product on iterator
    3. Start consuming it one by one
    4. If there are more than 2 options throw them away by squashing them to `Many`  (same as before)

This is also why I had to update some tests as the expressions get squashed to many more eagerly.

### Use fuel to avoid long search times and high memory usage
Now all the tactics use `should_continue:  Fn() -> bool` to chech if they should keep iterating _(Similarly to chalk)_.
This reduces the search times by a magnitude, for example from ~139ms/hole to ~14ms/hole for `ripgrep` crate.
There are slightly less expressions found, but I think speed gain worth it for usability.
Also note that syntactic hits decreases more because of squashing so you simple need to run search multiple times to get full terms.
Also the worst case time (For example `nalgebra` crate cus it has tons of generics) has search times mostly under 200ms.

Benchmarks on `ripgrep` crate
Before:
```
Tail Expr syntactic hits: 291/1692 (17%)
Tail Exprs found: 1253/1692 (74%)
Term search avg time: 139ms
````
After:
```
Tail Expr syntactic hits: 239/1692 (14%)
Tail Exprs found: 1226/1692 (72%)
Term search avg time: 14ms
```
This commit is contained in:
bors 2024-05-13 10:30:12 +00:00
commit 9db1258dbe
17 changed files with 141 additions and 37 deletions

View file

@ -127,6 +127,13 @@ impl LookupTable {
self.types_wishlist.insert(ty.clone());
}
// Collapse suggestions if there are many
if let Some(res) = &res {
if res.len() > self.many_threshold {
return Some(vec![Expr::Many(ty.clone())]);
}
}
res
}
@ -158,6 +165,13 @@ impl LookupTable {
self.types_wishlist.insert(ty.clone());
}
// Collapse suggestions if there are many
if let Some(res) = &res {
if res.len() > self.many_threshold {
return Some(vec![Expr::Many(ty.clone())]);
}
}
res
}
@ -255,13 +269,13 @@ pub struct TermSearchConfig {
pub enable_borrowcheck: bool,
/// Indicate when to squash multiple trees to `Many` as there are too many to keep track
pub many_alternatives_threshold: usize,
/// Depth of the search eg. number of cycles to run
pub depth: usize,
/// Fuel for term search in "units of work"
pub fuel: u64,
}
impl Default for TermSearchConfig {
fn default() -> Self {
Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 }
Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 400 }
}
}
@ -280,8 +294,7 @@ impl Default for TermSearchConfig {
/// transformation tactics. For example functions take as from set of types (arguments) to some
/// type (return type). Other transformations include methods on type, type constructors and
/// projections to struct fields (field access).
/// 3. Once we manage to find path to type we are interested in we continue for single round to see
/// if we can find more paths that take us to the `goal` type.
/// 3. If we run out of fuel (term search takes too long) we stop iterating.
/// 4. Return all the paths (type trees) that take us to the `goal` type.
///
/// Note that there are usually more ways we can get to the `goal` type but some are discarded to
@ -297,21 +310,31 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
});
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone());
let fuel = std::cell::Cell::new(ctx.config.fuel);
let should_continue = &|| {
let remaining = fuel.get();
fuel.set(remaining.saturating_sub(1));
if remaining == 0 {
tracing::debug!("fuel exhausted");
}
remaining > 0
};
// Try trivial tactic first, also populates lookup table
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
// Use well known types tactic before iterations as it does not depend on other tactics
solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup));
for _ in 0..ctx.config.depth {
while should_continue() {
lookup.new_round();
solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup));
solutions.extend(tactics::free_function(ctx, &defs, &mut lookup));
solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup));
solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup));
solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup));
solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup));
solutions.extend(tactics::type_constructor(ctx, &defs, &mut lookup, should_continue));
solutions.extend(tactics::free_function(ctx, &defs, &mut lookup, should_continue));
solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup, should_continue));
solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup, should_continue));
solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup, should_continue));
solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup, should_continue));
// Discard not interesting `ScopeDef`s for speedup
for def in lookup.exhausted_scopedefs() {

View file

@ -211,13 +211,13 @@ impl Expr {
}
}
Expr::Method { func, target, params, .. } => {
if target.contains_many_in_illegal_pos() {
if self.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&target.ty(db)));
}
let func_name = func.name(db).display(db.upcast()).to_string();
let self_param = func.self_param(db).unwrap();
let target = target.gen_source_code(
let target_str = target.gen_source_code(
sema_scope,
many_formatter,
prefer_no_std,
@ -236,9 +236,12 @@ impl Expr {
Some(trait_) => {
let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))?;
let target = match self_param.access(db) {
crate::Access::Shared => format!("&{target}"),
crate::Access::Exclusive => format!("&mut {target}"),
crate::Access::Owned => target,
crate::Access::Shared if !target.is_many() => format!("&{target_str}"),
crate::Access::Exclusive if !target.is_many() => {
format!("&mut {target_str}")
}
crate::Access::Owned => target_str,
_ => many_formatter(&target.ty(db)),
};
let res = match args.is_empty() {
true => format!("{trait_name}::{func_name}({target})",),
@ -246,7 +249,7 @@ impl Expr {
};
Ok(res)
}
None => Ok(format!("{target}.{func_name}({args})")),
None => Ok(format!("{target_str}.{func_name}({args})")),
}
}
Expr::Variant { variant, generics, params } => {
@ -381,7 +384,7 @@ impl Expr {
Ok(res)
}
Expr::Field { expr, field } => {
if expr.contains_many_in_illegal_pos() {
if expr.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&expr.ty(db)));
}
@ -395,7 +398,7 @@ impl Expr {
Ok(format!("{strukt}.{field}"))
}
Expr::Reference(expr) => {
if expr.contains_many_in_illegal_pos() {
if expr.contains_many_in_illegal_pos(db) {
return Ok(many_formatter(&expr.ty(db)));
}
@ -466,10 +469,15 @@ impl Expr {
/// macro!().bar()
/// &macro!()
/// ```
fn contains_many_in_illegal_pos(&self) -> bool {
fn contains_many_in_illegal_pos(&self, db: &dyn HirDatabase) -> bool {
match self {
Expr::Method { target, .. } => target.contains_many_in_illegal_pos(),
Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(),
Expr::Method { target, func, .. } => {
match func.as_assoc_item(db).and_then(|it| it.container_or_implemented_trait(db)) {
Some(_) => false,
None => target.is_many(),
}
}
Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(db),
Expr::Reference(target) => target.is_many(),
Expr::Many(_) => true,
_ => false,

View file

@ -4,6 +4,7 @@
//! * `ctx` - Context for the term search
//! * `defs` - Set of items in scope at term search target location
//! * `lookup` - Lookup table for types
//! * `should_continue` - Function that indicates when to stop iterating
//! And they return iterator that yields type trees that unify with the `goal` type.
use std::iter;
@ -97,16 +98,19 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn type_constructor<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
fn variant_helper(
db: &dyn HirDatabase,
lookup: &mut LookupTable,
should_continue: &dyn std::ops::Fn() -> bool,
parent_enum: Enum,
variant: Variant,
config: &TermSearchConfig,
@ -152,6 +156,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
.chain((non_default_type_params_len == 0).then_some(Vec::new()));
generic_params
.filter(|_| should_continue())
.filter_map(move |generics| {
// Insert default type params
let mut g = generics.into_iter();
@ -194,8 +199,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
defs.iter()
.filter_map(move |def| match def {
ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
let variant_exprs =
variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config);
let variant_exprs = variant_helper(
db,
lookup,
should_continue,
it.parent_enum(db),
*it,
&ctx.config,
);
if variant_exprs.is_empty() {
return None;
}
@ -213,7 +224,9 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
let exprs: Vec<(Type, Vec<Expr>)> = enum_
.variants(db)
.into_iter()
.flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config))
.flat_map(|it| {
variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config)
})
.collect();
if exprs.is_empty() {
@ -271,6 +284,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
.chain((non_default_type_params_len == 0).then_some(Vec::new()));
let exprs = generic_params
.filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@ -345,10 +359,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn free_function<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@ -390,6 +406,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
.permutations(non_default_type_params_len);
let exprs: Vec<_> = generic_params
.filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@ -474,10 +491,12 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_method<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@ -554,6 +573,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
.permutations(non_default_fn_type_params_len);
let exprs: Vec<_> = generic_params
.filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@ -645,10 +665,12 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn struct_projection<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@ -656,6 +678,7 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>(
.new_types(NewTypesKey::StructProjection)
.into_iter()
.map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup")))
.filter(|_| should_continue())
.flat_map(move |(ty, targets)| {
ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
if !field.is_visible_from(db, module) {
@ -716,10 +739,12 @@ pub(super) fn famous_types<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_static_method<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@ -728,6 +753,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
.clone()
.into_iter()
.chain(iter::once(ctx.goal.clone()))
.filter(|_| should_continue())
.flat_map(|ty| {
Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
})
@ -801,6 +827,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
.permutations(non_default_fn_type_params_len);
let exprs: Vec<_> = generic_params
.filter(|_| should_continue())
.filter_map(|generics| {
// Insert default type params
let mut g = generics.into_iter();
@ -884,10 +911,12 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
/// * `ctx` - Context for the term search
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn make_tuple<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
@ -896,6 +925,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>(
.types_wishlist()
.clone()
.into_iter()
.filter(|_| should_continue())
.filter(|ty| ty.is_tuple())
.filter_map(move |ty| {
// Double check to not contain unknown
@ -915,6 +945,7 @@ pub(super) fn make_tuple<'a, DB: HirDatabase>(
let exprs: Vec<Expr> = param_exprs
.into_iter()
.multi_cartesian_product()
.filter(|_| should_continue())
.map(|params| {
let tys: Vec<Type> = params.iter().map(|it| it.ty(db)).collect();
let tuple_ty = Type::new_tuple(module.krate().into(), &tys);

View file

@ -16,4 +16,5 @@ pub struct AssistConfig {
pub prefer_no_std: bool,
pub prefer_prelude: bool,
pub assist_emit_must_use: bool,
pub term_search_fuel: u64,
}

View file

@ -1,5 +1,5 @@
//! Term search assist
use hir::term_search::TermSearchCtx;
use hir::term_search::{TermSearchConfig, TermSearchCtx};
use ide_db::{
assists::{AssistId, AssistKind, GroupLabel},
famous_defs::FamousDefs,
@ -34,7 +34,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
sema: &ctx.sema,
scope: &scope,
goal: target_ty,
config: Default::default(),
config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
};
let paths = hir::term_search::term_search(&term_search_ctx);

View file

@ -31,6 +31,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
term_search_fuel: 400,
};
pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
@ -46,6 +47,7 @@ pub(crate) const TEST_CONFIG_NO_SNIPPET_CAP: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
term_search_fuel: 400,
};
pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
@ -61,6 +63,7 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig {
prefer_no_std: false,
prefer_prelude: true,
assist_emit_must_use: false,
term_search_fuel: 400,
};
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {

View file

@ -353,7 +353,7 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>)
config: hir::term_search::TermSearchConfig {
enable_borrowcheck: false,
many_alternatives_threshold: 1,
depth: 6,
fuel: 200,
},
};
let exprs = hir::term_search::term_search(&term_search_ctx);

View file

@ -15,6 +15,7 @@ pub struct CompletionConfig {
pub enable_self_on_the_fly: bool,
pub enable_private_editable: bool,
pub enable_term_search: bool,
pub term_search_fuel: u64,
pub full_function_signatures: bool,
pub callable: Option<CallableSnippets>,
pub snippet_cap: Option<SnippetCap>,

View file

@ -1853,8 +1853,7 @@ fn f() { A { bar: b$0 }; }
fn baz() [type]
ex baz() [type]
ex bar() [type]
ex A { bar: baz() }.bar [type]
ex A { bar: bar() }.bar [type]
ex A { bar: ... }.bar [type]
st A []
fn f() []
"#]],
@ -1992,8 +1991,8 @@ fn main() {
}
"#,
expect![[r#"
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
lc m [local]
lc t [local]
lc &t [type+local]
@ -2042,8 +2041,8 @@ fn main() {
}
"#,
expect![[r#"
ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
lc m [local]
lc t [local]
lc &mut t [type+local]

View file

@ -80,6 +80,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
},
snippets: Vec::new(),
limit: None,
term_search_fuel: 200,
};
pub(crate) fn completion_list(ra_fixture: &str) -> String {

View file

@ -1,6 +1,6 @@
use hir::{
db::ExpandDatabase,
term_search::{term_search, TermSearchCtx},
term_search::{term_search, TermSearchConfig, TermSearchCtx},
ClosureStyle, HirDisplay,
};
use ide_db::{
@ -47,7 +47,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>
sema: &ctx.sema,
scope: &scope,
goal: d.expected.clone(),
config: Default::default(),
config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() },
};
let paths = term_search(&term_search_ctx);
@ -274,7 +274,7 @@ impl Foo for Baz {
}
fn asd() -> Bar {
let a = Baz;
Foo::foo(a)
Foo::foo(_)
}
",
);
@ -363,7 +363,7 @@ impl Foo for A {
}
fn main() {
let a = A;
let c: Bar = Foo::foo(&a);
let c: Bar = Foo::foo(_);
}"#,
);
}

View file

@ -232,6 +232,7 @@ pub struct DiagnosticsConfig {
pub insert_use: InsertUseConfig,
pub prefer_no_std: bool,
pub prefer_prelude: bool,
pub term_search_fuel: u64,
}
impl DiagnosticsConfig {
@ -256,6 +257,7 @@ impl DiagnosticsConfig {
},
prefer_no_std: false,
prefer_prelude: true,
term_search_fuel: 400,
}
}
}

View file

@ -986,6 +986,7 @@ impl flags::AnalysisStats {
prefer_no_std: false,
prefer_prelude: true,
style_lints: false,
term_search_fuel: 400,
},
ide::AssistResolveStrategy::All,
file_id,

View file

@ -454,6 +454,9 @@ config_data! {
/// Local configurations can be overridden for every crate by placing a `rust-analyzer.toml` on crate root.
/// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle.
local: struct LocalDefaultConfigData <- LocalConfigInput -> {
/// Term search fuel in "units of work" for assists (Defaults to 400).
assist_termSearch_fuel: usize = 400,
/// Toggles the additional completions that automatically add imports when completed.
/// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.
completion_autoimport_enable: bool = true,
@ -515,6 +518,8 @@ config_data! {
}"#).unwrap(),
/// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
completion_termSearch_enable: bool = false,
/// Term search fuel in "units of work" for autocompletion (Defaults to 200).
completion_termSearch_fuel: usize = 200,
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = true,
@ -1015,6 +1020,7 @@ impl Config {
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
assist_emit_must_use: self.assist_emitMustUse().to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
}
}
@ -1048,6 +1054,7 @@ impl Config {
snippets: self.snippets.clone().to_vec(),
limit: self.completion_limit(source_root).to_owned(),
enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64,
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
}
}
@ -1067,6 +1074,7 @@ impl Config {
prefer_no_std: self.imports_preferNoStd(source_root).to_owned(),
prefer_prelude: self.imports_preferPrelude(source_root).to_owned(),
style_lints: self.diagnostics_styleLints_enable().to_owned(),
term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64,
}
}
pub fn expand_proc_attr_macros(&self) -> bool {

View file

@ -153,6 +153,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@ -197,6 +198,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@ -239,6 +241,7 @@ fn integrated_completion_benchmark() {
prefer_no_std: false,
prefer_prelude: true,
limit: None,
term_search_fuel: 200,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@ -295,6 +298,7 @@ fn integrated_diagnostics_benchmark() {
},
prefer_no_std: false,
prefer_prelude: false,
term_search_fuel: 400,
};
host.analysis()
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)

View file

@ -9,6 +9,11 @@ for enum variants.
--
Placeholder expression to use for missing expressions in assists.
--
[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `400`)::
+
--
Term search fuel in "units of work" for assists (Defaults to 400).
--
[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`)::
+
--
@ -373,6 +378,11 @@ Custom completion snippets.
--
Whether to enable term search based snippets like `Some(foo.bar().baz())`.
--
[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `200`)::
+
--
Term search fuel in "units of work" for autocompletion (Defaults to 200).
--
[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
+
--

View file

@ -534,6 +534,12 @@
"Fill missing expressions with reasonable defaults, `new` or `default` constructors."
]
},
"rust-analyzer.assist.termSearch.fuel": {
"markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 400).",
"default": 400,
"type": "integer",
"minimum": 0
},
"rust-analyzer.cachePriming.enable": {
"markdownDescription": "Warm up caches on project load.",
"default": true,
@ -930,6 +936,12 @@
"default": false,
"type": "boolean"
},
"rust-analyzer.completion.termSearch.fuel": {
"markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 200).",
"default": 200,
"type": "integer",
"minimum": 0
},
"rust-analyzer.diagnostics.disabled": {
"markdownDescription": "List of rust-analyzer diagnostics to disable.",
"default": [],