aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkjeremy <[email protected]>2020-07-24 22:55:17 +0100
committerJeremy Kolb <[email protected]>2020-08-01 01:57:53 +0100
commitfcfd7cb1e38bc20240e466dbda64386e0cc89247 (patch)
treef0e4b61091d5bf165b3321644c7a9bc3a5547439
parent2346a28c638dc8fe945059b68126d268dd7fb690 (diff)
Handle semantic token deltas
-rw-r--r--crates/rust-analyzer/src/caps.rs4
-rw-r--r--crates/rust-analyzer/src/document.rs6
-rw-r--r--crates/rust-analyzer/src/global_state.rs11
-rw-r--r--crates/rust-analyzer/src/handlers.rs44
-rw-r--r--crates/rust-analyzer/src/main_loop.rs5
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs139
-rw-r--r--crates/rust-analyzer/src/to_proto.rs17
7 files changed, 213 insertions, 13 deletions
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 37d695448..92a743fd8 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -76,7 +76,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
76 token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(), 76 token_modifiers: semantic_tokens::SUPPORTED_MODIFIERS.to_vec(),
77 }, 77 },
78 78
79 document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), 79 document_provider: Some(SemanticTokensDocumentProvider::Edits {
80 edits: Some(true),
81 }),
80 range_provider: Some(true), 82 range_provider: Some(true),
81 work_done_progress_options: Default::default(), 83 work_done_progress_options: Default::default(),
82 } 84 }
diff --git a/crates/rust-analyzer/src/document.rs b/crates/rust-analyzer/src/document.rs
index 43219e633..e882c9865 100644
--- a/crates/rust-analyzer/src/document.rs
+++ b/crates/rust-analyzer/src/document.rs
@@ -1,9 +1,9 @@
1//! In-memory document information. 1//! In-memory document information.
2 2
3/// Information about a document that the Language Client 3/// Information about a document that the Language Client
4// knows about. 4/// knows about.
5// Its lifetime is driven by the textDocument/didOpen and textDocument/didClose 5/// Its lifetime is driven by the textDocument/didOpen and textDocument/didClose
6// client notifications. 6/// client notifications.
7#[derive(Debug, Clone)] 7#[derive(Debug, Clone)]
8pub(crate) struct DocumentData { 8pub(crate) struct DocumentData {
9 pub version: Option<i64>, 9 pub version: Option<i64>,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index b2d65a6d1..4b34c3ec5 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -3,11 +3,14 @@
3//! 3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. 4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
5 5
6use std::{sync::Arc, time::Instant}; 6use std::{
7 sync::{Arc, Mutex},
8 time::Instant,
9};
7 10
8use crossbeam_channel::{unbounded, Receiver, Sender}; 11use crossbeam_channel::{unbounded, Receiver, Sender};
9use flycheck::FlycheckHandle; 12use flycheck::FlycheckHandle;
10use lsp_types::Url; 13use lsp_types::{SemanticTokens, Url};
11use parking_lot::RwLock; 14use parking_lot::RwLock;
12use ra_db::{CrateId, VfsPath}; 15use ra_db::{CrateId, VfsPath};
13use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; 16use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId};
@@ -71,6 +74,7 @@ pub(crate) struct GlobalState {
71 pub(crate) analysis_host: AnalysisHost, 74 pub(crate) analysis_host: AnalysisHost,
72 pub(crate) diagnostics: DiagnosticCollection, 75 pub(crate) diagnostics: DiagnosticCollection,
73 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>, 76 pub(crate) mem_docs: FxHashMap<VfsPath, DocumentData>,
77 pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
74 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 78 pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
75 pub(crate) status: Status, 79 pub(crate) status: Status,
76 pub(crate) source_root_config: SourceRootConfig, 80 pub(crate) source_root_config: SourceRootConfig,
@@ -86,6 +90,7 @@ pub(crate) struct GlobalStateSnapshot {
86 pub(crate) check_fixes: CheckFixes, 90 pub(crate) check_fixes: CheckFixes,
87 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>, 91 pub(crate) latest_requests: Arc<RwLock<LatestRequests>>,
88 mem_docs: FxHashMap<VfsPath, DocumentData>, 92 mem_docs: FxHashMap<VfsPath, DocumentData>,
93 pub semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
89 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, 94 vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>,
90 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, 95 pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
91} 96}
@@ -120,6 +125,7 @@ impl GlobalState {
120 analysis_host, 125 analysis_host,
121 diagnostics: Default::default(), 126 diagnostics: Default::default(),
122 mem_docs: FxHashMap::default(), 127 mem_docs: FxHashMap::default(),
128 semantic_tokens_cache: Arc::new(Default::default()),
123 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), 129 vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))),
124 status: Status::default(), 130 status: Status::default(),
125 source_root_config: SourceRootConfig::default(), 131 source_root_config: SourceRootConfig::default(),
@@ -186,6 +192,7 @@ impl GlobalState {
186 latest_requests: Arc::clone(&self.latest_requests), 192 latest_requests: Arc::clone(&self.latest_requests),
187 check_fixes: Arc::clone(&self.diagnostics.check_fixes), 193 check_fixes: Arc::clone(&self.diagnostics.check_fixes),
188 mem_docs: self.mem_docs.clone(), 194 mem_docs: self.mem_docs.clone(),
195 semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
189 } 196 }
190 } 197 }
191 198
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index e73b3a211..0b0ea23fd 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -13,9 +13,10 @@ use lsp_types::{
13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, 13 CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
14 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams, 14 CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DocumentFormattingParams,
15 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location, 15 DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, HoverContents, Location,
16 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, 16 Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensEditResult,
17 SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, 17 SemanticTokensEditsParams, SemanticTokensParams, SemanticTokensRangeParams,
18 SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit, 18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, SymbolTag,
19 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 20};
20use ra_ide::{ 21use ra_ide::{
21 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query, 22 FileId, FilePosition, FileRange, HoverAction, HoverGotoTypeData, NavigationTarget, Query,
@@ -1184,6 +1185,43 @@ pub(crate) fn handle_semantic_tokens(
1184 1185
1185 let highlights = snap.analysis.highlight(file_id)?; 1186 let highlights = snap.analysis.highlight(file_id)?;
1186 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1187 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1188
1189 // Unconditionally cache the tokens
1190 snap.semantic_tokens_cache
1191 .lock()
1192 .unwrap()
1193 .insert(params.text_document.uri, semantic_tokens.clone());
1194
1195 Ok(Some(semantic_tokens.into()))
1196}
1197
1198pub(crate) fn handle_semantic_tokens_edits(
1199 snap: GlobalStateSnapshot,
1200 params: SemanticTokensEditsParams,
1201) -> Result<Option<SemanticTokensEditResult>> {
1202 let _p = profile("handle_semantic_tokens_edits");
1203
1204 let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
1205 let text = snap.analysis.file_text(file_id)?;
1206 let line_index = snap.analysis.file_line_index(file_id)?;
1207
1208 let highlights = snap.analysis.highlight(file_id)?;
1209
1210 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1211
1212 let mut cache = snap.semantic_tokens_cache.lock().unwrap();
1213 let cached_tokens = cache.entry(params.text_document.uri).or_default();
1214
1215 if let Some(prev_id) = &cached_tokens.result_id {
1216 if *prev_id == params.previous_result_id {
1217 let edits = to_proto::semantic_token_edits(&cached_tokens, &semantic_tokens);
1218 *cached_tokens = semantic_tokens;
1219 return Ok(Some(edits.into()));
1220 }
1221 }
1222
1223 *cached_tokens = semantic_tokens.clone();
1224
1187 Ok(Some(semantic_tokens.into())) 1225 Ok(Some(semantic_tokens.into()))
1188} 1226}
1189 1227
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 0ace4cb45..eb2a86972 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -387,6 +387,9 @@ impl GlobalState {
387 handlers::handle_call_hierarchy_outgoing, 387 handlers::handle_call_hierarchy_outgoing,
388 )? 388 )?
389 .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)? 389 .on::<lsp_types::request::SemanticTokensRequest>(handlers::handle_semantic_tokens)?
390 .on::<lsp_types::request::SemanticTokensEditsRequest>(
391 handlers::handle_semantic_tokens_edits,
392 )?
390 .on::<lsp_types::request::SemanticTokensRangeRequest>( 393 .on::<lsp_types::request::SemanticTokensRangeRequest>(
391 handlers::handle_semantic_tokens_range, 394 handlers::handle_semantic_tokens_range,
392 )? 395 )?
@@ -449,6 +452,8 @@ impl GlobalState {
449 None => log::error!("orphan DidCloseTextDocument: {}", path), 452 None => log::error!("orphan DidCloseTextDocument: {}", path),
450 } 453 }
451 454
455 this.semantic_tokens_cache.lock().unwrap().remove(&params.text_document.uri);
456
452 if let Some(path) = path.as_path() { 457 if let Some(path) = path.as_path() {
453 this.loader.handle.invalidate(path.to_path_buf()); 458 this.loader.handle.invalidate(path.to_path_buf());
454 } 459 }
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 576bd8adc..afc38fb4e 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -2,7 +2,10 @@
2 2
3use std::ops; 3use std::ops;
4 4
5use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; 5use lsp_types::{
6 Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
7 SemanticTokensEdit,
8};
6 9
7macro_rules! define_semantic_token_types { 10macro_rules! define_semantic_token_types {
8 ($(($ident:ident, $string:literal)),*$(,)?) => { 11 ($(($ident:ident, $string:literal)),*$(,)?) => {
@@ -89,14 +92,18 @@ impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
89/// Tokens are encoded relative to each other. 92/// Tokens are encoded relative to each other.
90/// 93///
91/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45 94/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45
92#[derive(Default)]
93pub(crate) struct SemanticTokensBuilder { 95pub(crate) struct SemanticTokensBuilder {
96 id: String,
94 prev_line: u32, 97 prev_line: u32,
95 prev_char: u32, 98 prev_char: u32,
96 data: Vec<SemanticToken>, 99 data: Vec<SemanticToken>,
97} 100}
98 101
99impl SemanticTokensBuilder { 102impl SemanticTokensBuilder {
103 pub fn new(id: String) -> Self {
104 SemanticTokensBuilder { id, prev_line: 0, prev_char: 0, data: Default::default() }
105 }
106
100 /// Push a new token onto the builder 107 /// Push a new token onto the builder
101 pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) { 108 pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
102 let mut push_line = range.start.line as u32; 109 let mut push_line = range.start.line as u32;
@@ -127,10 +134,136 @@ impl SemanticTokensBuilder {
127 } 134 }
128 135
129 pub fn build(self) -> SemanticTokens { 136 pub fn build(self) -> SemanticTokens {
130 SemanticTokens { result_id: None, data: self.data } 137 SemanticTokens { result_id: Some(self.id), data: self.data }
138 }
139}
140
141pub fn diff_tokens(old: &[SemanticToken], new: &[SemanticToken]) -> Vec<SemanticTokensEdit> {
142 let offset = new.iter().zip(old.iter()).take_while(|&(n, p)| n == p).count();
143
144 let (_, old) = old.split_at(offset);
145 let (_, new) = new.split_at(offset);
146
147 let offset_from_end =
148 new.iter().rev().zip(old.iter().rev()).take_while(|&(n, p)| n == p).count();
149
150 let (old, _) = old.split_at(old.len() - offset_from_end);
151 let (new, _) = new.split_at(new.len() - offset_from_end);
152
153 if old.is_empty() && new.is_empty() {
154 vec![]
155 } else {
156 // The lsp data field is actually a byte-diff but we
157 // travel in tokens so `start` and `delete_count` are in multiples of the
158 // serialized size of `SemanticToken`.
159 vec![SemanticTokensEdit {
160 start: 5 * offset as u32,
161 delete_count: 5 * old.len() as u32,
162 data: Some(new.into()),
163 }]
131 } 164 }
132} 165}
133 166
134pub fn type_index(type_: SemanticTokenType) -> u32 { 167pub fn type_index(type_: SemanticTokenType) -> u32 {
135 SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32 168 SUPPORTED_TYPES.iter().position(|it| *it == type_).unwrap() as u32
136} 169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn from(t: (u32, u32, u32, u32, u32)) -> SemanticToken {
176 SemanticToken {
177 delta_line: t.0,
178 delta_start: t.1,
179 length: t.2,
180 token_type: t.3,
181 token_modifiers_bitset: t.4,
182 }
183 }
184
185 #[test]
186 fn test_diff_insert_at_end() {
187 let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
188 let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
189
190 let edits = diff_tokens(&before, &after);
191 assert_eq!(
192 edits[0],
193 SemanticTokensEdit {
194 start: 10,
195 delete_count: 0,
196 data: Some(vec![from((11, 12, 13, 14, 15))])
197 }
198 );
199 }
200
201 #[test]
202 fn test_diff_insert_at_beginning() {
203 let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
204 let after = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
205
206 let edits = diff_tokens(&before, &after);
207 assert_eq!(
208 edits[0],
209 SemanticTokensEdit {
210 start: 0,
211 delete_count: 0,
212 data: Some(vec![from((11, 12, 13, 14, 15))])
213 }
214 );
215 }
216
217 #[test]
218 fn test_diff_insert_in_middle() {
219 let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
220 let after = [
221 from((1, 2, 3, 4, 5)),
222 from((10, 20, 30, 40, 50)),
223 from((60, 70, 80, 90, 100)),
224 from((6, 7, 8, 9, 10)),
225 ];
226
227 let edits = diff_tokens(&before, &after);
228 assert_eq!(
229 edits[0],
230 SemanticTokensEdit {
231 start: 5,
232 delete_count: 0,
233 data: Some(vec![from((10, 20, 30, 40, 50)), from((60, 70, 80, 90, 100))])
234 }
235 );
236 }
237
238 #[test]
239 fn test_diff_remove_from_end() {
240 let before = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10)), from((11, 12, 13, 14, 15))];
241 let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
242
243 let edits = diff_tokens(&before, &after);
244 assert_eq!(edits[0], SemanticTokensEdit { start: 10, delete_count: 5, data: Some(vec![]) });
245 }
246
247 #[test]
248 fn test_diff_remove_from_beginning() {
249 let before = [from((11, 12, 13, 14, 15)), from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
250 let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
251
252 let edits = diff_tokens(&before, &after);
253 assert_eq!(edits[0], SemanticTokensEdit { start: 0, delete_count: 5, data: Some(vec![]) });
254 }
255
256 #[test]
257 fn test_diff_remove_from_middle() {
258 let before = [
259 from((1, 2, 3, 4, 5)),
260 from((10, 20, 30, 40, 50)),
261 from((60, 70, 80, 90, 100)),
262 from((6, 7, 8, 9, 10)),
263 ];
264 let after = [from((1, 2, 3, 4, 5)), from((6, 7, 8, 9, 10))];
265
266 let edits = diff_tokens(&before, &after);
267 assert_eq!(edits[0], SemanticTokensEdit { start: 5, delete_count: 10, data: Some(vec![]) });
268 }
269}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index fadcc5853..8da883ae4 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,5 +1,6 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use std::path::{self, Path}; 2use std::path::{self, Path};
3use std::time::SystemTime;
3 4
4use itertools::Itertools; 5use itertools::Itertools;
5use ra_db::{FileId, FileRange}; 6use ra_db::{FileId, FileRange};
@@ -308,7 +309,12 @@ pub(crate) fn semantic_tokens(
308 line_index: &LineIndex, 309 line_index: &LineIndex,
309 highlights: Vec<HighlightedRange>, 310 highlights: Vec<HighlightedRange>,
310) -> lsp_types::SemanticTokens { 311) -> lsp_types::SemanticTokens {
311 let mut builder = semantic_tokens::SemanticTokensBuilder::default(); 312 let id = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
313 Ok(d) => d.as_millis().to_string(),
314 Err(_) => String::new(),
315 };
316
317 let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
312 318
313 for highlight_range in highlights { 319 for highlight_range in highlights {
314 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight); 320 let (type_, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
@@ -328,6 +334,15 @@ pub(crate) fn semantic_tokens(
328 builder.build() 334 builder.build()
329} 335}
330 336
337pub(crate) fn semantic_token_edits(
338 previous: &lsp_types::SemanticTokens,
339 current: &lsp_types::SemanticTokens,
340) -> lsp_types::SemanticTokensEdits {
341 let result_id = current.result_id.clone();
342 let edits = semantic_tokens::diff_tokens(&previous.data, &current.data);
343 lsp_types::SemanticTokensEdits { result_id, edits }
344}
345
331fn semantic_token_type_and_modifiers( 346fn semantic_token_type_and_modifiers(
332 highlight: Highlight, 347 highlight: Highlight,
333) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) { 348) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {