⬆️ rust-analyzer

This commit is contained in:
Laurențiu Nicola 2022-08-30 14:51:24 +03:00
parent 31519bb394
commit 3e358a6827
74 changed files with 2091 additions and 951 deletions

View file

@ -184,10 +184,10 @@ pub(crate) fn resolve_doc_path_for_def(
Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
Definition::SelfType(it) => it.resolve_doc_path(db, link, ns),
Definition::BuiltinAttr(_)
| Definition::ToolModule(_)
| Definition::BuiltinType(_)
| Definition::SelfType(_)
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)

View file

@ -87,7 +87,7 @@ pub use crate::{
},
join_lines::JoinLinesConfig,
markup::Markup,
moniker::{MonikerKind, MonikerResult, PackageInformation},
moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation},
move_item::Direction,
navigation_target::NavigationTarget,
prime_caches::ParallelPrimeCachesProgress,
@ -98,7 +98,7 @@ pub use crate::{
static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
syntax_highlighting::{
tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
HlRange,
HighlightConfig, HlRange,
},
};
pub use hir::{Documentation, Semantics};
@ -517,8 +517,12 @@ impl Analysis {
}
/// Computes syntax highlighting for the given file
pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
pub fn highlight(
&self,
highlight_config: HighlightConfig,
file_id: FileId,
) -> Cancellable<Vec<HlRange>> {
self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
}
/// Computes all ranges to highlight for a given item in a file.
@ -533,9 +537,13 @@ impl Analysis {
}
/// Computes syntax highlighting for the given file range.
pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
pub fn highlight_range(
&self,
highlight_config: HighlightConfig,
frange: FileRange,
) -> Cancellable<Vec<HlRange>> {
self.with_db(|db| {
syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
})
}

View file

