From f38bac48e5cc193d88aaea17bfb4234f64f1ded0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 26 Feb 2020 17:08:15 +0100 Subject: More type safety for highlighting --- crates/ra_ide/src/lib.rs | 2 +- crates/ra_ide/src/syntax_highlighting.rs | 107 ++++++++------------- .../src/syntax_highlighting/highlight_tag.rs | 43 +++++++++ crates/rust-analyzer/src/conv.rs | 48 ++++----- crates/rust-analyzer/src/main_loop/handlers.rs | 2 +- crates/rust-analyzer/src/req.rs | 2 +- 6 files changed, 111 insertions(+), 93 deletions(-) create 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 f31d3c295..d74d32453 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -74,7 +74,7 @@ pub use crate::{ runnables::{Runnable, RunnableKind, TestId}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, ssr::SsrError, - syntax_highlighting::{tags, HighlightedRange}, + syntax_highlighting::{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 987476d2c..d422930bf 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -1,5 +1,7 @@ //! FIXME: write short doc here +mod highlight_tag; + use hir::{Name, Semantics}; use ra_db::SourceDatabase; use ra_ide_db::{ @@ -15,39 +17,12 @@ use rustc_hash::FxHashMap; use crate::{references::classify_name_ref, FileId}; -pub mod tags { - pub const FIELD: &str = "field"; - pub const FUNCTION: &str = "function"; - pub const MODULE: &str = "module"; - pub const CONSTANT: &str = "constant"; - pub const MACRO: &str = "macro"; - - pub const VARIABLE: &str = "variable"; - pub const VARIABLE_MUT: &str = "variable.mut"; - - pub const TYPE: &str = "type"; - pub const TYPE_BUILTIN: &str = "type.builtin"; - pub const TYPE_SELF: &str = "type.self"; - pub const TYPE_PARAM: &str = "type.param"; - pub const TYPE_LIFETIME: &str = "type.lifetime"; - - pub const LITERAL_BYTE: &str = "literal.byte"; - pub const LITERAL_NUMERIC: &str = "literal.numeric"; - pub const LITERAL_CHAR: &str = "literal.char"; - - pub const LITERAL_COMMENT: &str = "comment"; - pub const LITERAL_STRING: &str = "string"; - pub const LITERAL_ATTRIBUTE: &str = "attribute"; - - pub const KEYWORD: &str = "keyword"; - pub const KEYWORD_UNSAFE: &str = "keyword.unsafe"; - pub const KEYWORD_CONTROL: &str = "keyword.control"; -} +pub use highlight_tag::HighlightTag; #[derive(Debug)] pub struct HighlightedRange { pub range: TextRange, - pub tag: &'static str, + pub tag: HighlightTag, pub binding_hash: Option, } @@ -104,7 +79,7 @@ pub(crate) fn highlight( if let Some(range) = highlight_macro(node) { res.push(HighlightedRange { range, - tag: tags::MACRO, + tag: HighlightTag::MACRO, binding_hash: None, }); } @@ -175,7 +150,7 @@ fn highlight_token_tree( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, token: SyntaxToken, -) -> Option<(&'static str, Option)> { +) -> Option<(HighlightTag, Option)> { if token.parent().kind() != TOKEN_TREE { return None; } @@ -196,7 +171,7 @@ fn highlight_node( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, node: SyntaxElement, -) -> Option<(&'static str, Option)> { +) -> Option<(HighlightTag, Option)> { let db = sema.db; let mut binding_hash = None; let tag = match node.kind() { @@ -204,11 +179,11 @@ fn highlight_node( bindings_shadow_count.clear(); return None; } - COMMENT => tags::LITERAL_COMMENT, - STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, - ATTR => tags::LITERAL_ATTRIBUTE, + COMMENT => HighlightTag::LITERAL_COMMENT, + STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LITERAL_STRING, + ATTR => HighlightTag::LITERAL_ATTRIBUTE, // Special-case field init shorthand - NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, + NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => HighlightTag::FIELD, 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(); @@ -242,21 +217,21 @@ fn highlight_node( match name_kind { Some(name_kind) => highlight_name(db, name_kind), - None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { - STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, - TYPE_PARAM => tags::TYPE_PARAM, - RECORD_FIELD_DEF => tags::FIELD, - _ => tags::FUNCTION, + 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, }), } } - INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, - BYTE => tags::LITERAL_BYTE, - CHAR => tags::LITERAL_CHAR, - LIFETIME => tags::TYPE_LIFETIME, - T![unsafe] => tags::KEYWORD_UNSAFE, - k if is_control_keyword(k) => tags::KEYWORD_CONTROL, - k if k.is_keyword() => tags::KEYWORD, + 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, _ => return None, }; @@ -318,7 +293,7 @@ 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).collect::>().join(" "); + let classes = ranges.iter().map(|x| x.tag.to_string()).collect::>().join(" "); let binding_hash = ranges.first().and_then(|x| x.binding_hash); let color = match (rainbow, binding_hash) { (true, Some(hash)) => format!( @@ -335,26 +310,26 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo buf } -fn highlight_name(db: &RootDatabase, def: NameDefinition) -> &'static str { +fn highlight_name(db: &RootDatabase, def: NameDefinition) -> HighlightTag { match def { - NameDefinition::Macro(_) => tags::MACRO, - NameDefinition::StructField(_) => tags::FIELD, - NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE, - NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION, - NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT, - NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, - NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, - NameDefinition::SelfType(_) => tags::TYPE_SELF, - NameDefinition::TypeParam(_) => tags::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(_)) => HighlightTag::TYPE_BUILTIN, + NameDefinition::SelfType(_) => HighlightTag::TYPE_SELF, + NameDefinition::TypeParam(_) => HighlightTag::TYPE_PARAM, NameDefinition::Local(local) => { if local.is_mut(db) || local.ty(db).is_mutable_reference() { - tags::VARIABLE_MUT + HighlightTag::VARIABLE_MUT } else { - tags::VARIABLE + HighlightTag::VARIABLE } } } @@ -523,6 +498,6 @@ fn bar() { }) .unwrap(); - assert_eq!(highlights[0].tag, "field"); + assert_eq!(&highlights[0].tag.to_string(), "field"); } } diff --git a/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs b/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs new file mode 100644 index 000000000..af1ac07b3 --- /dev/null +++ b/crates/ra_ide/src/syntax_highlighting/highlight_tag.rs @@ -0,0 +1,43 @@ +//! 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/conv.rs b/crates/rust-analyzer/src/conv.rs index 5fcb46b61..5e5610a1e 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs @@ -9,8 +9,8 @@ use lsp_types::{ WorkspaceEdit, }; use ra_ide::{ - tags, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, - FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, + translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, + FileRange, FileSystemEdit, Fold, FoldKind, HighlightTag, InsertTextFormat, LineCol, LineIndex, NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, }; use ra_syntax::{SyntaxKind, TextRange, TextUnit}; @@ -303,51 +303,51 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold { } } -impl Conv for &'static str { +impl Conv for HighlightTag { type Output = (SemanticTokenType, Vec); fn conv(self) -> (SemanticTokenType, Vec) { let token_type: SemanticTokenType = match self { - tags::FIELD => SemanticTokenType::MEMBER, - tags::FUNCTION => SemanticTokenType::FUNCTION, - tags::MODULE => SemanticTokenType::NAMESPACE, - tags::CONSTANT => { + HighlightTag::FIELD => SemanticTokenType::MEMBER, + HighlightTag::FUNCTION => SemanticTokenType::FUNCTION, + HighlightTag::MODULE => SemanticTokenType::NAMESPACE, + HighlightTag::CONSTANT => { return ( SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY], ) } - tags::MACRO => SemanticTokenType::MACRO, + HighlightTag::MACRO => SemanticTokenType::MACRO, - tags::VARIABLE => { + HighlightTag::VARIABLE => { return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY]) } - tags::VARIABLE_MUT => SemanticTokenType::VARIABLE, + HighlightTag::VARIABLE_MUT => SemanticTokenType::VARIABLE, - tags::TYPE => SemanticTokenType::TYPE, - tags::TYPE_BUILTIN => SemanticTokenType::TYPE, - tags::TYPE_SELF => { + HighlightTag::TYPE => SemanticTokenType::TYPE, + HighlightTag::TYPE_BUILTIN => SemanticTokenType::TYPE, + HighlightTag::TYPE_SELF => { return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE]) } - tags::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER, - tags::TYPE_LIFETIME => { + HighlightTag::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER, + HighlightTag::TYPE_LIFETIME => { return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE]) } - tags::LITERAL_BYTE => SemanticTokenType::NUMBER, - tags::LITERAL_NUMERIC => SemanticTokenType::NUMBER, - tags::LITERAL_CHAR => SemanticTokenType::NUMBER, + HighlightTag::LITERAL_BYTE => SemanticTokenType::NUMBER, + HighlightTag::LITERAL_NUMERIC => SemanticTokenType::NUMBER, + HighlightTag::LITERAL_CHAR => SemanticTokenType::NUMBER, - tags::LITERAL_COMMENT => { + HighlightTag::LITERAL_COMMENT => { return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION]) } - tags::LITERAL_STRING => SemanticTokenType::STRING, - tags::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD, + HighlightTag::LITERAL_STRING => SemanticTokenType::STRING, + HighlightTag::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD, - tags::KEYWORD => SemanticTokenType::KEYWORD, - tags::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD, - tags::KEYWORD_CONTROL => SemanticTokenType::KEYWORD, + HighlightTag::KEYWORD => SemanticTokenType::KEYWORD, + HighlightTag::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD, + HighlightTag::KEYWORD_CONTROL => SemanticTokenType::KEYWORD, unknown => panic!("Unknown semantic token: {}", unknown), }; diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 267edd578..e9f1c4f4b 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -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, + tag: h.tag.to_string(), binding_hash: h.binding_hash.map(|x| x.to_string()), }) .collect(); diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index 642ac41ac..fd6aef597 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs @@ -112,7 +112,7 @@ pub struct PublishDecorationsParams { #[serde(rename_all = "camelCase")] pub struct Decoration { pub range: Range, - pub tag: &'static str, + pub tag: String, pub binding_hash: Option, } -- cgit v1.2.3