Skip to content

Commit 9f0cfb7

Browse files
committed
Teach the server about Semantic Tokens proposed LSP
1 parent 558d263 commit 9f0cfb7

File tree

9 files changed

+239
-40
lines changed

9 files changed

+239
-40
lines changed

crates/ra_ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub use crate::{
7575
runnables::{Runnable, RunnableKind, TestId},
7676
source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
7777
ssr::SsrError,
78-
syntax_highlighting::HighlightedRange,
78+
syntax_highlighting::{tags, HighlightedRange},
7979
};
8080

8181
pub use hir::Documentation;

crates/ra_ide/src/syntax_highlighting.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,32 @@ use crate::{
1717
};
1818

1919
pub mod tags {
20-
pub(crate) const FIELD: &str = "field";
21-
pub(crate) const FUNCTION: &str = "function";
22-
pub(crate) const MODULE: &str = "module";
23-
pub(crate) const CONSTANT: &str = "constant";
24-
pub(crate) const MACRO: &str = "macro";
25-
26-
pub(crate) const VARIABLE: &str = "variable";
27-
pub(crate) const VARIABLE_MUT: &str = "variable.mut";
28-
29-
pub(crate) const TYPE: &str = "type";
30-
pub(crate) const TYPE_BUILTIN: &str = "type.builtin";
31-
pub(crate) const TYPE_SELF: &str = "type.self";
32-
pub(crate) const TYPE_PARAM: &str = "type.param";
33-
pub(crate) const TYPE_LIFETIME: &str = "type.lifetime";
34-
35-
pub(crate) const LITERAL_BYTE: &str = "literal.byte";
36-
pub(crate) const LITERAL_NUMERIC: &str = "literal.numeric";
37-
pub(crate) const LITERAL_CHAR: &str = "literal.char";
38-
39-
pub(crate) const LITERAL_COMMENT: &str = "comment";
40-
pub(crate) const LITERAL_STRING: &str = "string";
41-
pub(crate) const LITERAL_ATTRIBUTE: &str = "attribute";
42-
43-
pub(crate) const KEYWORD: &str = "keyword";
44-
pub(crate) const KEYWORD_UNSAFE: &str = "keyword.unsafe";
45-
pub(crate) const KEYWORD_CONTROL: &str = "keyword.control";
20+
pub const FIELD: &str = "field";
21+
pub const FUNCTION: &str = "function";
22+
pub const MODULE: &str = "module";
23+
pub const CONSTANT: &str = "constant";
24+
pub const MACRO: &str = "macro";
25+
26+
pub const VARIABLE: &str = "variable";
27+
pub const VARIABLE_MUT: &str = "variable.mut";
28+
29+
pub const TYPE: &str = "type";
30+
pub const TYPE_BUILTIN: &str = "type.builtin";
31+
pub const TYPE_SELF: &str = "type.self";
32+
pub const TYPE_PARAM: &str = "type.param";
33+
pub const TYPE_LIFETIME: &str = "type.lifetime";
34+
35+
pub const LITERAL_BYTE: &str = "literal.byte";
36+
pub const LITERAL_NUMERIC: &str = "literal.numeric";
37+
pub const LITERAL_CHAR: &str = "literal.char";
38+
39+
pub const LITERAL_COMMENT: &str = "comment";
40+
pub const LITERAL_STRING: &str = "string";
41+
pub const LITERAL_ATTRIBUTE: &str = "attribute";
42+
43+
pub const KEYWORD: &str = "keyword";
44+
pub const KEYWORD_UNSAFE: &str = "keyword.unsafe";
45+
pub const KEYWORD_CONTROL: &str = "keyword.control";
4646
}
4747

4848
#[derive(Debug)]

crates/rust-analyzer/src/caps.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
//! Advertizes the capabilities of the LSP Server.
22
3+
use crate::semantic_tokens;
4+
35
use lsp_types::{
46
CallHierarchyServerCapability, CodeActionProviderCapability, CodeLensOptions,
57
CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
68
ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions,
7-
SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8-
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
9-
TypeDefinitionProviderCapability, WorkDoneProgressOptions,
9+
SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend,
10+
SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities,
11+
SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind,
12+
TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions,
1013
};
1114

1215
pub fn server_capabilities() -> ServerCapabilities {
@@ -57,7 +60,20 @@ pub fn server_capabilities() -> ServerCapabilities {
5760
execute_command_provider: None,
5861
workspace: None,
5962
call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)),
60-
semantic_tokens_provider: None,
63+
semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions(
64+
SemanticTokensOptions {
65+
legend: SemanticTokensLegend {
66+
token_types: semantic_tokens::supported_token_types().iter().cloned().collect(),
67+
token_modifiers: semantic_tokens::supported_token_modifiers()
68+
.iter()
69+
.cloned()
70+
.collect(),
71+
},
72+
73+
document_provider: Some(SemanticTokensDocumentProvider::Bool(true)),
74+
..SemanticTokensOptions::default()
75+
},
76+
)),
6177
experimental: Default::default(),
6278
}
6379
}

crates/rust-analyzer/src/conv.rs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@
44
use lsp_types::{
55
self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation,
66
Location, LocationLink, MarkupContent, MarkupKind, Position, Range, RenameFile, ResourceOp,
7-
SymbolKind, TextDocumentEdit, TextDocumentIdentifier, TextDocumentItem,
8-
TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkspaceEdit,
7+
SemanticTokenModifier, SemanticTokenType, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
8+
TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
9+
WorkspaceEdit,
910
};
1011
use ra_ide::{
11-
translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
12+
tags, translate_offset_with_edit, CompletionItem, CompletionItemKind, FileId, FilePosition,
1213
FileRange, FileSystemEdit, Fold, FoldKind, InsertTextFormat, LineCol, LineIndex,
1314
NavigationTarget, RangeInfo, ReferenceAccess, Severity, SourceChange, SourceFileEdit,
1415
};
1516
use ra_syntax::{SyntaxKind, TextRange, TextUnit};
1617
use ra_text_edit::{AtomTextEdit, TextEdit};
1718
use ra_vfs::LineEndings;
1819

19-
use crate::{req, world::WorldSnapshot, Result};
20+
use crate::{req, semantic_tokens, world::WorldSnapshot, Result};
2021

2122
pub trait Conv {
2223
type Output;
@@ -302,6 +303,76 @@ impl ConvWith<&FoldConvCtx<'_>> for Fold {
302303
}
303304
}
304305

