2020-02-24 21:17:20 -05:00
//! Semantic Tokens helpers
2020-02-26 19:39:32 +01:00
use std ::ops ;
2020-07-24 17:55:17 -04:00
use lsp_types ::{
Range , SemanticToken , SemanticTokenModifier , SemanticTokenType , SemanticTokens ,
SemanticTokensEdit ,
} ;
2020-02-14 17:56:28 -05:00
2020-04-28 17:14:05 +02:00
macro_rules ! define_semantic_token_types {
( $( ( $ident :ident , $string :literal ) ) , * $(, ) ? ) = > {
$( pub ( crate ) const $ident : SemanticTokenType = SemanticTokenType ::new ( $string ) ; ) *
2020-02-26 19:39:32 +01:00
2020-04-28 17:14:05 +02:00
pub ( crate ) const SUPPORTED_TYPES : & [ SemanticTokenType ] = & [
SemanticTokenType ::COMMENT ,
SemanticTokenType ::KEYWORD ,
SemanticTokenType ::STRING ,
SemanticTokenType ::NUMBER ,
SemanticTokenType ::REGEXP ,
SemanticTokenType ::OPERATOR ,
SemanticTokenType ::NAMESPACE ,
SemanticTokenType ::TYPE ,
SemanticTokenType ::STRUCT ,
SemanticTokenType ::CLASS ,
SemanticTokenType ::INTERFACE ,
SemanticTokenType ::ENUM ,
2020-09-01 13:10:23 -04:00
SemanticTokenType ::ENUM_MEMBER ,
2020-04-28 17:14:05 +02:00
SemanticTokenType ::TYPE_PARAMETER ,
SemanticTokenType ::FUNCTION ,
2020-12-01 08:33:21 -05:00
SemanticTokenType ::METHOD ,
2020-04-28 17:14:05 +02:00
SemanticTokenType ::PROPERTY ,
SemanticTokenType ::MACRO ,
SemanticTokenType ::VARIABLE ,
SemanticTokenType ::PARAMETER ,
$( $ident ) , *
] ;
} ;
}
2020-02-26 19:39:32 +01:00
2020-04-28 17:14:05 +02:00
define_semantic_token_types! [
2021-01-10 13:33:03 +01:00
( ANGLE , " angle " ) ,
2020-04-28 17:14:05 +02:00
( ATTRIBUTE , " attribute " ) ,
2020-05-21 17:40:52 +01:00
( BOOLEAN , " boolean " ) ,
2021-01-10 13:33:03 +01:00
( BRACE , " brace " ) ,
( BRACKET , " bracket " ) ,
2020-04-28 17:14:05 +02:00
( BUILTIN_TYPE , " builtinType " ) ,
2021-02-05 23:46:39 +00:00
( CHAR_LITERAL , " characterLiteral " ) ,
2021-01-10 13:33:03 +01:00
( COMMA , " comma " ) ,
( COLON , " colon " ) ,
( DOT , " dot " ) ,
2020-06-22 10:28:07 -04:00
( ESCAPE_SEQUENCE , " escapeSequence " ) ,
( FORMAT_SPECIFIER , " formatSpecifier " ) ,
2020-06-23 09:17:53 -04:00
( GENERIC , " generic " ) ,
2021-01-01 10:07:01 +01:00
( CONST_PARAMETER , " constParameter " ) ,
2020-04-28 17:14:05 +02:00
( LIFETIME , " lifetime " ) ,
2020-12-23 17:15:01 +01:00
( LABEL , " label " ) ,
2021-01-10 13:33:03 +01:00
( PARENTHESIS , " parenthesis " ) ,
2020-07-15 16:34:11 +02:00
( PUNCTUATION , " punctuation " ) ,
2020-05-25 11:51:56 +03:00
( SELF_KEYWORD , " selfKeyword " ) ,
2021-01-10 13:33:03 +01:00
( SEMICOLON , " semicolon " ) ,
2020-04-28 17:14:05 +02:00
( TYPE_ALIAS , " typeAlias " ) ,
( UNION , " union " ) ,
( UNRESOLVED_REFERENCE , " unresolvedReference " ) ,
2020-02-14 17:56:28 -05:00
] ;
2020-04-28 17:14:05 +02:00
macro_rules ! define_semantic_token_modifiers {
( $( ( $ident :ident , $string :literal ) ) , * $(, ) ? ) = > {
$( pub ( crate ) const $ident : SemanticTokenModifier = SemanticTokenModifier ::new ( $string ) ; ) *
pub ( crate ) const SUPPORTED_MODIFIERS : & [ SemanticTokenModifier ] = & [
SemanticTokenModifier ::DOCUMENTATION ,
SemanticTokenModifier ::DECLARATION ,
SemanticTokenModifier ::DEFINITION ,
SemanticTokenModifier ::STATIC ,
SemanticTokenModifier ::ABSTRACT ,
SemanticTokenModifier ::DEPRECATED ,
SemanticTokenModifier ::READONLY ,
$( $ident ) , *
] ;
} ;
}
define_semantic_token_modifiers! [
( CONSTANT , " constant " ) ,
( CONTROL_FLOW , " controlFlow " ) ,
2020-06-18 09:37:22 -04:00
( INJECTED , " injected " ) ,
2020-04-28 17:14:05 +02:00
( MUTABLE , " mutable " ) ,
2020-07-30 10:20:06 -04:00
( CONSUMING , " consuming " ) ,
2020-04-28 17:14:05 +02:00
( UNSAFE , " unsafe " ) ,
2020-05-12 21:58:51 -07:00
( ATTRIBUTE_MODIFIER , " attribute " ) ,
2020-10-16 06:38:32 -03:00
( CALLABLE , " callable " ) ,
2020-02-14 17:56:28 -05:00
] ;
2020-02-26 19:39:32 +01:00
#[ derive(Default) ]
pub ( crate ) struct ModifierSet ( pub ( crate ) u32 ) ;
2020-02-14 17:56:28 -05:00
2020-02-26 19:39:32 +01:00
impl ops ::BitOrAssign < SemanticTokenModifier > for ModifierSet {
fn bitor_assign ( & mut self , rhs : SemanticTokenModifier ) {
let idx = SUPPORTED_MODIFIERS . iter ( ) . position ( | it | it = = & rhs ) . unwrap ( ) ;
self . 0 | = 1 < < idx ;
}
2020-02-14 17:56:28 -05:00
}
2020-02-24 21:17:20 -05:00
/// Tokens are encoded relative to each other.
2020-02-25 12:42:44 +01:00
///
2020-02-24 21:17:20 -05:00
/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45
2020-02-14 17:56:28 -05:00
pub ( crate ) struct SemanticTokensBuilder {
2020-07-24 17:55:17 -04:00
id : String ,
2020-02-14 17:56:28 -05:00
prev_line : u32 ,
prev_char : u32 ,
data : Vec < SemanticToken > ,
}
impl SemanticTokensBuilder {
2020-11-02 16:31:38 +01:00
pub ( crate ) fn new ( id : String ) -> Self {
2020-07-24 17:55:17 -04:00
SemanticTokensBuilder { id , prev_line : 0 , prev_char : 0 , data : Default ::default ( ) }
}
2020-02-24 21:17:20 -05:00
/// Push a new token onto the builder
2020-11-02 16:31:38 +01:00
pub ( crate ) fn push ( & mut self , range : Range , token_index : u32 , modifier_bitset : u32 ) {
2020-02-14 17:56:28 -05:00
let mut push_line = range . start . line as u32 ;
let mut push_char = range . start . character as u32 ;
if ! self . data . is_empty ( ) {
push_line - = self . prev_line ;
if push_line = = 0 {
push_char - = self . prev_char ;
}
}
// A token cannot be multiline
let token_len = range . end . character - range . start . character ;
let token = SemanticToken {
delta_line : push_line ,
delta_start : push_char ,
length : token_len as u32 ,
token_type : token_index ,
token_modifiers_bitset : modifier_bitset ,
} ;
self . data . push ( token ) ;
self . prev_line = range . start . line as u32 ;
self . prev_char = range . start . character as u32 ;
}
2020-11-02 16:31:38 +01:00
pub ( crate ) fn build ( self ) -> SemanticTokens {
2020-07-24 17:55:17 -04:00
SemanticTokens { result_id : Some ( self . id ) , data : self . data }
}
}
2020-11-02 16:31:38 +01:00
pub ( crate ) fn diff_tokens ( old : & [ SemanticToken ] , new : & [ SemanticToken ] ) -> Vec < SemanticTokensEdit > {
2020-07-24 17:55:17 -04:00
let offset = new . iter ( ) . zip ( old . iter ( ) ) . take_while ( | & ( n , p ) | n = = p ) . count ( ) ;
let ( _ , old ) = old . split_at ( offset ) ;
let ( _ , new ) = new . split_at ( offset ) ;
let offset_from_end =
new . iter ( ) . rev ( ) . zip ( old . iter ( ) . rev ( ) ) . take_while ( | & ( n , p ) | n = = p ) . count ( ) ;
let ( old , _ ) = old . split_at ( old . len ( ) - offset_from_end ) ;
let ( new , _ ) = new . split_at ( new . len ( ) - offset_from_end ) ;
if old . is_empty ( ) & & new . is_empty ( ) {
vec! [ ]
} else {
// The lsp data field is actually a byte-diff but we
// travel in tokens so `start` and `delete_count` are in multiples of the
// serialized size of `SemanticToken`.
vec! [ SemanticTokensEdit {
start : 5 * offset as u32 ,
delete_count : 5 * old . len ( ) as u32 ,
data : Some ( new . into ( ) ) ,
} ]
2020-02-14 17:56:28 -05:00
}
}
2020-02-26 19:39:32 +01:00
2020-11-02 16:31:38 +01:00
pub ( crate ) fn type_index ( type_ : SemanticTokenType ) -> u32 {
2020-02-26 19:39:32 +01:00
SUPPORTED_TYPES . iter ( ) . position ( | it | * it = = type_ ) . unwrap ( ) as u32
}
2020-07-24 17:55:17 -04:00
#[ cfg(test) ]
mod tests {
use super ::* ;
fn from ( t : ( u32 , u32 , u32 , u32 , u32 ) ) -> SemanticToken {
SemanticToken {
delta_line : t . 0 ,
delta_start : t . 1 ,
length : t . 2 ,
token_type : t . 3 ,
token_modifiers_bitset : t . 4 ,
}
}
#[ test ]
fn test_diff_insert_at_end ( ) {
let before = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let after = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) , from ( ( 11 , 12 , 13 , 14 , 15 ) ) ] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! (
edits [ 0 ] ,
SemanticTokensEdit {
start : 10 ,
delete_count : 0 ,
data : Some ( vec! [ from ( ( 11 , 12 , 13 , 14 , 15 ) ) ] )
}
) ;
}
#[ test ]
fn test_diff_insert_at_beginning ( ) {
let before = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let after = [ from ( ( 11 , 12 , 13 , 14 , 15 ) ) , from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! (
edits [ 0 ] ,
SemanticTokensEdit {
start : 0 ,
delete_count : 0 ,
data : Some ( vec! [ from ( ( 11 , 12 , 13 , 14 , 15 ) ) ] )
}
) ;
}
#[ test ]
fn test_diff_insert_in_middle ( ) {
let before = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let after = [
from ( ( 1 , 2 , 3 , 4 , 5 ) ) ,
from ( ( 10 , 20 , 30 , 40 , 50 ) ) ,
from ( ( 60 , 70 , 80 , 90 , 100 ) ) ,
from ( ( 6 , 7 , 8 , 9 , 10 ) ) ,
] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! (
edits [ 0 ] ,
SemanticTokensEdit {
start : 5 ,
delete_count : 0 ,
data : Some ( vec! [ from ( ( 10 , 20 , 30 , 40 , 50 ) ) , from ( ( 60 , 70 , 80 , 90 , 100 ) ) ] )
}
) ;
}
#[ test ]
fn test_diff_remove_from_end ( ) {
let before = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) , from ( ( 11 , 12 , 13 , 14 , 15 ) ) ] ;
let after = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! ( edits [ 0 ] , SemanticTokensEdit { start : 10 , delete_count : 5 , data : Some ( vec! [ ] ) } ) ;
}
#[ test ]
fn test_diff_remove_from_beginning ( ) {
let before = [ from ( ( 11 , 12 , 13 , 14 , 15 ) ) , from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let after = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! ( edits [ 0 ] , SemanticTokensEdit { start : 0 , delete_count : 5 , data : Some ( vec! [ ] ) } ) ;
}
#[ test ]
fn test_diff_remove_from_middle ( ) {
let before = [
from ( ( 1 , 2 , 3 , 4 , 5 ) ) ,
from ( ( 10 , 20 , 30 , 40 , 50 ) ) ,
from ( ( 60 , 70 , 80 , 90 , 100 ) ) ,
from ( ( 6 , 7 , 8 , 9 , 10 ) ) ,
] ;
let after = [ from ( ( 1 , 2 , 3 , 4 , 5 ) ) , from ( ( 6 , 7 , 8 , 9 , 10 ) ) ] ;
let edits = diff_tokens ( & before , & after ) ;
assert_eq! ( edits [ 0 ] , SemanticTokensEdit { start : 5 , delete_count : 10 , data : Some ( vec! [ ] ) } ) ;
}
}