diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index 5262770f30..9db35b17c0 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -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 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() } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6649f42b4e..732d01595e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -391,6 +391,16 @@ config_data! { /// By disabling semantic tokens for strings, other grammars can be used to highlight /// their contents. semanticHighlighting_strings_enable: bool = "true", + /// Use semantic tokens for punctuations. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when + /// they are tagged with modifiers. + semanticHighlighting_punctuation_enable: bool = "false", + /// Use specialized semantic tokens for punctuations. + /// + /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead + /// of the generic `punctuation` token type. + semanticHighlighting_punctuation_specialize: bool = "false", /// Show full signature of the callable. Only shows parameters if disabled. signatureInfo_detail: SignatureDetail = "\"full\"", @@ -523,6 +533,13 @@ impl HoverActionsConfig { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HighlightingConfig { + pub strings: bool, + pub punctuation: bool, + pub specialize_punctuation: bool, +} + #[derive(Debug, Clone)] pub struct FilesConfig { pub watcher: FilesWatcher, @@ -1171,8 +1188,12 @@ impl Config { } } - pub fn highlighting_strings(&self) -> bool { - self.data.semanticHighlighting_strings_enable + pub fn highlighting_config(&self) -> HighlightingConfig { + HighlightingConfig { + strings: self.data.semanticHighlighting_strings_enable, + punctuation: self.data.semanticHighlighting_punctuation_enable, + specialize_punctuation: self.data.semanticHighlighting_punctuation_specialize, + } } pub fn hover(&self) -> HoverConfig { diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e0bcc80b31..beec3433b7 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1505,9 +1505,9 @@ pub(crate) fn handle_semantic_tokens_full( let line_index = snap.file_line_index(file_id)?; let highlights = snap.analysis.highlight(file_id)?; - let highlight_strings = snap.config.highlighting_strings(); + let highlighting_config = snap.config.highlighting_config(); let semantic_tokens = - to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings); + to_proto::semantic_tokens(&text, &line_index, highlights, highlighting_config); // Unconditionally cache the tokens snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone()); @@ -1526,7 +1526,7 @@ pub(crate) fn handle_semantic_tokens_full_delta( let line_index = snap.file_line_index(file_id)?; let highlights = snap.analysis.highlight(file_id)?; - let highlight_strings = snap.config.highlighting_strings(); + let highlight_strings = snap.config.highlighting_config(); let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings); @@ -1557,7 +1557,7 @@ pub(crate) fn handle_semantic_tokens_range( let line_index = snap.file_line_index(frange.file_id)?; let highlights = snap.analysis.highlight_range(frange)?; - let highlight_strings = snap.config.highlighting_strings(); + let highlight_strings = snap.config.highlighting_config(); let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights, highlight_strings); Ok(Some(semantic_tokens.into())) diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index e7115b0732..21fd6c7255 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -18,7 +18,7 @@ use vfs::AbsPath; use crate::{ cargo_target_spec::CargoTargetSpec, - config::{CallInfoConfig, Config}, + config::{CallInfoConfig, Config, HighlightingConfig}, global_state::GlobalStateSnapshot, line_index::{LineEndings, LineIndex, OffsetEncoding}, lsp_ext, @@ -517,19 +517,37 @@ pub(crate) fn semantic_tokens( text: &str, line_index: &LineIndex, highlights: Vec, - highlight_strings: bool, + config: HighlightingConfig, ) -> lsp_types::SemanticTokens { let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string(); let mut builder = semantic_tokens::SemanticTokensBuilder::new(id); - for highlight_range in highlights { + for mut highlight_range in highlights { if highlight_range.highlight.is_empty() { continue; } - let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); - if !highlight_strings && ty == lsp_types::SemanticTokenType::STRING { - continue; + + // apply config filtering + match &mut highlight_range.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.punctuation || !config.specialize_punctuation => + { + *tag = HlTag::Symbol(SymbolKind::Macro); + } + HlTag::Punctuation(_) + if !config.punctuation && highlight_range.highlight.mods.is_empty() => + { + continue + } + tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => { + *tag = HlTag::Punctuation(HlPunct::Other); + } + _ => (), } + + let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); let token_index = semantic_tokens::type_index(ty); let modifier_bitset = mods.0;