Improve fixture support
Support more features beside highlighting, and support items from minicore.
This commit is contained in:
parent
082ecd8f73
commit
db6734e22f
69 changed files with 1652 additions and 512 deletions
|
|
@ -930,6 +930,7 @@ dependencies = [
|
|||
"ide-diagnostics",
|
||||
"ide-ssr",
|
||||
"itertools",
|
||||
"macros",
|
||||
"nohash-hasher",
|
||||
"oorandom",
|
||||
"profile",
|
||||
|
|
@ -976,6 +977,7 @@ dependencies = [
|
|||
"hir",
|
||||
"ide-db",
|
||||
"itertools",
|
||||
"macros",
|
||||
"smallvec",
|
||||
"stdx",
|
||||
"syntax",
|
||||
|
|
@ -1000,6 +1002,7 @@ dependencies = [
|
|||
"indexmap",
|
||||
"itertools",
|
||||
"line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"macros",
|
||||
"memchr",
|
||||
"nohash-hasher",
|
||||
"parser",
|
||||
|
|
@ -1009,6 +1012,7 @@ dependencies = [
|
|||
"rustc-hash 2.1.1",
|
||||
"salsa",
|
||||
"salsa-macros",
|
||||
"smallvec",
|
||||
"span",
|
||||
"stdx",
|
||||
"syntax",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ syntax.workspace = true
|
|||
# completions crate should depend only on the top-level `hir` package. if you need
|
||||
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
|
||||
hir.workspace = true
|
||||
macros.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.5.1"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub(crate) mod lifetime;
|
|||
pub(crate) mod mod_;
|
||||
pub(crate) mod pattern;
|
||||
pub(crate) mod postfix;
|
||||
pub(crate) mod ra_fixture;
|
||||
pub(crate) mod record;
|
||||
pub(crate) mod snippet;
|
||||
pub(crate) mod r#type;
|
||||
|
|
@ -74,6 +75,10 @@ impl Completions {
|
|||
self.buf.push(item)
|
||||
}
|
||||
|
||||
fn add_many(&mut self, items: impl IntoIterator<Item = CompletionItem>) {
|
||||
self.buf.extend(items)
|
||||
}
|
||||
|
||||
fn add_opt(&mut self, item: Option<CompletionItem>) {
|
||||
if let Some(item) = item {
|
||||
self.buf.push(item)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
//! Injected completions for `#[rust_analyzer::rust_fixture]`.
|
||||
|
||||
use hir::FilePositionWrapper;
|
||||
use ide_db::{
|
||||
impl_empty_upmap_from_ra_fixture,
|
||||
ra_fixture::{RaFixtureAnalysis, UpmapFromRaFixture},
|
||||
};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
CompletionItemKind, CompletionItemRefMode, CompletionRelevance, completions::Completions,
|
||||
context::CompletionContext, item::CompletionItemLabel,
|
||||
};
|
||||
|
||||
pub(crate) fn complete_ra_fixture(
|
||||
acc: &mut Completions,
|
||||
ctx: &CompletionContext<'_>,
|
||||
original: &ast::String,
|
||||
expanded: &ast::String,
|
||||
) -> Option<()> {
|
||||
let analysis = RaFixtureAnalysis::analyze_ra_fixture(
|
||||
&ctx.sema,
|
||||
original.clone(),
|
||||
expanded,
|
||||
ctx.config.minicore,
|
||||
&mut |_| {},
|
||||
)?;
|
||||
let (virtual_file_id, virtual_offset) = analysis.map_offset_down(ctx.position.offset)?;
|
||||
let completions = hir::attach_db_allow_change(&analysis.db, || {
|
||||
crate::completions(
|
||||
&analysis.db,
|
||||
ctx.config,
|
||||
FilePositionWrapper { file_id: virtual_file_id, offset: virtual_offset },
|
||||
ctx.trigger_character,
|
||||
)
|
||||
})?;
|
||||
let completions =
|
||||
completions.upmap_from_ra_fixture(&analysis, virtual_file_id, ctx.position.file_id).ok()?;
|
||||
acc.add_many(completions);
|
||||
Some(())
|
||||
}
|
||||
|
||||
impl_empty_upmap_from_ra_fixture!(
|
||||
CompletionItemLabel,
|
||||
CompletionItemKind,
|
||||
CompletionRelevance,
|
||||
CompletionItemRefMode,
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::expect;
|
||||
|
||||
use crate::tests::check;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
check(
|
||||
r##"
|
||||
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
|
||||
|
||||
fn foo() {
|
||||
fixture(r#"
|
||||
fn complete_me() {}
|
||||
|
||||
fn baz() {
|
||||
let foo_bar_baz = 123;
|
||||
f$0
|
||||
}
|
||||
"#);
|
||||
}
|
||||
"##,
|
||||
expect![[r#"
|
||||
fn baz() fn()
|
||||
fn complete_me() fn()
|
||||
lc foo_bar_baz i32
|
||||
bt u32 u32
|
||||
kw async
|
||||
kw const
|
||||
kw crate::
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw impl for
|
||||
kw let
|
||||
kw letm
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,13 @@
|
|||
|
||||
use hir::FindPathConfig;
|
||||
use ide_db::{
|
||||
SnippetCap,
|
||||
MiniCore, SnippetCap,
|
||||
imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig},
|
||||
};
|
||||
|
||||
use crate::{CompletionFieldsToResolve, snippet::Snippet};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CompletionConfig<'a> {
|
||||
pub enable_postfix_completions: bool,
|
||||
pub enable_imports_on_the_fly: bool,
|
||||
|
|
@ -35,6 +35,7 @@ pub struct CompletionConfig<'a> {
|
|||
pub fields_to_resolve: CompletionFieldsToResolve,
|
||||
pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>,
|
||||
pub exclude_traits: &'a [String],
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(crate) config: &'a CompletionConfig<'a>,
|
||||
pub(crate) position: FilePosition,
|
||||
|
||||
pub(crate) trigger_character: Option<char>,
|
||||
/// The token before the cursor, in the original file.
|
||||
pub(crate) original_token: SyntaxToken,
|
||||
/// The token before the cursor, in the macro-expanded file.
|
||||
|
|
@ -703,6 +704,7 @@ impl<'db> CompletionContext<'db> {
|
|||
db: &'db RootDatabase,
|
||||
position @ FilePosition { file_id, offset }: FilePosition,
|
||||
config: &'db CompletionConfig<'db>,
|
||||
trigger_character: Option<char>,
|
||||
) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
|
||||
let _p = tracing::info_span!("CompletionContext::new").entered();
|
||||
let sema = Semantics::new(db);
|
||||
|
|
@ -871,6 +873,7 @@ impl<'db> CompletionContext<'db> {
|
|||
db,
|
||||
config,
|
||||
position,
|
||||
trigger_character,
|
||||
original_token,
|
||||
token,
|
||||
krate,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
|||
let (db, pos) = position(ra_fixture);
|
||||
let config = TEST_CONFIG;
|
||||
let (completion_context, _analysis) =
|
||||
hir::attach_db(&db, || CompletionContext::new(&db, pos, &config).unwrap());
|
||||
hir::attach_db(&db, || CompletionContext::new(&db, pos, &config, None).unwrap());
|
||||
|
||||
let ty = completion_context
|
||||
.expected_type
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use ide_db::{
|
|||
imports::import_assets::LocatedImport,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{format_to, impl_from, never};
|
||||
use syntax::{Edition, SmolStr, TextRange, TextSize, format_smolstr};
|
||||
|
|
@ -23,7 +24,7 @@ use crate::{
|
|||
///
|
||||
/// It is basically a POD with various properties. To construct a [`CompletionItem`],
|
||||
/// use [`Builder::new`] method and the [`Builder`] struct.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, UpmapFromRaFixture)]
|
||||
#[non_exhaustive]
|
||||
pub struct CompletionItem {
|
||||
/// Label in the completion pop up which identifies completion.
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ pub fn completions(
|
|||
position: FilePosition,
|
||||
trigger_character: Option<char>,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
let (ctx, analysis) = &CompletionContext::new(db, position, config)?;
|
||||
let (ctx, analysis) = &CompletionContext::new(db, position, config, trigger_character)?;
|
||||
let mut completions = Completions::default();
|
||||
|
||||
// prevent `(` from triggering unwanted completion noise
|
||||
|
|
@ -241,6 +241,7 @@ pub fn completions(
|
|||
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
|
||||
completions::format_string::format_string(acc, ctx, original, expanded);
|
||||
completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded);
|
||||
completions::ra_fixture::complete_ra_fixture(acc, ctx, original, expanded);
|
||||
}
|
||||
CompletionAnalysis::UnexpandedAttrTT {
|
||||
colon_prefix,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use expect_test::Expect;
|
|||
use hir::db::HirDatabase;
|
||||
use hir::{PrefixKind, setup_tracing};
|
||||
use ide_db::{
|
||||
FilePosition, RootDatabase, SnippetCap,
|
||||
FilePosition, MiniCore, RootDatabase, SnippetCap,
|
||||
imports::insert_use::{ImportGranularity, InsertUseConfig},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
|
@ -90,6 +90,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
|
|||
exclude_traits: &[],
|
||||
enable_auto_await: true,
|
||||
enable_auto_iter: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
|
||||
pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ fn check_with_config(
|
|||
expect: Expect,
|
||||
) {
|
||||
let (db, position) = crate::tests::position(ra_fixture);
|
||||
let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap();
|
||||
let (ctx, analysis) =
|
||||
crate::context::CompletionContext::new(&db, position, &config, None).unwrap();
|
||||
|
||||
let mut acc = crate::completions::Completions::default();
|
||||
hir::attach_db(ctx.db, || {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ query-group.workspace = true
|
|||
triomphe.workspace = true
|
||||
nohash-hasher.workspace = true
|
||||
bitflags.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
||||
# local deps
|
||||
base-db.workspace = true
|
||||
|
|
@ -42,15 +43,15 @@ vfs.workspace = true
|
|||
# ide should depend only on the top-level `hir` package. if you need
|
||||
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
|
||||
hir.workspace = true
|
||||
macros.workspace = true
|
||||
|
||||
test-utils.workspace = true
|
||||
test-fixture.workspace = true
|
||||
|
||||
line-index.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
expect-test = "1.5.1"
|
||||
|
||||
# local deps
|
||||
test-utils.workspace = true
|
||||
test-fixture.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
//!
|
||||
//! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search.
|
||||
|
||||
extern crate self as ide_db;
|
||||
|
||||
mod apply_change;
|
||||
|
||||
pub mod active_parameter;
|
||||
|
|
@ -14,6 +16,8 @@ pub mod items_locator;
|
|||
pub mod label;
|
||||
pub mod path_transform;
|
||||
pub mod prime_caches;
|
||||
pub mod ra_fixture;
|
||||
pub mod range_mapper;
|
||||
pub mod rename;
|
||||
pub mod rust_doc;
|
||||
pub mod search;
|
||||
|
|
@ -364,3 +368,25 @@ pub enum Severity {
|
|||
WeakWarning,
|
||||
Allow,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MiniCore<'a>(&'a str);
|
||||
|
||||
impl<'a> MiniCore<'a> {
|
||||
#[inline]
|
||||
pub fn new(minicore: &'a str) -> Self {
|
||||
Self(minicore)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn default() -> Self {
|
||||
Self(test_utils::MiniCore::RAW_SOURCE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for MiniCore<'a> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
532
src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs
Normal file
532
src/tools/rust-analyzer/crates/ide-db/src/ra_fixture.rs
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
//! Working with the fixtures in r-a tests, and providing IDE services for them.
|
||||
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
|
||||
use hir::{CfgExpr, FilePositionWrapper, FileRangeWrapper, Semantics};
|
||||
use smallvec::SmallVec;
|
||||
use span::{TextRange, TextSize};
|
||||
use syntax::{
|
||||
AstToken, SmolStr,
|
||||
ast::{self, IsString},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
MiniCore, RootDatabase, SymbolKind, active_parameter::ActiveParameter,
|
||||
documentation::Documentation, range_mapper::RangeMapper, search::ReferenceCategory,
|
||||
};
|
||||
|
||||
pub use span::FileId;
|
||||
|
||||
impl RootDatabase {
|
||||
fn from_ra_fixture(
|
||||
text: &str,
|
||||
minicore: MiniCore<'_>,
|
||||
) -> Result<(RootDatabase, Vec<(FileId, usize)>, Vec<FileId>), ()> {
|
||||
// We don't want a mistake in the fixture to crash r-a, so we wrap this in `catch_unwind()`.
|
||||
std::panic::catch_unwind(|| {
|
||||
let mut db = RootDatabase::default();
|
||||
let fixture = test_fixture::ChangeFixture::parse_with_proc_macros(
|
||||
&db,
|
||||
text,
|
||||
minicore.0,
|
||||
Vec::new(),
|
||||
);
|
||||
db.apply_change(fixture.change);
|
||||
let files = fixture
|
||||
.files
|
||||
.into_iter()
|
||||
.zip(fixture.file_lines)
|
||||
.map(|(file_id, range)| (file_id.file_id(&db), range))
|
||||
.collect();
|
||||
(db, files, fixture.sysroot_files)
|
||||
})
|
||||
.map_err(|error| {
|
||||
tracing::error!(
|
||||
"cannot crate the crate graph: {}\nCrate graph:\n{}\n",
|
||||
if let Some(&s) = error.downcast_ref::<&'static str>() {
|
||||
s
|
||||
} else if let Some(s) = error.downcast_ref::<String>() {
|
||||
s.as_str()
|
||||
} else {
|
||||
"Box<dyn Any>"
|
||||
},
|
||||
text,
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RaFixtureAnalysis {
|
||||
pub db: RootDatabase,
|
||||
tmp_file_ids: Vec<(FileId, usize)>,
|
||||
line_offsets: Vec<TextSize>,
|
||||
virtual_file_id_to_line: Vec<usize>,
|
||||
mapper: RangeMapper,
|
||||
literal: ast::String,
|
||||
// `minicore` etc..
|
||||
sysroot_files: Vec<FileId>,
|
||||
combined_len: TextSize,
|
||||
}
|
||||
|
||||
impl RaFixtureAnalysis {
|
||||
pub fn analyze_ra_fixture(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
literal: ast::String,
|
||||
expanded: &ast::String,
|
||||
minicore: MiniCore<'_>,
|
||||
on_cursor: &mut dyn FnMut(TextRange),
|
||||
) -> Option<RaFixtureAnalysis> {
|
||||
if !literal.is_raw() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?;
|
||||
let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
|
||||
attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
|
||||
path.segments()
|
||||
.zip(["rust_analyzer", "rust_fixture"])
|
||||
.all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
|
||||
})
|
||||
});
|
||||
if !has_rust_fixture_attr {
|
||||
return None;
|
||||
}
|
||||
let value = literal.value().ok()?;
|
||||
|
||||
let mut mapper = RangeMapper::default();
|
||||
|
||||
// This is used for the `Injector`, to resolve precise location in the string literal,
|
||||
// which will then be used to resolve precise location in the enclosing file.
|
||||
let mut offset_with_indent = TextSize::new(0);
|
||||
// This is used to resolve the location relative to the virtual file into a location
|
||||
// relative to the indentation-trimmed file which will then (by the `Injector`) used
|
||||
// to resolve to a location in the actual file.
|
||||
// Besides indentation, we also skip `$0` cursors for this, since they are not included
|
||||
// in the virtual files.
|
||||
let mut offset_without_indent = TextSize::new(0);
|
||||
|
||||
let mut text = &*value;
|
||||
if let Some(t) = text.strip_prefix('\n') {
|
||||
offset_with_indent += TextSize::of("\n");
|
||||
text = t;
|
||||
}
|
||||
// This stores the offsets of each line, **after we remove indentation**.
|
||||
let mut line_offsets = Vec::new();
|
||||
for mut line in text.split_inclusive('\n') {
|
||||
line_offsets.push(offset_without_indent);
|
||||
|
||||
if line.starts_with("@@") {
|
||||
// Introducing `//` into a fixture inside fixture causes all sorts of problems,
|
||||
// so for testing purposes we escape it as `@@` and replace it here.
|
||||
mapper.add("//", TextRange::at(offset_with_indent, TextSize::of("@@")));
|
||||
line = &line["@@".len()..];
|
||||
offset_with_indent += TextSize::of("@@");
|
||||
offset_without_indent += TextSize::of("@@");
|
||||
}
|
||||
|
||||
// Remove indentation to simplify the mapping with fixture (which de-indents).
|
||||
// Removing indentation shouldn't affect highlighting.
|
||||
let mut unindented_line = line.trim_start();
|
||||
if unindented_line.is_empty() {
|
||||
// The whole line was whitespaces, but we need the newline.
|
||||
unindented_line = "\n";
|
||||
}
|
||||
offset_with_indent += TextSize::of(line) - TextSize::of(unindented_line);
|
||||
|
||||
let marker = "$0";
|
||||
match unindented_line.find(marker) {
|
||||
Some(marker_pos) => {
|
||||
let (before_marker, after_marker) = unindented_line.split_at(marker_pos);
|
||||
let after_marker = &after_marker[marker.len()..];
|
||||
|
||||
mapper.add(
|
||||
before_marker,
|
||||
TextRange::at(offset_with_indent, TextSize::of(before_marker)),
|
||||
);
|
||||
offset_with_indent += TextSize::of(before_marker);
|
||||
offset_without_indent += TextSize::of(before_marker);
|
||||
|
||||
if let Some(marker_range) = literal
|
||||
.map_range_up(TextRange::at(offset_with_indent, TextSize::of(marker)))
|
||||
{
|
||||
on_cursor(marker_range);
|
||||
}
|
||||
offset_with_indent += TextSize::of(marker);
|
||||
|
||||
mapper.add(
|
||||
after_marker,
|
||||
TextRange::at(offset_with_indent, TextSize::of(after_marker)),
|
||||
);
|
||||
offset_with_indent += TextSize::of(after_marker);
|
||||
offset_without_indent += TextSize::of(after_marker);
|
||||
}
|
||||
None => {
|
||||
mapper.add(
|
||||
unindented_line,
|
||||
TextRange::at(offset_with_indent, TextSize::of(unindented_line)),
|
||||
);
|
||||
offset_with_indent += TextSize::of(unindented_line);
|
||||
offset_without_indent += TextSize::of(unindented_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let combined = mapper.take_text();
|
||||
let combined_len = TextSize::of(&combined);
|
||||
let (analysis, tmp_file_ids, sysroot_files) =
|
||||
RootDatabase::from_ra_fixture(&combined, minicore).ok()?;
|
||||
|
||||
// We use a `Vec` because we know the `FileId`s will always be close.
|
||||
let mut virtual_file_id_to_line = Vec::new();
|
||||
for &(file_id, line) in &tmp_file_ids {
|
||||
virtual_file_id_to_line.resize(file_id.index() as usize + 1, usize::MAX);
|
||||
virtual_file_id_to_line[file_id.index() as usize] = line;
|
||||
}
|
||||
|
||||
Some(RaFixtureAnalysis {
|
||||
db: analysis,
|
||||
tmp_file_ids,
|
||||
line_offsets,
|
||||
virtual_file_id_to_line,
|
||||
mapper,
|
||||
literal,
|
||||
sysroot_files,
|
||||
combined_len,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn files(&self) -> impl Iterator<Item = FileId> {
|
||||
self.tmp_file_ids.iter().map(|(file, _)| *file)
|
||||
}
|
||||
|
||||
/// This returns `None` for minicore or other sysroot files.
|
||||
fn virtual_file_id_to_line(&self, file_id: FileId) -> Option<usize> {
|
||||
if self.is_sysroot_file(file_id) {
|
||||
None
|
||||
} else {
|
||||
Some(self.virtual_file_id_to_line[file_id.index() as usize])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_offset_down(&self, offset: TextSize) -> Option<(FileId, TextSize)> {
|
||||
let inside_literal_range = self.literal.map_offset_down(offset)?;
|
||||
let combined_offset = self.mapper.map_offset_down(inside_literal_range)?;
|
||||
// There is usually a small number of files, so a linear search is smaller and faster.
|
||||
let (_, &(file_id, file_line)) =
|
||||
self.tmp_file_ids.iter().enumerate().find(|&(idx, &(_, file_line))| {
|
||||
let file_start = self.line_offsets[file_line];
|
||||
let file_end = self
|
||||
.tmp_file_ids
|
||||
.get(idx + 1)
|
||||
.map(|&(_, next_file_line)| self.line_offsets[next_file_line])
|
||||
.unwrap_or_else(|| self.combined_len);
|
||||
TextRange::new(file_start, file_end).contains(combined_offset)
|
||||
})?;
|
||||
let file_line_offset = self.line_offsets[file_line];
|
||||
let file_offset = combined_offset - file_line_offset;
|
||||
Some((file_id, file_offset))
|
||||
}
|
||||
|
||||
pub fn map_range_down(&self, range: TextRange) -> Option<(FileId, TextRange)> {
|
||||
let (start_file_id, start_offset) = self.map_offset_down(range.start())?;
|
||||
let (end_file_id, end_offset) = self.map_offset_down(range.end())?;
|
||||
if start_file_id != end_file_id {
|
||||
None
|
||||
} else {
|
||||
Some((start_file_id, TextRange::new(start_offset, end_offset)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_range_up(
|
||||
&self,
|
||||
virtual_file: FileId,
|
||||
range: TextRange,
|
||||
) -> impl Iterator<Item = TextRange> {
|
||||
// This could be `None` if the file is empty.
|
||||
self.virtual_file_id_to_line(virtual_file)
|
||||
.and_then(|line| self.line_offsets.get(line))
|
||||
.into_iter()
|
||||
.flat_map(move |&tmp_file_offset| {
|
||||
// Resolve the offset relative to the virtual file to an offset relative to the combined indentation-trimmed file
|
||||
let range = range + tmp_file_offset;
|
||||
// Then resolve that to an offset relative to the real file.
|
||||
self.mapper.map_range_up(range)
|
||||
})
|
||||
// And finally resolve the offset relative to the literal to relative to the file.
|
||||
.filter_map(|range| self.literal.map_range_up(range))
|
||||
}
|
||||
|
||||
pub fn map_offset_up(&self, virtual_file: FileId, offset: TextSize) -> Option<TextSize> {
|
||||
self.map_range_up(virtual_file, TextRange::empty(offset)).next().map(|range| range.start())
|
||||
}
|
||||
|
||||
pub fn is_sysroot_file(&self, file_id: FileId) -> bool {
|
||||
self.sysroot_files.contains(&file_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpmapFromRaFixture: Sized {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
trait IsEmpty {
|
||||
fn is_empty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> IsEmpty for Vec<T> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, const N: usize> IsEmpty for SmallVec<[T; N]> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
impl<K, V, S> IsEmpty for std::collections::HashMap<K, V, S> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn upmap_collection<T, Collection>(
|
||||
collection: Collection,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Collection, ()>
|
||||
where
|
||||
T: UpmapFromRaFixture,
|
||||
Collection: IntoIterator<Item = T> + FromIterator<T> + IsEmpty,
|
||||
{
|
||||
if collection.is_empty() {
|
||||
// The collection was already empty, don't mark it as failing just because of that.
|
||||
return Ok(collection);
|
||||
}
|
||||
let result = collection
|
||||
.into_iter()
|
||||
.filter_map(|item| item.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok())
|
||||
.collect::<Collection>();
|
||||
if result.is_empty() {
|
||||
// The collection was emptied by the upmapping - all items errored, therefore mark it as erroring as well.
|
||||
Err(())
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UpmapFromRaFixture> UpmapFromRaFixture for Option<T> {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(match self {
|
||||
Some(it) => Some(it.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UpmapFromRaFixture> UpmapFromRaFixture for Vec<T> {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
upmap_collection(self, analysis, virtual_file_id, real_file_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: UpmapFromRaFixture, const N: usize> UpmapFromRaFixture for SmallVec<[T; N]> {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
upmap_collection(self, analysis, virtual_file_id, real_file_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
impl<K: UpmapFromRaFixture + Hash + Eq, V: UpmapFromRaFixture, S: BuildHasher + Default>
|
||||
UpmapFromRaFixture for std::collections::HashMap<K, V, S>
|
||||
{
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
upmap_collection(self, analysis, virtual_file_id, real_file_id)
|
||||
}
|
||||
}
|
||||
|
||||
// A map of `FileId`s is treated as associating the ranges in the values with the keys.
|
||||
#[allow(clippy::disallowed_types)]
|
||||
impl<V: UpmapFromRaFixture, S: BuildHasher + Default> UpmapFromRaFixture
|
||||
for std::collections::HashMap<FileId, V, S>
|
||||
{
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
_virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
if self.is_empty() {
|
||||
return Ok(self);
|
||||
}
|
||||
let result = self
|
||||
.into_iter()
|
||||
.filter_map(|(virtual_file_id, value)| {
|
||||
Some((
|
||||
real_file_id,
|
||||
value.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id).ok()?,
|
||||
))
|
||||
})
|
||||
.collect::<std::collections::HashMap<_, _, _>>();
|
||||
if result.is_empty() { Err(()) } else { Ok(result) }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_tuple {
|
||||
() => {}; // Base case.
|
||||
( $first:ident, $( $rest:ident, )* ) => {
|
||||
impl<
|
||||
$first: UpmapFromRaFixture,
|
||||
$( $rest: UpmapFromRaFixture, )*
|
||||
> UpmapFromRaFixture for ( $first, $( $rest, )* ) {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
#[allow(non_snake_case)]
|
||||
let ( $first, $($rest,)* ) = self;
|
||||
Ok((
|
||||
$first.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
|
||||
$( $rest.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?, )*
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl_tuple!( $($rest,)* );
|
||||
};
|
||||
}
|
||||
impl_tuple!(A, B, C, D, E,);
|
||||
|
||||
impl UpmapFromRaFixture for TextSize {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
_real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
analysis.map_offset_up(virtual_file_id, self).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
impl UpmapFromRaFixture for TextRange {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
virtual_file_id: FileId,
|
||||
_real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
analysis.map_range_up(virtual_file_id, self).next().ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
// Deliberately do not implement that, as it's easy to get things misbehave and be treated with the wrong FileId:
|
||||
//
|
||||
// impl UpmapFromRaFixture for FileId {
|
||||
// fn upmap_from_ra_fixture(
|
||||
// self,
|
||||
// _analysis: &RaFixtureAnalysis,
|
||||
// _virtual_file_id: FileId,
|
||||
// real_file_id: FileId,
|
||||
// ) -> Result<Self, ()> {
|
||||
// Ok(real_file_id)
|
||||
// }
|
||||
// }
|
||||
|
||||
impl UpmapFromRaFixture for FilePositionWrapper<FileId> {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
_virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(FilePositionWrapper {
|
||||
file_id: real_file_id,
|
||||
offset: self.offset.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl UpmapFromRaFixture for FileRangeWrapper<FileId> {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &RaFixtureAnalysis,
|
||||
_virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(FileRangeWrapper {
|
||||
file_id: real_file_id,
|
||||
range: self.range.upmap_from_ra_fixture(analysis, self.file_id, real_file_id)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_empty_upmap_from_ra_fixture {
|
||||
( $( $ty:ty ),* $(,)? ) => {
|
||||
$(
|
||||
impl $crate::ra_fixture::UpmapFromRaFixture for $ty {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
_analysis: &$crate::ra_fixture::RaFixtureAnalysis,
|
||||
_virtual_file_id: $crate::ra_fixture::FileId,
|
||||
_real_file_id: $crate::ra_fixture::FileId,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_empty_upmap_from_ra_fixture!(
|
||||
bool,
|
||||
i8,
|
||||
i16,
|
||||
i32,
|
||||
i64,
|
||||
i128,
|
||||
u8,
|
||||
u16,
|
||||
u32,
|
||||
u64,
|
||||
u128,
|
||||
f32,
|
||||
f64,
|
||||
&str,
|
||||
String,
|
||||
SmolStr,
|
||||
Documentation,
|
||||
SymbolKind,
|
||||
CfgExpr,
|
||||
ReferenceCategory,
|
||||
);
|
||||
65
src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs
Normal file
65
src/tools/rust-analyzer/crates/ide-db/src/range_mapper.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
//! Maps between ranges in documents.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use stdx::equal_range_by;
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RangeMapper {
|
||||
buf: String,
|
||||
ranges: Vec<(TextRange, Option<TextRange>)>,
|
||||
}
|
||||
|
||||
impl RangeMapper {
|
||||
pub fn add(&mut self, text: &str, source_range: TextRange) {
|
||||
let len = TextSize::of(text);
|
||||
assert_eq!(len, source_range.len());
|
||||
self.add_impl(text, Some(source_range.start()));
|
||||
}
|
||||
|
||||
pub fn add_unmapped(&mut self, text: &str) {
|
||||
self.add_impl(text, None);
|
||||
}
|
||||
|
||||
fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
|
||||
let len = TextSize::of(text);
|
||||
let target_range = TextRange::at(TextSize::of(&self.buf), len);
|
||||
self.ranges.push((target_range, source.map(|it| TextRange::at(it, len))));
|
||||
self.buf.push_str(text);
|
||||
}
|
||||
|
||||
pub fn take_text(&mut self) -> String {
|
||||
std::mem::take(&mut self.buf)
|
||||
}
|
||||
|
||||
pub fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
|
||||
equal_range_by(&self.ranges, |&(r, _)| {
|
||||
if range.is_empty() && r.contains(range.start()) {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
TextRange::ordering(r, range)
|
||||
}
|
||||
})
|
||||
.filter_map(move |i| {
|
||||
let (target_range, source_range) = self.ranges[i];
|
||||
let intersection = target_range.intersect(range).unwrap();
|
||||
let source_range = source_range?;
|
||||
Some(intersection - target_range.start() + source_range.start())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> {
|
||||
// Using a binary search here is a bit complicated because of the `None` entries.
|
||||
// But the number of lines in fixtures is usually low.
|
||||
let (target_range, source_range) =
|
||||
self.ranges.iter().find_map(|&(target_range, source_range)| {
|
||||
let source_range = source_range?;
|
||||
if !source_range.contains(offset) {
|
||||
return None;
|
||||
}
|
||||
Some((target_range, source_range))
|
||||
})?;
|
||||
Some(offset - source_range.start() + target_range.start())
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ use crate::text_edit::{TextEdit, TextEditBuilder};
|
|||
use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff};
|
||||
use base_db::AnchoredPathBuf;
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use nohash_hasher::IntMap;
|
||||
use rustc_hash::FxHashMap;
|
||||
use span::FileId;
|
||||
|
|
@ -20,7 +21,7 @@ use syntax::{
|
|||
};
|
||||
|
||||
/// An annotation ID associated with an indel, to describe changes.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, UpmapFromRaFixture)]
|
||||
pub struct ChangeAnnotationId(u32);
|
||||
|
||||
impl fmt::Display for ChangeAnnotationId {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
//! rust-analyzer.
|
||||
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
pub use span::{TextRange, TextSize};
|
||||
use std::cmp::max;
|
||||
|
||||
|
|
@ -13,14 +14,14 @@ use crate::source_change::ChangeAnnotationId;
|
|||
/// `InsertDelete` -- a single "atomic" change to text
|
||||
///
|
||||
/// Must not overlap with other `InDel`s
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, UpmapFromRaFixture)]
|
||||
pub struct Indel {
|
||||
pub insert: String,
|
||||
/// Refers to offsets in the original text
|
||||
pub delete: TextRange,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone, UpmapFromRaFixture)]
|
||||
pub struct TextEdit {
|
||||
/// Invariant: disjoint and sorted by `delete`.
|
||||
indels: Vec<Indel>,
|
||||
|
|
|
|||
|
|
@ -311,7 +311,7 @@ fn minicore_smoke_test() {
|
|||
}
|
||||
|
||||
fn check(minicore: MiniCore) {
|
||||
let source = minicore.source_code();
|
||||
let source = minicore.source_code(MiniCore::RAW_SOURCE);
|
||||
let mut config = DiagnosticsConfig::test_sample();
|
||||
// This should be ignored since we conditionally remove code which creates single item use with braces
|
||||
config.disabled.insert("unused_braces".to_owned());
|
||||
|
|
@ -321,7 +321,7 @@ fn minicore_smoke_test() {
|
|||
}
|
||||
|
||||
// Checks that there is no diagnostic in minicore for each flag.
|
||||
for flag in MiniCore::available_flags() {
|
||||
for flag in MiniCore::available_flags(MiniCore::RAW_SOURCE) {
|
||||
if flag == "clone" {
|
||||
// Clone without copy has `moved-out-of-ref`, so ignoring.
|
||||
// FIXME: Maybe we should merge copy and clone in a single flag?
|
||||
|
|
@ -332,5 +332,5 @@ fn minicore_smoke_test() {
|
|||
}
|
||||
// And one time for all flags, to check codes which are behind multiple flags + prevent name collisions
|
||||
eprintln!("Checking all minicore flags");
|
||||
check(MiniCore::from_flags(MiniCore::available_flags()))
|
||||
check(MiniCore::from_flags(MiniCore::available_flags(MiniCore::RAW_SOURCE)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ span.workspace = true
|
|||
# ide should depend only on the top-level `hir` package. if you need
|
||||
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
|
||||
hir.workspace = true
|
||||
macros.workspace = true
|
||||
|
||||
[target.'cfg(not(any(target_arch = "wasm32", target_os = "emscripten")))'.dependencies]
|
||||
toolchain.workspace = true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use hir::{HasSource, InFile, InRealFile, Semantics};
|
||||
use ide_db::{
|
||||
FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition,
|
||||
FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition,
|
||||
helpers::visit_file_defs,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
|
@ -11,7 +11,7 @@ use crate::{
|
|||
annotations::fn_references::find_all_methods,
|
||||
goto_implementation::goto_implementation,
|
||||
navigation_target,
|
||||
references::find_all_refs,
|
||||
references::{FindAllRefsConfig, find_all_refs},
|
||||
runnables::{Runnable, runnables},
|
||||
};
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ pub enum AnnotationKind {
|
|||
HasReferences { pos: FilePosition, data: Option<Vec<FileRange>> },
|
||||
}
|
||||
|
||||
pub struct AnnotationConfig {
|
||||
pub struct AnnotationConfig<'a> {
|
||||
pub binary_target: bool,
|
||||
pub annotate_runnables: bool,
|
||||
pub annotate_impls: bool,
|
||||
|
|
@ -44,6 +44,7 @@ pub struct AnnotationConfig {
|
|||
pub annotate_method_references: bool,
|
||||
pub annotate_enum_variant_references: bool,
|
||||
pub location: AnnotationLocation,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
pub enum AnnotationLocation {
|
||||
|
|
@ -53,7 +54,7 @@ pub enum AnnotationLocation {
|
|||
|
||||
pub(crate) fn annotations(
|
||||
db: &RootDatabase,
|
||||
config: &AnnotationConfig,
|
||||
config: &AnnotationConfig<'_>,
|
||||
file_id: FileId,
|
||||
) -> Vec<Annotation> {
|
||||
let mut annotations = FxIndexSet::default();
|
||||
|
|
@ -196,13 +197,22 @@ pub(crate) fn annotations(
|
|||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
|
||||
pub(crate) fn resolve_annotation(
|
||||
db: &RootDatabase,
|
||||
config: &AnnotationConfig<'_>,
|
||||
mut annotation: Annotation,
|
||||
) -> Annotation {
|
||||
match annotation.kind {
|
||||
AnnotationKind::HasImpls { pos, ref mut data } => {
|
||||
*data = goto_implementation(db, pos).map(|range| range.info);
|
||||
}
|
||||
AnnotationKind::HasReferences { pos, ref mut data } => {
|
||||
*data = find_all_refs(&Semantics::new(db), pos, None).map(|result| {
|
||||
*data = find_all_refs(
|
||||
&Semantics::new(db),
|
||||
pos,
|
||||
&FindAllRefsConfig { search_scope: None, minicore: config.minicore },
|
||||
)
|
||||
.map(|result| {
|
||||
result
|
||||
.into_iter()
|
||||
.flat_map(|res| res.references)
|
||||
|
|
@ -228,12 +238,13 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{Expect, expect};
|
||||
use ide_db::MiniCore;
|
||||
|
||||
use crate::{Annotation, AnnotationConfig, fixture};
|
||||
|
||||
use super::AnnotationLocation;
|
||||
|
||||
const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig {
|
||||
const DEFAULT_CONFIG: AnnotationConfig<'_> = AnnotationConfig {
|
||||
binary_target: true,
|
||||
annotate_runnables: true,
|
||||
annotate_impls: true,
|
||||
|
|
@ -241,12 +252,13 @@ mod tests {
|
|||
annotate_method_references: true,
|
||||
annotate_enum_variant_references: true,
|
||||
location: AnnotationLocation::AboveName,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
|
||||
fn check_with_config(
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
expect: Expect,
|
||||
config: &AnnotationConfig,
|
||||
config: &AnnotationConfig<'_>,
|
||||
) {
|
||||
let (analysis, file_id) = fixture::file(ra_fixture);
|
||||
|
||||
|
|
@ -254,7 +266,7 @@ mod tests {
|
|||
.annotations(config, file_id)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|annotation| analysis.resolve_annotation(annotation).unwrap())
|
||||
.map(|annotation| analysis.resolve_annotation(&DEFAULT_CONFIG, annotation).unwrap())
|
||||
.collect();
|
||||
|
||||
expect.assert_debug_eq(&annotations);
|
||||
|
|
|
|||
|
|
@ -4,14 +4,16 @@ use std::iter;
|
|||
|
||||
use hir::Semantics;
|
||||
use ide_db::{
|
||||
FileRange, FxIndexMap, RootDatabase,
|
||||
FileRange, FxIndexMap, MiniCore, RootDatabase,
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
helpers::pick_best_token,
|
||||
search::FileReference,
|
||||
};
|
||||
use syntax::{AstNode, SyntaxKind::IDENT, ast};
|
||||
|
||||
use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav, goto_definition};
|
||||
use crate::{
|
||||
FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo, TryToNav, goto_definition,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallItem {
|
||||
|
|
@ -19,22 +21,28 @@ pub struct CallItem {
|
|||
pub ranges: Vec<FileRange>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CallHierarchyConfig {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CallHierarchyConfig<'a> {
|
||||
/// Whether to exclude tests from the call hierarchy
|
||||
pub exclude_tests: bool,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
pub(crate) fn call_hierarchy(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||
goto_definition::goto_definition(db, position)
|
||||
goto_definition::goto_definition(
|
||||
db,
|
||||
position,
|
||||
&GotoDefinitionConfig { minicore: config.minicore },
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn incoming_calls(
|
||||
db: &RootDatabase,
|
||||
CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
) -> Option<Vec<CallItem>> {
|
||||
let sema = &Semantics::new(db);
|
||||
|
|
@ -71,7 +79,7 @@ pub(crate) fn incoming_calls(
|
|||
});
|
||||
|
||||
if let Some((def, nav)) = def_nav {
|
||||
if exclude_tests && def.is_test(db) {
|
||||
if config.exclude_tests && def.is_test(db) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +97,7 @@ pub(crate) fn incoming_calls(
|
|||
|
||||
pub(crate) fn outgoing_calls(
|
||||
db: &RootDatabase,
|
||||
CallHierarchyConfig { exclude_tests }: CallHierarchyConfig,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
) -> Option<Vec<CallItem>> {
|
||||
let sema = Semantics::new(db);
|
||||
|
|
@ -119,7 +127,7 @@ pub(crate) fn outgoing_calls(
|
|||
let callable = sema.type_of_expr(&expr)?.original.as_callable(db)?;
|
||||
match callable.kind() {
|
||||
hir::CallableKind::Function(it) => {
|
||||
if exclude_tests && it.is_test(db) {
|
||||
if config.exclude_tests && it.is_test(db) {
|
||||
return None;
|
||||
}
|
||||
it.try_to_nav(&sema)
|
||||
|
|
@ -132,7 +140,7 @@ pub(crate) fn outgoing_calls(
|
|||
}
|
||||
ast::CallableExpr::MethodCall(expr) => {
|
||||
let function = sema.resolve_method_call(&expr)?;
|
||||
if exclude_tests && function.is_test(db) {
|
||||
if config.exclude_tests && function.is_test(db) {
|
||||
return None;
|
||||
}
|
||||
function
|
||||
|
|
@ -166,7 +174,7 @@ impl CallLocations {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{Expect, expect};
|
||||
use ide_db::FilePosition;
|
||||
use ide_db::{FilePosition, MiniCore};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::fixture;
|
||||
|
|
@ -189,21 +197,20 @@ mod tests {
|
|||
)
|
||||
}
|
||||
|
||||
let config = crate::CallHierarchyConfig { exclude_tests, minicore: MiniCore::default() };
|
||||
let (analysis, pos) = fixture::position(ra_fixture);
|
||||
|
||||
let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info;
|
||||
let mut navs = analysis.call_hierarchy(pos, &config).unwrap().unwrap().info;
|
||||
assert_eq!(navs.len(), 1);
|
||||
let nav = navs.pop().unwrap();
|
||||
expected_nav.assert_eq(&nav.debug_render());
|
||||
|
||||
let config = crate::CallHierarchyConfig { exclude_tests };
|
||||
|
||||
let item_pos =
|
||||
FilePosition { file_id: nav.file_id, offset: nav.focus_or_full_range().start() };
|
||||
let incoming_calls = analysis.incoming_calls(config, item_pos).unwrap().unwrap();
|
||||
let incoming_calls = analysis.incoming_calls(&config, item_pos).unwrap().unwrap();
|
||||
expected_incoming.assert_eq(&incoming_calls.into_iter().map(debug_render).join("\n"));
|
||||
|
||||
let outgoing_calls = analysis.outgoing_calls(config, item_pos).unwrap().unwrap();
|
||||
let outgoing_calls = analysis.outgoing_calls(&config, item_pos).unwrap().unwrap();
|
||||
expected_outgoing.assert_eq(&outgoing_calls.into_iter().map(debug_render).join("\n"));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use ide_db::{
|
|||
use syntax::{AstNode, SyntaxKind::*, T, ast, match_ast};
|
||||
|
||||
use crate::{
|
||||
FilePosition, NavigationTarget, RangeInfo, goto_definition::goto_definition,
|
||||
navigation_target::TryToNav,
|
||||
FilePosition, GotoDefinitionConfig, NavigationTarget, RangeInfo,
|
||||
goto_definition::goto_definition, navigation_target::TryToNav,
|
||||
};
|
||||
|
||||
// Feature: Go to Declaration
|
||||
|
|
@ -21,6 +21,7 @@ use crate::{
|
|||
pub(crate) fn goto_declaration(
|
||||
db: &RootDatabase,
|
||||
position @ FilePosition { file_id, offset }: FilePosition,
|
||||
config: &GotoDefinitionConfig<'_>,
|
||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||
let sema = Semantics::new(db);
|
||||
let file = sema.parse_guess_edition(file_id).syntax().clone();
|
||||
|
|
@ -69,20 +70,27 @@ pub(crate) fn goto_declaration(
|
|||
.flatten()
|
||||
.collect();
|
||||
|
||||
if info.is_empty() { goto_definition(db, position) } else { Some(RangeInfo::new(range, info)) }
|
||||
if info.is_empty() {
|
||||
goto_definition(db, position, config)
|
||||
} else {
|
||||
Some(RangeInfo::new(range, info))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ide_db::FileRange;
|
||||
use ide_db::{FileRange, MiniCore};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::fixture;
|
||||
use crate::{GotoDefinitionConfig, fixture};
|
||||
|
||||
const TEST_CONFIG: GotoDefinitionConfig<'_> =
|
||||
GotoDefinitionConfig { minicore: MiniCore::default() };
|
||||
|
||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
||||
let navs = analysis
|
||||
.goto_declaration(position)
|
||||
.goto_declaration(position, &TEST_CONFIG)
|
||||
.unwrap()
|
||||
.expect("no declaration or definition found")
|
||||
.info;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::{iter, mem::discriminant};
|
||||
|
||||
use crate::Analysis;
|
||||
use crate::{
|
||||
FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult,
|
||||
doc_links::token_as_doc_comment,
|
||||
|
|
@ -8,6 +9,7 @@ use crate::{
|
|||
use hir::{
|
||||
AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym,
|
||||
};
|
||||
use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture};
|
||||
use ide_db::{
|
||||
RootDatabase, SymbolKind,
|
||||
base_db::{AnchoredPath, SourceDatabase},
|
||||
|
|
@ -25,6 +27,11 @@ use syntax::{
|
|||
match_ast,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GotoDefinitionConfig<'a> {
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
// Feature: Go to Definition
|
||||
//
|
||||
// Navigates to the definition of an identifier.
|
||||
|
|
@ -39,6 +46,7 @@ use syntax::{
|
|||
pub(crate) fn goto_definition(
|
||||
db: &RootDatabase,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
config: &GotoDefinitionConfig<'_>,
|
||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||
let sema = &Semantics::new(db);
|
||||
let file = sema.parse_guess_edition(file_id).syntax().clone();
|
||||
|
|
@ -83,52 +91,64 @@ pub(crate) fn goto_definition(
|
|||
return Some(RangeInfo::new(original_token.text_range(), navs));
|
||||
}
|
||||
|
||||
let navs = sema
|
||||
.descend_into_macros_no_opaque(original_token.clone(), false)
|
||||
.into_iter()
|
||||
.filter_map(|token| {
|
||||
if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
|
||||
return Some(navs);
|
||||
}
|
||||
let tokens = sema.descend_into_macros_no_opaque(original_token.clone(), false);
|
||||
let mut navs = Vec::new();
|
||||
for token in tokens {
|
||||
if let Some(n) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
|
||||
navs.extend(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = token.value.parent()?;
|
||||
if let Some(token) = ast::String::cast(token.value.clone())
|
||||
&& let Some(original_token) = ast::String::cast(original_token.clone())
|
||||
&& let Some((analysis, fixture_analysis)) =
|
||||
Analysis::from_ra_fixture(sema, original_token, &token, config.minicore)
|
||||
&& let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)
|
||||
{
|
||||
return hir::attach_db_allow_change(&analysis.db, || {
|
||||
goto_definition(
|
||||
&analysis.db,
|
||||
FilePosition { file_id: virtual_file_id, offset: file_offset },
|
||||
config,
|
||||
)
|
||||
})
|
||||
.and_then(|navs| {
|
||||
navs.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id).ok()
|
||||
});
|
||||
}
|
||||
|
||||
let token_file_id = token.file_id;
|
||||
if let Some(token) = ast::String::cast(token.value.clone())
|
||||
&& let Some(x) =
|
||||
try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
|
||||
{
|
||||
return Some(vec![x]);
|
||||
}
|
||||
let parent = token.value.parent()?;
|
||||
|
||||
if ast::TokenTree::can_cast(parent.kind())
|
||||
&& let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
|
||||
{
|
||||
return Some(vec![x]);
|
||||
}
|
||||
let token_file_id = token.file_id;
|
||||
if let Some(token) = ast::String::cast(token.value.clone())
|
||||
&& let Some(x) =
|
||||
try_lookup_include_path(sema, InFile::new(token_file_id, token), file_id)
|
||||
{
|
||||
navs.push(x);
|
||||
continue;
|
||||
}
|
||||
|
||||
Some(
|
||||
IdentClass::classify_node(sema, &parent)?
|
||||
.definitions()
|
||||
if ast::TokenTree::can_cast(parent.kind())
|
||||
&& let Some(x) = try_lookup_macro_def_in_macro_use(sema, token.value)
|
||||
{
|
||||
navs.push(x);
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(ident_class) = IdentClass::classify_node(sema, &parent) else { continue };
|
||||
navs.extend(ident_class.definitions().into_iter().flat_map(|(def, _)| {
|
||||
if let Definition::ExternCrateDecl(crate_def) = def {
|
||||
return crate_def
|
||||
.resolved_crate(db)
|
||||
.map(|it| it.root_module().to_nav(sema.db))
|
||||
.into_iter()
|
||||
.flat_map(|(def, _)| {
|
||||
if let Definition::ExternCrateDecl(crate_def) = def {
|
||||
return crate_def
|
||||
.resolved_crate(db)
|
||||
.map(|it| it.root_module().to_nav(sema.db))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect();
|
||||
}
|
||||
try_filter_trait_item_definition(sema, &def)
|
||||
.unwrap_or_else(|| def_to_nav(sema, def))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.unique()
|
||||
.collect::<Vec<NavigationTarget>>();
|
||||
.flatten()
|
||||
.collect();
|
||||
}
|
||||
try_filter_trait_item_definition(sema, &def).unwrap_or_else(|| def_to_nav(sema, def))
|
||||
}));
|
||||
}
|
||||
let navs = navs.into_iter().unique().collect();
|
||||
|
||||
Some(RangeInfo::new(original_token.text_range(), navs))
|
||||
}
|
||||
|
|
@ -584,15 +604,22 @@ fn expr_to_nav(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::fixture;
|
||||
use ide_db::FileRange;
|
||||
use crate::{GotoDefinitionConfig, fixture};
|
||||
use ide_db::{FileRange, MiniCore};
|
||||
use itertools::Itertools;
|
||||
use syntax::SmolStr;
|
||||
|
||||
const TEST_CONFIG: GotoDefinitionConfig<'_> =
|
||||
GotoDefinitionConfig { minicore: MiniCore::default() };
|
||||
|
||||
#[track_caller]
|
||||
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||
let (analysis, position, expected) = fixture::annotations(ra_fixture);
|
||||
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
||||
let navs = analysis
|
||||
.goto_definition(position, &TEST_CONFIG)
|
||||
.unwrap()
|
||||
.expect("no definition found")
|
||||
.info;
|
||||
|
||||
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
|
||||
let navs = navs
|
||||
|
|
@ -611,14 +638,22 @@ mod tests {
|
|||
|
||||
fn check_unresolved(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||
let (analysis, position) = fixture::position(ra_fixture);
|
||||
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
||||
let navs = analysis
|
||||
.goto_definition(position, &TEST_CONFIG)
|
||||
.unwrap()
|
||||
.expect("no definition found")
|
||||
.info;
|
||||
|
||||
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
|
||||
}
|
||||
|
||||
fn check_name(expected_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||
let (analysis, position, _) = fixture::annotations(ra_fixture);
|
||||
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
||||
let navs = analysis
|
||||
.goto_definition(position, &TEST_CONFIG)
|
||||
.unwrap()
|
||||
.expect("no definition found")
|
||||
.info;
|
||||
assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
|
||||
let Some(target) = navs.into_iter().next() else {
|
||||
panic!("expected single navigation target but encountered none");
|
||||
|
|
@ -3961,4 +3996,23 @@ mod prim_str {}
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ra_fixture() {
|
||||
check(
|
||||
r##"
|
||||
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
|
||||
|
||||
fn foo() {
|
||||
fixture(r#"
|
||||
fn foo() {}
|
||||
// ^^^
|
||||
fn bar() {
|
||||
f$0oo();
|
||||
}
|
||||
"#)
|
||||
}
|
||||
"##,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,29 +11,32 @@ use hir::{
|
|||
db::DefDatabase,
|
||||
};
|
||||
use ide_db::{
|
||||
FileRange, FxIndexSet, Ranker, RootDatabase,
|
||||
FileRange, FxIndexSet, MiniCore, Ranker, RootDatabase,
|
||||
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
|
||||
famous_defs::FamousDefs,
|
||||
helpers::pick_best_token,
|
||||
ra_fixture::UpmapFromRaFixture,
|
||||
};
|
||||
use itertools::{Itertools, multizip};
|
||||
use span::Edition;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use span::{Edition, TextRange};
|
||||
use syntax::{
|
||||
AstNode,
|
||||
AstNode, AstToken,
|
||||
SyntaxKind::{self, *},
|
||||
SyntaxNode, T, ast,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
|
||||
Analysis, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav,
|
||||
doc_links::token_as_doc_comment,
|
||||
markdown_remove::remove_markdown,
|
||||
markup::Markup,
|
||||
navigation_target::UpmappingResult,
|
||||
runnables::{runnable_fn, runnable_mod},
|
||||
};
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct HoverConfig {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HoverConfig<'a> {
|
||||
pub links_in_hover: bool,
|
||||
pub memory_layout: Option<MemoryLayoutHoverConfig>,
|
||||
pub documentation: bool,
|
||||
|
|
@ -44,6 +47,7 @@ pub struct HoverConfig {
|
|||
pub max_enum_variants_count: Option<usize>,
|
||||
pub max_subst_ty_len: SubstTyLen,
|
||||
pub show_drop_glue: bool,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
@ -75,7 +79,7 @@ pub enum HoverDocFormat {
|
|||
PlainText,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)]
|
||||
pub enum HoverAction {
|
||||
Runnable(Runnable),
|
||||
Implementation(FilePosition),
|
||||
|
|
@ -108,14 +112,14 @@ impl HoverAction {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, UpmapFromRaFixture)]
|
||||
pub struct HoverGotoTypeData {
|
||||
pub mod_path: String,
|
||||
pub nav: NavigationTarget,
|
||||
}
|
||||
|
||||
/// Contains the results when hovering over an item
|
||||
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Default, Hash, PartialEq, Eq, UpmapFromRaFixture)]
|
||||
pub struct HoverResult {
|
||||
pub markup: Markup,
|
||||
pub actions: Vec<HoverAction>,
|
||||
|
|
@ -130,7 +134,7 @@ pub struct HoverResult {
|
|||
pub(crate) fn hover(
|
||||
db: &RootDatabase,
|
||||
frange @ FileRange { file_id, range }: FileRange,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
let sema = &hir::Semantics::new(db);
|
||||
let file = sema.parse_guess_edition(file_id).syntax().clone();
|
||||
|
|
@ -161,7 +165,7 @@ fn hover_offset(
|
|||
sema: &Semantics<'_, RootDatabase>,
|
||||
FilePosition { file_id, offset }: FilePosition,
|
||||
file: SyntaxNode,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
|
|
@ -219,6 +223,21 @@ fn hover_offset(
|
|||
return Some(RangeInfo::new(range, res));
|
||||
}
|
||||
|
||||
if let Some(literal) = ast::String::cast(original_token.clone())
|
||||
&& let Some((analysis, fixture_analysis)) =
|
||||
Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
|
||||
{
|
||||
let (virtual_file_id, virtual_offset) = fixture_analysis.map_offset_down(offset)?;
|
||||
return analysis
|
||||
.hover(
|
||||
config,
|
||||
FileRange { file_id: virtual_file_id, range: TextRange::empty(virtual_offset) },
|
||||
)
|
||||
.ok()??
|
||||
.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
|
||||
.ok();
|
||||
}
|
||||
|
||||
// prefer descending the same token kind in attribute expansions, in normal macros text
|
||||
// equivalency is more important
|
||||
let mut descended = sema.descend_into_macros(original_token.clone());
|
||||
|
|
@ -383,9 +402,9 @@ fn hover_offset(
|
|||
|
||||
fn hover_ranged(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
FileRange { range, .. }: FileRange,
|
||||
FileRange { file_id, range }: FileRange,
|
||||
file: SyntaxNode,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<RangeInfo<HoverResult>> {
|
||||
|
|
@ -404,6 +423,20 @@ fn hover_ranged(
|
|||
{
|
||||
render::deref_expr(sema, config, prefix_expr, edition, display_target)
|
||||
}
|
||||
Either::Left(ast::Expr::Literal(literal)) => {
|
||||
if let Some(literal) = ast::String::cast(literal.token())
|
||||
&& let Some((analysis, fixture_analysis)) =
|
||||
Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
|
||||
{
|
||||
let (virtual_file_id, virtual_range) = fixture_analysis.map_range_down(range)?;
|
||||
return analysis
|
||||
.hover(config, FileRange { file_id: virtual_file_id, range: virtual_range })
|
||||
.ok()??
|
||||
.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
|
||||
.ok();
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let res =
|
||||
|
|
@ -426,7 +459,7 @@ pub(crate) fn hover_for_definition(
|
|||
scope_node: &SyntaxNode,
|
||||
macro_arm: Option<u32>,
|
||||
render_extras: bool,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> HoverResult {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ use crate::{
|
|||
|
||||
pub(super) fn type_info_of(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
_config: &HoverConfig,
|
||||
_config: &HoverConfig<'_>,
|
||||
expr_or_pat: &Either<ast::Expr, ast::Pat>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -49,7 +49,7 @@ pub(super) fn type_info_of(
|
|||
|
||||
pub(super) fn closure_expr(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
c: ast::ClosureExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -60,7 +60,7 @@ pub(super) fn closure_expr(
|
|||
|
||||
pub(super) fn try_expr(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
_config: &HoverConfig,
|
||||
_config: &HoverConfig<'_>,
|
||||
try_expr: &ast::TryExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -155,7 +155,7 @@ pub(super) fn try_expr(
|
|||
|
||||
pub(super) fn deref_expr(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
_config: &HoverConfig,
|
||||
_config: &HoverConfig<'_>,
|
||||
deref_expr: &ast::PrefixExpr,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -219,7 +219,7 @@ pub(super) fn deref_expr(
|
|||
|
||||
pub(super) fn underscore(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
token: &SyntaxToken,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -263,7 +263,7 @@ pub(super) fn underscore(
|
|||
|
||||
pub(super) fn keyword(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
token: &SyntaxToken,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -290,7 +290,7 @@ pub(super) fn keyword(
|
|||
/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
|
||||
pub(super) fn struct_rest_pat(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
_config: &HoverConfig,
|
||||
_config: &HoverConfig<'_>,
|
||||
pattern: &ast::RecordPat,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -371,7 +371,7 @@ pub(super) fn process_markup(
|
|||
def: Definition,
|
||||
markup: &Markup,
|
||||
markup_range_map: Option<DocsRangeMap>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
) -> Markup {
|
||||
let markup = markup.as_str();
|
||||
let markup = if config.links_in_hover {
|
||||
|
|
@ -481,7 +481,7 @@ pub(super) fn definition(
|
|||
macro_arm: Option<u32>,
|
||||
render_extras: bool,
|
||||
subst_types: Option<&Vec<(Symbol, Type<'_>)>>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
) -> (Markup, Option<DocsRangeMap>) {
|
||||
|
|
@ -979,7 +979,7 @@ fn render_notable_trait(
|
|||
|
||||
fn type_info(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
ty: TypeInfo<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
@ -1038,7 +1038,7 @@ fn type_info(
|
|||
|
||||
fn closure_ty(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
TypeInfo { original, adjusted }: &TypeInfo<'_>,
|
||||
edition: Edition,
|
||||
display_target: DisplayTarget,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use expect_test::{Expect, expect};
|
||||
use ide_db::{FileRange, base_db::SourceDatabase};
|
||||
use ide_db::{FileRange, MiniCore, base_db::SourceDatabase};
|
||||
use syntax::TextRange;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
|
||||
use hir::setup_tracing;
|
||||
|
||||
const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
||||
const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig {
|
||||
links_in_hover: false,
|
||||
memory_layout: Some(MemoryLayoutHoverConfig {
|
||||
size: Some(MemoryLayoutHoverRenderKind::Both),
|
||||
|
|
@ -25,6 +25,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
|||
max_enum_variants_count: Some(5),
|
||||
max_subst_ty_len: super::SubstTyLen::Unlimited,
|
||||
show_drop_glue: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
|
||||
fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@ use hir::{
|
|||
ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError,
|
||||
HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
|
||||
};
|
||||
use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder};
|
||||
use ide_db::{
|
||||
FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder,
|
||||
};
|
||||
use ide_db::{FxHashSet, text_edit::TextEdit};
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
|
|
@ -37,6 +40,7 @@ mod implicit_static;
|
|||
mod implied_dyn_trait;
|
||||
mod lifetime;
|
||||
mod param_name;
|
||||
mod ra_fixture;
|
||||
mod range_exclusive;
|
||||
|
||||
// Feature: Inlay Hints
|
||||
|
|
@ -80,7 +84,7 @@ pub(crate) fn inlay_hints(
|
|||
db: &RootDatabase,
|
||||
file_id: FileId,
|
||||
range_limit: Option<TextRange>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
) -> Vec<InlayHint> {
|
||||
let _p = tracing::info_span!("inlay_hints").entered();
|
||||
let sema = Semantics::new(db);
|
||||
|
|
@ -132,7 +136,7 @@ pub(crate) fn inlay_hints_resolve(
|
|||
file_id: FileId,
|
||||
resolve_range: TextRange,
|
||||
hash: u64,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
hasher: impl Fn(&InlayHint) -> u64,
|
||||
) -> Option<InlayHint> {
|
||||
let _p = tracing::info_span!("inlay_hints_resolve").entered();
|
||||
|
|
@ -208,7 +212,7 @@ fn hints(
|
|||
hints: &mut Vec<InlayHint>,
|
||||
ctx: &mut InlayHintCtx,
|
||||
famous_defs @ FamousDefs(sema, _krate): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
file_id: EditionedFileId,
|
||||
display_target: DisplayTarget,
|
||||
node: SyntaxNode,
|
||||
|
|
@ -239,6 +243,7 @@ fn hints(
|
|||
closure_ret::hints(hints, famous_defs, config, display_target, it)
|
||||
},
|
||||
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
|
||||
ast::Expr::Literal(it) => ra_fixture::hints(hints, famous_defs.0, file_id, config, it),
|
||||
_ => Some(()),
|
||||
}
|
||||
},
|
||||
|
|
@ -294,8 +299,8 @@ fn hints(
|
|||
};
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct InlayHintsConfig {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InlayHintsConfig<'a> {
|
||||
pub render_colons: bool,
|
||||
pub type_hints: bool,
|
||||
pub sized_bound: bool,
|
||||
|
|
@ -321,9 +326,10 @@ pub struct InlayHintsConfig {
|
|||
pub max_length: Option<usize>,
|
||||
pub closing_brace_hints_min_lines: Option<usize>,
|
||||
pub fields_to_resolve: InlayFieldsToResolve,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
impl InlayHintsConfig {
|
||||
impl InlayHintsConfig<'_> {
|
||||
fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
|
||||
if self.fields_to_resolve.resolve_text_edits {
|
||||
LazyProperty::Lazy
|
||||
|
|
@ -466,7 +472,7 @@ pub enum InlayHintPosition {
|
|||
After,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, UpmapFromRaFixture)]
|
||||
pub struct InlayHint {
|
||||
/// The text range this inlay hint applies to.
|
||||
pub range: TextRange,
|
||||
|
|
@ -485,9 +491,10 @@ pub struct InlayHint {
|
|||
}
|
||||
|
||||
/// A type signaling that a value is either computed, or is available for computation.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default, UpmapFromRaFixture)]
|
||||
pub enum LazyProperty<T> {
|
||||
Computed(T),
|
||||
#[default]
|
||||
Lazy,
|
||||
}
|
||||
|
||||
|
|
@ -537,7 +544,7 @@ pub enum InlayTooltip {
|
|||
Markdown(String),
|
||||
}
|
||||
|
||||
#[derive(Default, Hash)]
|
||||
#[derive(Default, Hash, UpmapFromRaFixture)]
|
||||
pub struct InlayHintLabel {
|
||||
pub parts: SmallVec<[InlayHintLabelPart; 1]>,
|
||||
}
|
||||
|
|
@ -623,6 +630,7 @@ impl fmt::Debug for InlayHintLabel {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(UpmapFromRaFixture)]
|
||||
pub struct InlayHintLabelPart {
|
||||
pub text: String,
|
||||
/// Source location represented by this label part. The client will use this to fetch the part's
|
||||
|
|
@ -724,7 +732,7 @@ impl InlayHintLabelBuilder<'_> {
|
|||
|
||||
fn label_of_ty(
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
ty: &hir::Type<'_>,
|
||||
display_target: DisplayTarget,
|
||||
) -> Option<InlayHintLabel> {
|
||||
|
|
@ -734,7 +742,7 @@ fn label_of_ty(
|
|||
mut max_length: Option<usize>,
|
||||
ty: &hir::Type<'_>,
|
||||
label_builder: &mut InlayHintLabelBuilder<'_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
) -> Result<(), HirDisplayError> {
|
||||
hir::attach_db(sema.db, || {
|
||||
|
|
@ -829,7 +837,7 @@ fn hint_iterator<'db>(
|
|||
|
||||
fn ty_to_text_edit(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
node_for_hint: &SyntaxNode,
|
||||
ty: &hir::Type<'_>,
|
||||
offset_to_insert_ty: TextSize,
|
||||
|
|
@ -860,6 +868,7 @@ mod tests {
|
|||
|
||||
use expect_test::Expect;
|
||||
use hir::ClosureStyle;
|
||||
use ide_db::MiniCore;
|
||||
use itertools::Itertools;
|
||||
use test_utils::extract_annotations;
|
||||
|
||||
|
|
@ -869,7 +878,7 @@ mod tests {
|
|||
|
||||
use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve};
|
||||
|
||||
pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
|
||||
pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
|
||||
discriminant_hints: DiscriminantHints::Never,
|
||||
render_colons: false,
|
||||
type_hints: false,
|
||||
|
|
@ -899,8 +908,9 @@ mod tests {
|
|||
fields_to_resolve: InlayFieldsToResolve::empty(),
|
||||
implicit_drop_hints: false,
|
||||
range_exclusive_hints: false,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
|
||||
pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
|
||||
type_hints: true,
|
||||
parameter_hints: true,
|
||||
chaining_hints: true,
|
||||
|
|
@ -917,7 +927,7 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
pub(super) fn check_with_config(
|
||||
config: InlayHintsConfig,
|
||||
config: InlayHintsConfig<'_>,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
) {
|
||||
let (analysis, file_id) = fixture::file(ra_fixture);
|
||||
|
|
@ -936,7 +946,7 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
pub(super) fn check_expect(
|
||||
config: InlayHintsConfig,
|
||||
config: InlayHintsConfig<'_>,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
|
|
@ -951,7 +961,7 @@ mod tests {
|
|||
/// expect test.
|
||||
#[track_caller]
|
||||
pub(super) fn check_edit(
|
||||
config: InlayHintsConfig,
|
||||
config: InlayHintsConfig<'_>,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
|
|
@ -974,7 +984,7 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
pub(super) fn check_no_edit(
|
||||
config: InlayHintsConfig,
|
||||
config: InlayHintsConfig<'_>,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
) {
|
||||
let (analysis, file_id) = fixture::file(ra_fixture);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<()> {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
pat: &ast::IdentPat,
|
||||
) -> Option<()> {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
pat: &ast::Pat,
|
||||
) -> Option<()> {
|
||||
if !config.binding_mode_hints {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
params: ast::GenericParamList,
|
||||
) -> Option<()> {
|
||||
if !config.sized_bound {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use super::label_of_ty;
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
expr: &ast::Expr,
|
||||
) -> Option<()> {
|
||||
|
|
@ -93,7 +93,7 @@ mod tests {
|
|||
|
||||
#[track_caller]
|
||||
pub(super) fn check_expect_clear_loc(
|
||||
config: InlayHintsConfig,
|
||||
config: InlayHintsConfig<'_>,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
InRealFile { file_id, value: node }: InRealFile<SyntaxNode>,
|
||||
) -> Option<()> {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
closure: ast::ClosureExpr,
|
||||
) -> Option<()> {
|
||||
if !config.closure_capture_hints {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: DisplayTarget,
|
||||
closure: ast::ClosureExpr,
|
||||
) -> Option<()> {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
pub(super) fn enum_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
enum_: ast::Enum,
|
||||
) -> Option<()> {
|
||||
if let DiscriminantHints::Never = config.discriminant_hints {
|
||||
|
|
@ -41,7 +41,7 @@ pub(super) fn enum_hints(
|
|||
|
||||
fn variant_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
enum_: &ast::Enum,
|
||||
variant: &ast::Variant,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use crate::{InlayHint, InlayHintsConfig};
|
|||
pub(super) fn extern_block_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
extern_block: ast::ExternBlock,
|
||||
) -> Option<()> {
|
||||
if extern_block.unsafe_token().is_some() {
|
||||
|
|
@ -33,7 +33,7 @@ pub(super) fn extern_block_hints(
|
|||
pub(super) fn fn_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
fn_: &ast::Fn,
|
||||
extern_block: &ast::ExternBlock,
|
||||
) -> Option<()> {
|
||||
|
|
@ -51,7 +51,7 @@ pub(super) fn fn_hints(
|
|||
pub(super) fn static_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
static_: &ast::Static,
|
||||
extern_block: &ast::ExternBlock,
|
||||
) -> Option<()> {
|
||||
|
|
@ -67,7 +67,7 @@ pub(super) fn static_hints(
|
|||
}
|
||||
|
||||
fn item_hint(
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
extern_block: &ast::ExternBlock,
|
||||
token: SyntaxToken,
|
||||
) -> InlayHint {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use super::param_name::is_argument_similar_to_param_name;
|
|||
pub(crate) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
node: AnyHasGenericArgs,
|
||||
) -> Option<()> {
|
||||
let GenericParameterHints { type_hints, lifetime_hints, const_hints } =
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
display_target: hir::DisplayTarget,
|
||||
node: &ast::Fn,
|
||||
) -> Option<()> {
|
||||
|
|
@ -147,7 +147,7 @@ mod tests {
|
|||
inlay_hints::tests::{DISABLED_CONFIG, check_with_config},
|
||||
};
|
||||
|
||||
const ONLY_DROP_CONFIG: InlayHintsConfig =
|
||||
const ONLY_DROP_CONFIG: InlayHintsConfig<'_> =
|
||||
InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG };
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
statik_or_const: Either<ast::Static, ast::Const>,
|
||||
) -> Option<()> {
|
||||
if config.lifetime_elision_hints != LifetimeElisionHints::Always {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
path: Either<ast::PathType, ast::DynTraitType>,
|
||||
) -> Option<()> {
|
||||
let parent = path.syntax().parent()?;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ pub(super) fn fn_hints(
|
|||
acc: &mut Vec<InlayHint>,
|
||||
ctx: &mut InlayHintCtx,
|
||||
fd: &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
func: ast::Fn,
|
||||
) -> Option<()> {
|
||||
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
|
||||
|
|
@ -70,7 +70,7 @@ pub(super) fn fn_ptr_hints(
|
|||
acc: &mut Vec<InlayHint>,
|
||||
ctx: &mut InlayHintCtx,
|
||||
fd: &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
func: ast::FnPtrType,
|
||||
) -> Option<()> {
|
||||
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
|
||||
|
|
@ -135,7 +135,7 @@ pub(super) fn fn_path_hints(
|
|||
acc: &mut Vec<InlayHint>,
|
||||
ctx: &mut InlayHintCtx,
|
||||
fd: &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
func: &ast::PathType,
|
||||
) -> Option<()> {
|
||||
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
|
||||
|
|
@ -196,7 +196,7 @@ fn hints_(
|
|||
acc: &mut Vec<InlayHint>,
|
||||
ctx: &mut InlayHintCtx,
|
||||
FamousDefs(_, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
params: impl Iterator<Item = (Option<ast::Name>, ast::Type)>,
|
||||
generic_param_list: Option<ast::GenericParamList>,
|
||||
ret_type: Option<ast::RetType>,
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(sema, krate): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
file_id: EditionedFileId,
|
||||
expr: ast::Expr,
|
||||
) -> Option<()> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
//! Injected inlay hints for `#[rust_analyzer::rust_fixture]`.
|
||||
|
||||
use hir::{EditionedFileId, Semantics};
|
||||
use ide_db::{RootDatabase, impl_empty_upmap_from_ra_fixture, ra_fixture::UpmapFromRaFixture};
|
||||
use syntax::{AstToken, ast};
|
||||
|
||||
use crate::{Analysis, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip};
|
||||
|
||||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
file_id: EditionedFileId,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
literal: ast::Literal,
|
||||
) -> Option<()> {
|
||||
let file_id = file_id.file_id(sema.db);
|
||||
let literal = ast::String::cast(literal.token())?;
|
||||
let (analysis, fixture_analysis) =
|
||||
Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)?;
|
||||
for virtual_file_id in fixture_analysis.files() {
|
||||
acc.extend(
|
||||
analysis
|
||||
.inlay_hints(config, virtual_file_id, None)
|
||||
.ok()?
|
||||
.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, file_id)
|
||||
.ok()?,
|
||||
);
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
impl_empty_upmap_from_ra_fixture!(InlayHintPosition, InlayKind, InlayTooltip);
|
||||
|
|
@ -11,7 +11,7 @@ use crate::{InlayHint, InlayHintsConfig};
|
|||
pub(super) fn hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
range: impl ast::RangeItem,
|
||||
) -> Option<()> {
|
||||
(config.range_exclusive_hints && range.end().is_some())
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe};
|
|||
|
||||
use cfg::CfgOptions;
|
||||
use fetch_crates::CrateInfo;
|
||||
use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, db::HirDatabase, sym};
|
||||
use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
|
||||
use ide_db::{
|
||||
FxHashMap, FxIndexSet, LineIndexDatabase,
|
||||
base_db::{
|
||||
|
|
@ -71,7 +71,9 @@ use ide_db::{
|
|||
},
|
||||
prime_caches, symbol_index,
|
||||
};
|
||||
use syntax::SourceFile;
|
||||
use ide_db::{MiniCore, ra_fixture::RaFixtureAnalysis};
|
||||
use macros::UpmapFromRaFixture;
|
||||
use syntax::{SourceFile, ast};
|
||||
use triomphe::Arc;
|
||||
use view_memory_layout::{RecursiveMemoryLayout, view_memory_layout};
|
||||
|
||||
|
|
@ -83,6 +85,7 @@ pub use crate::{
|
|||
expand_macro::ExpandedMacro,
|
||||
file_structure::{FileStructureConfig, StructureNode, StructureNodeKind},
|
||||
folding_ranges::{Fold, FoldKind},
|
||||
goto_definition::GotoDefinitionConfig,
|
||||
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
||||
hover::{
|
||||
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
|
||||
|
|
@ -102,7 +105,7 @@ pub use crate::{
|
|||
},
|
||||
move_item::Direction,
|
||||
navigation_target::{NavigationTarget, TryToNav, UpmappingResult},
|
||||
references::ReferenceSearchResult,
|
||||
references::{FindAllRefsConfig, ReferenceSearchResult},
|
||||
rename::RenameError,
|
||||
runnables::{Runnable, RunnableKind, TestId, UpdateTest},
|
||||
signature_help::SignatureHelp,
|
||||
|
|
@ -144,7 +147,7 @@ pub use syntax::{TextRange, TextSize};
|
|||
pub type Cancellable<T> = Result<T, Cancelled>;
|
||||
|
||||
/// Info associated with a text range.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, UpmapFromRaFixture)]
|
||||
pub struct RangeInfo<T> {
|
||||
pub range: TextRange,
|
||||
pub info: T,
|
||||
|
|
@ -274,6 +277,28 @@ impl Analysis {
|
|||
(host.analysis(), file_id)
|
||||
}
|
||||
|
||||
pub(crate) fn from_ra_fixture(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
literal: ast::String,
|
||||
expanded: &ast::String,
|
||||
minicore: MiniCore<'_>,
|
||||
) -> Option<(Analysis, RaFixtureAnalysis)> {
|
||||
Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, minicore, &mut |_| {})
|
||||
}
|
||||
|
||||
/// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position.
|
||||
pub(crate) fn from_ra_fixture_with_on_cursor(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
literal: ast::String,
|
||||
expanded: &ast::String,
|
||||
minicore: MiniCore<'_>,
|
||||
on_cursor: &mut dyn FnMut(TextRange),
|
||||
) -> Option<(Analysis, RaFixtureAnalysis)> {
|
||||
let analysis =
|
||||
RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, minicore, on_cursor)?;
|
||||
Some((Analysis { db: analysis.db.clone() }, analysis))
|
||||
}
|
||||
|
||||
/// Debug info about the current state of the analysis.
|
||||
pub fn status(&self, file_id: Option<FileId>) -> Cancellable<String> {
|
||||
self.with_db(|db| status::status(db, file_id))
|
||||
|
|
@ -446,7 +471,7 @@ impl Analysis {
|
|||
/// Returns a list of the places in the file where type hints can be displayed.
|
||||
pub fn inlay_hints(
|
||||
&self,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
file_id: FileId,
|
||||
range: Option<TextRange>,
|
||||
) -> Cancellable<Vec<InlayHint>> {
|
||||
|
|
@ -454,7 +479,7 @@ impl Analysis {
|
|||
}
|
||||
pub fn inlay_hints_resolve(
|
||||
&self,
|
||||
config: &InlayHintsConfig,
|
||||
config: &InlayHintsConfig<'_>,
|
||||
file_id: FileId,
|
||||
resolve_range: TextRange,
|
||||
hash: u64,
|
||||
|
|
@ -495,16 +520,18 @@ impl Analysis {
|
|||
pub fn goto_definition(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
config: &GotoDefinitionConfig<'_>,
|
||||
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||
self.with_db(|db| goto_definition::goto_definition(db, position))
|
||||
self.with_db(|db| goto_definition::goto_definition(db, position, config))
|
||||
}
|
||||
|
||||
/// Returns the declaration from the symbol at `position`.
|
||||
pub fn goto_declaration(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
config: &GotoDefinitionConfig<'_>,
|
||||
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||
self.with_db(|db| goto_declaration::goto_declaration(db, position))
|
||||
self.with_db(|db| goto_declaration::goto_declaration(db, position, config))
|
||||
}
|
||||
|
||||
/// Returns the impls from the symbol at `position`.
|
||||
|
|
@ -526,19 +553,16 @@ impl Analysis {
|
|||
pub fn find_all_refs(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
search_scope: Option<SearchScope>,
|
||||
config: &FindAllRefsConfig<'_>,
|
||||
) -> Cancellable<Option<Vec<ReferenceSearchResult>>> {
|
||||
let search_scope = AssertUnwindSafe(search_scope);
|
||||
self.with_db(|db| {
|
||||
let _ = &search_scope;
|
||||
references::find_all_refs(&Semantics::new(db), position, search_scope.0)
|
||||
})
|
||||
let config = AssertUnwindSafe(config);
|
||||
self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, &config))
|
||||
}
|
||||
|
||||
/// Returns a short text describing element at position.
|
||||
pub fn hover(
|
||||
&self,
|
||||
config: &HoverConfig,
|
||||
config: &HoverConfig<'_>,
|
||||
range: FileRange,
|
||||
) -> Cancellable<Option<RangeInfo<HoverResult>>> {
|
||||
self.with_db(|db| hover::hover(db, range, config))
|
||||
|
|
@ -576,14 +600,15 @@ impl Analysis {
|
|||
pub fn call_hierarchy(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
) -> Cancellable<Option<RangeInfo<Vec<NavigationTarget>>>> {
|
||||
self.with_db(|db| call_hierarchy::call_hierarchy(db, position))
|
||||
self.with_db(|db| call_hierarchy::call_hierarchy(db, position, config))
|
||||
}
|
||||
|
||||
/// Computes incoming calls for the given file position.
|
||||
pub fn incoming_calls(
|
||||
&self,
|
||||
config: CallHierarchyConfig,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
position: FilePosition,
|
||||
) -> Cancellable<Option<Vec<CallItem>>> {
|
||||
self.with_db(|db| call_hierarchy::incoming_calls(db, config, position))
|
||||
|
|
@ -592,7 +617,7 @@ impl Analysis {
|
|||
/// Computes outgoing calls for the given file position.
|
||||
pub fn outgoing_calls(
|
||||
&self,
|
||||
config: CallHierarchyConfig,
|
||||
config: &CallHierarchyConfig<'_>,
|
||||
position: FilePosition,
|
||||
) -> Cancellable<Option<Vec<CallItem>>> {
|
||||
self.with_db(|db| call_hierarchy::outgoing_calls(db, config, position))
|
||||
|
|
@ -675,28 +700,22 @@ impl Analysis {
|
|||
/// Computes syntax highlighting for the given file
|
||||
pub fn highlight(
|
||||
&self,
|
||||
highlight_config: HighlightConfig,
|
||||
highlight_config: HighlightConfig<'_>,
|
||||
file_id: FileId,
|
||||
) -> Cancellable<Vec<HlRange>> {
|
||||
// highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
|
||||
// highlighting instead sets up the attach hook where neceesary for the trait solver
|
||||
Cancelled::catch(|| {
|
||||
syntax_highlighting::highlight(&self.db, highlight_config, file_id, None)
|
||||
})
|
||||
self.with_db(|db| syntax_highlighting::highlight(db, &highlight_config, file_id, None))
|
||||
}
|
||||
|
||||
/// Computes syntax highlighting for the given file range.
|
||||
pub fn highlight_range(
|
||||
&self,
|
||||
highlight_config: HighlightConfig,
|
||||
highlight_config: HighlightConfig<'_>,
|
||||
frange: FileRange,
|
||||
) -> Cancellable<Vec<HlRange>> {
|
||||
// highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
|
||||
// highlighting instead sets up the attach hook where neceesary for the trait solver
|
||||
Cancelled::catch(|| {
|
||||
self.with_db(|db| {
|
||||
syntax_highlighting::highlight(
|
||||
&self.db,
|
||||
highlight_config,
|
||||
db,
|
||||
&highlight_config,
|
||||
frange.file_id,
|
||||
Some(frange.range),
|
||||
)
|
||||
|
|
@ -706,22 +725,18 @@ impl Analysis {
|
|||
/// Computes syntax highlighting for the given file.
|
||||
pub fn highlight_as_html_with_config(
|
||||
&self,
|
||||
config: HighlightConfig,
|
||||
config: HighlightConfig<'_>,
|
||||
file_id: FileId,
|
||||
rainbow: bool,
|
||||
) -> Cancellable<String> {
|
||||
// highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
|
||||
// highlighting instead sets up the attach hook where neceesary for the trait solver
|
||||
Cancelled::catch(|| {
|
||||
syntax_highlighting::highlight_as_html_with_config(&self.db, config, file_id, rainbow)
|
||||
self.with_db(|db| {
|
||||
syntax_highlighting::highlight_as_html_with_config(db, &config, file_id, rainbow)
|
||||
})
|
||||
}
|
||||
|
||||
/// Computes syntax highlighting for the given file.
|
||||
pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable<String> {
|
||||
// highlighting may construct a new database for "speculative" execution, so we can't currently attach the database
|
||||
// highlighting instead sets up the attach hook where neceesary for the trait solver
|
||||
Cancelled::catch(|| syntax_highlighting::highlight_as_html(&self.db, file_id, rainbow))
|
||||
self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow))
|
||||
}
|
||||
|
||||
/// Computes completions at the given position.
|
||||
|
|
@ -853,14 +868,18 @@ impl Analysis {
|
|||
|
||||
pub fn annotations(
|
||||
&self,
|
||||
config: &AnnotationConfig,
|
||||
config: &AnnotationConfig<'_>,
|
||||
file_id: FileId,
|
||||
) -> Cancellable<Vec<Annotation>> {
|
||||
self.with_db(|db| annotations::annotations(db, config, file_id))
|
||||
}
|
||||
|
||||
pub fn resolve_annotation(&self, annotation: Annotation) -> Cancellable<Annotation> {
|
||||
self.with_db(|db| annotations::resolve_annotation(db, annotation))
|
||||
pub fn resolve_annotation(
|
||||
&self,
|
||||
config: &AnnotationConfig<'_>,
|
||||
annotation: Annotation,
|
||||
) -> Cancellable<Annotation> {
|
||||
self.with_db(|db| annotations::resolve_annotation(db, config, annotation))
|
||||
}
|
||||
|
||||
pub fn move_item(
|
||||
|
|
@ -899,12 +918,8 @@ impl Analysis {
|
|||
where
|
||||
F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe,
|
||||
{
|
||||
hir::attach_db(&self.db, || {
|
||||
// the trait solver code may invoke `as_view<HirDatabase>` outside of queries,
|
||||
// so technically we might run into a panic in salsa if the downcaster has not yet been registered.
|
||||
HirDatabase::zalsa_register_downcaster(&self.db);
|
||||
Cancelled::catch(|| f(&self.db))
|
||||
})
|
||||
// We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database.
|
||||
hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
//! what is used by LSP, so let's keep it simple.
|
||||
use std::fmt;
|
||||
|
||||
use ide_db::impl_empty_upmap_from_ra_fixture;
|
||||
|
||||
#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Markup {
|
||||
text: String,
|
||||
|
|
@ -39,3 +41,5 @@ impl Markup {
|
|||
format!("```text\n{contents}\n```").into()
|
||||
}
|
||||
}
|
||||
|
||||
impl_empty_upmap_from_ra_fixture!(Markup);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ use ide_db::{
|
|||
defs::{Definition, find_std_module},
|
||||
documentation::{Documentation, HasDocs},
|
||||
famous_defs::FamousDefs,
|
||||
ra_fixture::UpmapFromRaFixture,
|
||||
};
|
||||
use span::Edition;
|
||||
use stdx::never;
|
||||
|
|
@ -78,6 +79,44 @@ impl fmt::Debug for NavigationTarget {
|
|||
}
|
||||
}
|
||||
|
||||
impl UpmapFromRaFixture for NavigationTarget {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
analysis: &ide_db::ra_fixture::RaFixtureAnalysis,
|
||||
_virtual_file_id: FileId,
|
||||
real_file_id: FileId,
|
||||
) -> Result<Self, ()> {
|
||||
let virtual_file_id = self.file_id;
|
||||
Ok(NavigationTarget {
|
||||
file_id: real_file_id,
|
||||
full_range: self.full_range.upmap_from_ra_fixture(
|
||||
analysis,
|
||||
virtual_file_id,
|
||||
real_file_id,
|
||||
)?,
|
||||
focus_range: self.focus_range.upmap_from_ra_fixture(
|
||||
analysis,
|
||||
virtual_file_id,
|
||||
real_file_id,
|
||||
)?,
|
||||
name: self.name.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
|
||||
kind: self.kind.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
|
||||
container_name: self.container_name.upmap_from_ra_fixture(
|
||||
analysis,
|
||||
virtual_file_id,
|
||||
real_file_id,
|
||||
)?,
|
||||
description: self.description.upmap_from_ra_fixture(
|
||||
analysis,
|
||||
virtual_file_id,
|
||||
real_file_id,
|
||||
)?,
|
||||
docs: self.docs.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
|
||||
alias: self.alias.upmap_from_ra_fixture(analysis, virtual_file_id, real_file_id)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ToNav {
|
||||
fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,14 +19,17 @@
|
|||
|
||||
use hir::{PathResolution, Semantics};
|
||||
use ide_db::{
|
||||
FileId, RootDatabase,
|
||||
FileId, MiniCore, RootDatabase,
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
helpers::pick_best_token,
|
||||
ra_fixture::UpmapFromRaFixture,
|
||||
search::{ReferenceCategory, SearchScope, UsageSearchResult},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use nohash_hasher::IntMap;
|
||||
use span::Edition;
|
||||
use syntax::AstToken;
|
||||
use syntax::{
|
||||
AstNode,
|
||||
SyntaxKind::*,
|
||||
|
|
@ -35,10 +38,12 @@ use syntax::{
|
|||
match_ast,
|
||||
};
|
||||
|
||||
use crate::{FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related};
|
||||
use crate::{
|
||||
Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related,
|
||||
};
|
||||
|
||||
/// Result of a reference search operation.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, UpmapFromRaFixture)]
|
||||
pub struct ReferenceSearchResult {
|
||||
/// Information about the declaration site of the searched item.
|
||||
/// For ADTs (structs/enums), this points to the type definition.
|
||||
|
|
@ -54,7 +59,7 @@ pub struct ReferenceSearchResult {
|
|||
}
|
||||
|
||||
/// Information about the declaration site of a searched item.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, UpmapFromRaFixture)]
|
||||
pub struct Declaration {
|
||||
/// Navigation information to jump to the declaration
|
||||
pub nav: NavigationTarget,
|
||||
|
|
@ -82,6 +87,12 @@ pub struct Declaration {
|
|||
//
|
||||
// 
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FindAllRefsConfig<'a> {
|
||||
pub search_scope: Option<SearchScope>,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
/// Find all references to the item at the given position.
|
||||
///
|
||||
/// # Arguments
|
||||
|
|
@ -110,14 +121,14 @@ pub struct Declaration {
|
|||
pub(crate) fn find_all_refs(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
position: FilePosition,
|
||||
search_scope: Option<SearchScope>,
|
||||
config: &FindAllRefsConfig<'_>,
|
||||
) -> Option<Vec<ReferenceSearchResult>> {
|
||||
let _p = tracing::info_span!("find_all_refs").entered();
|
||||
let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();
|
||||
let make_searcher = |literal_search: bool| {
|
||||
move |def: Definition| {
|
||||
let mut usages =
|
||||
def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all();
|
||||
def.usages(sema).set_scope(config.search_scope.as_ref()).include_self_refs().all();
|
||||
if literal_search {
|
||||
retain_adt_literal_usages(&mut usages, def, sema);
|
||||
}
|
||||
|
|
@ -165,6 +176,20 @@ pub(crate) fn find_all_refs(
|
|||
return Some(vec![res]);
|
||||
}
|
||||
|
||||
if let Some(token) = syntax.token_at_offset(position.offset).left_biased()
|
||||
&& let Some(token) = ast::String::cast(token.clone())
|
||||
&& let Some((analysis, fixture_analysis)) =
|
||||
Analysis::from_ra_fixture(sema, token.clone(), &token, config.minicore)
|
||||
&& let Some((virtual_file_id, file_offset)) =
|
||||
fixture_analysis.map_offset_down(position.offset)
|
||||
{
|
||||
return analysis
|
||||
.find_all_refs(FilePosition { file_id: virtual_file_id, offset: file_offset }, config)
|
||||
.ok()??
|
||||
.upmap_from_ra_fixture(&fixture_analysis, virtual_file_id, position.file_id)
|
||||
.ok();
|
||||
}
|
||||
|
||||
match name_for_constructor_search(&syntax, position) {
|
||||
Some(name) => {
|
||||
let def = match NameClass::classify(sema, &name)? {
|
||||
|
|
@ -433,10 +458,10 @@ fn handle_control_flow_keywords(
|
|||
mod tests {
|
||||
use expect_test::{Expect, expect};
|
||||
use hir::EditionedFileId;
|
||||
use ide_db::{FileId, RootDatabase};
|
||||
use ide_db::{FileId, MiniCore, RootDatabase};
|
||||
use stdx::format_to;
|
||||
|
||||
use crate::{SearchScope, fixture};
|
||||
use crate::{SearchScope, fixture, references::FindAllRefsConfig};
|
||||
|
||||
#[test]
|
||||
fn exclude_tests() {
|
||||
|
|
@ -1513,8 +1538,11 @@ fn main() {
|
|||
expect: Expect,
|
||||
) {
|
||||
let (analysis, pos) = fixture::position(ra_fixture);
|
||||
let refs =
|
||||
analysis.find_all_refs(pos, search_scope.map(|it| it(&analysis.db))).unwrap().unwrap();
|
||||
let config = FindAllRefsConfig {
|
||||
search_scope: search_scope.map(|it| it(&analysis.db)),
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap();
|
||||
|
||||
let mut actual = String::new();
|
||||
for mut refs in refs {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use hir::{
|
|||
sym,
|
||||
};
|
||||
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
|
||||
use ide_db::impl_empty_upmap_from_ra_fixture;
|
||||
use ide_db::{
|
||||
FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind,
|
||||
base_db::RootQueryDb,
|
||||
|
|
@ -17,6 +18,7 @@ use ide_db::{
|
|||
search::{FileReferenceNode, SearchScope},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use macros::UpmapFromRaFixture;
|
||||
use smallvec::SmallVec;
|
||||
use span::{Edition, TextSize};
|
||||
use stdx::format_to;
|
||||
|
|
@ -28,7 +30,7 @@ use syntax::{
|
|||
|
||||
use crate::{FileId, NavigationTarget, ToNav, TryToNav, references};
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, UpmapFromRaFixture)]
|
||||
pub struct Runnable {
|
||||
pub use_name_in_title: bool,
|
||||
pub nav: NavigationTarget,
|
||||
|
|
@ -37,6 +39,8 @@ pub struct Runnable {
|
|||
pub update_test: UpdateTest,
|
||||
}
|
||||
|
||||
impl_empty_upmap_from_ra_fixture!(RunnableKind, UpdateTest);
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||
pub enum TestId {
|
||||
Name(SmolStr),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
use arrayvec::ArrayVec;
|
||||
use hir::{Crate, Module, Semantics, db::HirDatabase};
|
||||
use ide_db::{
|
||||
FileId, FileRange, FxHashMap, FxHashSet, RootDatabase,
|
||||
FileId, FileRange, FxHashMap, FxHashSet, MiniCore, RootDatabase,
|
||||
base_db::{RootQueryDb, SourceDatabase, VfsPath},
|
||||
defs::{Definition, IdentClass},
|
||||
documentation::Documentation,
|
||||
|
|
@ -184,6 +184,7 @@ impl StaticIndex<'_> {
|
|||
closing_brace_hints_min_lines: Some(25),
|
||||
fields_to_resolve: InlayFieldsToResolve::empty(),
|
||||
range_exclusive_hints: false,
|
||||
minicore: MiniCore::default(),
|
||||
},
|
||||
file_id,
|
||||
None,
|
||||
|
|
@ -215,6 +216,7 @@ impl StaticIndex<'_> {
|
|||
max_enum_variants_count: Some(5),
|
||||
max_subst_ty_len: SubstTyLen::Unlimited,
|
||||
show_drop_glue: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
let tokens = tokens.filter(|token| {
|
||||
matches!(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
pub(crate) mod tags;
|
||||
|
||||
mod highlights;
|
||||
mod injector;
|
||||
|
||||
mod escape;
|
||||
mod format;
|
||||
|
|
@ -16,7 +15,7 @@ use std::ops::ControlFlow;
|
|||
|
||||
use either::Either;
|
||||
use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics};
|
||||
use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind};
|
||||
use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind};
|
||||
use syntax::{
|
||||
AstNode, AstToken, NodeOrToken,
|
||||
SyntaxKind::*,
|
||||
|
|
@ -44,8 +43,8 @@ pub struct HlRange {
|
|||
pub binding_hash: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct HighlightConfig {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct HighlightConfig<'a> {
|
||||
/// Whether to highlight strings
|
||||
pub strings: bool,
|
||||
/// Whether to highlight comments
|
||||
|
|
@ -64,6 +63,7 @@ pub struct HighlightConfig {
|
|||
pub macro_bang: bool,
|
||||
/// Whether to highlight unresolved things be their syntax
|
||||
pub syntactic_name_ref_highlighting: bool,
|
||||
pub minicore: MiniCore<'a>,
|
||||
}
|
||||
|
||||
// Feature: Semantic Syntax Highlighting
|
||||
|
|
@ -191,7 +191,7 @@ pub struct HighlightConfig {
|
|||
// 
|
||||
pub(crate) fn highlight(
|
||||
db: &RootDatabase,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
file_id: FileId,
|
||||
range_to_highlight: Option<TextRange>,
|
||||
) -> Vec<HlRange> {
|
||||
|
|
@ -226,7 +226,7 @@ pub(crate) fn highlight(
|
|||
fn traverse(
|
||||
hl: &mut Highlights,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
InRealFile { file_id, value: root }: InRealFile<&SyntaxNode>,
|
||||
krate: Option<hir::Crate>,
|
||||
range_to_highlight: TextRange,
|
||||
|
|
@ -426,12 +426,9 @@ fn traverse(
|
|||
let edition = descended_element.file_id.edition(sema.db);
|
||||
let (unsafe_ops, bindings_shadow_count) = match current_body {
|
||||
Some(current_body) => {
|
||||
let (ops, bindings) = per_body_cache.entry(current_body).or_insert_with(|| {
|
||||
(
|
||||
hir::attach_db(sema.db, || sema.get_unsafe_ops(current_body)),
|
||||
Default::default(),
|
||||
)
|
||||
});
|
||||
let (ops, bindings) = per_body_cache
|
||||
.entry(current_body)
|
||||
.or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default()));
|
||||
(&*ops, Some(bindings))
|
||||
}
|
||||
None => (&empty, None),
|
||||
|
|
@ -494,7 +491,7 @@ fn traverse(
|
|||
fn string_injections(
|
||||
hl: &mut Highlights,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
file_id: EditionedFileId,
|
||||
krate: Option<hir::Crate>,
|
||||
token: SyntaxToken,
|
||||
|
|
@ -591,7 +588,7 @@ fn descend_token(
|
|||
})
|
||||
}
|
||||
|
||||
fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool {
|
||||
fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool {
|
||||
match &mut highlight.tag {
|
||||
HlTag::StringLiteral if !config.strings => return false,
|
||||
HlTag::Comment if !config.comments => return false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Renders a bit of code as HTML.
|
||||
|
||||
use hir::{EditionedFileId, Semantics};
|
||||
use ide_db::MiniCore;
|
||||
use oorandom::Rand32;
|
||||
use stdx::format_to;
|
||||
use syntax::AstNode;
|
||||
|
|
@ -12,7 +13,7 @@ use crate::{
|
|||
|
||||
pub(crate) fn highlight_as_html_with_config(
|
||||
db: &RootDatabase,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
file_id: FileId,
|
||||
rainbow: bool,
|
||||
) -> String {
|
||||
|
|
@ -60,7 +61,7 @@ pub(crate) fn highlight_as_html_with_config(
|
|||
pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
|
||||
highlight_as_html_with_config(
|
||||
db,
|
||||
HighlightConfig {
|
||||
&HighlightConfig {
|
||||
strings: true,
|
||||
comments: true,
|
||||
punctuation: true,
|
||||
|
|
@ -70,6 +71,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
|
|||
inject_doc_comment: true,
|
||||
macro_bang: true,
|
||||
syntactic_name_ref_highlighting: false,
|
||||
minicore: MiniCore::default(),
|
||||
},
|
||||
file_id,
|
||||
rainbow,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use std::mem;
|
|||
|
||||
use either::Either;
|
||||
use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym};
|
||||
use ide_db::range_mapper::RangeMapper;
|
||||
use ide_db::{
|
||||
SymbolKind, active_parameter::ActiveParameter, defs::Definition,
|
||||
documentation::docs_with_rangemap, rust_doc::is_rust_fence,
|
||||
SymbolKind, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence,
|
||||
};
|
||||
use syntax::{
|
||||
AstToken, NodeOrToken, SyntaxNode, TextRange, TextSize,
|
||||
|
|
@ -16,85 +16,56 @@ use syntax::{
|
|||
use crate::{
|
||||
Analysis, HlMod, HlRange, HlTag, RootDatabase,
|
||||
doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
|
||||
syntax_highlighting::{HighlightConfig, highlights::Highlights, injector::Injector},
|
||||
syntax_highlighting::{HighlightConfig, highlights::Highlights},
|
||||
};
|
||||
|
||||
pub(super) fn ra_fixture(
|
||||
hl: &mut Highlights,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
literal: &ast::String,
|
||||
expanded: &ast::String,
|
||||
) -> Option<()> {
|
||||
let active_parameter =
|
||||
hir::attach_db(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?;
|
||||
let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| {
|
||||
attrs.filter_map(|attr| attr.as_simple_path()).any(|path| {
|
||||
path.segments()
|
||||
.zip(["rust_analyzer", "rust_fixture"])
|
||||
.all(|(seg, name)| seg.name_ref().map_or(false, |nr| nr.text() == name))
|
||||
})
|
||||
});
|
||||
if !has_rust_fixture_attr {
|
||||
return None;
|
||||
}
|
||||
let value = literal.value().ok()?;
|
||||
let (analysis, fixture_analysis) = Analysis::from_ra_fixture_with_on_cursor(
|
||||
sema,
|
||||
literal.clone(),
|
||||
expanded,
|
||||
config.minicore,
|
||||
&mut |range| {
|
||||
hl.add(HlRange {
|
||||
range,
|
||||
highlight: HlTag::Keyword | HlMod::Injected,
|
||||
binding_hash: None,
|
||||
});
|
||||
},
|
||||
)?;
|
||||
|
||||
if let Some(range) = literal.open_quote_text_range() {
|
||||
hl.add(HlRange { range, highlight: HlTag::StringLiteral.into(), binding_hash: None })
|
||||
}
|
||||
|
||||
let mut inj = Injector::default();
|
||||
|
||||
let mut text = &*value;
|
||||
let mut offset: TextSize = 0.into();
|
||||
|
||||
while !text.is_empty() {
|
||||
let marker = "$0";
|
||||
let idx = text.find(marker).unwrap_or(text.len());
|
||||
let (chunk, next) = text.split_at(idx);
|
||||
inj.add(chunk, TextRange::at(offset, TextSize::of(chunk)));
|
||||
|
||||
text = next;
|
||||
offset += TextSize::of(chunk);
|
||||
|
||||
if let Some(next) = text.strip_prefix(marker) {
|
||||
if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
|
||||
hl.add(HlRange {
|
||||
range,
|
||||
highlight: HlTag::Keyword | HlMod::Injected,
|
||||
binding_hash: None,
|
||||
});
|
||||
}
|
||||
|
||||
text = next;
|
||||
|
||||
let marker_len = TextSize::of(marker);
|
||||
offset += marker_len;
|
||||
}
|
||||
}
|
||||
|
||||
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
|
||||
|
||||
for mut hl_range in analysis
|
||||
.highlight(
|
||||
HighlightConfig {
|
||||
syntactic_name_ref_highlighting: false,
|
||||
comments: true,
|
||||
punctuation: true,
|
||||
operator: true,
|
||||
strings: true,
|
||||
specialize_punctuation: config.specialize_punctuation,
|
||||
specialize_operator: config.operator,
|
||||
inject_doc_comment: config.inject_doc_comment,
|
||||
macro_bang: config.macro_bang,
|
||||
},
|
||||
tmp_file_id,
|
||||
)
|
||||
.unwrap()
|
||||
{
|
||||
for range in inj.map_range_up(hl_range.range) {
|
||||
if let Some(range) = literal.map_range_up(range) {
|
||||
for tmp_file_id in fixture_analysis.files() {
|
||||
for mut hl_range in analysis
|
||||
.highlight(
|
||||
HighlightConfig {
|
||||
syntactic_name_ref_highlighting: false,
|
||||
comments: true,
|
||||
punctuation: true,
|
||||
operator: true,
|
||||
strings: true,
|
||||
specialize_punctuation: config.specialize_punctuation,
|
||||
specialize_operator: config.operator,
|
||||
inject_doc_comment: config.inject_doc_comment,
|
||||
macro_bang: config.macro_bang,
|
||||
// What if there is a fixture inside a fixture? It's fixtures all the way down.
|
||||
// (In fact, we have a fixture inside a fixture in our test suite!)
|
||||
minicore: config.minicore,
|
||||
},
|
||||
tmp_file_id,
|
||||
)
|
||||
.unwrap()
|
||||
{
|
||||
for range in fixture_analysis.map_range_up(tmp_file_id, hl_range.range) {
|
||||
hl_range.range = range;
|
||||
hl_range.highlight |= HlMod::Injected;
|
||||
hl.add(hl_range);
|
||||
|
|
@ -116,7 +87,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
|
|||
pub(super) fn doc_comment(
|
||||
hl: &mut Highlights,
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
config: HighlightConfig,
|
||||
config: &HighlightConfig<'_>,
|
||||
src_file_id: EditionedFileId,
|
||||
node: &SyntaxNode,
|
||||
) {
|
||||
|
|
@ -128,39 +99,37 @@ pub(super) fn doc_comment(
|
|||
|
||||
// Extract intra-doc links and emit highlights for them.
|
||||
if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
|
||||
hir::attach_db(sema.db, || {
|
||||
extract_definitions_from_docs(&docs)
|
||||
.into_iter()
|
||||
.filter_map(|(range, link, ns)| {
|
||||
doc_mapping
|
||||
.map(range)
|
||||
.filter(|(mapping, _)| mapping.file_id == src_file_id)
|
||||
.and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
|
||||
Some(mapped_range).zip(resolve_doc_path_for_def(
|
||||
sema.db,
|
||||
def,
|
||||
&link,
|
||||
ns,
|
||||
attr_id.is_inner_attr(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.for_each(|(range, def)| {
|
||||
hl.add(HlRange {
|
||||
range,
|
||||
highlight: module_def_to_hl_tag(def)
|
||||
| HlMod::Documentation
|
||||
| HlMod::Injected
|
||||
| HlMod::IntraDocLink,
|
||||
binding_hash: None,
|
||||
extract_definitions_from_docs(&docs)
|
||||
.into_iter()
|
||||
.filter_map(|(range, link, ns)| {
|
||||
doc_mapping
|
||||
.map(range)
|
||||
.filter(|(mapping, _)| mapping.file_id == src_file_id)
|
||||
.and_then(|(InFile { value: mapped_range, .. }, attr_id)| {
|
||||
Some(mapped_range).zip(resolve_doc_path_for_def(
|
||||
sema.db,
|
||||
def,
|
||||
&link,
|
||||
ns,
|
||||
attr_id.is_inner_attr(),
|
||||
))
|
||||
})
|
||||
})
|
||||
.for_each(|(range, def)| {
|
||||
hl.add(HlRange {
|
||||
range,
|
||||
highlight: module_def_to_hl_tag(def)
|
||||
| HlMod::Documentation
|
||||
| HlMod::Injected
|
||||
| HlMod::IntraDocLink,
|
||||
binding_hash: None,
|
||||
})
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// Extract doc-test sources from the docs and calculate highlighting for them.
|
||||
|
||||
let mut inj = Injector::default();
|
||||
let mut inj = RangeMapper::default();
|
||||
inj.add_unmapped("fn doctest() {\n");
|
||||
|
||||
let attrs_source_map = attributes.source_map(sema.db);
|
||||
|
|
@ -249,7 +218,7 @@ pub(super) fn doc_comment(
|
|||
if let Ok(ranges) = analysis.with_db(|db| {
|
||||
super::highlight(
|
||||
db,
|
||||
HighlightConfig {
|
||||
&HighlightConfig {
|
||||
syntactic_name_ref_highlighting: true,
|
||||
comments: true,
|
||||
punctuation: true,
|
||||
|
|
@ -259,6 +228,7 @@ pub(super) fn doc_comment(
|
|||
specialize_operator: config.operator,
|
||||
inject_doc_comment: config.inject_doc_comment,
|
||||
macro_bang: config.macro_bang,
|
||||
minicore: config.minicore,
|
||||
},
|
||||
tmp_file_id,
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
//! Extracts a subsequence of a text document, remembering the mapping of ranges
|
||||
//! between original and extracted texts.
|
||||
use std::ops::{self, Sub};
|
||||
|
||||
use stdx::equal_range_by;
|
||||
use syntax::{TextRange, TextSize};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct Injector {
|
||||
buf: String,
|
||||
ranges: Vec<(TextRange, Option<Delta<TextSize>>)>,
|
||||
}
|
||||
|
||||
impl Injector {
|
||||
pub(super) fn add(&mut self, text: &str, source_range: TextRange) {
|
||||
let len = TextSize::of(text);
|
||||
assert_eq!(len, source_range.len());
|
||||
self.add_impl(text, Some(source_range.start()));
|
||||
}
|
||||
|
||||
pub(super) fn add_unmapped(&mut self, text: &str) {
|
||||
self.add_impl(text, None);
|
||||
}
|
||||
|
||||
fn add_impl(&mut self, text: &str, source: Option<TextSize>) {
|
||||
let len = TextSize::of(text);
|
||||
let target_range = TextRange::at(TextSize::of(&self.buf), len);
|
||||
self.ranges.push((target_range, source.map(|it| Delta::new(target_range.start(), it))));
|
||||
self.buf.push_str(text);
|
||||
}
|
||||
|
||||
pub(super) fn take_text(&mut self) -> String {
|
||||
std::mem::take(&mut self.buf)
|
||||
}
|
||||
|
||||
pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
|
||||
equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
|
||||
let (target_range, delta) = self.ranges[i];
|
||||
let intersection = target_range.intersect(range).unwrap();
|
||||
Some(intersection + delta?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Delta<T> {
|
||||
Add(T),
|
||||
Sub(T),
|
||||
}
|
||||
|
||||
impl<T> Delta<T> {
|
||||
fn new(from: T, to: T) -> Delta<T>
|
||||
where
|
||||
T: Ord + Sub<Output = T>,
|
||||
{
|
||||
if to >= from { Delta::Add(to - from) } else { Delta::Sub(from - to) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<Delta<TextSize>> for TextSize {
|
||||
type Output = TextSize;
|
||||
|
||||
fn add(self, rhs: Delta<TextSize>) -> TextSize {
|
||||
match rhs {
|
||||
Delta::Add(it) => self + it,
|
||||
Delta::Sub(it) => self - it,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<Delta<TextSize>> for TextRange {
|
||||
type Output = TextRange;
|
||||
|
||||
fn add(self, rhs: Delta<TextSize>) -> TextRange {
|
||||
TextRange::at(self.start() + rhs, self.len())
|
||||
}
|
||||
}
|
||||
|
|
@ -43,18 +43,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
|||
<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
|
||||
|
||||
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
||||
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
|
||||
</span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span><span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="string_literal injected">"2 + 2 = {}"</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">4</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="none injected">
|
||||
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
|
||||
@@- minicore: sized
|
||||
<span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="colon injected">:</span><span class="none injected"> </span><span class="trait default_library injected library">Sized</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="string_literal injected">"2 + 2 = {}"</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">4</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
|
||||
</span> <span class="brace injected">}</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="string_literal">"#</span>
|
||||
<span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span><span class="none injected">
|
||||
</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span><span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
|
||||
</span><span class="numeric_literal injected">92</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
|
||||
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
|
||||
<span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="numeric_literal injected">92</span><span class="none injected">
|
||||
</span> <span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="string_literal">"</span>
|
||||
<span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||
<span class="brace">}</span></code></pre>
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
|
||||
<style>
|
||||
body { margin: 0; }
|
||||
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
|
||||
|
||||
.lifetime { color: #DFAF8F; font-style: italic; }
|
||||
.label { color: #DFAF8F; font-style: italic; }
|
||||
.comment { color: #7F9F7F; }
|
||||
.documentation { color: #629755; }
|
||||
.intra_doc_link { font-style: italic; }
|
||||
.injected { opacity: 0.65 ; }
|
||||
.struct, .enum { color: #7CB8BB; }
|
||||
.enum_variant { color: #BDE0F3; }
|
||||
.string_literal { color: #CC9393; }
|
||||
.field { color: #94BFF3; }
|
||||
.function { color: #93E0E3; }
|
||||
.parameter { color: #94BFF3; }
|
||||
.text { color: #DCDCCC; }
|
||||
.type { color: #7CB8BB; }
|
||||
.builtin_type { color: #8CD0D3; }
|
||||
.type_param { color: #DFAF8F; }
|
||||
.attribute { color: #94BFF3; }
|
||||
.numeric_literal { color: #BFEBBF; }
|
||||
.bool_literal { color: #BFE6EB; }
|
||||
.macro { color: #94BFF3; }
|
||||
.proc_macro { color: #94BFF3; text-decoration: underline; }
|
||||
.derive { color: #94BFF3; font-style: italic; }
|
||||
.module { color: #AFD8AF; }
|
||||
.value_param { color: #DCDCCC; }
|
||||
.variable { color: #DCDCCC; }
|
||||
.format_specifier { color: #CC696B; }
|
||||
.mutable { text-decoration: underline; }
|
||||
.escape_sequence { color: #94BFF3; }
|
||||
.keyword { color: #F0DFAF; font-weight: bold; }
|
||||
.control { font-style: italic; }
|
||||
.reference { font-style: italic; font-weight: bold; }
|
||||
.const { font-weight: bolder; }
|
||||
.unsafe { color: #BC8383; }
|
||||
|
||||
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
|
||||
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
|
||||
</style>
|
||||
<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="tool_module attribute">rust_analyzer</span><span class="operator attribute">::</span><span class="tool_module attribute">rust_fixture</span><span class="attribute_bracket attribute">]</span> <span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
|
||||
|
||||
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
|
||||
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
|
||||
@@- /main.rs crate:main deps:other_crate
|
||||
<span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">test</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">x</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="module crate_root injected library">other_crate</span><span class="operator injected">::</span><span class="module injected library">foo</span><span class="operator injected">::</span><span class="struct injected library">S</span><span class="operator injected">::</span><span class="function associated injected library static">thing</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span><span class="none injected">
|
||||
</span> <span class="variable injected">x</span><span class="semicolon injected">;</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="none injected"> </span><span class="comment injected">//^ i128</span><span class="none injected">
|
||||
</span><span class="none injected">
|
||||
</span>@@- /lib.rs crate:other_crate
|
||||
<span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">mod</span><span class="none injected"> </span><span class="module declaration injected public">foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">struct</span><span class="none injected"> </span><span class="struct declaration injected public">S</span><span class="semicolon injected">;</span><span class="none injected">
|
||||
</span> <span class="keyword injected">impl</span><span class="none injected"> </span><span class="struct injected public">S</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
|
||||
</span> <span class="keyword injected">pub</span><span class="none injected"> </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected public static">thing</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">-></span><span class="none injected"> </span><span class="builtin_type injected">i128</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="numeric_literal injected">0</span><span class="none injected"> </span><span class="brace injected">}</span><span class="none injected">
|
||||
</span> <span class="brace injected">}</span><span class="none injected">
|
||||
</span><span class="brace injected">}</span><span class="none injected">
|
||||
</span> <span class="none injected"> </span><span class="string_literal">"#</span><span class="parenthesis">)</span><span class="semicolon">;</span>
|
||||
<span class="brace">}</span></code></pre>
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
use std::time::Instant;
|
||||
|
||||
use expect_test::{ExpectFile, expect_file};
|
||||
use ide_db::SymbolKind;
|
||||
use ide_db::{MiniCore, SymbolKind};
|
||||
use span::Edition;
|
||||
use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests};
|
||||
|
||||
use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture};
|
||||
|
||||
const HL_CONFIG: HighlightConfig = HighlightConfig {
|
||||
const HL_CONFIG: HighlightConfig<'_> = HighlightConfig {
|
||||
strings: true,
|
||||
comments: true,
|
||||
punctuation: true,
|
||||
|
|
@ -17,6 +17,7 @@ const HL_CONFIG: HighlightConfig = HighlightConfig {
|
|||
inject_doc_comment: true,
|
||||
macro_bang: true,
|
||||
syntactic_name_ref_highlighting: false,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
|
@ -1016,6 +1017,35 @@ impl t for foo {
|
|||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_injection_2() {
|
||||
check_highlighting(
|
||||
r##"
|
||||
fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
|
||||
|
||||
fn main() {
|
||||
fixture(r#"
|
||||
@@- /main.rs crate:main deps:other_crate
|
||||
fn test() {
|
||||
let x = other_crate::foo::S::thing();
|
||||
x;
|
||||
} //^ i128
|
||||
|
||||
@@- /lib.rs crate:other_crate
|
||||
pub mod foo {
|
||||
pub struct S;
|
||||
impl S {
|
||||
pub fn thing() -> i128 { 0 }
|
||||
}
|
||||
}
|
||||
"#);
|
||||
}
|
||||
"##,
|
||||
expect_file!["./test_data/highlight_injection_2.html"],
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_injection() {
|
||||
check_highlighting(
|
||||
|
|
@ -1024,7 +1054,8 @@ fn fixture(#[rust_analyzer::rust_fixture] ra_fixture: &str) {}
|
|||
|
||||
fn main() {
|
||||
fixture(r#"
|
||||
trait Foo {
|
||||
@@- minicore: sized
|
||||
trait Foo: Sized {
|
||||
fn foo() {
|
||||
println!("2 + 2 = {}", 4);
|
||||
}
|
||||
|
|
@ -1223,7 +1254,7 @@ fn foo(x: &fn(&dyn Trait)) {}
|
|||
/// Note that the `snapshot` file is overwritten by the rendered HTML.
|
||||
fn check_highlighting_with_config(
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
config: HighlightConfig,
|
||||
config: HighlightConfig<'_>,
|
||||
expect: ExpectFile,
|
||||
rainbow: bool,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -162,3 +162,42 @@ fn has_ignore_attr(attrs: &[syn::Attribute], name: &'static str, meta: &'static
|
|||
|
||||
ignored
|
||||
}
|
||||
|
||||
decl_derive!(
|
||||
[UpmapFromRaFixture] => upmap_from_ra_fixture
|
||||
);
|
||||
|
||||
fn upmap_from_ra_fixture(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
|
||||
if let syn::Data::Union(_) = s.ast().data {
|
||||
panic!("cannot derive on union")
|
||||
}
|
||||
|
||||
s.add_bounds(synstructure::AddBounds::Generics);
|
||||
s.bind_with(|_| synstructure::BindStyle::Move);
|
||||
let body = s.each_variant(|vi| {
|
||||
let bindings = vi.bindings();
|
||||
vi.construct(|_, index| {
|
||||
let bind = &bindings[index];
|
||||
|
||||
quote! {
|
||||
::ide_db::ra_fixture::UpmapFromRaFixture::upmap_from_ra_fixture(
|
||||
#bind, __analysis, __virtual_file_id, __real_file_id,
|
||||
)?
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
s.bound_impl(
|
||||
quote!(::ide_db::ra_fixture::UpmapFromRaFixture),
|
||||
quote! {
|
||||
fn upmap_from_ra_fixture(
|
||||
self,
|
||||
__analysis: &::ide_db::ra_fixture::RaFixtureAnalysis,
|
||||
__virtual_file_id: ::ide_db::ra_fixture::FileId,
|
||||
__real_file_id: ::ide_db::ra_fixture::FileId,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(match self { #body })
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use ide::{
|
|||
InlayHintsConfig, LineCol, RootDatabase,
|
||||
};
|
||||
use ide_db::{
|
||||
EditionedFileId, LineIndexDatabase, SnippetCap,
|
||||
EditionedFileId, LineIndexDatabase, MiniCore, SnippetCap,
|
||||
base_db::{SourceDatabase, salsa::Database},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
|
|
@ -1194,6 +1194,7 @@ impl flags::AnalysisStats {
|
|||
closing_brace_hints_min_lines: Some(20),
|
||||
fields_to_resolve: InlayFieldsToResolve::empty(),
|
||||
range_exclusive_hints: true,
|
||||
minicore: MiniCore::default(),
|
||||
},
|
||||
analysis.editioned_file_id_to_vfs(file_id),
|
||||
None,
|
||||
|
|
@ -1203,26 +1204,25 @@ impl flags::AnalysisStats {
|
|||
bar.finish_and_clear();
|
||||
|
||||
let mut bar = create_bar();
|
||||
let annotation_config = AnnotationConfig {
|
||||
binary_target: true,
|
||||
annotate_runnables: true,
|
||||
annotate_impls: true,
|
||||
annotate_references: false,
|
||||
annotate_method_references: false,
|
||||
annotate_enum_variant_references: false,
|
||||
location: ide::AnnotationLocation::AboveName,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
for &file_id in file_ids {
|
||||
let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db)));
|
||||
bar.set_message(move || msg.clone());
|
||||
analysis
|
||||
.annotations(
|
||||
&AnnotationConfig {
|
||||
binary_target: true,
|
||||
annotate_runnables: true,
|
||||
annotate_impls: true,
|
||||
annotate_references: false,
|
||||
annotate_method_references: false,
|
||||
annotate_enum_variant_references: false,
|
||||
location: ide::AnnotationLocation::AboveName,
|
||||
},
|
||||
analysis.editioned_file_id_to_vfs(file_id),
|
||||
)
|
||||
.annotations(&annotation_config, analysis.editioned_file_id_to_vfs(file_id))
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.for_each(|annotation| {
|
||||
_ = analysis.resolve_annotation(annotation);
|
||||
_ = analysis.resolve_annotation(&annotation_config, annotation);
|
||||
});
|
||||
bar.inc(1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ use std::{env, fmt, iter, ops::Not, sync::OnceLock};
|
|||
use cfg::{CfgAtom, CfgDiff};
|
||||
use hir::Symbol;
|
||||
use ide::{
|
||||
AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
|
||||
CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, HighlightConfig,
|
||||
HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig,
|
||||
JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
|
||||
SourceRootId,
|
||||
AnnotationConfig, AssistConfig, CallHierarchyConfig, CallableSnippets, CompletionConfig,
|
||||
CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig,
|
||||
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
|
||||
InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
|
||||
Snippet, SnippetScope, SourceRootId,
|
||||
};
|
||||
use ide_db::{
|
||||
SnippetCap,
|
||||
MiniCore, SnippetCap,
|
||||
assists::ExprFillDefaultMode,
|
||||
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
|
||||
};
|
||||
|
|
@ -1454,6 +1454,23 @@ impl LensConfig {
|
|||
pub fn references(&self) -> bool {
|
||||
self.method_refs || self.refs_adt || self.refs_trait || self.enum_variant_refs
|
||||
}
|
||||
|
||||
pub fn into_annotation_config<'a>(
|
||||
self,
|
||||
binary_target: bool,
|
||||
minicore: MiniCore<'a>,
|
||||
) -> AnnotationConfig<'a> {
|
||||
AnnotationConfig {
|
||||
binary_target,
|
||||
annotate_runnables: self.runnable(),
|
||||
annotate_impls: self.implementations,
|
||||
annotate_references: self.refs_adt,
|
||||
annotate_method_references: self.method_refs,
|
||||
annotate_enum_variant_references: self.enum_variant_refs,
|
||||
location: self.location.into(),
|
||||
minicore,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
@ -1688,11 +1705,15 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn call_hierarchy(&self) -> CallHierarchyConfig {
|
||||
CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned() }
|
||||
pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> {
|
||||
CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore }
|
||||
}
|
||||
|
||||
pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig<'_> {
|
||||
pub fn completion<'a>(
|
||||
&'a self,
|
||||
source_root: Option<SourceRootId>,
|
||||
minicore: MiniCore<'a>,
|
||||
) -> CompletionConfig<'a> {
|
||||
let client_capability_fields = self.completion_resolve_support_properties();
|
||||
CompletionConfig {
|
||||
enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
|
||||
|
|
@ -1746,6 +1767,7 @@ impl Config {
|
|||
})
|
||||
.collect(),
|
||||
exclude_traits: self.completion_excludeTraits(source_root),
|
||||
minicore,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1820,7 +1842,7 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn hover(&self) -> HoverConfig {
|
||||
pub fn hover<'a>(&self, minicore: MiniCore<'a>) -> HoverConfig<'a> {
|
||||
let mem_kind = |kind| match kind {
|
||||
MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
|
||||
MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
|
||||
|
|
@ -1853,10 +1875,15 @@ impl Config {
|
|||
None => ide::SubstTyLen::Unlimited,
|
||||
},
|
||||
show_drop_glue: *self.hover_dropGlue_enable(),
|
||||
minicore,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inlay_hints(&self) -> InlayHintsConfig {
|
||||
pub fn goto_definition<'a>(&self, minicore: MiniCore<'a>) -> GotoDefinitionConfig<'a> {
|
||||
GotoDefinitionConfig { minicore }
|
||||
}
|
||||
|
||||
pub fn inlay_hints<'a>(&self, minicore: MiniCore<'a>) -> InlayHintsConfig<'a> {
|
||||
let client_capability_fields = self.inlay_hint_resolve_support_properties();
|
||||
|
||||
InlayHintsConfig {
|
||||
|
|
@ -1938,6 +1965,7 @@ impl Config {
|
|||
),
|
||||
implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(),
|
||||
range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(),
|
||||
minicore,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1975,7 +2003,7 @@ impl Config {
|
|||
self.semanticHighlighting_nonStandardTokens().to_owned()
|
||||
}
|
||||
|
||||
pub fn highlighting_config(&self) -> HighlightConfig {
|
||||
pub fn highlighting_config<'a>(&self, minicore: MiniCore<'a>) -> HighlightConfig<'a> {
|
||||
HighlightConfig {
|
||||
strings: self.semanticHighlighting_strings_enable().to_owned(),
|
||||
comments: self.semanticHighlighting_comments_enable().to_owned(),
|
||||
|
|
@ -1990,6 +2018,7 @@ impl Config {
|
|||
.to_owned(),
|
||||
inject_doc_comment: self.semanticHighlighting_doc_comment_inject_enable().to_owned(),
|
||||
syntactic_name_ref_highlighting: false,
|
||||
minicore,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ use cargo_metadata::PackageId;
|
|||
use crossbeam_channel::{Receiver, Sender, unbounded};
|
||||
use hir::ChangeWithProcMacros;
|
||||
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
|
||||
use ide_db::base_db::{Crate, ProcMacroPaths, SourceDatabase};
|
||||
use ide_db::{
|
||||
MiniCore,
|
||||
base_db::{Crate, ProcMacroPaths, SourceDatabase},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use load_cargo::SourceRootConfig;
|
||||
use lsp_types::{SemanticTokens, Url};
|
||||
|
|
@ -188,6 +191,14 @@ pub(crate) struct GlobalState {
|
|||
/// This is marked true if we failed to load a crate root file at crate graph creation,
|
||||
/// which will usually end up causing a bunch of incorrect diagnostics on startup.
|
||||
pub(crate) incomplete_crate_graph: bool,
|
||||
|
||||
pub(crate) minicore: MiniCoreRustAnalyzerInternalOnly,
|
||||
}
|
||||
|
||||
// FIXME: This should move to the VFS once the rewrite is done.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub(crate) struct MiniCoreRustAnalyzerInternalOnly {
|
||||
pub(crate) minicore_text: Option<String>,
|
||||
}
|
||||
|
||||
/// An immutable snapshot of the world's state at a point in time.
|
||||
|
|
@ -204,6 +215,7 @@ pub(crate) struct GlobalStateSnapshot {
|
|||
// FIXME: Can we derive this from somewhere else?
|
||||
pub(crate) proc_macros_loaded: bool,
|
||||
pub(crate) flycheck: Arc<[FlycheckHandle]>,
|
||||
minicore: MiniCoreRustAnalyzerInternalOnly,
|
||||
}
|
||||
|
||||
impl std::panic::UnwindSafe for GlobalStateSnapshot {}
|
||||
|
|
@ -304,6 +316,8 @@ impl GlobalState {
|
|||
|
||||
deferred_task_queue: task_queue,
|
||||
incomplete_crate_graph: false,
|
||||
|
||||
minicore: MiniCoreRustAnalyzerInternalOnly::default(),
|
||||
};
|
||||
// Apply any required database inputs from the config.
|
||||
this.update_configuration(config);
|
||||
|
|
@ -550,6 +564,7 @@ impl GlobalState {
|
|||
workspaces: Arc::clone(&self.workspaces),
|
||||
analysis: self.analysis_host.analysis(),
|
||||
vfs: Arc::clone(&self.vfs),
|
||||
minicore: self.minicore.clone(),
|
||||
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
|
||||
mem_docs: self.mem_docs.clone(),
|
||||
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
|
||||
|
|
@ -838,6 +853,14 @@ impl GlobalStateSnapshot {
|
|||
pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
|
||||
self.vfs.read().0.exists(file_id)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn minicore(&self) -> MiniCore<'_> {
|
||||
match &self.minicore.minicore_text {
|
||||
Some(minicore) => MiniCore::new(minicore),
|
||||
None => MiniCore::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ use anyhow::Context;
|
|||
|
||||
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||
use ide::{
|
||||
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
|
||||
FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData,
|
||||
AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition,
|
||||
FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData,
|
||||
InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
|
||||
SingleResolve, SourceChange, TextEdit,
|
||||
};
|
||||
|
|
@ -811,7 +811,8 @@ pub(crate) fn handle_goto_definition(
|
|||
let _p = tracing::info_span!("handle_goto_definition").entered();
|
||||
let position =
|
||||
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
|
||||
let nav_info = match snap.analysis.goto_definition(position)? {
|
||||
let config = snap.config.goto_definition(snap.minicore());
|
||||
let nav_info = match snap.analysis.goto_definition(position, &config)? {
|
||||
None => return Ok(None),
|
||||
Some(it) => it,
|
||||
};
|
||||
|
|
@ -829,7 +830,8 @@ pub(crate) fn handle_goto_declaration(
|
|||
&snap,
|
||||
params.text_document_position_params.clone()
|
||||
)?);
|
||||
let nav_info = match snap.analysis.goto_declaration(position)? {
|
||||
let config = snap.config.goto_definition(snap.minicore());
|
||||
let nav_info = match snap.analysis.goto_declaration(position, &config)? {
|
||||
None => return handle_goto_definition(snap, params),
|
||||
Some(it) => it,
|
||||
};
|
||||
|
|
@ -1106,7 +1108,7 @@ pub(crate) fn handle_completion(
|
|||
context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
|
||||
|
||||
let source_root = snap.analysis.source_root_id(position.file_id)?;
|
||||
let completion_config = &snap.config.completion(Some(source_root));
|
||||
let completion_config = &snap.config.completion(Some(source_root), snap.minicore());
|
||||
// FIXME: We should fix up the position when retrying the cancelled request instead
|
||||
position.offset = position.offset.min(line_index.index.len());
|
||||
let items = match snap.analysis.completions(
|
||||
|
|
@ -1160,7 +1162,8 @@ pub(crate) fn handle_completion_resolve(
|
|||
};
|
||||
let source_root = snap.analysis.source_root_id(file_id)?;
|
||||
|
||||
let mut forced_resolve_completions_config = snap.config.completion(Some(source_root));
|
||||
let mut forced_resolve_completions_config =
|
||||
snap.config.completion(Some(source_root), snap.minicore());
|
||||
forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty();
|
||||
|
||||
let position = FilePosition { file_id, offset };
|
||||
|
|
@ -1274,7 +1277,7 @@ pub(crate) fn handle_hover(
|
|||
};
|
||||
let file_range = try_default!(from_proto::file_range(&snap, ¶ms.text_document, range)?);
|
||||
|
||||
let hover = snap.config.hover();
|
||||
let hover = snap.config.hover(snap.minicore());
|
||||
let info = match snap.analysis.hover(&hover, file_range)? {
|
||||
None => return Ok(None),
|
||||
Some(info) => info,
|
||||
|
|
@ -1360,7 +1363,11 @@ pub(crate) fn handle_references(
|
|||
let exclude_imports = snap.config.find_all_refs_exclude_imports();
|
||||
let exclude_tests = snap.config.find_all_refs_exclude_tests();
|
||||
|
||||
let Some(refs) = snap.analysis.find_all_refs(position, None)? else {
|
||||
let Some(refs) = snap.analysis.find_all_refs(
|
||||
position,
|
||||
&FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
|
||||
)?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
|
|
@ -1615,8 +1622,8 @@ pub(crate) fn handle_code_lens(
|
|||
let target_spec = TargetSpec::for_file(&snap, file_id)?;
|
||||
|
||||
let annotations = snap.analysis.annotations(
|
||||
&AnnotationConfig {
|
||||
binary_target: target_spec
|
||||
&lens_config.into_annotation_config(
|
||||
target_spec
|
||||
.map(|spec| {
|
||||
matches!(
|
||||
spec.target_kind(),
|
||||
|
|
@ -1624,13 +1631,8 @@ pub(crate) fn handle_code_lens(
|
|||
)
|
||||
})
|
||||
.unwrap_or(false),
|
||||
annotate_runnables: lens_config.runnable(),
|
||||
annotate_impls: lens_config.implementations,
|
||||
annotate_references: lens_config.refs_adt,
|
||||
annotate_method_references: lens_config.method_refs,
|
||||
annotate_enum_variant_references: lens_config.enum_variant_refs,
|
||||
location: lens_config.location.into(),
|
||||
},
|
||||
snap.minicore(),
|
||||
),
|
||||
file_id,
|
||||
)?;
|
||||
|
||||
|
|
@ -1653,7 +1655,8 @@ pub(crate) fn handle_code_lens_resolve(
|
|||
let Some(annotation) = from_proto::annotation(&snap, code_lens.range, resolve)? else {
|
||||
return Ok(code_lens);
|
||||
};
|
||||
let annotation = snap.analysis.resolve_annotation(annotation)?;
|
||||
let config = snap.config.lens().into_annotation_config(false, snap.minicore());
|
||||
let annotation = snap.analysis.resolve_annotation(&config, annotation)?;
|
||||
|
||||
let mut acc = Vec::new();
|
||||
to_proto::code_lens(&mut acc, &snap, annotation)?;
|
||||
|
|
@ -1736,7 +1739,7 @@ pub(crate) fn handle_inlay_hints(
|
|||
range.end().min(line_index.index.len()),
|
||||
);
|
||||
|
||||
let inlay_hints_config = snap.config.inlay_hints();
|
||||
let inlay_hints_config = snap.config.inlay_hints(snap.minicore());
|
||||
Ok(Some(
|
||||
snap.analysis
|
||||
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
|
||||
|
|
@ -1777,7 +1780,7 @@ pub(crate) fn handle_inlay_hints_resolve(
|
|||
let line_index = snap.file_line_index(file_id)?;
|
||||
let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?;
|
||||
|
||||
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
|
||||
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(snap.minicore());
|
||||
forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
|
||||
let resolve_hints = snap.analysis.inlay_hints_resolve(
|
||||
&forced_resolve_inlay_hints_config,
|
||||
|
|
@ -1816,7 +1819,8 @@ pub(crate) fn handle_call_hierarchy_prepare(
|
|||
let position =
|
||||
try_default!(from_proto::file_position(&snap, params.text_document_position_params)?);
|
||||
|
||||
let nav_info = match snap.analysis.call_hierarchy(position)? {
|
||||
let config = snap.config.call_hierarchy(snap.minicore());
|
||||
let nav_info = match snap.analysis.call_hierarchy(position, &config)? {
|
||||
None => return Ok(None),
|
||||
Some(it) => it,
|
||||
};
|
||||
|
|
@ -1842,8 +1846,8 @@ pub(crate) fn handle_call_hierarchy_incoming(
|
|||
let frange = try_default!(from_proto::file_range(&snap, &doc, item.selection_range)?);
|
||||
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
||||
|
||||
let config = snap.config.call_hierarchy();
|
||||
let call_items = match snap.analysis.incoming_calls(config, fpos)? {
|
||||
let config = snap.config.call_hierarchy(snap.minicore());
|
||||
let call_items = match snap.analysis.incoming_calls(&config, fpos)? {
|
||||
None => return Ok(None),
|
||||
Some(it) => it,
|
||||
};
|
||||
|
|
@ -1881,8 +1885,8 @@ pub(crate) fn handle_call_hierarchy_outgoing(
|
|||
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
|
||||
let line_index = snap.file_line_index(fpos.file_id)?;
|
||||
|
||||
let config = snap.config.call_hierarchy();
|
||||
let call_items = match snap.analysis.outgoing_calls(config, fpos)? {
|
||||
let config = snap.config.call_hierarchy(snap.minicore());
|
||||
let call_items = match snap.analysis.outgoing_calls(&config, fpos)? {
|
||||
None => return Ok(None),
|
||||
Some(it) => it,
|
||||
};
|
||||
|
|
@ -1916,7 +1920,7 @@ pub(crate) fn handle_semantic_tokens_full(
|
|||
let text = snap.analysis.file_text(file_id)?;
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
|
||||
let mut highlight_config = snap.config.highlighting_config();
|
||||
let mut highlight_config = snap.config.highlighting_config(snap.minicore());
|
||||
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
|
||||
highlight_config.syntactic_name_ref_highlighting =
|
||||
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
|
||||
|
|
@ -1946,7 +1950,7 @@ pub(crate) fn handle_semantic_tokens_full_delta(
|
|||
let text = snap.analysis.file_text(file_id)?;
|
||||
let line_index = snap.file_line_index(file_id)?;
|
||||
|
||||
let mut highlight_config = snap.config.highlighting_config();
|
||||
let mut highlight_config = snap.config.highlighting_config(snap.minicore());
|
||||
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
|
||||
highlight_config.syntactic_name_ref_highlighting =
|
||||
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
|
||||
|
|
@ -1988,7 +1992,7 @@ pub(crate) fn handle_semantic_tokens_range(
|
|||
let text = snap.analysis.file_text(frange.file_id)?;
|
||||
let line_index = snap.file_line_index(frange.file_id)?;
|
||||
|
||||
let mut highlight_config = snap.config.highlighting_config();
|
||||
let mut highlight_config = snap.config.highlighting_config(snap.minicore());
|
||||
// Avoid flashing a bunch of unresolved references when the proc-macro servers haven't been spawned yet.
|
||||
highlight_config.syntactic_name_ref_highlighting =
|
||||
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
|
||||
|
|
@ -2156,7 +2160,13 @@ fn show_ref_command_link(
|
|||
) -> Option<lsp_ext::CommandLinkGroup> {
|
||||
if snap.config.hover_actions().references
|
||||
&& snap.config.client_commands().show_reference
|
||||
&& let Some(ref_search_res) = snap.analysis.find_all_refs(*position, None).unwrap_or(None)
|
||||
&& let Some(ref_search_res) = snap
|
||||
.analysis
|
||||
.find_all_refs(
|
||||
*position,
|
||||
&FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
|
||||
)
|
||||
.unwrap_or(None)
|
||||
{
|
||||
let uri = to_proto::url(snap, position.file_id);
|
||||
let line_index = snap.file_line_index(position.file_id).ok()?;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use ide::{
|
|||
FilePosition, TextSize,
|
||||
};
|
||||
use ide_db::{
|
||||
SnippetCap,
|
||||
MiniCore, SnippetCap,
|
||||
imports::insert_use::{ImportGranularity, InsertUseConfig},
|
||||
};
|
||||
use project_model::CargoConfig;
|
||||
|
|
@ -186,6 +186,7 @@ fn integrated_completion_benchmark() {
|
|||
exclude_traits: &[],
|
||||
enable_auto_await: true,
|
||||
enable_auto_iter: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
let position =
|
||||
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
|
||||
|
|
@ -240,6 +241,7 @@ fn integrated_completion_benchmark() {
|
|||
exclude_traits: &[],
|
||||
enable_auto_await: true,
|
||||
enable_auto_iter: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
let position =
|
||||
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
|
||||
|
|
@ -292,6 +294,7 @@ fn integrated_completion_benchmark() {
|
|||
exclude_traits: &[],
|
||||
enable_auto_await: true,
|
||||
enable_auto_iter: true,
|
||||
minicore: MiniCore::default(),
|
||||
};
|
||||
let position =
|
||||
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ use ide::{
|
|||
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
|
||||
UpdateTest,
|
||||
};
|
||||
use ide_db::{FxHasher, assists, rust_doc::format_docs, source_change::ChangeAnnotationId};
|
||||
use ide_db::{
|
||||
FxHasher, MiniCore, assists, rust_doc::format_docs, source_change::ChangeAnnotationId,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use paths::{Utf8Component, Utf8Prefix};
|
||||
use semver::VersionReq;
|
||||
|
|
@ -270,7 +272,7 @@ pub(crate) fn completion_items(
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(limit) = config.completion(None).limit {
|
||||
if let Some(limit) = config.completion(None, MiniCore::default()).limit {
|
||||
res.sort_by(|item1, item2| item1.sort_text.cmp(&item2.sort_text));
|
||||
res.truncate(limit);
|
||||
}
|
||||
|
|
@ -400,16 +402,17 @@ fn completion_item(
|
|||
|
||||
set_score(&mut lsp_item, max_relevance, item.relevance);
|
||||
|
||||
let imports =
|
||||
if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
|
||||
item.import_to_add
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let imports = if config.completion(None, MiniCore::default()).enable_imports_on_the_fly
|
||||
&& !item.import_to_add.is_empty()
|
||||
{
|
||||
item.import_to_add
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let (ref_resolve_data, resolve_data) = if something_to_resolve || !imports.is_empty() {
|
||||
let ref_resolve_data = if ref_match.is_some() {
|
||||
let ref_resolve_data = lsp_ext::CompletionResolveData {
|
||||
|
|
|
|||
|
|
@ -847,6 +847,13 @@ impl GlobalState {
|
|||
self.debounce_workspace_fetch();
|
||||
let vfs = &mut self.vfs.write().0;
|
||||
for (path, contents) in files {
|
||||
if matches!(path.name_and_extension(), Some(("minicore", Some("rs")))) {
|
||||
// Not a lot of bad can happen from mistakenly identifying `minicore`, so proceed with that.
|
||||
self.minicore.minicore_text = contents
|
||||
.as_ref()
|
||||
.and_then(|contents| String::from_utf8(contents.clone()).ok());
|
||||
}
|
||||
|
||||
let path = VfsPath::from(path);
|
||||
// if the file is in mem docs, it's managed by the client via notifications
|
||||
// so only set it if its not in there
|
||||
|
|
|
|||
|
|
@ -201,6 +201,10 @@ pub trait IsString: AstToken {
|
|||
None
|
||||
}
|
||||
}
|
||||
fn map_offset_down(&self, offset: TextSize) -> Option<TextSize> {
|
||||
let contents_range = self.text_range_between_quotes()?;
|
||||
offset.checked_sub(contents_range.start())
|
||||
}
|
||||
}
|
||||
|
||||
impl IsString for ast::String {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use paths::AbsPathBuf;
|
|||
use span::{Edition, FileId, Span};
|
||||
use stdx::itertools::Itertools;
|
||||
use test_utils::{
|
||||
CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, RangeOrOffset,
|
||||
CURSOR_MARKER, ESCAPED_CURSOR_MARKER, Fixture, FixtureWithProjectMeta, MiniCore, RangeOrOffset,
|
||||
extract_range_or_offset,
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
|
@ -69,7 +69,12 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
|
|||
proc_macros: Vec<(String, ProcMacro)>,
|
||||
) -> Self {
|
||||
let mut db = Self::default();
|
||||
let fixture = ChangeFixture::parse_with_proc_macros(&db, ra_fixture, proc_macros);
|
||||
let fixture = ChangeFixture::parse_with_proc_macros(
|
||||
&db,
|
||||
ra_fixture,
|
||||
MiniCore::RAW_SOURCE,
|
||||
proc_macros,
|
||||
);
|
||||
fixture.change.apply(&mut db);
|
||||
assert!(fixture.file_position.is_none());
|
||||
db
|
||||
|
|
@ -112,8 +117,10 @@ impl<DB: ExpandDatabase + SourceDatabase + Default + 'static> WithFixture for DB
|
|||
|
||||
pub struct ChangeFixture {
|
||||
pub file_position: Option<(EditionedFileId, RangeOrOffset)>,
|
||||
pub file_lines: Vec<usize>,
|
||||
pub files: Vec<EditionedFileId>,
|
||||
pub change: ChangeWithProcMacros,
|
||||
pub sysroot_files: Vec<FileId>,
|
||||
}
|
||||
|
||||
const SOURCE_ROOT_PREFIX: &str = "/";
|
||||
|
|
@ -123,12 +130,13 @@ impl ChangeFixture {
|
|||
db: &dyn salsa::Database,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
) -> ChangeFixture {
|
||||
Self::parse_with_proc_macros(db, ra_fixture, Vec::new())
|
||||
Self::parse_with_proc_macros(db, ra_fixture, MiniCore::RAW_SOURCE, Vec::new())
|
||||
}
|
||||
|
||||
pub fn parse_with_proc_macros(
|
||||
db: &dyn salsa::Database,
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
minicore_raw: &str,
|
||||
mut proc_macro_defs: Vec<(String, ProcMacro)>,
|
||||
) -> ChangeFixture {
|
||||
let FixtureWithProjectMeta {
|
||||
|
|
@ -149,6 +157,8 @@ impl ChangeFixture {
|
|||
let mut source_change = FileChange::default();
|
||||
|
||||
let mut files = Vec::new();
|
||||
let mut sysroot_files = Vec::new();
|
||||
let mut file_lines = Vec::new();
|
||||
let mut crate_graph = CrateGraphBuilder::default();
|
||||
let mut crates = FxIndexMap::default();
|
||||
let mut crate_deps = Vec::new();
|
||||
|
|
@ -173,6 +183,8 @@ impl ChangeFixture {
|
|||
let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()));
|
||||
|
||||
for entry in fixture {
|
||||
file_lines.push(entry.line);
|
||||
|
||||
let mut range_or_offset = None;
|
||||
let text = if entry.text.contains(CURSOR_MARKER) {
|
||||
if entry.text.contains(ESCAPED_CURSOR_MARKER) {
|
||||
|
|
@ -259,7 +271,9 @@ impl ChangeFixture {
|
|||
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_owned()));
|
||||
roots.push(SourceRoot::new_library(fs));
|
||||
|
||||
source_change.change_file(core_file, Some(mini_core.source_code()));
|
||||
sysroot_files.push(core_file);
|
||||
|
||||
source_change.change_file(core_file, Some(mini_core.source_code(minicore_raw)));
|
||||
|
||||
let core_crate = crate_graph.add_crate_root(
|
||||
core_file,
|
||||
|
|
@ -348,6 +362,8 @@ impl ChangeFixture {
|
|||
);
|
||||
roots.push(SourceRoot::new_library(fs));
|
||||
|
||||
sysroot_files.push(proc_lib_file);
|
||||
|
||||
source_change.change_file(proc_lib_file, Some(source));
|
||||
|
||||
let all_crates = crate_graph.iter().collect::<Vec<_>>();
|
||||
|
|
@ -396,7 +412,7 @@ impl ChangeFixture {
|
|||
change.source_change.set_roots(roots);
|
||||
change.source_change.set_crate_graph(crate_graph);
|
||||
|
||||
ChangeFixture { file_position, files, change }
|
||||
ChangeFixture { file_position, file_lines, files, change, sysroot_files }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -132,13 +132,17 @@ pub struct Fixture {
|
|||
pub library: bool,
|
||||
/// Actual file contents. All meta comments are stripped.
|
||||
pub text: String,
|
||||
/// The line number in the original fixture of the beginning of this fixture.
|
||||
pub line: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MiniCore {
|
||||
activated_flags: Vec<String>,
|
||||
valid_flags: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FixtureWithProjectMeta {
|
||||
pub fixture: Vec<Fixture>,
|
||||
pub mini_core: Option<MiniCore>,
|
||||
|
|
@ -184,40 +188,49 @@ impl FixtureWithProjectMeta {
|
|||
let mut mini_core = None;
|
||||
let mut res: Vec<Fixture> = Vec::new();
|
||||
let mut proc_macro_names = vec![];
|
||||
let mut first_row = 0;
|
||||
|
||||
if let Some(meta) = fixture.strip_prefix("//- toolchain:") {
|
||||
first_row += 1;
|
||||
let (meta, remain) = meta.split_once('\n').unwrap();
|
||||
toolchain = Some(meta.trim().to_owned());
|
||||
fixture = remain;
|
||||
}
|
||||
|
||||
if let Some(meta) = fixture.strip_prefix("//- target_data_layout:") {
|
||||
first_row += 1;
|
||||
let (meta, remain) = meta.split_once('\n').unwrap();
|
||||
meta.trim().clone_into(&mut target_data_layout);
|
||||
fixture = remain;
|
||||
}
|
||||
|
||||
if let Some(meta) = fixture.strip_prefix("//- target_arch:") {
|
||||
first_row += 1;
|
||||
let (meta, remain) = meta.split_once('\n').unwrap();
|
||||
meta.trim().clone_into(&mut target_arch);
|
||||
fixture = remain;
|
||||
}
|
||||
|
||||
if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
|
||||
first_row += 1;
|
||||
let (meta, remain) = meta.split_once('\n').unwrap();
|
||||
proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect();
|
||||
fixture = remain;
|
||||
}
|
||||
|
||||
if let Some(meta) = fixture.strip_prefix("//- minicore:") {
|
||||
first_row += 1;
|
||||
let (meta, remain) = meta.split_once('\n').unwrap();
|
||||
mini_core = Some(MiniCore::parse(meta));
|
||||
fixture = remain;
|
||||
}
|
||||
|
||||
let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
|
||||
let default =
|
||||
if fixture.contains("//- /") { None } else { Some((first_row - 1, "//- /main.rs")) };
|
||||
|
||||
for (ix, line) in default.into_iter().chain(fixture.split_inclusive('\n')).enumerate() {
|
||||
for (ix, line) in
|
||||
default.into_iter().chain((first_row..).zip(fixture.split_inclusive('\n')))
|
||||
{
|
||||
if line.contains("//-") {
|
||||
assert!(
|
||||
line.starts_with("//-"),
|
||||
|
|
@ -228,7 +241,7 @@ impl FixtureWithProjectMeta {
|
|||
}
|
||||
|
||||
if let Some(line) = line.strip_prefix("//-") {
|
||||
let meta = Self::parse_meta_line(line);
|
||||
let meta = Self::parse_meta_line(line, (ix + 1).try_into().unwrap());
|
||||
res.push(meta);
|
||||
} else {
|
||||
if matches!(line.strip_prefix("// "), Some(l) if l.trim().starts_with('/')) {
|
||||
|
|
@ -252,7 +265,7 @@ impl FixtureWithProjectMeta {
|
|||
}
|
||||
|
||||
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
|
||||
fn parse_meta_line(meta: &str) -> Fixture {
|
||||
fn parse_meta_line(meta: &str, line: usize) -> Fixture {
|
||||
let meta = meta.trim();
|
||||
let mut components = meta.split_ascii_whitespace();
|
||||
|
||||
|
|
@ -317,6 +330,7 @@ impl FixtureWithProjectMeta {
|
|||
Fixture {
|
||||
path,
|
||||
text: String::new(),
|
||||
line,
|
||||
krate,
|
||||
deps,
|
||||
extern_prelude,
|
||||
|
|
@ -330,7 +344,7 @@ impl FixtureWithProjectMeta {
|
|||
}
|
||||
|
||||
impl MiniCore {
|
||||
const RAW_SOURCE: &'static str = include_str!("./minicore.rs");
|
||||
pub const RAW_SOURCE: &'static str = include_str!("./minicore.rs");
|
||||
|
||||
fn has_flag(&self, flag: &str) -> bool {
|
||||
self.activated_flags.iter().any(|it| it == flag)
|
||||
|
|
@ -363,8 +377,8 @@ impl MiniCore {
|
|||
res
|
||||
}
|
||||
|
||||
pub fn available_flags() -> impl Iterator<Item = &'static str> {
|
||||
let lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
|
||||
pub fn available_flags(raw_source: &str) -> impl Iterator<Item = &str> {
|
||||
let lines = raw_source.split_inclusive('\n');
|
||||
lines
|
||||
.map_while(|x| x.strip_prefix("//!"))
|
||||
.skip_while(|line| !line.contains("Available flags:"))
|
||||
|
|
@ -375,9 +389,9 @@ impl MiniCore {
|
|||
/// Strips parts of minicore.rs which are flagged by inactive flags.
|
||||
///
|
||||
/// This is probably over-engineered to support flags dependencies.
|
||||
pub fn source_code(mut self) -> String {
|
||||
pub fn source_code(mut self, raw_source: &str) -> String {
|
||||
let mut buf = String::new();
|
||||
let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
|
||||
let mut lines = raw_source.split_inclusive('\n');
|
||||
|
||||
let mut implications = Vec::new();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue