Skip to content

Commit f045f14

Browse files
committed
Auto merge of #13084 - Veykril:highlight-config, r=Veykril
Add some more highlighting configurations The following can be enabled/disabled now in terms of highlighting: - doc comment injection (enabled by default) - punctuation highlighting (disabled by default) - operator highlighting (enabled by default) - punctuation specialized highlighting (disabled by default) - operator specialized highlighting (disabled by default) - macro call bang highlighting (disabled by default) This PR also changes our `attribute` semantic token type to the `decorator` type which landed upstream (but not yet in lsp-types). Specialized highlighting is disabled by default, as all clients will have to ship something to map these back to the standard punctuation/operator token (we do this in VSCode via the inheritance mapping for example). This is a lot of maintenance work, and not something every client wants to do, pushing that need to use the user. As this is a rather niche use in the first place this will just be disabled by default. Punctuation highlighting is disabled by default, punctuation is usually something that can be done by the native syntactic highlighting of the client, so there is no loss in quality. The main reason for this though is that punctuation adds a lot of extra token data that we sent over, a lot of clients struggle with applying this, so disabling this improves the UX for a lot of people. Note that we still highlight punctuations with special meaning as that special entity, (the never type `!` will still be tagged as a builtin type if it occurs as such) Separate highlighting of the macro call bang `!` is disabled by default, as I think people actually didn't like that change that much, though at the same time I feel like not many people even noticed that change (I prefer it be separate, but that's not enough reason for it to be enabled by default I believe :^) ) cc #12783 #13066
2 parents 631ed2a + 2a26b05 commit f045f14

File tree

14 files changed

+277
-81
lines changed

14 files changed

+277
-81
lines changed

Cargo.lock

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ide/src/lib.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub use crate::{
9898
static_index::{StaticIndex, StaticIndexedFile, TokenId, TokenStaticData},
9999
syntax_highlighting::{
100100
tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
101-
HlRange,
101+
HighlightConfig, HlRange,
102102
},
103103
};
104104
pub use hir::{Documentation, Semantics};
@@ -517,8 +517,12 @@ impl Analysis {
517517
}
518518

519519
/// Computes syntax highlighting for the given file
520-
pub fn highlight(&self, file_id: FileId) -> Cancellable<Vec<HlRange>> {
521-
self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
520+
pub fn highlight(
521+
&self,
522+
highlight_config: HighlightConfig,
523+
file_id: FileId,
524+
) -> Cancellable<Vec<HlRange>> {
525+
self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None))
522526
}
523527

524528
/// Computes all ranges to highlight for a given item in a file.
@@ -533,9 +537,13 @@ impl Analysis {
533537
}
534538

535539
/// Computes syntax highlighting for the given file range.
536-
pub fn highlight_range(&self, frange: FileRange) -> Cancellable<Vec<HlRange>> {
540+
pub fn highlight_range(
541+
&self,
542+
highlight_config: HighlightConfig,
543+
frange: FileRange,
544+
) -> Cancellable<Vec<HlRange>> {
537545
self.with_db(|db| {
538-
syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
546+
syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range))
539547
})
540548
}
541549

crates/ide/src/syntax_highlighting.rs

+56-18
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod html;
1414
mod tests;
1515

1616
use hir::{Name, Semantics};
17-
use ide_db::{FxHashMap, RootDatabase};
17+
use ide_db::{FxHashMap, RootDatabase, SymbolKind};
1818
use syntax::{
1919
ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
2020
};
@@ -24,7 +24,7 @@ use crate::{
2424
escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
2525
macro_::MacroHighlighter, tags::Highlight,
2626
},
27-
FileId, HlMod, HlTag,
27+
FileId, HlMod, HlOperator, HlPunct, HlTag,
2828
};
2929

3030
pub(crate) use html::highlight_as_html;
@@ -36,6 +36,26 @@ pub struct HlRange {
3636
pub binding_hash: Option<u64>,
3737
}
3838

