mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 22:31:43 +00:00
Improve user snippet import performance
This commit is contained in:
parent
1cca1fa5bf
commit
66bfa6fc88
1 changed files with 25 additions and 20 deletions
|
@ -56,7 +56,7 @@ use std::ops::Deref;
|
||||||
// It does not act as a tabstop.
|
// It does not act as a tabstop.
|
||||||
use ide_db::helpers::{import_assets::LocatedImport, insert_use::ImportScope};
|
use ide_db::helpers::{import_assets::LocatedImport, insert_use::ImportScope};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::ast;
|
use syntax::{ast, AstNode, GreenNode, SyntaxNode};
|
||||||
|
|
||||||
use crate::{context::CompletionContext, ImportEdit};
|
use crate::{context::CompletionContext, ImportEdit};
|
||||||
|
|
||||||
|
@ -75,9 +75,12 @@ pub struct Snippet {
|
||||||
pub postfix_triggers: Box<[Box<str>]>,
|
pub postfix_triggers: Box<[Box<str>]>,
|
||||||
pub prefix_triggers: Box<[Box<str>]>,
|
pub prefix_triggers: Box<[Box<str>]>,
|
||||||
pub scope: SnippetScope,
|
pub scope: SnippetScope,
|
||||||
snippet: String,
|
|
||||||
pub description: Option<Box<str>>,
|
pub description: Option<Box<str>>,
|
||||||
pub requires: Box<[Box<str>]>,
|
snippet: String,
|
||||||
|
// These are `ast::Path`'s but due to SyntaxNodes not being Send we store these
|
||||||
|
// and reconstruct them on demand instead. This is cheaper than reparsing them
|
||||||
|
// from strings
|
||||||
|
requires: Box<[GreenNode]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snippet {
|
impl Snippet {
|
||||||
|
@ -92,7 +95,7 @@ impl Snippet {
|
||||||
if prefix_triggers.is_empty() && postfix_triggers.is_empty() {
|
if prefix_triggers.is_empty() && postfix_triggers.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let (snippet, description) = validate_snippet(snippet, description, requires)?;
|
let (requires, snippet, description) = validate_snippet(snippet, description, requires)?;
|
||||||
Some(Snippet {
|
Some(Snippet {
|
||||||
// Box::into doesn't work as that has a Copy bound 😒
|
// Box::into doesn't work as that has a Copy bound 😒
|
||||||
postfix_triggers: postfix_triggers.iter().map(Deref::deref).map(Into::into).collect(),
|
postfix_triggers: postfix_triggers.iter().map(Deref::deref).map(Into::into).collect(),
|
||||||
|
@ -100,7 +103,7 @@ impl Snippet {
|
||||||
scope,
|
scope,
|
||||||
snippet,
|
snippet,
|
||||||
description,
|
description,
|
||||||
requires: requires.iter().map(Deref::deref).map(Into::into).collect(),
|
requires,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +128,10 @@ impl Snippet {
|
||||||
fn import_edits(
|
fn import_edits(
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
import_scope: &ImportScope,
|
import_scope: &ImportScope,
|
||||||
requires: &[Box<str>],
|
requires: &[GreenNode],
|
||||||
) -> Option<Vec<ImportEdit>> {
|
) -> Option<Vec<ImportEdit>> {
|
||||||
let resolve = |import| {
|
let resolve = |import: &GreenNode| {
|
||||||
let path = ast::Path::parse(import).ok()?;
|
let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?;
|
||||||
let item = match ctx.scope.speculative_resolve(&path)? {
|
let item = match ctx.scope.speculative_resolve(&path)? {
|
||||||
hir::PathResolution::Macro(mac) => mac.into(),
|
hir::PathResolution::Macro(mac) => mac.into(),
|
||||||
hir::PathResolution::Def(def) => def.into(),
|
hir::PathResolution::Def(def) => def.into(),
|
||||||
|
@ -158,19 +161,21 @@ fn validate_snippet(
|
||||||
snippet: &[String],
|
snippet: &[String],
|
||||||
description: &str,
|
description: &str,
|
||||||
requires: &[String],
|
requires: &[String],
|
||||||
) -> Option<(String, Option<Box<str>>)> {
|
) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
|
||||||
// validate that these are indeed simple paths
|
let mut imports = Vec::with_capacity(requires.len());
|
||||||
// we can't save the paths unfortunately due to them not being Send+Sync
|
for path in requires.iter() {
|
||||||
if requires.iter().any(|path| match ast::Path::parse(path) {
|
let path = ast::Path::parse(path).ok()?;
|
||||||
Ok(path) => path.segments().any(|seg| {
|
let valid_use_path = path.segments().all(|seg| {
|
||||||
!matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
|
matches!(seg.kind(), Some(ast::PathSegmentKind::Name(_)))
|
||||||
|| seg.generic_arg_list().is_some()
|
|| seg.generic_arg_list().is_none()
|
||||||
}),
|
});
|
||||||
Err(_) => true,
|
if !valid_use_path {
|
||||||
}) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let green = path.syntax().green().into_owned();
|
||||||
|
imports.push(green);
|
||||||
|
}
|
||||||
let snippet = snippet.iter().join("\n");
|
let snippet = snippet.iter().join("\n");
|
||||||
let description = if description.is_empty() { None } else { Some(description.into()) };
|
let description = if description.is_empty() { None } else { Some(description.into()) };
|
||||||
Some((snippet, description))
|
Some((imports.into_boxed_slice(), snippet, description))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue