aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-02-26 16:08:15 +0000
committerAleksey Kladov <[email protected]>2020-02-26 16:17:15 +0000
commitf38bac48e5cc193d88aaea17bfb4234f64f1ded0 (patch)
treeedd6cef6d68d06efebbcd667074af262e099940b /crates
parent640ede4ade02e6caae57532506409370c5ca63c9 (diff)
More type safety for highlighting
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/lib.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs107
-rw-r--r--crates/ra_ide/src/syntax_highlighting/highlight_tag.rs43
-rw-r--r--crates/rust-analyzer/src/conv.rs48
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs2
-rw-r--r--crates/rust-analyzer/src/req.rs2
6 files changed, 111 insertions, 93 deletions
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::{
74 runnables::{Runnable, RunnableKind, TestId}, 74 runnables::{Runnable, RunnableKind, TestId},
75 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 75 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
76 ssr::SsrError, 76 ssr::SsrError,
77 syntax_highlighting::{tags, HighlightedRange}, 77 syntax_highlighting::{HighlightTag, HighlightedRange},
78}; 78};
79 79
80pub use hir::Documentation; 80pub 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 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod highlight_tag;
4
3use hir::{Name, Semantics}; 5use hir::{Name, Semantics};
4use ra_db::SourceDatabase; 6use ra_db::SourceDatabase;
5use ra_ide_db::{ 7use ra_ide_db::{
@@ -15,39 +17,12 @@ use rustc_hash::FxHashMap;
15 17
16use crate::{references::classify_name_ref, FileId}; 18use crate::{references::classify_name_ref, FileId};
17 19
18pub mod tags { 20pub use highlight_tag::HighlightTag;
19 pub const FIELD: &str = "field";
20 pub const FUNCTION: &str = "function";
21 pub const MODULE: &str = "module";
22 pub const CONSTANT: &str = "constant";
23 pub const MACRO: &str = "macro";
24
25 pub const VARIABLE: &str = "variable";
26 pub const VARIABLE_MUT: &str = "variable.mut";
27
28 pub const TYPE: &str = "type";
29 pub const TYPE_BUILTIN: &str = "type.builtin";
30 pub const TYPE_SELF: &str = "type.self";
31 pub const TYPE_PARAM: &str = "type.param";
32 pub const TYPE_LIFETIME: &str = "type.lifetime";
33
34 pub const LITERAL_BYTE: &str = "literal.byte";
35 pub const LITERAL_NUMERIC: &str = "literal.numeric";
36 pub const LITERAL_CHAR: &str = "literal.char";
37
38 pub const LITERAL_COMMENT: &str = "comment";
39 pub const LITERAL_STRING: &str = "string";
40 pub const LITERAL_ATTRIBUTE: &str = "attribute";
41
42 pub const KEYWORD: &str = "keyword";
43 pub const KEYWORD_UNSAFE: &str = "keyword.unsafe";
44 pub const KEYWORD_CONTROL: &str = "keyword.control";
45}
46 21
47#[derive(Debug)] 22#[derive(Debug)]
48pub struct HighlightedRange { 23pub struct HighlightedRange {
49 pub range: TextRange, 24 pub range: TextRange,
50 pub tag: &'static str, 25 pub tag: HighlightTag,
51 pub binding_hash: Option<u64>, 26 pub binding_hash: Option<u64>,
52} 27}
53 28
@@ -104,7 +79,7 @@ pub(crate) fn highlight(
104 if let Some(range) = highlight_macro(node) { 79 if let Some(range) = highlight_macro(node) {
105 res.push(HighlightedRange { 80 res.push(HighlightedRange {
106 range, 81 range,
107 tag: tags::MACRO, 82 tag: HighlightTag::MACRO,
108 binding_hash: None, 83 binding_hash: None,
109 }); 84 });
110 } 85 }
@@ -175,7 +150,7 @@ fn highlight_token_tree(
175 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
176 bindings_shadow_count: &mut FxHashMap<Name, u32>, 151 bindings_shadow_count: &mut FxHashMap<Name, u32>,
177 token: SyntaxToken, 152 token: SyntaxToken,
178) -> Option<(&'static str, Option<u64>)> { 153) -> Option<(HighlightTag, Option<u64>)> {
179 if token.parent().kind() != TOKEN_TREE { 154 if token.parent().kind() != TOKEN_TREE {
180 return None; 155 return None;
181 } 156 }
@@ -196,7 +171,7 @@ fn highlight_node(
196 sema: &Semantics<RootDatabase>, 171 sema: &Semantics<RootDatabase>,
197 bindings_shadow_count: &mut FxHashMap<Name, u32>, 172 bindings_shadow_count: &mut FxHashMap<Name, u32>,
198 node: SyntaxElement, 173 node: SyntaxElement,
199) -> Option<(&'static str, Option<u64>)> { 174) -> Option<(HighlightTag, Option<u64>)> {
200 let db = sema.db; 175 let db = sema.db;
201 let mut binding_hash = None; 176 let mut binding_hash = None;
202 let tag = match node.kind() { 177 let tag = match node.kind() {
@@ -204,11 +179,11 @@ fn highlight_node(
204 bindings_shadow_count.clear(); 179 bindings_shadow_count.clear();
205 return None; 180 return None;
206 } 181 }
207 COMMENT => tags::LITERAL_COMMENT, 182 COMMENT => HighlightTag::LITERAL_COMMENT,
208 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => tags::LITERAL_STRING, 183 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => HighlightTag::LITERAL_STRING,
209 ATTR => tags::LITERAL_ATTRIBUTE, 184 ATTR => HighlightTag::LITERAL_ATTRIBUTE,
210 // Special-case field init shorthand 185 // Special-case field init shorthand
211 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => tags::FIELD, 186 NAME_REF if node.parent().and_then(ast::RecordField::cast).is_some() => HighlightTag::FIELD,
212 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None, 187 NAME_REF if node.ancestors().any(|it| it.kind() == ATTR) => return None,
213 NAME_REF => { 188 NAME_REF => {
214 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap(); 189 let name_ref = node.as_node().cloned().and_then(ast::NameRef::cast).unwrap();
@@ -242,21 +217,21 @@ fn highlight_node(
242 217
243 match name_kind { 218 match name_kind {
244 Some(name_kind) => highlight_name(db, name_kind), 219 Some(name_kind) => highlight_name(db, name_kind),
245 None => name.syntax().parent().map_or(tags::FUNCTION, |x| match x.kind() { 220 None => name.syntax().parent().map_or(HighlightTag::FUNCTION, |x| match x.kind() {
246 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => tags::TYPE, 221 STRUCT_DEF | ENUM_DEF | TRAIT_DEF | TYPE_ALIAS_DEF => HighlightTag::TYPE,
247 TYPE_PARAM => tags::TYPE_PARAM, 222 TYPE_PARAM => HighlightTag::TYPE_PARAM,
248 RECORD_FIELD_DEF => tags::FIELD, 223 RECORD_FIELD_DEF => HighlightTag::FIELD,
249 _ => tags::FUNCTION, 224 _ => HighlightTag::FUNCTION,
250 }), 225 }),
251 } 226 }
252 } 227 }
253 INT_NUMBER | FLOAT_NUMBER => tags::LITERAL_NUMERIC, 228 INT_NUMBER | FLOAT_NUMBER => HighlightTag::LITERAL_NUMERIC,
254 BYTE => tags::LITERAL_BYTE, 229 BYTE => HighlightTag::LITERAL_BYTE,
255 CHAR => tags::LITERAL_CHAR, 230 CHAR => HighlightTag::LITERAL_CHAR,
256 LIFETIME => tags::TYPE_LIFETIME, 231 LIFETIME => HighlightTag::TYPE_LIFETIME,
257 T![unsafe] => tags::KEYWORD_UNSAFE, 232 T![unsafe] => HighlightTag::KEYWORD_UNSAFE,
258 k if is_control_keyword(k) => tags::KEYWORD_CONTROL, 233 k if is_control_keyword(k) => HighlightTag::KEYWORD_CONTROL,
259 k if k.is_keyword() => tags::KEYWORD, 234 k if k.is_keyword() => HighlightTag::KEYWORD,
260 235
261 _ => return None, 236 _ => return None,
262 }; 237 };
@@ -318,7 +293,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
318 if ranges.is_empty() { 293 if ranges.is_empty() {
319 buf.push_str(&text); 294 buf.push_str(&text);
320 } else { 295 } else {
321 let classes = ranges.iter().map(|x| x.tag).collect::<Vec<_>>().join(" "); 296 let classes = ranges.iter().map(|x| x.tag.to_string()).collect::<Vec<_>>().join(" ");
322 let binding_hash = ranges.first().and_then(|x| x.binding_hash); 297 let binding_hash = ranges.first().and_then(|x| x.binding_hash);
323 let color = match (rainbow, binding_hash) { 298 let color = match (rainbow, binding_hash) {
324 (true, Some(hash)) => format!( 299 (true, Some(hash)) => format!(
@@ -335,26 +310,26 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
335 buf 310 buf
336} 311}
337 312
338fn highlight_name(db: &RootDatabase, def: NameDefinition) -> &'static str { 313fn highlight_name(db: &RootDatabase, def: NameDefinition) -> HighlightTag {
339 match def { 314 match def {
340 NameDefinition::Macro(_) => tags::MACRO, 315 NameDefinition::Macro(_) => HighlightTag::MACRO,
341 NameDefinition::StructField(_) => tags::FIELD, 316 NameDefinition::StructField(_) => HighlightTag::FIELD,
342 NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => tags::MODULE, 317 NameDefinition::ModuleDef(hir::ModuleDef::Module(_)) => HighlightTag::MODULE,
343 NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => tags::FUNCTION, 318 NameDefinition::ModuleDef(hir::ModuleDef::Function(_)) => HighlightTag::FUNCTION,
344 NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => tags::TYPE, 319 NameDefinition::ModuleDef(hir::ModuleDef::Adt(_)) => HighlightTag::TYPE,
345 NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => tags::CONSTANT, 320 NameDefinition::ModuleDef(hir::ModuleDef::EnumVariant(_)) => HighlightTag::CONSTANT,
346 NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => tags::CONSTANT, 321 NameDefinition::ModuleDef(hir::ModuleDef::Const(_)) => HighlightTag::CONSTANT,
347 NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => tags::CONSTANT, 322 NameDefinition::ModuleDef(hir::ModuleDef::Static(_)) => HighlightTag::CONSTANT,
348 NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => tags::TYPE, 323 NameDefinition::ModuleDef(hir::ModuleDef::Trait(_)) => HighlightTag::TYPE,
349 NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => tags::TYPE, 324 NameDefinition::ModuleDef(hir::ModuleDef::TypeAlias(_)) => HighlightTag::TYPE,
350 NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => tags::TYPE_BUILTIN, 325 NameDefinition::ModuleDef(hir::ModuleDef::BuiltinType(_)) => HighlightTag::TYPE_BUILTIN,
351 NameDefinition::SelfType(_) => tags::TYPE_SELF, 326 NameDefinition::SelfType(_) => HighlightTag::TYPE_SELF,
352 NameDefinition::TypeParam(_) => tags::TYPE_PARAM, 327 NameDefinition::TypeParam(_) => HighlightTag::TYPE_PARAM,
353 NameDefinition::Local(local) => { 328 NameDefinition::Local(local) => {
354 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 329 if local.is_mut(db) || local.ty(db).is_mutable_reference() {
355 tags::VARIABLE_MUT 330 HighlightTag::VARIABLE_MUT
356 } else { 331 } else {
357 tags::VARIABLE 332 HighlightTag::VARIABLE
358 } 333 }
359 } 334 }
360 } 335 }
@@ -523,6 +498,6 @@ fn bar() {
523 }) 498 })
524 .unwrap(); 499 .unwrap();
525 500
526 assert_eq!(highlights[0].tag, "field"); 501 assert_eq!(&highlights[0].tag.to_string(), "field");
527 } 502 }
528} 503}
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 @@
1//! Defines token tags we use for syntax highlighting.
2//! A tag is not unlike a CSS class.
3
4use std::fmt;
5
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7pub struct HighlightTag(&'static str);
8
9impl fmt::Display for HighlightTag {
10 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11 fmt::Display::fmt(self.0, f)
12 }
13}
14
15#[rustfmt::skip]
16impl HighlightTag {
17 pub const FIELD: HighlightTag = HighlightTag("field");
18 pub const FUNCTION: HighlightTag = HighlightTag("function");
19 pub const MODULE: HighlightTag = HighlightTag("module");
20 pub const CONSTANT: HighlightTag = HighlightTag("constant");
21 pub const MACRO: HighlightTag = HighlightTag("macro");
22
23 pub const VARIABLE: HighlightTag = HighlightTag("variable");
24 pub const VARIABLE_MUT: HighlightTag = HighlightTag("variable.mut");
25
26 pub const TYPE: HighlightTag = HighlightTag("type");
27 pub const TYPE_BUILTIN: HighlightTag = HighlightTag("type.builtin");
28 pub const TYPE_SELF: HighlightTag = HighlightTag("type.self");
29 pub const TYPE_PARAM: HighlightTag = HighlightTag("type.param");
30 pub const TYPE_LIFETIME: HighlightTag = HighlightTag("type.lifetime");
31
32 pub const LITERAL_BYTE: HighlightTag = HighlightTag("literal.byte");
33 pub const LITERAL_NUMERIC: HighlightTag = HighlightTag("literal.numeric");
34 pub const LITERAL_CHAR: HighlightTag = HighlightTag("literal.char");
35
36 pub const LITERAL_COMMENT: HighlightTag = HighlightTag("comment");
37 pub const LITERAL_STRING: HighlightTag = HighlightTag("string");
38 pub const LITERAL_ATTRIBUTE: HighlightTag = HighlightTag("attribute");
39
40 pub const KEYWORD: HighlightTag = HighlightTag("keyword");
41 pub const KEYWORD_UNSAFE: HighlightTag = HighlightTag("keyword.unsafe");
42 pub const KEYWORD_CONTROL: HighlightTag = HighlightTag("keyword.control");
43}
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::{
9 WorkspaceEdit, 9 WorkspaceEdit,
10}; 10};
11use ra_ide::{ 11use ra_ide::{
12 tags, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition, 12 translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
13 FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex, 13 FileRange, FileSystemEdit, Fold, FoldKind, HighlightTag, InsertTextFormat, LineCol, LineIndex,
14 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit, 14 NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
15}; 15};
16use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 16use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -303,51 +303,51 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
303 } 303 }
304} 304}
305 305
306impl Conv for &'static str { 306impl Conv for HighlightTag {
307 type Output = (SemanticTokenType, Vec<SemanticTokenModifier>); 307 type Output = (SemanticTokenType, Vec<SemanticTokenModifier>);
308 308
309 fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) { 309 fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) {
310 let token_type: SemanticTokenType = match self { 310 let token_type: SemanticTokenType = match self {
311 tags::FIELD => SemanticTokenType::MEMBER, 311 HighlightTag::FIELD => SemanticTokenType::MEMBER,
312 tags::FUNCTION => SemanticTokenType::FUNCTION, 312 HighlightTag::FUNCTION => SemanticTokenType::FUNCTION,
313 tags::MODULE => SemanticTokenType::NAMESPACE, 313 HighlightTag::MODULE => SemanticTokenType::NAMESPACE,
314 tags::CONSTANT => { 314 HighlightTag::CONSTANT => {
315 return ( 315 return (
316 SemanticTokenType::VARIABLE, 316 SemanticTokenType::VARIABLE,
317 vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY], 317 vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY],
318 ) 318 )
319 } 319 }
320 tags::MACRO => SemanticTokenType::MACRO, 320 HighlightTag::MACRO => SemanticTokenType::MACRO,
321 321
322 tags::VARIABLE => { 322 HighlightTag::VARIABLE => {
323 return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY]) 323 return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY])
324 } 324 }
325 tags::VARIABLE_MUT => SemanticTokenType::VARIABLE, 325 HighlightTag::VARIABLE_MUT => SemanticTokenType::VARIABLE,
326 326
327 tags::TYPE => SemanticTokenType::TYPE, 327 HighlightTag::TYPE => SemanticTokenType::TYPE,
328 tags::TYPE_BUILTIN => SemanticTokenType::TYPE, 328 HighlightTag::TYPE_BUILTIN => SemanticTokenType::TYPE,
329 tags::TYPE_SELF => { 329 HighlightTag::TYPE_SELF => {
330 return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE]) 330 return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE])
331 } 331 }
332 tags::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER, 332 HighlightTag::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER,
333 tags::TYPE_LIFETIME => { 333 HighlightTag::TYPE_LIFETIME => {
334 return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE]) 334 return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE])
335 } 335 }
336 336
337 tags::LITERAL_BYTE => SemanticTokenType::NUMBER, 337 HighlightTag::LITERAL_BYTE => SemanticTokenType::NUMBER,
338 tags::LITERAL_NUMERIC => SemanticTokenType::NUMBER, 338 HighlightTag::LITERAL_NUMERIC => SemanticTokenType::NUMBER,
339 tags::LITERAL_CHAR => SemanticTokenType::NUMBER, 339 HighlightTag::LITERAL_CHAR => SemanticTokenType::NUMBER,
340 340
341 tags::LITERAL_COMMENT => { 341 HighlightTag::LITERAL_COMMENT => {
342 return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION]) 342 return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION])
343 } 343 }
344 344
345 tags::LITERAL_STRING => SemanticTokenType::STRING, 345 HighlightTag::LITERAL_STRING => SemanticTokenType::STRING,
346 tags::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD, 346 HighlightTag::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD,
347 347
348 tags::KEYWORD => SemanticTokenType::KEYWORD, 348 HighlightTag::KEYWORD => SemanticTokenType::KEYWORD,
349 tags::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD, 349 HighlightTag::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD,
350 tags::KEYWORD_CONTROL => SemanticTokenType::KEYWORD, 350 HighlightTag::KEYWORD_CONTROL => SemanticTokenType::KEYWORD,
351 unknown => panic!("Unknown semantic token: {}", unknown), 351 unknown => panic!("Unknown semantic token: {}", unknown),
352 }; 352 };
353 353
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<Vec<Decoration>>
954 .into_iter() 954 .into_iter()
955 .map(|h| Decoration { 955 .map(|h| Decoration {
956 range: h.range.conv_with(&line_index), 956 range: h.range.conv_with(&line_index),
957 tag: h.tag, 957 tag: h.tag.to_string(),
958 binding_hash: h.binding_hash.map(|x| x.to_string()), 958 binding_hash: h.binding_hash.map(|x| x.to_string()),
959 }) 959 })
960 .collect(); 960 .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 {
112#[serde(rename_all = "camelCase")] 112#[serde(rename_all = "camelCase")]
113pub struct Decoration { 113pub struct Decoration {
114 pub range: Range, 114 pub range: Range,
115 pub tag: &'static str, 115 pub tag: String,
116 pub binding_hash: Option<String>, 116 pub binding_hash: Option<String>,
117} 117}
118 118