39+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
40+
pub struct HighlightConfig {
41+
/// Whether to highlight strings
42+
pub strings: bool,
43+
/// Whether to highlight punctuation
44+
pub punctuation: bool,
45+
/// Whether to specialize punctuation highlights
46+
pub specialize_punctuation: bool,
47+
/// Whether to highlight operator
48+
pub operator: bool,
49+
/// Whether to specialize operator highlights
50+
pub specialize_operator: bool,
51+
/// Whether to inject highlights into doc comments
52+
pub inject_doc_comment: bool,
53+
/// Whether to highlight the macro call bang
54+
pub macro_bang: bool,
55+
/// Whether to highlight unresolved things be their syntax
56+
pub syntactic_name_ref_highlighting: bool,
57+
}
58+
3959
// Feature: Semantic Syntax Highlighting
4060
//
4161
// rust-analyzer highlights the code semantically.
@@ -155,9 +175,9 @@ pub struct HlRange {
155175
// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[]
156176
pub(crate) fn highlight(
157177
db: &RootDatabase,
178+
config: HighlightConfig,
158179
file_id: FileId,
159180
range_to_highlight: Option<TextRange>,
160-
syntactic_name_ref_highlighting: bool,
161181
) -> Vec<HlRange> {
162182
let _p = profile::span("highlight");
163183
let sema = Semantics::new(db);
@@ -183,26 +203,18 @@ pub(crate) fn highlight(
183203
Some(it) => it.krate(),
184204
None => return hl.to_vec(),
185205
};
186-
traverse(
187-
&mut hl,
188-
&sema,
189-
file_id,
190-
&root,
191-
krate,
192-
range_to_highlight,
193-
syntactic_name_ref_highlighting,
194-
);
206+
traverse(&mut hl, &sema, config, file_id, &root, krate, range_to_highlight);
195207
hl.to_vec()
196208
}
197209

198210
fn traverse(
199211
hl: &mut Highlights,
200212
sema: &Semantics<'_, RootDatabase>,
213+
config: HighlightConfig,
201214
file_id: FileId,
202215
root: &SyntaxNode,
203216
krate: hir::Crate,
204217
range_to_highlight: TextRange,
205-
syntactic_name_ref_highlighting: bool,
206218
) {
207219
let is_unlinked = sema.to_module_def(file_id).is_none();
208220
let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
@@ -323,9 +335,11 @@ fn traverse(
323335
Enter(it) => it,
324336
Leave(NodeOrToken::Token(_)) => continue,
325337
Leave(NodeOrToken::Node(node)) => {
326-
// Doc comment highlighting injection, we do this when leaving the node
327-
// so that we overwrite the highlighting of the doc comment itself.
328-
inject::doc_comment(hl, sema, file_id, &node);
338+
if config.inject_doc_comment {
339+
// Doc comment highlighting injection, we do this when leaving the node
340+
// so that we overwrite the highlighting of the doc comment itself.
341+
inject::doc_comment(hl, sema, config, file_id, &node);
342+
}
329343
continue;
330344
}
331345
};
@@ -400,7 +414,8 @@ fn traverse(
400414
let string_to_highlight = ast::String::cast(descended_token.clone());
401415
if let Some((string, expanded_string)) = string.zip(string_to_highlight) {
402416
if string.is_raw() {
403-
if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() {
417+
if inject::ra_fixture(hl, sema, config, &string, &expanded_string).is_some()
418+
{
404419
continue;
405420
}
406421
}
@@ -421,7 +436,7 @@ fn traverse(
421436
sema,
422437
krate,
423438
&mut bindings_shadow_count,
424-
syntactic_name_ref_highlighting,
439+
config.syntactic_name_ref_highlighting,
425440
name_like,
426441
),
427442
NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)),
@@ -439,6 +454,29 @@ fn traverse(
439454
// something unresolvable. FIXME: There should be a way to prevent that
440455
continue;
441456
}
457+
458+
// apply config filtering
459+
match &mut highlight.tag {
460+
HlTag::StringLiteral if !config.strings => continue,
461+
// If punctuation is disabled, make the macro bang part of the macro call again.
462+
tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
463+
if !config.macro_bang {
464+
*tag = HlTag::Symbol(SymbolKind::Macro);
465+
} else if !config.specialize_punctuation {
466+
*tag = HlTag::Punctuation(HlPunct::Other);
467+
}
468+
}
469+
HlTag::Punctuation(_) if !config.punctuation => continue,
470+
tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
471+
*tag = HlTag::Punctuation(HlPunct::Other);
472+
}
473+
HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
474+
tag @ HlTag::Operator(_) if !config.specialize_operator => {
475+
*tag = HlTag::Operator(HlOperator::Other);
476+
}
477+
_ => (),
478+
}
479+
442480
if inside_attribute {
443481
highlight |= HlMod::Attribute
444482
}

crates/ide/src/syntax_highlighting/html.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ use oorandom::Rand32;
55
use stdx::format_to;
66
use syntax::AstNode;
77

8-
use crate::{syntax_highlighting::highlight, FileId, RootDatabase};
8+
use crate::{
9+
syntax_highlighting::{highlight, HighlightConfig},
10+
FileId, RootDatabase,
11+
};
912

1013
pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
1114
let parse = db.parse(file_id);
@@ -20,7 +23,21 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
2023
)
2124
}
2225

