From f7db49bfc6dd77471865c8591fdf7d5d00992830 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 26 Feb 2020 19:39:32 +0100 Subject: Better highlightign API --- crates/ra_ide/src/lib.rs | 4 +- crates/ra_ide/src/syntax_highlighting.rs | 107 ++++++++------ crates/ra_ide/src/syntax_highlighting/highlight.rs | 163 +++++++++++++++++++++ .../src/syntax_highlighting/highlight_tag.rs | 43 ------ crates/rust-analyzer/src/caps.rs | 7 +- crates/rust-analyzer/src/conv.rs | 111 ++++++-------- crates/rust-analyzer/src/main_loop/handlers.rs | 22 ++- crates/rust-analyzer/src/semantic_tokens.rs | 36 +++-- editors/code/package.json | 19 +++ 9 files changed, 332 insertions(+), 180 deletions(-) create mode 100644 crates/ra_ide/src/syntax_highlighting/highlight.rs delete mode 100644 crates/ra_ide/src/syntax_highlighting/highlight_tag.rs diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index d74d32453..d509de14e 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -74,7 +74,9 @@ pub use crate::{ runnables::{Runnable, RunnableKind, TestId}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, ssr::SsrError, - syntax_highlighting::{HighlightTag, HighlightedRange}, + syntax_highlighting::{ + Highlight, HighlightModifier, HighlightModifiers, HighlightTag, HighlightedRange, + }, }; pub use hir::Documentation; diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index d422930bf..18980dc20 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -mod highlight_tag; +mod highlight; use hir::{Name, Semantics}; use ra_db::SourceDatabase; @@ -17,12 +17,12 @@ use rustc_hash::FxHashMap; use crate::{references::classify_name_ref, FileId}; -pub use highlight_tag::HighlightTag; +pub use highlight::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; #[derive(Debug)] pub struct HighlightedRange { pub range: TextRange, - pub tag: HighlightTag, + pub highlight: Highlight, pub binding_hash: Option, } @@ -79,33 +79,33 @@ pub(crate) fn highlight( if let Some(range) = highlight_macro(node) { res.push(HighlightedRange { range, - tag: HighlightTag::MACRO, + highlight: HighlightTag::Macro.into(), binding_hash: None, }); } } _ if in_macro_call.is_some() => { if let Some(token) = node.as_token() { - if let Some((tag, binding_hash)) = highlight_token_tree( + if let Some((highlight, binding_hash)) = highlight_token_tree( &sema, &mut bindings_shadow_count, token.clone(), ) { res.push(HighlightedRange { range: node.text_range(), - tag, + highlight, binding_hash, }); } } } _ => { - if let Some((tag, binding_hash)) = + if let Some((highlight, binding_hash)) = highlight_node(&sema, &mut bindings_shadow_count, node.clone()) { res.push(HighlightedRange { range: node.text_range(), - tag, + highlight, binding_hash, }); } @@ -150,7 +150,7 @@ fn highlight_token_tree( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, token: SyntaxToken, -) -> Option<(HighlightTag, Option)> { +) -> Option<(Highlight, Option)> { if token.parent().kind() != TOKEN_TREE { return None; } @@ -171,19 +171,21 @@ fn highlight_node( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, node: SyntaxElement, -) -> Option<(HighlightTag, Option)> { +) -> Option<(Highlight, Option)> { let db = sema.db; let mut binding_hash = None; - let tag = match node.kind() { + let highlight: Highlight = match node.kind() { FN_DEF => { bindings_shadow_count.clear(); return None; } - COMMENT => HighlightTag::LITERAL_COMMENT, - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LITERAL_STRING, - ATTR => HighlightTag::LITERAL_ATTRIBUTE, + COMMENT => HighlightTag::Comment.into(), + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LiteralString.into(), + ATTR => HighlightTag::Attribute.into(), // Special-case field init shorthand - NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => HighlightTag::FIELD, + NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => { + HighlightTag::Field.into() + } NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, NAME_REF => { let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); @@ -217,26 +219,30 @@ fn highlight_node( match name_kind { Some(name_kind) => highlight_name(db, name_kind), - None => name.syntax().parent().map_or(HighlightTag::FUNCTION, |x| match x.kind() { - STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => HighlightTag::TYPE, - TYPE_PARAM => HighlightTag::TYPE_PARAM, - RECORD_FIELD_DEF => HighlightTag::FIELD, - _ => HighlightTag::FUNCTION, + None => name.syntax().parent().map_or(HighlightTag::Function.into(), |x| { + match x.kind() { + STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => { + HighlightTag::Type.into() + } + TYPE_PARAM => HighlightTag::TypeParam.into(), + RECORD_FIELD_DEF => HighlightTag::Field.into(), + _ => HighlightTag::Function.into(), + } }), } } - INT_NUMBER | FLOAT_NUMBER => HighlightTag::LITERAL_NUMERIC, - BYTE => HighlightTag::LITERAL_BYTE, - CHAR => HighlightTag::LITERAL_CHAR, - LIFETIME => HighlightTag::TYPE_LIFETIME, - T![unsafe] => HighlightTag::KEYWORD_UNSAFE, - k if is_control_keyword(k) => HighlightTag::KEYWORD_CONTROL, - k if k.is_keyword() => HighlightTag::KEYWORD, + INT_NUMBER | FLOAT_NUMBER => HighlightTag::LiteralNumeric.into(), + BYTE => HighlightTag::LiteralByte.into(), + CHAR => HighlightTag::LiteralChar.into(), + LIFETIME => HighlightTag::TypeLifetime.into(), + T![unsafe] => HighlightTag::Keyword | HighlightModifier::Unsafe, + k if is_control_keyword(k) => HighlightTag::Keyword | HighlightModifier::Control, + k if k.is_keyword() => HighlightTag::Keyword.into(), _ => return None, }; - return Some((tag, binding_hash)); + return Some((highlight, binding_hash)); fn calc_binding_hash(name: &Name, shadow_count: u32) -> u64 { fn hash(x: T) -> u64 { @@ -293,7 +299,11 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo if ranges.is_empty() { buf.push_str(&text); } else { - let classes = ranges.iter().map(|x| x.tag.to_string()).collect::>().join(" "); + let classes = ranges + .iter() + .map(|it| it.highlight.to_string().replace('.', " ")) + .collect::>() + .join(" "); let binding_hash = ranges.first().and_then(|x| x.binding_hash); let color = match (rainbow, binding_hash) { (true, Some(hash)) => format!( @@ -310,29 +320,32 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo buf } -fn highlight_name(db: &RootDatabase, def: NameDefinition) -> HighlightTag { +fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight { match def { - NameDefinition::Macro(_) => HighlightTag::MACRO, - NameDefinition::StructField(_) => HighlightTag::FIELD, - NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::MODULE, - NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::FUNCTION, - NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => HighlightTag::TYPE_BUILTIN, - NameDefinition::SelfType(_) => HighlightTag::TYPE_SELF, - NameDefinition::TypeParam(_) => HighlightTag::TYPE_PARAM, + NameDefinition::Macro(_) => HighlightTag::Macro, + NameDefinition::StructField(_) => HighlightTag::Field, + NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::Module, + NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::Function, + NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::Type, + NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::Constant, + NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::Constant, + NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::Constant, + NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::Type, + NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::Type, + NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => { + return HighlightTag::Type | HighlightModifier::Builtin + } + NameDefinition::SelfType(_) => HighlightTag::TypeSelf, + NameDefinition::TypeParam(_) => HighlightTag::TypeParam, NameDefinition::Local(local) => { + let mut h = Highlight::new(HighlightTag::Variable); if local.is_mut(db) || local.ty(db).is_mutable_reference() { - HighlightTag::VARIABLE_MUT - } else { - HighlightTag::VARIABLE + h |= HighlightModifier::Mutable; } + return h; } } + .into() } //FIXME: like, real html escaping @@ -498,6 +511,6 @@ fn bar() { }) .unwrap(); - assert_eq!(&highlights[0].tag.to_string(), "field"); + assert_eq!(&highlights[0].highlight.to_string(), "field"); } } diff --git a/crates/ra_ide/src/syntax_highlighting/highlight.rs b/crates/ra_ide/src/syntax_highlighting/highlight.rs new file mode 100644 index 000000000..383c74c98 --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/highlight.rs @@ -0,0 +1,163 @@ +//! Defines token tags we use for syntax highlighting. +//! A tag is not unlike a CSS class. + +use std::{fmt, ops}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Highlight { + pub tag: HighlightTag, + pub modifiers: HighlightModifiers, +} + +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct HighlightModifiers(u32); + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum HighlightTag { + Field, + Function, + Module, + Constant, + Macro, + Variable, + + Type, + TypeSelf, + TypeParam, + TypeLifetime, + + LiteralByte, + LiteralNumeric, + LiteralChar, + + Comment, + LiteralString, + Attribute, + + Keyword, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[repr(u8)] +pub enum HighlightModifier { + Mutable = 0, + Unsafe, + /// Used with keywords like `if` and `break`. + Control, + Builtin, +} + +impl HighlightTag { + fn as_str(self) -> &'static str { + match self { + HighlightTag::Field => "field", + HighlightTag::Function => "function", + HighlightTag::Module => "module", + HighlightTag::Constant => "constant", + HighlightTag::Macro => "macro", + HighlightTag::Variable => "variable", + HighlightTag::Type => "type", + HighlightTag::TypeSelf => "type.self", + HighlightTag::TypeParam => "type.param", + HighlightTag::TypeLifetime => "type.lifetime", + HighlightTag::LiteralByte => "literal.byte", + HighlightTag::LiteralNumeric => "literal.numeric", + HighlightTag::LiteralChar => "literal.char", + HighlightTag::Comment => "comment", + HighlightTag::LiteralString => "string", + HighlightTag::Attribute => "attribute", + HighlightTag::Keyword => "keyword", + } + } +} + +impl fmt::Display for HighlightTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} + +impl HighlightModifier { + const ALL: &'static [HighlightModifier] = &[ + HighlightModifier::Mutable, + HighlightModifier::Unsafe, + HighlightModifier::Control, + HighlightModifier::Builtin, + ]; + + fn as_str(self) -> &'static str { + match self { + HighlightModifier::Mutable => "mutable", + HighlightModifier::Unsafe => "unsafe", + HighlightModifier::Control => "control", + HighlightModifier::Builtin => "builtin", + } + } + + fn mask(self) -> u32 { + 1 << (self as u32) + } +} + +impl fmt::Display for HighlightModifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} + +impl fmt::Display for Highlight { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.tag)?; + for modifier in self.modifiers.iter() { + write!(f, ".{}", modifier)? + } + Ok(()) + } +} + +impl From for Highlight { + fn from(tag: HighlightTag) -> Highlight { + Highlight::new(tag) + } +} + +impl Highlight { + pub(crate) fn new(tag: HighlightTag) -> Highlight { + Highlight { tag, modifiers: HighlightModifiers::default() } + } +} + +impl ops::BitOr for HighlightTag { + type Output = Highlight; + + fn bitor(self, rhs: HighlightModifier) -> Highlight { + Highlight::new(self) | rhs + } +} + +impl ops::BitOrAssign for HighlightModifiers { + fn bitor_assign(&mut self, rhs: HighlightModifier) { + self.0 |= rhs.mask(); + } +} + +impl ops::BitOrAssign for Highlight { + fn bitor_assign(&mut self, rhs: HighlightModifier) { + self.modifiers |= rhs; + } +} + +impl ops::BitOr for Highlight { + type Output = Highlight; + + fn bitor(mut self, rhs: HighlightModifier) -> Highlight { + self |= rhs; + self + } +} + +impl HighlightModifiers { + pub fn iter(self) -> impl Iterator { + HighlightModifier::ALL.iter().copied().filter(move |it| self.0 & it.mask() == it.mask()) + } +} diff --git a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs b/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs deleted file mode 100644 index af1ac07b3..000000000 --- a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Defines token tags we use for syntax highlighting. -//! A tag is not unlike a CSS class. - -use std::fmt; - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub struct HighlightTag(&'static str); - -impl fmt::Display for HighlightTag { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.0, f) - } -} - -#[rustfmt::skip] -impl HighlightTag { - pub const FIELD: HighlightTag = HighlightTag("field"); - pub const FUNCTION: HighlightTag = HighlightTag("function"); - pub const MODULE: HighlightTag = HighlightTag("module"); - pub const CONSTANT: HighlightTag = HighlightTag("constant"); - pub const MACRO: HighlightTag = HighlightTag("macro"); - - pub const VARIABLE: HighlightTag = HighlightTag("variable"); - pub const VARIABLE_MUT: HighlightTag = HighlightTag("variable.mut"); - - pub const TYPE: HighlightTag = HighlightTag("type"); - pub const TYPE_BUILTIN: HighlightTag = HighlightTag("type.builtin"); - pub const TYPE_SELF: HighlightTag = HighlightTag("type.self"); - pub const TYPE_PARAM: HighlightTag = HighlightTag("type.param"); - pub const TYPE_LIFETIME: HighlightTag = HighlightTag("type.lifetime"); - - pub const LITERAL_BYTE: HighlightTag = HighlightTag("literal.byte"); - pub const LITERAL_NUMERIC: HighlightTag = HighlightTag("literal.numeric"); - pub const LITERAL_CHAR: HighlightTag = HighlightTag("literal.char"); - - pub const LITERAL_COMMENT: HighlightTag = HighlightTag("comment"); - pub const LITERAL_STRING: HighlightTag = HighlightTag("string"); - pub const LITERAL_ATTRIBUTE: HighlightTag = HighlightTag("attribute"); - - pub const KEYWORD: HighlightTag = HighlightTag("keyword"); - pub const KEYWORD_UNSAFE: HighlightTag = HighlightTag("keyword.unsafe"); - pub const KEYWORD_CONTROL: HighlightTag = HighlightTag("keyword.control"); -} diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index db82eeb1c..759bceb32 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -63,11 +63,8 @@ pub fn server_capabilities() -> ServerCapabilities { semantic_tokens_provider: Some( SemanticTokensOptions { legend: SemanticTokensLegend { - token_types: semantic_tokens::supported_token_types().iter().cloned().collect(), - token_modifiers: semantic_tokens::supported_token_modifiers() - .iter() - .cloned() - .collect(), + token_types: semantic_tokens::SUPPORTED_TYPES.iter().cloned().collect(), + token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.iter().cloned().collect(), }, document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 5596967bd..86851c1f1 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -10,14 +10,21 @@ use lsp_types::{ }; use ra_ide::{ translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, - FileRange, FileSystemEdit, Fold, FoldKind, HighlightTag, InsertTextFormat, LineCol, LineIndex, - NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, + FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HighlightModifier, HighlightTag, + InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, ReferenceAccess, Severity, + SourceChange, SourceFileEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextUnit}; use ra_text_edit::{AtomTextEdit, TextEdit}; use ra_vfs::LineEndings; -use crate::{req, semantic_tokens, world::WorldSnapshot, Result}; +use crate::{ + req, + semantic_tokens::{self, ModifierSet, BUILTIN, CONTROL, MUTABLE, UNSAFE}, + world::WorldSnapshot, + Result, +}; +use semantic_tokens::ATTRIBUTE; pub trait Conv { type Output; @@ -303,74 +310,52 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold { } } -impl Conv for HighlightTag { - type Output = (SemanticTokenType, Vec); - - fn conv(self) -> (SemanticTokenType, Vec) { - let token_type: SemanticTokenType = match self { - HighlightTag::FIELD => SemanticTokenType::MEMBER, - HighlightTag::FUNCTION => SemanticTokenType::FUNCTION, - HighlightTag::MODULE => SemanticTokenType::NAMESPACE, - HighlightTag::CONSTANT => { - return ( - SemanticTokenType::VARIABLE, - vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY], - ) - } - HighlightTag::MACRO => SemanticTokenType::MACRO, - - HighlightTag::VARIABLE => { - return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY]) - } - HighlightTag::VARIABLE_MUT => SemanticTokenType::VARIABLE, +impl Conv for Highlight { + type Output = (u32, u32); - HighlightTag::TYPE => SemanticTokenType::TYPE, - HighlightTag::TYPE_BUILTIN => SemanticTokenType::TYPE, - HighlightTag::TYPE_SELF => { - return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE]) + fn conv(self) -> Self::Output { + let mut mods = ModifierSet::default(); + let type_ = match self.tag { + HighlightTag::Field => SemanticTokenType::MEMBER, + HighlightTag::Function => SemanticTokenType::FUNCTION, + HighlightTag::Module => SemanticTokenType::NAMESPACE, + HighlightTag::Constant => { + mods |= SemanticTokenModifier::STATIC; + mods |= SemanticTokenModifier::READONLY; + SemanticTokenType::VARIABLE } - HighlightTag::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER, - HighlightTag::TYPE_LIFETIME => { - return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE]) + HighlightTag::Macro => SemanticTokenType::MACRO, + HighlightTag::Variable => SemanticTokenType::VARIABLE, + HighlightTag::Type => SemanticTokenType::TYPE, + HighlightTag::TypeSelf => { + mods |= SemanticTokenModifier::REFERENCE; + SemanticTokenType::TYPE } - - HighlightTag::LITERAL_BYTE => SemanticTokenType::NUMBER, - HighlightTag::LITERAL_NUMERIC => SemanticTokenType::NUMBER, - HighlightTag::LITERAL_CHAR => SemanticTokenType::NUMBER, - - HighlightTag::LITERAL_COMMENT => { - return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION]) + HighlightTag::TypeParam => SemanticTokenType::TYPE_PARAMETER, + HighlightTag::TypeLifetime => { + mods |= SemanticTokenModifier::REFERENCE; + SemanticTokenType::LABEL } - - HighlightTag::LITERAL_STRING => SemanticTokenType::STRING, - HighlightTag::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD, - - HighlightTag::KEYWORD => SemanticTokenType::KEYWORD, - HighlightTag::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD, - HighlightTag::KEYWORD_CONTROL => SemanticTokenType::KEYWORD, - unknown => panic!("Unknown semantic token: {}", unknown), + HighlightTag::LiteralByte => SemanticTokenType::NUMBER, + HighlightTag::LiteralNumeric => SemanticTokenType::NUMBER, + HighlightTag::LiteralChar => SemanticTokenType::NUMBER, + HighlightTag::Comment => SemanticTokenType::COMMENT, + HighlightTag::LiteralString => SemanticTokenType::STRING, + HighlightTag::Attribute => ATTRIBUTE, + HighlightTag::Keyword => SemanticTokenType::KEYWORD, }; - (token_type, vec![]) - } -} - -impl Conv for (SemanticTokenType, Vec) { - type Output = (u32, u32); - - fn conv(self) -> Self::Output { - let token_index = - semantic_tokens::supported_token_types().iter().position(|it| *it == self.0).unwrap(); - let mut token_modifier_bitset = 0; - for modifier in self.1.iter() { - let modifier_index = semantic_tokens::supported_token_modifiers() - .iter() - .position(|it| it == modifier) - .unwrap(); - token_modifier_bitset |= 1 << modifier_index; + for modifier in self.modifiers.iter() { + let modifier = match modifier { + HighlightModifier::Mutable => MUTABLE, + HighlightModifier::Unsafe => UNSAFE, + HighlightModifier::Control => CONTROL, + HighlightModifier::Builtin => BUILTIN, + }; + mods |= modifier; } - (token_index as u32, token_modifier_bitset as u32) + (semantic_tokens::type_index(type_), mods.0) } } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index e9f1c4f4b..9ed53169c 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -16,9 +16,9 @@ use lsp_types::{ CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, - PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType, - SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, - SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, + PrepareRenameResponse, Range, RenameParams, SemanticTokens, SemanticTokensParams, + SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, + TextDocumentIdentifier, TextEdit, WorkspaceEdit, }; use ra_ide::{ AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, @@ -954,7 +954,7 @@ fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result> .into_iter() .map(|h| Decoration { range: h.range.conv_with(&line_index), - tag: h.tag.to_string(), + tag: h.highlight.to_string(), binding_hash: h.binding_hash.map(|x| x.to_string()), }) .collect(); @@ -1082,10 +1082,9 @@ pub fn handle_semantic_tokens( let mut builder = SemanticTokensBuilder::default(); - for h in world.analysis().highlight(file_id)?.into_iter() { - let type_and_modifiers: (SemanticTokenType, Vec) = h.tag.conv(); - let (token_type, token_modifiers) = type_and_modifiers.conv(); - builder.push(h.range.conv_with(&line_index), token_type, token_modifiers); + for highlight_range in world.analysis().highlight(file_id)?.into_iter() { + let (token_type, token_modifiers) = highlight_range.highlight.conv(); + builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); } let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; @@ -1104,10 +1103,9 @@ pub fn handle_semantic_tokens_range( let mut builder = SemanticTokensBuilder::default(); - for h in world.analysis().highlight_range(frange)?.into_iter() { - let type_and_modifiers: (SemanticTokenType, Vec) = h.tag.conv(); - let (token_type, token_modifiers) = type_and_modifiers.conv(); - builder.push(h.range.conv_with(&line_index), token_type, token_modifiers); + for highlight_range in world.analysis().highlight_range(frange)?.into_iter() { + let (token_type, token_modifiers) = highlight_range.highlight.conv(); + builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); } let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index e6a8eb146..bf21dc68e 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -1,8 +1,17 @@ //! Semantic Tokens helpers +use std::ops; + use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; -const SUPPORTED_TYPES: &[SemanticTokenType] = &[ +pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); + +pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable"); +pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe"); +pub(crate) const CONTROL: SemanticTokenModifier = SemanticTokenModifier::new("control"); +pub(crate) const BUILTIN: SemanticTokenModifier = SemanticTokenModifier::new("builtin"); + +pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ SemanticTokenType::COMMENT, SemanticTokenType::KEYWORD, SemanticTokenType::STRING, @@ -23,9 +32,10 @@ const SUPPORTED_TYPES: &[SemanticTokenType] = &[ SemanticTokenType::VARIABLE, SemanticTokenType::PARAMETER, SemanticTokenType::LABEL, + ATTRIBUTE, ]; -const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ +pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ SemanticTokenModifier::DOCUMENTATION, SemanticTokenModifier::DECLARATION, SemanticTokenModifier::DEFINITION, @@ -36,16 +46,20 @@ const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ SemanticTokenModifier::ASYNC, SemanticTokenModifier::VOLATILE, SemanticTokenModifier::READONLY, + MUTABLE, + UNSAFE, + CONTROL, + BUILTIN, ]; -/// Token types that the server supports -pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] { - SUPPORTED_TYPES -} +#[derive(Default)] +pub(crate) struct ModifierSet(pub(crate) u32); -/// Token modifiers that the server supports -pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] { - SUPPORTED_MODIFIERS +impl ops::BitOrAssign for ModifierSet { + fn bitor_assign(&mut self, rhs: SemanticTokenModifier) { + let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap(); + self.0 |= 1 << idx; + } } /// Tokens are encoded relative to each other. @@ -92,3 +106,7 @@ impl SemanticTokensBuilder { self.data } } + +pub fn type_index(type_: SemanticTokenType) -> u32 { + SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32 +} diff --git a/editors/code/package.json b/editors/code/package.json index c5f9d50ac..3fbf3f0bb 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -380,6 +380,25 @@ } } ], + "semanticTokenTypes": [ + { + "id": "attribute" + } + ], + "semanticTokenModifiers": [ + { + "id": "mutable" + }, + { + "id": "unsafe" + }, + { + "id": "control" + }, + { + "id": "builtin" + } + ], "semanticTokenStyleDefaults": [ { "selector": "*.mutable", -- cgit v1.2.3 From 995c46024b6d3a10ee781a09ed16ef1f6de95e58 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 26 Feb 2020 23:09:55 +0100 Subject: Fix html tests --- crates/ra_ide/src/snapshots/highlighting.html | 52 +++++++++++----------- .../ra_ide/src/snapshots/rainbow_highlighting.html | 14 +++--- crates/ra_ide/src/syntax_highlighting.rs | 12 ++--- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index a02dbaf2f..51851763e 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -10,29 +10,29 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } -.type\.builtin { color: #8CD0D3; } -.type\.param { color: #20999D; } +.type.builtin { color: #8CD0D3; } +.type.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } -.literal\.numeric { color: #6A8759; } +.literal.numeric { color: #6A8759; } .macro { color: #94BFF3; } .module { color: #AFD8AF; } .variable { color: #DCDCCC; } -.variable\.mut { color: #DCDCCC; text-decoration: underline; } +.variable.mut { color: #DCDCCC; text-decoration: underline; } .keyword { color: #F0DFAF; } -.keyword\.unsafe { color: #DFAF8F; } -.keyword\.control { color: #F0DFAF; font-weight: bold; } +.keyword.unsafe { color: #DFAF8F; } +.keyword.control { color: #F0DFAF; font-weight: bold; }
#[derive(Clone, Debug)]
 struct Foo {
-    pub x: i32,
-    pub y: i32,
+    pub x: i32,
+    pub y: i32,
 }
 