306+
impl Conv for &'static str {
307+
type Output = (SemanticTokenType, Vec<SemanticTokenModifier>);
308+
309+
fn conv(self) -> (SemanticTokenType, Vec<SemanticTokenModifier>) {
310+
let token_type: SemanticTokenType = match self {
311+
tags::FIELD => SemanticTokenType::MEMBER,
312+
tags::FUNCTION => SemanticTokenType::FUNCTION,
313+
tags::MODULE => SemanticTokenType::NAMESPACE,
314+
tags::CONSTANT => {
315+
return (
316+
SemanticTokenType::VARIABLE,
317+
vec![SemanticTokenModifier::STATIC, SemanticTokenModifier::READONLY],
318+
)
319+
}
320+
tags::MACRO => SemanticTokenType::MACRO,
321+
322+
tags::VARIABLE => {
323+
return (SemanticTokenType::VARIABLE, vec![SemanticTokenModifier::READONLY])
324+
}
325+
tags::VARIABLE_MUT => SemanticTokenType::VARIABLE,
326+
327+
tags::TYPE => SemanticTokenType::TYPE,
328+
tags::TYPE_BUILTIN => SemanticTokenType::TYPE,
329+
tags::TYPE_SELF => {
330+
return (SemanticTokenType::TYPE, vec![SemanticTokenModifier::REFERENCE])
331+
}
332+
tags::TYPE_PARAM => SemanticTokenType::TYPE_PARAMETER,
333+
tags::TYPE_LIFETIME => {
334+
return (SemanticTokenType::LABEL, vec![SemanticTokenModifier::REFERENCE])
335+
}
336+
337+
tags::LITERAL_BYTE => SemanticTokenType::NUMBER,
338+
tags::LITERAL_NUMERIC => SemanticTokenType::NUMBER,
339+
tags::LITERAL_CHAR => SemanticTokenType::NUMBER,
340+
341+
tags::LITERAL_COMMENT => {
342+
return (SemanticTokenType::COMMENT, vec![SemanticTokenModifier::DOCUMENTATION])
343+
}
344+
345+
tags::LITERAL_STRING => SemanticTokenType::STRING,
346+
tags::LITERAL_ATTRIBUTE => SemanticTokenType::KEYWORD,
347+
348+
tags::KEYWORD => SemanticTokenType::KEYWORD,
349+
tags::KEYWORD_UNSAFE => SemanticTokenType::KEYWORD,
350+
tags::KEYWORD_CONTROL => SemanticTokenType::KEYWORD,
351+
unknown => panic!("Unknown semantic token: {}", unknown),
352+
};
353+
354+
(token_type, vec![])
355+
}
356+
}
357+
358+
impl Conv for (SemanticTokenType, Vec<SemanticTokenModifier>) {
359+
type Output = (u32, u32);
360+
361+
fn conv(self) -> Self::Output {
362+
let token_index =
363+
semantic_tokens::supported_token_types().iter().position(|it| *it == self.0).unwrap();
364+
let mut token_modifier_bitset = 0;
365+
for modifier in self.1.iter() {
366+
token_modifier_bitset |= semantic_tokens::supported_token_modifiers()
367+
.iter()
368+
.position(|it| it == modifier)
369+
.unwrap();
370+
}
371+
372+
(token_index as u32, token_modifier_bitset as u32)
373+
}
374+
}
375+
305376
impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> {
306377
type Output = Option<T::Output>;
307378

crates/rust-analyzer/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub mod req;
3636
mod config;
3737
mod world;
3838
mod diagnostics;
39+
mod semantic_tokens;
3940

4041
use serde::de::DeserializeOwned;
4142

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ fn on_request(
528528
.on::<req::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)?
529529
.on::<req::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)?
530530
.on::<req::Ssr>(handlers::handle_ssr)?
531+
.on::<req::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
531532
.finish();
532533
Ok(())
533534
}