23-
let hl_ranges = highlight(db, file_id, None, false);
26+
let hl_ranges = highlight(
27+
db,
28+
HighlightConfig {
29+
strings: true,
30+
punctuation: true,
31+
specialize_punctuation: true,
32+
specialize_operator: true,
33+
operator: true,
34+
inject_doc_comment: true,
35+
macro_bang: true,
36+
syntactic_name_ref_highlighting: false,
37+
},
38+
file_id,
39+
None,
40+
);
2441
let text = parse.tree().syntax().to_string();
2542
let mut buf = String::new();
2643
buf.push_str(STYLE);

crates/ide/src/syntax_highlighting/inject.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ use syntax::{
1515

1616
use crate::{
1717
doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def},
18-
syntax_highlighting::{highlights::Highlights, injector::Injector},
18+
syntax_highlighting::{highlights::Highlights, injector::Injector, HighlightConfig},
1919
Analysis, HlMod, HlRange, HlTag, RootDatabase,
2020
};
2121

2222
pub(super) fn ra_fixture(
2323
hl: &mut Highlights,
2424
sema: &Semantics<'_, RootDatabase>,
25+
config: HighlightConfig,
2526
literal: &ast::String,
2627
expanded: &ast::String,
2728
) -> Option<()> {
@@ -63,7 +64,13 @@ pub(super) fn ra_fixture(
6364

6465
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
6566

66-
for mut hl_range in analysis.highlight(tmp_file_id).unwrap() {
67+
for mut hl_range in analysis
68+
.highlight(
69+
HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
70+
tmp_file_id,
71+
)
72+
.unwrap()
73+
{
6774
for range in inj.map_range_up(hl_range.range) {
6875
if let Some(range) = literal.map_range_up(range) {
6976
hl_range.range = range;
@@ -86,6 +93,7 @@ const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
8693
pub(super) fn doc_comment(
8794
hl: &mut Highlights,
8895
sema: &Semantics<'_, RootDatabase>,
96+
config: HighlightConfig,
8997
src_file_id: FileId,
9098
node: &SyntaxNode,
9199
) {
@@ -206,7 +214,14 @@ pub(super) fn doc_comment(
206214

207215
let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
208216

209-
if let Ok(ranges) = analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)) {
217+
if let Ok(ranges) = analysis.with_db(|db| {
218+
super::highlight(
219+
db,
220+
HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
221+
tmp_file_id,
222+
None,
223+
)
224+
}) {
210225
for HlRange { range, highlight, binding_hash } in ranges {
211226
for range in inj.map_range_up(range) {
212227
hl.add(HlRange { range, highlight: highlight | HlMod::Injected, binding_hash });

crates/ide/src/syntax_highlighting/tags.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl fmt::Display for HlTag {
199199
}
200200

201201
impl HlMod {
202-
const ALL: &'static [HlMod; HlMod::Unsafe as u8 as usize + 1] = &[
202+
const ALL: &'static [HlMod; 19] = &[
203203
HlMod::Associated,
204204
HlMod::Async,
205205
HlMod::Attribute,
@@ -296,7 +296,7 @@ impl Highlight {
296296
Highlight { tag, mods: HlMods::default() }
297297
}
298298
pub fn is_empty(&self) -> bool {
299-
self.tag == HlTag::None && self.mods == HlMods::default()
299+
self.tag == HlTag::None && self.mods.is_empty()
300300
}
301301
}
302302

@@ -330,6 +330,10 @@ impl ops::BitOr<HlMod> for Highlight {
330330
}
331331

332332
impl HlMods {
333+
pub fn is_empty(&self) -> bool {
334+
self.0 == 0
335+
}
336+
333337
pub fn contains(self, m: HlMod) -> bool {
334338
self.0 & m.mask() == m.mask()
335339
}

crates/ide/src/syntax_highlighting/tests.rs

+20-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,18 @@ use expect_test::{expect_file, ExpectFile};
44
use ide_db::SymbolKind;
55
use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear};
66

7-
use crate::{fixture, FileRange, HlTag, TextRange};
7+
use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange};
8+
9+
const HL_CONFIG: HighlightConfig = HighlightConfig {
10+
strings: true,
11+
punctuation: true,
12+
specialize_punctuation: true,
13+
specialize_operator: true,
14+
operator: true,
15+
inject_doc_comment: true,
16+
macro_bang: true,
17+
syntactic_name_ref_highlighting: false,
18+
};
819

920
#[test]
1021
fn attributes() {
@@ -996,7 +1007,10 @@ struct Foo {
9961007

9971008
// The "x"
9981009
let highlights = &analysis
999-
.highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
1010+
.highlight_range(
1011+
HL_CONFIG,
1012+
FileRange { file_id, range: TextRange::at(45.into(), 1.into()) },
1013+
)
10001014
.unwrap();
10011015

10021016
assert_eq!(&highlights[0].highlight.to_string(), "field.declaration.public");
@@ -1011,7 +1025,7 @@ macro_rules! test {}
10111025
}"#
10121026
.trim(),
10131027
);
1014-
let _ = analysis.highlight(file_id).unwrap();
1028+
let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
10151029
}
10161030

10171031
/// Highlights the code given by the `ra_fixture` argument, renders the
@@ -1035,7 +1049,7 @@ fn benchmark_syntax_highlighting_long_struct() {
10351049
let hash = {
10361050
let _pt = bench("syntax highlighting long struct");
10371051
analysis
1038-
.highlight(file_id)
1052+
.highlight(HL_CONFIG, file_id)
10391053
.unwrap()
10401054
.iter()
10411055
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1061,7 +1075,7 @@ fn syntax_highlighting_not_quadratic() {
10611075
let time = Instant::now();
10621076

10631077
let hash = analysis
1064-
.highlight(file_id)
1078+
.highlight(HL_CONFIG, file_id)
10651079
.unwrap()
10661080
.iter()
10671081
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct))
@@ -1086,7 +1100,7 @@ fn benchmark_syntax_highlighting_parser() {
10861100
let hash = {
10871101
let _pt = bench("syntax highlighting parser");
10881102
analysis
1089-
.highlight(file_id)
1103+
.highlight(HL_CONFIG, file_id)
10901104
.unwrap()
10911105
.iter()
10921106
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))

0 commit comments

Comments
 (0)