@ -13,17 +13,39 @@ use syntax::{AstNode, SyntaxKind::*, T};
use crate::{doc_links::token_as_doc_comment, RangeInfo};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MonikerDescriptorKind {
Namespace,
Type,
Term,
Method,
TypeParameter,
Parameter,
Macro,
Meta,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerDescriptor {
pub name: Name,
pub desc: MonikerDescriptorKind,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerIdentifier {
crate_name: String,
path: Vec<Name>,
pub crate_name: String,
pub description: Vec<MonikerDescriptor>,
}
impl ToString for MonikerIdentifier {
fn to_string(&self) -> String {
match self {
MonikerIdentifier { path, crate_name } => {
format!("{}::{}", crate_name, path.iter().map(|x| x.to_string()).join("::"))
MonikerIdentifier { description, crate_name } => {
format!(
"{}::{}",
crate_name,
description.iter().map(|x| x.name.to_string()).join("::")
)
}
}
}
@ -42,6 +64,12 @@ pub struct MonikerResult {
pub package_information: PackageInformation,
}
impl MonikerResult {
pub fn from_def(db: &RootDatabase, def: Definition, from_crate: Crate) -> Option<Self> {
def_to_moniker(db, def, from_crate)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PackageInformation {
pub name: String,
@ -105,13 +133,23 @@ pub(crate) fn def_to_moniker(
def: Definition,
from_crate: Crate,
) -> Option<MonikerResult> {
if matches!(def, Definition::GenericParam(_) | Definition::SelfType(_) | Definition::Local(_)) {
if matches!(
def,
Definition::GenericParam(_)
| Definition::Label(_)
| Definition::DeriveHelper(_)
| Definition::BuiltinAttr(_)
| Definition::ToolModule(_)
) {
return None;
}
let module = def.module(db)?;
let krate = module.krate();
let mut path = vec![];
path.extend(module.path_to_root(db).into_iter().filter_map(|x| x.name(db)));
let mut description = vec![];
description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
}));
// Handle associated items within a trait
if let Some(assoc) = def.as_assoc_item(db) {
@ -120,31 +158,98 @@ pub(crate) fn def_to_moniker(
AssocItemContainer::Trait(trait_) => {
// Because different traits can have functions with the same name,
// we have to include the trait name as part of the moniker for uniqueness.
path.push(trait_.name(db));
description.push(MonikerDescriptor {
name: trait_.name(db),
desc: MonikerDescriptorKind::Type,
});
}
AssocItemContainer::Impl(impl_) => {
// Because a struct can implement multiple traits, for implementations
// we add both the struct name and the trait name to the path
if let Some(adt) = impl_.self_ty(db).as_adt() {
path.push(adt.name(db));
description.push(MonikerDescriptor {
name: adt.name(db),
desc: MonikerDescriptorKind::Type,
});
}
if let Some(trait_) = impl_.trait_(db) {
path.push(trait_.name(db));
description.push(MonikerDescriptor {
name: trait_.name(db),
desc: MonikerDescriptorKind::Type,
});
}
}
}
}
if let Definition::Field(it) = def {
path.push(it.parent_def(db).name(db));
description.push(MonikerDescriptor {
name: it.parent_def(db).name(db),
desc: MonikerDescriptorKind::Type,
});
}
path.push(def.name(db)?);
let name_desc = match def {
// These are handled by top-level guard (for performance).
Definition::GenericParam(_)
| Definition::Label(_)
| Definition::DeriveHelper(_)
| Definition::BuiltinAttr(_)
| Definition::ToolModule(_) => return None,
Definition::Local(local) => {
if !local.is_param(db) {
return None;
}
MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
}
Definition::Macro(m) => {
MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
}
Definition::Function(f) => {
MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
}
Definition::Variant(v) => {
MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
}
Definition::Const(c) => {
MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
}
Definition::Trait(trait_) => {
MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
}
Definition::TypeAlias(ta) => {
MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
}
Definition::Module(m) => {
MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
}
Definition::BuiltinType(b) => {
MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
}
Definition::SelfType(imp) => MonikerDescriptor {
name: imp.self_ty(db).as_adt()?.name(db),
desc: MonikerDescriptorKind::Type,
},
Definition::Field(it) => {
MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
}
Definition::Adt(adt) => {
MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
}
Definition::Static(s) => {
MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
}
};
description.push(name_desc);
Some(MonikerResult {
identifier: MonikerIdentifier {
crate_name: krate.display_name(db)?.crate_name().to_string(),
path,
description,
},
kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
package_information: {

View file

@ -12,8 +12,9 @@ use ide_db::{
salsa::{Database, ParallelDatabase, Snapshot},
Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
},
FxHashSet, FxIndexMap,
FxIndexMap,
};
use stdx::hash::NoHashHashSet;
use crate::RootDatabase;
@ -141,7 +142,7 @@ pub(crate) fn parallel_prime_caches(
}
}
fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
// We're only interested in the workspace crates and the `ImportMap`s of their direct
// dependencies, though in practice the latter also compute the `DefMap`s.
// We don't prime transitive dependencies because they're generally not visible in

View file

@ -14,8 +14,9 @@ use ide_db::{
base_db::FileId,
defs::{Definition, NameClass, NameRefClass},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
FxHashMap, RootDatabase,
RootDatabase,
};
use stdx::hash::NoHashHashMap;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName},
@ -29,7 +30,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
}
#[derive(Debug, Clone)]

View file

@ -14,7 +14,7 @@ mod html;
mod tests;
use hir::{Name, Semantics};
use ide_db::{FxHashMap, RootDatabase};
use ide_db::{FxHashMap, RootDatabase, SymbolKind};
use syntax::{
ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
};
@ -24,7 +24,7 @@ use crate::{
escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
macro_::MacroHighlighter, tags::Highlight,
},
FileId, HlMod, HlTag,
FileId, HlMod, HlOperator, HlPunct, HlTag,
};
pub(crate) use html::highlight_as_html;
@ -36,6 +36,26 @@ pub struct HlRange {
pub binding_hash: Option<u64>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct HighlightConfig {
/// Whether to highlight strings
pub strings: bool,
/// Whether to highlight punctuation
pub punctuation: bool,
/// Whether to specialize punctuation highlights
pub specialize_punctuation: bool,
/// Whether to highlight operator
pub operator: bool,
/// Whether to specialize operator highlights
pub specialize_operator: bool,
/// Whether to inject highlights into doc comments
pub inject_doc_comment: bool,
/// Whether to highlight the macro call bang
pub macro_bang: bool,
/// Whether to highlight unresolved things be their syntax
pub syntactic_name_ref_highlighting: bool,
}
// Feature: Semantic Syntax Highlighting
//
// rust-analyzer highlights the code semantically.
@ -155,9 +175,9 @@ pub struct HlRange {
// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
pub(crate) fn highlight(
db: &RootDatabase,
config: HighlightConfig,
file_id: FileId,
range_to_highlight: Option<TextRange>,
syntactic_name_ref_highlighting: bool,
) -> Vec<HlRange> {
let _p = profile::span("highlight");
let sema = Semantics::new(db);
@ -183,26 +203,18 @@ pub(crate) fn highlight(
Some(it) => it.krate(),
None => return hl.to_vec(),
};
traverse(
&mut hl,
&sema,
file_id,
&root,
krate,
range_to_highlight,
syntactic_name_ref_highlighting,
);
traverse(&mut hl, &sema, config, file_id, &root, krate, range_to_highlight);
hl.to_vec()
}
fn traverse(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
config: HighlightConfig,
file_id: FileId,
root: &SyntaxNode,
krate: hir::Crate,
range_to_highlight: TextRange,
syntactic_name_ref_highlighting: bool,
) {
let is_unlinked = sema.to_module_def(file_id).is_none();
let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
@ -323,9 +335,11 @@ fn traverse(
Enter(it) => it,
Leave(NodeOrToken::Token(_)) => continue,
Leave(NodeOrToken::Node(node)) => {
// Doc comment highlighting injection, we do this when leaving the node
// so that we overwrite the highlighting of the doc comment itself.
inject::doc_comment(hl, sema, file_id, &node);
if config.inject_doc_comment {
// Doc comment highlighting injection, we do this when leaving the node
// so that we overwrite the highlighting of the doc comment itself.
inject::doc_comment(hl, sema, config, file_id, &node);
}
continue;
}
};
@ -400,7 +414,8 @@ fn traverse(
let string_to_highlight = ast::String::cast(descended_token.clone());
if let Some((string, expanded_string)) = string.zip(string_to_highlight) {
if string.is_raw() {
if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() {
if inject::ra_fixture(hl, sema, config, &string, &expanded_string).is_some()
{
continue;
}
}
@ -421,7 +436,7 @@ fn traverse(
sema,
krate,
&mut bindings_shadow_count,
syntactic_name_ref_highlighting,
config.syntactic_name_ref_highlighting,
name_like,
),
NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
@ -439,6 +454,29 @@ fn traverse(
// something unresolvable. FIXME: There should be a way to prevent that
continue;
}
// apply config filtering
match &mut highlight.tag {
HlTag::StringLiteral if !config.strings => continue,
// If punctuation is disabled, make the macro bang part of the macro call again.
tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
if !config.macro_bang {
*tag = HlTag::Symbol(SymbolKind::Macro);
} else if !config.specialize_punctuation {
*tag = HlTag::Punctuation(HlPunct::Other);
}
}
HlTag::Punctuation(_) if !config.punctuation => continue,
tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
*tag = HlTag::Punctuation(HlPunct::Other);
}
HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
tag @ HlTag::Operator(_) if !config.specialize_operator => {
*tag = HlTag::Operator(HlOperator::Other);
}
_ => (),
}
if inside_attribute {
highlight |= HlMod::Attribute
}

View file

@ -5,7 +5,10 @@ use oorandom::Rand32;
use stdx::format_to;
use syntax::AstNode;
use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
use crate::{
syntax_highlighting::{highlight, HighlightConfig},
FileId, RootDatabase,
};
pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
let parse = db.parse(file_id);
@ -20,7 +23,21 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
)
}
let hl_ranges = highlight(db, file_id, None, false);
let hl_ranges = highlight(
db,
HighlightConfig {
strings: true,
punctuation: true,
specialize_punctuation: true,
specialize_operator: true,
operator: true,
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
},
file_id,
None,
);
let text = parse.tree().syntax().to_string();
let mut buf = String::new();
buf.push_str(STYLE);

View file

@ -15,13 +15,14 @@ use syntax::{
use crate::{
doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
syntax_highlighting::{highlights::Highlights, injector::Injector},
syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig},
Analysis, HlMod, HlRange, HlTag, RootDatabase,
};
pub(super) fn ra_fixture(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
config: HighlightConfig,
literal: &ast::String,
expanded: &ast::String,
) -> Option<()> {
@ -63,7 +64,13 @@ pub(super) fn ra_fixture(
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
for mut hl_range in analysis
.highlight(
HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
tmp_file_id,
)
.unwrap()
{
for range in inj.map_range_up(hl_range.range) {
if let Some(range) = literal.map_range_up(range) {
hl_range.range = range;
@ -86,6 +93,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
pub(super) fn doc_comment(
hl: &mut Highlights,
sema: &Semantics<'_, RootDatabase>,
config: HighlightConfig,
src_file_id: FileId,
node: &SyntaxNode,
) {
@ -206,7 +214,14 @@ pub(super) fn doc_comment(
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
if let Ok(ranges) = analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)) {
if let Ok(ranges) = analysis.with_db(|db| {
super::highlight(
db,
HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
tmp_file_id,
None,
)
}) {
for HlRange { range, highlight, binding_hash } in ranges {
for range in inj.map_range_up(range) {
hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash });

View file

@ -199,7 +199,7 @@ impl fmt::Display for HlTag {
}
impl HlMod {
const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
const ALL: &'static [HlMod; 19] = &[
HlMod::Associated,
HlMod::Async,
HlMod::Attribute,
@ -296,7 +296,7 @@ impl Highlight {
Highlight { tag, mods: HlMods::default() }
}
pub fn is_empty(&self) -> bool {
self.tag == HlTag::None && self.mods == HlMods::default()
self.tag == HlTag::None && self.mods.is_empty()
}
}
@ -330,6 +330,10 @@ impl ops::BitOr<HlMod> for Highlight {
}
impl HlMods {
pub fn is_empty(&self) -> bool {
self.0 == 0
}
pub fn contains(self, m: HlMod) -> bool {
self.0 & m.mask() == m.mask()
}

View file

@ -56,7 +56,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="field declaration">bar</span><span class="colon">:</span> <span class="builtin_type">bool</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="comment documentation">/// This is an impl with a code block.</span>
<span class="comment documentation">/// This is an impl of </span><span class="struct documentation injected intra_doc_link">[`Foo`]</span><span class="comment documentation"> with a code block.</span>
<span class="comment documentation">///</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="comment documentation"> </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>

View file

@ -4,7 +4,18 @@ use expect_test::{expect_file, ExpectFile};
use ide_db::SymbolKind;
use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
use crate::{fixture, FileRange, HlTag, TextRange};
use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange};
const HL_CONFIG: HighlightConfig = HighlightConfig {
strings: true,
punctuation: true,
specialize_punctuation: true,
specialize_operator: true,
operator: true,
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
};
#[test]
fn attributes() {
@ -613,7 +624,7 @@ struct Foo {
bar: bool,
}
/// This is an impl with a code block.
/// This is an impl of [`Foo`] with a code block.
///
/// ```
/// fn foo() {
@ -996,7 +1007,10 @@ struct Foo {
// The "x"
let highlights = &analysis
.highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
.highlight_range(
HL_CONFIG,
FileRange { file_id, range: TextRange::at(45.into(), 1.into()) },
)
.unwrap();
assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
@ -1011,7 +1025,7 @@ macro_rules! test {}
}"#
.trim(),
);
let _ = analysis.highlight(file_id).unwrap();
let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
}
/// Highlights the code given by the `ra_fixture` argument, renders the
@ -1035,7 +1049,7 @@ fn benchmark_syntax_highlighting_long_struct() {
let hash = {
let _pt = bench("syntax highlighting long struct");
analysis
.highlight(file_id)
.highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@ -1061,7 +1075,7 @@ fn syntax_highlighting_not_quadratic() {
let time = Instant::now();
let hash = analysis
.highlight(file_id)
.highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@ -1086,7 +1100,7 @@ fn benchmark_syntax_highlighting_parser() {
let hash = {
let _pt = bench("syntax highlighting parser");
analysis
.highlight(file_id)
.highlight(HL_CONFIG, file_id)
.unwrap()
.iter()
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))

View file

@ -3,8 +3,9 @@ use std::sync::Arc;
use dot::{Id, LabelText};
use ide_db::{
base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
FxHashSet, RootDatabase,
RootDatabase,
};
use stdx::hash::NoHashHashSet;
// Feature: View Crate Graph
//
@ -41,7 +42,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
struct DotCrateGraph {
graph: Arc<CrateGraph>,
crates_to_render: FxHashSet<CrateId>,
crates_to_render: NoHashHashSet<CrateId>,
}
type Edge<'a> = (CrateId, &'a Dependency);