crates/rust-analyzer/src/main_loop/handlers.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ use lsp_types::{
1616
CodeAction, CodeActionOrCommand, CodeActionResponse, CodeLens, Command, CompletionItem,
1717
Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange,
1818
FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position,
19-
PrepareRenameResponse, Range, RenameParams, SymbolInformation, TextDocumentIdentifier,
20-
TextEdit, WorkspaceEdit,
19+
PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType,
20+
SemanticTokens, SemanticTokensParams, SemanticTokensResult, SymbolInformation,
21+
TextDocumentIdentifier, TextEdit, WorkspaceEdit,
2122
};
2223
use ra_ide::{
2324
AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind,
@@ -38,6 +39,7 @@ use crate::{
3839
diagnostics::DiagnosticTask,
3940
from_json,
4041
req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
42+
semantic_tokens::SemanticTokensBuilder,
4143
world::WorldSnapshot,
4244
LspError, Result,
4345
};
@@ -1068,3 +1070,25 @@ pub fn handle_call_hierarchy_outgoing(
10681070

10691071
Ok(Some(res))
10701072
}
1073+
1074+
pub fn handle_semantic_tokens(
1075+
world: WorldSnapshot,
1076+
params: SemanticTokensParams,
1077+
) -> Result<Option<SemanticTokensResult>> {
1078+
let _p = profile("handle_semantic_tokens");
1079+
1080+
let file_id = params.text_document.try_conv_with(&world)?;
1081+
let line_index = world.analysis().file_line_index(file_id)?;
1082+
1083+
let mut builder = SemanticTokensBuilder::default();
1084+
1085+
for h in world.analysis().highlight(file_id)?.into_iter() {
1086+
let type_and_modifiers: (SemanticTokenType, Vec<SemanticTokenModifier>) = h.tag.conv();
1087+
let (token_type, token_modifiers) = type_and_modifiers.conv();
1088+
builder.push(h.range.conv_with(&line_index), token_type, token_modifiers);
1089+
}
1090+
1091+
let tokens = SemanticTokens { data: builder.build(), ..Default::default() };
1092+
1093+
Ok(Some(tokens.into()))
1094+
}

crates/rust-analyzer/src/req.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ pub use lsp_types::{
1212
DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
1313
PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
1414
PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange,
15-
SelectionRangeParams, ServerCapabilities, ShowMessageParams, SignatureHelp, SymbolKind,
16-
TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkDoneProgressParams, WorkspaceEdit,
17-
WorkspaceSymbolParams,
15+
SelectionRangeParams, SemanticTokensParams, SemanticTokensResult, ServerCapabilities,
16+
ShowMessageParams, SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams,
17+
TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams,
1818
};
1919

2020
pub enum AnalyzerStatus {}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};
2+
3+
const SUPPORTED_TYPES: &[SemanticTokenType] = &[
4+
SemanticTokenType::COMMENT,
5+
SemanticTokenType::KEYWORD,
6+
SemanticTokenType::STRING,
7+
SemanticTokenType::NUMBER,
8+
SemanticTokenType::REGEXP,
9+
SemanticTokenType::OPERATOR,
10+
SemanticTokenType::NAMESPACE,
11+
SemanticTokenType::TYPE,
12+
SemanticTokenType::STRUCT,
13+
SemanticTokenType::CLASS,
14+
SemanticTokenType::INTERFACE,
15+
SemanticTokenType::ENUM,
16+
SemanticTokenType::TYPE_PARAMETER,
17+
SemanticTokenType::FUNCTION,
18+
SemanticTokenType::MEMBER,
19+
SemanticTokenType::PROPERTY,
20+
SemanticTokenType::MACRO,
21+
SemanticTokenType::VARIABLE,
22+
SemanticTokenType::PARAMETER,
23+
SemanticTokenType::LABEL,
24+
];
25+
26+
const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
27+
SemanticTokenModifier::DOCUMENTATION,
28+
SemanticTokenModifier::DECLARATION,
29+
SemanticTokenModifier::DEFINITION,
30+
SemanticTokenModifier::REFERENCE,
31+
SemanticTokenModifier::STATIC,
32+
SemanticTokenModifier::ABSTRACT,
33+
SemanticTokenModifier::DEPRECATED,
34+
SemanticTokenModifier::ASYNC,
35+
SemanticTokenModifier::VOLATILE,
36+
SemanticTokenModifier::READONLY,
37+
];
38+
39+
pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] {
40+
SUPPORTED_TYPES
41+
}
42+
43+
pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] {
44+
SUPPORTED_MODIFIERS
45+
}
46+
47+
#[derive(Default)]
48+
pub(crate) struct SemanticTokensBuilder {
49+
prev_line: u32,
50+
prev_char: u32,
51+
data: Vec<SemanticToken>,
52+
}
53+
54+
impl SemanticTokensBuilder {
55+
pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
56+
let mut push_line = range.start.line as u32;
57+
let mut push_char = range.start.character as u32;
58+
59+
if !self.data.is_empty() {
60+
push_line -= self.prev_line;
61+
if push_line == 0 {
62+
push_char -= self.prev_char;
63+
}
64+
}
65+
66+
// A token cannot be multiline
67+
let token_len = range.end.character - range.start.character;
68+
69+
let token = SemanticToken {
70+
delta_line: push_line,
71+
delta_start: push_char,
72+
length: token_len as u32,
73+
token_type: token_index,
74+
token_modifiers_bitset: modifier_bitset,
75+
};
76+
77+
self.data.push(token);
78+
79+
self.prev_line = range.start.line as u32;
80+
self.prev_char = range.start.character as u32;
81+
}
82+
83+
pub fn build(self) -> Vec<SemanticToken> {
84+
self.data
85+
}
86+
}

0 commit comments

Comments
 (0)