-fn foo<T>() -> T {
+fn foo<T>() -> T {
     unimplemented!();
-    foo::<i32>();
+    foo::<i32>();
 }
 
 macro_rules! def_fn {
@@ -40,33 +40,33 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 def_fn!{
-    fn bar() -> u32 {
-        100
+    fn bar() -> u32 {
+        100
     }
 }
 
 // comment
 fn main() {
-    println!("Hello, {}!", 92);
+    println!("Hello, {}!", 92);
 
-    let mut vec = Vec::new();
-    if true {
-        let x = 92;
-        vec.push(Foo { x, y: 1 });
+    let mut vec = Vec::new();
+    if true {
+        let x = 92;
+        vec.push(Foo { x, y: 1 });
     }
-    unsafe { vec.set_len(0); }
+    unsafe { vec.set_len(0); }
 
-    let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let mut x = 42;
+    let y = &mut x;
+    let z = &y;
 
-    y;
+    y;
 }
 
-enum E<X> {
-    V(X)
+enum E<X> {
+    V(X)
 }
 
-impl<X> E<X> {
-    fn new<T>() -> E<T> {}
+impl<X> E<X> {
+    fn new<T>() -> E<T> {}
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index d6a7da953..1f869867f 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -10,19 +10,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } -.type\.builtin { color: #8CD0D3; } -.type\.param { color: #20999D; } +.type.builtin { color: #8CD0D3; } +.type.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } -.literal\.numeric { color: #6A8759; } +.literal.numeric { color: #6A8759; } .macro { color: #94BFF3; } .module { color: #AFD8AF; } .variable { color: #DCDCCC; } -.variable\.mut { color: #DCDCCC; text-decoration: underline; } +.variable.mut { color: #DCDCCC; text-decoration: underline; } .keyword { color: #F0DFAF; } -.keyword\.unsafe { color: #DFAF8F; } -.keyword\.control { color: #F0DFAF; font-weight: bold; } +.keyword.unsafe { color: #DFAF8F; } +.keyword.control { color: #F0DFAF; font-weight: bold; }
fn main() {
     let hello = "hello";
@@ -34,5 +34,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 fn bar() {
-    let mut hello = "hello";
+    let mut hello = "hello";
 }
\ No newline at end of file diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 18980dc20..c0f13f171 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -365,19 +365,19 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .parameter { color: #94BFF3; } .text { color: #DCDCCC; } .type { color: #7CB8BB; } -.type\\.builtin { color: #8CD0D3; } -.type\\.param { color: #20999D; } +.type.builtin { color: #8CD0D3; } +.type.param { color: #20999D; } .attribute { color: #94BFF3; } .literal { color: #BFEBBF; } -.literal\\.numeric { color: #6A8759; } +.literal.numeric { color: #6A8759; } .macro { color: #94BFF3; } .module { color: #AFD8AF; } .variable { color: #DCDCCC; } -.variable\\.mut { color: #DCDCCC; text-decoration: underline; } +.variable.mut { color: #DCDCCC; text-decoration: underline; } .keyword { color: #F0DFAF; } -.keyword\\.unsafe { color: #DFAF8F; } -.keyword\\.control { color: #F0DFAF; font-weight: bold; } +.keyword.unsafe { color: #DFAF8F; } +.keyword.control { color: #F0DFAF; font-weight: bold; } "; -- cgit v1.2.3 From 9784ab9f394cff5a6c7a9d1e4f09f375a4e0d388 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 26 Feb 2020 23:13:48 +0100 Subject: Move html highlightig to a separate module --- crates/ra_ide/src/syntax_highlighting.rs | 100 +------------------------ crates/ra_ide/src/syntax_highlighting/html.rs | 104 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 97 deletions(-) create mode 100644 crates/ra_ide/src/syntax_highlighting/html.rs diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index c0f13f171..4e95b9ce5 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,9 +1,9 @@ //! FIXME: write short doc here mod highlight; +mod html; use hir::{Name, Semantics}; -use ra_db::SourceDatabase; use ra_ide_db::{ defs::{classify_name, NameDefinition}, RootDatabase, @@ -19,6 +19,8 @@ use crate::{references::classify_name_ref, FileId}; pub use highlight::{Highlight, HighlightModifier, HighlightModifiers, HighlightTag}; +pub(crate) use html::highlight_as_html; + #[derive(Debug)] pub struct HighlightedRange { pub range: TextRange, @@ -257,69 +259,6 @@ fn highlight_node( } } -pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { - let parse = db.parse(file_id); - - fn rainbowify(seed: u64) -> String { - use rand::prelude::*; - let mut rng = SmallRng::seed_from_u64(seed); - format!( - "hsl({h},{s}%,{l}%)", - h = rng.gen_range::(0, 361), - s = rng.gen_range::(42, 99), - l = rng.gen_range::(40, 91), - ) - } - - let mut ranges = highlight(db, file_id, None); - ranges.sort_by_key(|it| it.range.start()); - // quick non-optimal heuristic to intersect token ranges and highlighted ranges - let mut frontier = 0; - let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); - - let mut buf = String::new(); - buf.push_str(&STYLE); - buf.push_str("
");
-    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
-    for token in tokens {
-        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
-        while let Some(r) = ranges.get(frontier) {
-            if r.range.start() <= token.text_range().end() {
-                could_intersect.push(r);
-                frontier += 1;
-            } else {
-                break;
-            }
-        }
-        let text = html_escape(&token.text());
-        let ranges = could_intersect
-            .iter()
-            .filter(|it| token.text_range().is_subrange(&it.range))
-            .collect::>();
-        if ranges.is_empty() {
-            buf.push_str(&text);
-        } else {
-            let classes = ranges
-                .iter()
-                .map(|it| it.highlight.to_string().replace('.', " "))
-                .collect::>()
-                .join(" ");
-            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
-            let color = match (rainbow, binding_hash) {
-                (true, Some(hash)) => format!(
-                    " data-binding-hash=\"{}\" style=\"color: {};\"",
-                    hash,
-                    rainbowify(hash)
-                ),
-                _ => "".into(),
-            };
-            buf.push_str(&format!("{}", classes, color, text));
-        }
-    }
-    buf.push_str("
"); - buf -} - fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight { match def { NameDefinition::Macro(_) => HighlightTag::Macro, @@ -348,39 +287,6 @@ fn highlight_name(db: &RootDatabase, def: NameDefinition) -> Highlight { .into() } -//FIXME: like, real html escaping -fn html_escape(text: &str) -> String { - text.replace("<", "<").replace(">", ">") -} - -const STYLE: &str = " - -"; - #[cfg(test)] mod tests { use std::fs; diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs new file mode 100644 index 000000000..210d9a57b --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/html.rs @@ -0,0 +1,104 @@ +//! Renders a bit of code as HTML. + +use ra_db::SourceDatabase; +use ra_syntax::AstNode; + +use crate::{FileId, HighlightedRange, RootDatabase}; + +use super::highlight; + +pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { + let parse = db.parse(file_id); + + fn rainbowify(seed: u64) -> String { + use rand::prelude::*; + let mut rng = SmallRng::seed_from_u64(seed); + format!( + "hsl({h},{s}%,{l}%)", + h = rng.gen_range::(0, 361), + s = rng.gen_range::(42, 99), + l = rng.gen_range::(40, 91), + ) + } + + let mut ranges = highlight(db, file_id, None); + ranges.sort_by_key(|it| it.range.start()); + // quick non-optimal heuristic to intersect token ranges and highlighted ranges + let mut frontier = 0; + let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); + + let mut buf = String::new(); + buf.push_str(&STYLE); + buf.push_str("
");
+    let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
+    for token in tokens {
+        could_intersect.retain(|it| token.text_range().start() <= it.range.end());
+        while let Some(r) = ranges.get(frontier) {
+            if r.range.start() <= token.text_range().end() {
+                could_intersect.push(r);
+                frontier += 1;
+            } else {
+                break;
+            }
+        }
+        let text = html_escape(&token.text());
+        let ranges = could_intersect
+            .iter()
+            .filter(|it| token.text_range().is_subrange(&it.range))
+            .collect::>();
+        if ranges.is_empty() {
+            buf.push_str(&text);
+        } else {
+            let classes = ranges
+                .iter()
+                .map(|it| it.highlight.to_string().replace('.', " "))
+                .collect::>()
+                .join(" ");
+            let binding_hash = ranges.first().and_then(|x| x.binding_hash);
+            let color = match (rainbow, binding_hash) {
+                (true, Some(hash)) => format!(
+                    " data-binding-hash=\"{}\" style=\"color: {};\"",
+                    hash,
+                    rainbowify(hash)
+                ),
+                _ => "".into(),
+            };
+            buf.push_str(&format!("{}", classes, color, text));
+        }
+    }
+    buf.push_str("
"); + buf +} + +//FIXME: like, real html escaping +fn html_escape(text: &str) -> String { + text.replace("<", "<").replace(">", ">") +} + +const STYLE: &str = " + +"; -- cgit v1.2.3 From a8e68ff814801a72ecac6cd918a3e7fbb25d11c0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 26 Feb 2020 23:16:57 +0100 Subject: Color constants --- crates/rust-analyzer/src/conv.rs | 4 ++-- crates/rust-analyzer/src/semantic_tokens.rs | 2 ++ editors/code/package.json | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index 86851c1f1..b012f5dd5 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -20,7 +20,7 @@ use ra_vfs::LineEndings; use crate::{ req, - semantic_tokens::{self, ModifierSet, BUILTIN, CONTROL, MUTABLE, UNSAFE}, + semantic_tokens::{self, ModifierSet, BUILTIN, CONSTANT, CONTROL, MUTABLE, UNSAFE}, world::WorldSnapshot, Result, }; @@ -322,7 +322,7 @@ impl Conv for Highlight { HighlightTag::Constant => { mods |= SemanticTokenModifier::STATIC; mods |= SemanticTokenModifier::READONLY; - SemanticTokenType::VARIABLE + CONSTANT } HighlightTag::Macro => SemanticTokenType::MACRO, HighlightTag::Variable => SemanticTokenType::VARIABLE, diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index bf21dc68e..d8362409d 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -5,6 +5,7 @@ use std::ops; use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); +pub(crate) const CONSTANT: SemanticTokenType = SemanticTokenType::new("constant"); pub(crate) const MUTABLE: SemanticTokenModifier = SemanticTokenModifier::new("mutable"); pub(crate) const UNSAFE: SemanticTokenModifier = SemanticTokenModifier::new("unsafe"); @@ -33,6 +34,7 @@ pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ SemanticTokenType::PARAMETER, SemanticTokenType::LABEL, ATTRIBUTE, + CONSTANT, ]; pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[ diff --git a/editors/code/package.json b/editors/code/package.json index 3fbf3f0bb..ad1101603 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -383,6 +383,9 @@ "semanticTokenTypes": [ { "id": "attribute" + }, + { + "id": "constant" } ], "semanticTokenModifiers": [ @@ -411,6 +414,12 @@ "highContrast": { "fontStyle": "underline" } + }, + { + "selector": "constant", + "scope": [ + "entity.name.constant" + ] } ] } -- cgit v1.2.3