diff options
Diffstat (limited to 'cli/lsp/semantic_tokens.rs')
-rw-r--r-- | cli/lsp/semantic_tokens.rs | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/cli/lsp/semantic_tokens.rs b/cli/lsp/semantic_tokens.rs index a2a56cce0..0cf154d0f 100644 --- a/cli/lsp/semantic_tokens.rs +++ b/cli/lsp/semantic_tokens.rs @@ -7,6 +7,7 @@ use std::ops::Index; use std::ops::IndexMut; +use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::SemanticToken; use tower_lsp::lsp_types::SemanticTokenModifier; use tower_lsp::lsp_types::SemanticTokenType; @@ -247,6 +248,54 @@ impl SemanticTokensBuilder { } } +pub fn tokens_within_range( + tokens: &SemanticTokens, + range: lsp::Range, +) -> SemanticTokens { + let mut line = 0; + let mut character = 0; + + let mut first_token_line = 0; + let mut first_token_char = 0; + let mut keep_start_idx = tokens.data.len(); + let mut keep_end_idx = keep_start_idx; + for (i, token) in tokens.data.iter().enumerate() { + if token.delta_line != 0 { + character = 0; + } + line += token.delta_line; + character += token.delta_start; + let token_start = lsp::Position::new(line, character); + if i < keep_start_idx && token_start >= range.start { + keep_start_idx = i; + first_token_line = line; + first_token_char = character; + } + if token_start > range.end { + keep_end_idx = i; + break; + } + } + if keep_end_idx == keep_start_idx { + return SemanticTokens { + result_id: None, + data: Vec::new(), + }; + } + + let mut data = tokens.data[keep_start_idx..keep_end_idx].to_vec(); + // we need to adjust the delta_line and delta_start on the first token + // as it is relative to 0 now, not the previous token + let first_token = &mut data[0]; + first_token.delta_line = first_token_line; + first_token.delta_start = first_token_char; + + SemanticTokens { + result_id: None, + data, + } +} + #[cfg(test)] mod tests { use super::*; @@ -352,4 +401,129 @@ mod tests { ] ); } + + #[test] + fn test_tokens_within_range() { + let mut builder = SemanticTokensBuilder::new(); + builder.push(1, 0, 5, 0, 0); + builder.push(2, 1, 1, 1, 0); + builder.push(2, 2, 3, 2, 0); + builder.push(2, 5, 5, 3, 0); + builder.push(3, 0, 4, 4, 0); + builder.push(5, 2, 3, 5, 0); + let tokens = builder.build(None); + let range = lsp::Range { + start: lsp::Position { + line: 2, + character: 2, + }, + end: lsp::Position { + line: 4, + character: 0, + }, + }; + + let result = tokens_within_range(&tokens, range); + + assert_eq!( + result.data, + vec![ + // line 2 char 2 + SemanticToken { + delta_line: 2, + delta_start: 2, + length: 3, + token_type: 2, + token_modifiers_bitset: 0 + }, + // line 2 char 5 + SemanticToken { + delta_line: 0, + delta_start: 3, + length: 5, + token_type: 3, + token_modifiers_bitset: 0 + }, + // line 3 char 0 + SemanticToken { + delta_line: 1, + delta_start: 0, + length: 4, + token_type: 4, + token_modifiers_bitset: 0 + } + ] + ); + } + + #[test] + fn test_tokens_within_range_include_end() { + let mut builder = SemanticTokensBuilder::new(); + builder.push(1, 0, 1, 0, 0); + builder.push(2, 1, 2, 1, 0); + builder.push(2, 3, 3, 2, 0); + builder.push(3, 0, 4, 3, 0); + let tokens = builder.build(None); + let range = lsp::Range { + start: lsp::Position { + line: 2, + character: 2, + }, + end: lsp::Position { + line: 3, + character: 4, + }, + }; + let result = tokens_within_range(&tokens, range); + + assert_eq!( + result.data, + vec![ + // line 2 char 3 + SemanticToken { + delta_line: 2, + delta_start: 3, + length: 3, + token_type: 2, + token_modifiers_bitset: 0 + }, + // line 3 char 0 + SemanticToken { + delta_line: 1, + delta_start: 0, + length: 4, + token_type: 3, + token_modifiers_bitset: 0 + } + ] + ); + } + + #[test] + fn test_tokens_within_range_empty() { + let mut builder = SemanticTokensBuilder::new(); + builder.push(1, 0, 1, 0, 0); + builder.push(2, 1, 2, 1, 0); + builder.push(2, 3, 3, 2, 0); + builder.push(3, 0, 4, 3, 0); + let tokens = builder.build(None); + let range = lsp::Range { + start: lsp::Position { + line: 3, + character: 2, + }, + end: lsp::Position { + line: 3, + character: 4, + }, + }; + let result = tokens_within_range(&tokens, range); + + assert_eq!(result.data, vec![]); + + assert_eq!( + tokens_within_range(&SemanticTokens::default(), range).data, + vec![] + ); + } } |