mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 20:42:04 +00:00
Use stateless completion resolve
This commit is contained in:
parent
93bc009a59
commit
deda74edd8
6 changed files with 123 additions and 70 deletions
|
@ -9,7 +9,7 @@ use test_utils::mark;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{render_resolution_with_import, RenderContext},
|
render::{render_resolution_with_import, RenderContext},
|
||||||
CompletionContext, Completions,
|
CompletionContext, Completions, ImportEdit,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
|
@ -103,9 +103,11 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
|
||||||
.filter_map(|(import_path, definition)| {
|
.filter_map(|(import_path, definition)| {
|
||||||
render_resolution_with_import(
|
render_resolution_with_import(
|
||||||
RenderContext::new(ctx),
|
RenderContext::new(ctx),
|
||||||
import_path.clone(),
|
ImportEdit {
|
||||||
import_scope.clone(),
|
import_path: import_path.clone(),
|
||||||
ctx.config.merge,
|
import_scope: import_scope.clone(),
|
||||||
|
merge_behaviour: ctx.config.merge,
|
||||||
|
},
|
||||||
&definition,
|
&definition,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
|
@ -276,7 +276,6 @@ pub struct ImportEdit {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportEdit {
|
impl ImportEdit {
|
||||||
// TODO kb remove this at all now, since it's used only once?
|
|
||||||
/// Attempts to insert the import to the given scope, producing a text edit.
|
/// Attempts to insert the import to the given scope, producing a text edit.
|
||||||
/// May return no edit in edge cases, such as scope already containing the import.
|
/// May return no edit in edge cases, such as scope already containing the import.
|
||||||
pub fn to_text_edit(&self) -> Option<TextEdit> {
|
pub fn to_text_edit(&self) -> Option<TextEdit> {
|
||||||
|
|
|
@ -11,8 +11,11 @@ mod render;
|
||||||
|
|
||||||
mod completions;
|
mod completions;
|
||||||
|
|
||||||
use ide_db::base_db::FilePosition;
|
use ide_db::{
|
||||||
use ide_db::RootDatabase;
|
base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase,
|
||||||
|
};
|
||||||
|
use syntax::AstNode;
|
||||||
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
|
use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};
|
||||||
|
|
||||||
|
@ -131,6 +134,31 @@ pub fn completions(
|
||||||
Some(acc)
|
Some(acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves additional completion data at the position given.
|
||||||
|
pub fn resolve_completion_edits(
|
||||||
|
db: &RootDatabase,
|
||||||
|
config: &CompletionConfig,
|
||||||
|
position: FilePosition,
|
||||||
|
full_import_path: &str,
|
||||||
|
imported_name: &str,
|
||||||
|
) -> Option<TextEdit> {
|
||||||
|
let ctx = CompletionContext::new(db, position, config)?;
|
||||||
|
let anchor = ctx.name_ref_syntax.as_ref()?;
|
||||||
|
let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
|
||||||
|
|
||||||
|
let current_module = ctx.sema.scope(anchor.syntax()).module()?;
|
||||||
|
let current_crate = current_module.krate();
|
||||||
|
|
||||||
|
let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name)
|
||||||
|
.filter_map(|candidate| {
|
||||||
|
let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
|
||||||
|
current_module.find_use_path(db, item)
|
||||||
|
})
|
||||||
|
.find(|mod_path| mod_path.to_string() == full_import_path)?;
|
||||||
|
|
||||||
|
ImportEdit { import_path, import_scope, merge_behaviour: config.merge }.to_text_edit()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::config::CompletionConfig;
|
use crate::config::CompletionConfig;
|
||||||
|
|
|
@ -9,8 +9,7 @@ pub(crate) mod type_alias;
|
||||||
|
|
||||||
mod builder_ext;
|
mod builder_ext;
|
||||||
|
|
||||||
use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
|
use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
|
||||||
use ide_db::helpers::insert_use::{ImportScope, MergeBehaviour};
|
|
||||||
use ide_db::RootDatabase;
|
use ide_db::RootDatabase;
|
||||||
use syntax::TextRange;
|
use syntax::TextRange;
|
||||||
use test_utils::mark;
|
use test_utils::mark;
|
||||||
|
@ -48,15 +47,12 @@ pub(crate) fn render_resolution<'a>(
|
||||||
|
|
||||||
pub(crate) fn render_resolution_with_import<'a>(
|
pub(crate) fn render_resolution_with_import<'a>(
|
||||||
ctx: RenderContext<'a>,
|
ctx: RenderContext<'a>,
|
||||||
import_path: ModPath,
|
import_edit: ImportEdit,
|
||||||
import_scope: ImportScope,
|
|
||||||
merge_behaviour: Option<MergeBehaviour>,
|
|
||||||
resolution: &ScopeDef,
|
resolution: &ScopeDef,
|
||||||
) -> Option<CompletionItem> {
|
) -> Option<CompletionItem> {
|
||||||
let local_name = import_path.segments.last()?.to_string();
|
|
||||||
Render::new(ctx).render_resolution(
|
Render::new(ctx).render_resolution(
|
||||||
local_name,
|
import_edit.import_path.segments.last()?.to_string(),
|
||||||
Some(ImportEdit { import_path, import_scope, merge_behaviour }),
|
Some(import_edit),
|
||||||
resolution,
|
resolution,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -469,6 +469,28 @@ impl Analysis {
|
||||||
self.with_db(|db| completion::completions(db, config, position).map(Into::into))
|
self.with_db(|db| completion::completions(db, config, position).map(Into::into))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves additional completion data at the position given.
|
||||||
|
pub fn resolve_completion_edits(
|
||||||
|
&self,
|
||||||
|
config: &CompletionConfig,
|
||||||
|
position: FilePosition,
|
||||||
|
full_import_path: &str,
|
||||||
|
imported_name: &str,
|
||||||
|
) -> Cancelable<Vec<TextEdit>> {
|
||||||
|
Ok(self
|
||||||
|
.with_db(|db| {
|
||||||
|
completion::resolve_completion_edits(
|
||||||
|
db,
|
||||||
|
config,
|
||||||
|
position,
|
||||||
|
full_import_path,
|
||||||
|
imported_name,
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.map(|edit| vec![edit])
|
||||||
|
.unwrap_or_default())
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes resolved assists with source changes for the given position.
|
/// Computes resolved assists with source changes for the given position.
|
||||||
pub fn resolved_assists(
|
pub fn resolved_assists(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -8,8 +8,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ide::{
|
use ide::{
|
||||||
CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData,
|
CompletionConfig, CompletionResolveCapability, FileId, FilePosition, FileRange, HoverAction,
|
||||||
ImportEdit, LineIndex, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
HoverGotoTypeData, NavigationTarget, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
|
||||||
TextEdit,
|
TextEdit,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -22,7 +22,7 @@ use lsp_types::{
|
||||||
HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
|
HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
|
||||||
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
|
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
|
||||||
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
|
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
|
||||||
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
|
SymbolTag, TextDocumentIdentifier, TextDocumentPositionParams, Url, WorkspaceEdit,
|
||||||
};
|
};
|
||||||
use project_model::TargetKind;
|
use project_model::TargetKind;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -35,7 +35,6 @@ use crate::{
|
||||||
config::RustfmtConfig,
|
config::RustfmtConfig,
|
||||||
from_json, from_proto,
|
from_json, from_proto,
|
||||||
global_state::{GlobalState, GlobalStateSnapshot},
|
global_state::{GlobalState, GlobalStateSnapshot},
|
||||||
line_endings::LineEndings,
|
|
||||||
lsp_ext::{self, InlayHint, InlayHintsParams},
|
lsp_ext::{self, InlayHint, InlayHintsParams},
|
||||||
to_proto, LspError, Result,
|
to_proto, LspError, Result,
|
||||||
};
|
};
|
||||||
|
@ -541,7 +540,7 @@ pub(crate) fn handle_completion(
|
||||||
params: lsp_types::CompletionParams,
|
params: lsp_types::CompletionParams,
|
||||||
) -> Result<Option<lsp_types::CompletionResponse>> {
|
) -> Result<Option<lsp_types::CompletionResponse>> {
|
||||||
let _p = profile::span("handle_completion");
|
let _p = profile::span("handle_completion");
|
||||||
let text_document_url = params.text_document_position.text_document.uri.clone();
|
let text_document_position = params.text_document_position.clone();
|
||||||
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
||||||
let completion_triggered_after_single_colon = {
|
let completion_triggered_after_single_colon = {
|
||||||
let mut res = false;
|
let mut res = false;
|
||||||
|
@ -574,23 +573,18 @@ pub(crate) fn handle_completion(
|
||||||
|
|
||||||
let items: Vec<CompletionItem> = items
|
let items: Vec<CompletionItem> = items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.flat_map(|item| {
|
||||||
.flat_map(|(item_index, item)| {
|
|
||||||
let mut new_completion_items =
|
let mut new_completion_items =
|
||||||
to_proto::completion_item(&line_index, line_endings, item.clone());
|
to_proto::completion_item(&line_index, line_endings, item.clone());
|
||||||
|
|
||||||
if snap.config.completion.resolve_additional_edits_lazily() {
|
for new_item in &mut new_completion_items {
|
||||||
// TODO kb add resolve data somehow here
|
let _ = fill_resolve_data(
|
||||||
if let Some(import_edit) = item.import_to_add() {
|
&mut new_item.data,
|
||||||
// let data = serde_json::to_value(&CompletionData {
|
&item,
|
||||||
// document_url: text_document_url.clone(),
|
&snap.config.completion,
|
||||||
// import_id: item_index,
|
&text_document_position,
|
||||||
// })
|
)
|
||||||
// .expect(&format!("Should be able to serialize usize value {}", item_index));
|
.take();
|
||||||
for new_item in &mut new_completion_items {
|
|
||||||
// new_item.data = Some(data.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
new_completion_items
|
new_completion_items
|
||||||
|
@ -603,8 +597,8 @@ pub(crate) fn handle_completion(
|
||||||
|
|
||||||
pub(crate) fn handle_completion_resolve(
|
pub(crate) fn handle_completion_resolve(
|
||||||
snap: GlobalStateSnapshot,
|
snap: GlobalStateSnapshot,
|
||||||
mut original_completion: lsp_types::CompletionItem,
|
mut original_completion: CompletionItem,
|
||||||
) -> Result<lsp_types::CompletionItem> {
|
) -> Result<CompletionItem> {
|
||||||
let _p = profile::span("handle_resolve_completion");
|
let _p = profile::span("handle_resolve_completion");
|
||||||
|
|
||||||
// FIXME resolve the other capabilities also?
|
// FIXME resolve the other capabilities also?
|
||||||
|
@ -627,21 +621,30 @@ pub(crate) fn handle_completion_resolve(
|
||||||
None => return Ok(original_completion),
|
None => return Ok(original_completion),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO kb get the resolve data and somehow reparse the whole ast again?
|
let file_id = from_proto::file_id(&snap, &resolve_data.position.text_document.uri)?;
|
||||||
// let file_id = from_proto::file_id(&snap, &document_url)?;
|
let line_index = snap.analysis.file_line_index(file_id)?;
|
||||||
// let root = snap.analysis.parse(file_id)?;
|
let line_endings = snap.file_line_endings(file_id);
|
||||||
|
let offset = from_proto::offset(&line_index, resolve_data.position.position);
|
||||||
|
|
||||||
// if let Some(import_to_add) =
|
let mut additional_edits = snap
|
||||||
// import_edit_ptr.and_then(|import_edit| import_edit.into_import_edit(root.syntax()))
|
.analysis
|
||||||
// {
|
.resolve_completion_edits(
|
||||||
// // FIXME actually add all additional edits here? see `to_proto::completion_item` for more
|
&snap.config.completion,
|
||||||
// append_import_edits(
|
FilePosition { file_id, offset },
|
||||||
// &mut original_completion,
|
&resolve_data.full_import_path,
|
||||||
// &import_to_add,
|
&resolve_data.imported_name,
|
||||||
// snap.analysis.file_line_index(file_id)?.as_ref(),
|
)?
|
||||||
// snap.file_line_endings(file_id),
|
.into_iter()
|
||||||
// );
|
.flat_map(|edit| {
|
||||||
// }
|
edit.into_iter().map(|indel| to_proto::text_edit(&line_index, line_endings, indel))
|
||||||
|
})
|
||||||
|
.collect_vec();
|
||||||
|
|
||||||
|
if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
|
||||||
|
original_additional_edits.extend(additional_edits.drain(..))
|
||||||
|
} else {
|
||||||
|
original_completion.additional_text_edits = Some(additional_edits);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(original_completion)
|
Ok(original_completion)
|
||||||
}
|
}
|
||||||
|
@ -1606,27 +1609,30 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>)
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct CompletionResolveData {
|
struct CompletionResolveData {
|
||||||
document_url: Url,
|
position: lsp_types::TextDocumentPositionParams,
|
||||||
import_id: usize,
|
full_import_path: String,
|
||||||
|
imported_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn append_import_edits(
|
fn fill_resolve_data(
|
||||||
completion: &mut lsp_types::CompletionItem,
|
resolve_data: &mut Option<serde_json::Value>,
|
||||||
import_to_add: &ImportEdit,
|
item: &ide::CompletionItem,
|
||||||
line_index: &LineIndex,
|
completion_config: &CompletionConfig,
|
||||||
line_endings: LineEndings,
|
position: &TextDocumentPositionParams,
|
||||||
) {
|
) -> Option<()> {
|
||||||
let import_edits = import_to_add.to_text_edit().map(|import_edit| {
|
if completion_config.resolve_additional_edits_lazily() {
|
||||||
import_edit
|
let import_edit = item.import_to_add()?;
|
||||||
.into_iter()
|
let full_import_path = import_edit.import_path.to_string();
|
||||||
.map(|indel| to_proto::text_edit(line_index, line_endings, indel))
|
let imported_name = import_edit.import_path.segments.clone().pop()?.to_string();
|
||||||
.collect_vec()
|
|
||||||
});
|
*resolve_data = Some(
|
||||||
if let Some(original_additional_edits) = completion.additional_text_edits.as_mut() {
|
serde_json::to_value(CompletionResolveData {
|
||||||
if let Some(mut new_edits) = import_edits {
|
position: position.to_owned(),
|
||||||
original_additional_edits.extend(new_edits.drain(..))
|
full_import_path,
|
||||||
}
|
imported_name,
|
||||||
} else {
|
})
|
||||||
completion.additional_text_edits = import_edits;
|
.expect("Failed to serialize a regular struct with derives"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue