From 8f6f864547bad3d2bb34ea0cf5a32e8adee58c4e Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Tue, 25 Feb 2020 08:38:50 -0500 Subject: Semantic Ranges --- crates/ra_ide/src/lib.rs | 7 ++++ crates/ra_ide/src/syntax_highlighting.rs | 49 ++++++++++++++++++++++++-- crates/rust-analyzer/src/caps.rs | 16 +++++---- crates/rust-analyzer/src/main_loop.rs | 3 +- crates/rust-analyzer/src/main_loop/handlers.rs | 26 ++++++++++++-- crates/rust-analyzer/src/req.rs | 7 ++-- 6 files changed, 92 insertions(+), 16 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 82e10bc7e..c02bb08a0 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -430,6 +430,13 @@ impl Analysis { self.with_db(|db| syntax_highlighting::highlight(db, file_id)) } + /// Computes syntax highlighting for the given file range. + pub fn highlight_range(&self, frange: FileRange) -> Cancelable> { + self.with_db(|db| { + syntax_highlighting::highlight_range(db, frange.file_id, Some(frange.range)) + }) + } + /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancelable { self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 812229b4e..22c84561f 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -5,8 +5,8 @@ use ra_db::SourceDatabase; use ra_ide_db::{defs::NameDefinition, RootDatabase}; use ra_prof::profile; use ra_syntax::{ - ast, AstNode, Direction, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, TextRange, - WalkEvent, T, + ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxToken, + TextRange, WalkEvent, T, }; use rustc_hash::FxHashMap; @@ -69,6 +69,16 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec { let _p = profile("highlight"); + highlight_range(db, file_id, None) +} + +pub(crate) fn highlight_range( + db: &RootDatabase, + file_id: FileId, + range: Option, +) -> Vec { + let _p = profile("highlight_range"); + let parse = db.parse(file_id); let root = parse.tree().syntax().clone(); @@ -79,6 +89,15 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec match root.covering_element(range) { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }, + None => root, + }; + for event in root.preorder_with_tokens() { match event { WalkEvent::Enter(node) => match node.kind() { @@ -374,7 +393,10 @@ mod tests { use test_utils::{assert_eq_text, project_dir, read_text}; - use crate::mock_analysis::{single_file, MockAnalysis}; + use crate::{ + mock_analysis::{single_file, MockAnalysis}, + FileRange, TextRange, + }; #[test] fn test_highlighting() { @@ -475,4 +497,25 @@ fn bar() { let _ = host.analysis().highlight(file_id).unwrap(); // eprintln!("elapsed: {:?}", t.elapsed()); } + + #[test] + fn test_ranges() { + let (analysis, file_id) = single_file( + r#" + #[derive(Clone, Debug)] + struct Foo { + pub x: i32, + pub y: i32, + }"#, + ); + + let highlights = &analysis + .highlight_range(FileRange { + file_id, + range: TextRange::offset_len(82.into(), 1.into()), // "x" + }) + .unwrap(); + + assert_eq!(highlights[0].tag, "field"); + } } diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs index 638987ee8..db82eeb1c 100644 --- a/crates/rust-analyzer/src/caps.rs +++ b/crates/rust-analyzer/src/caps.rs @@ -7,9 +7,9 @@ use lsp_types::{ CompletionOptions, DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, SelectionRangeProviderCapability, SemanticTokensDocumentProvider, SemanticTokensLegend, - SemanticTokensOptions, SemanticTokensServerCapabilities, ServerCapabilities, - SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, - TextDocumentSyncOptions, TypeDefinitionProviderCapability, WorkDoneProgressOptions, + SemanticTokensOptions, ServerCapabilities, SignatureHelpOptions, TextDocumentSyncCapability, + TextDocumentSyncKind, TextDocumentSyncOptions, TypeDefinitionProviderCapability, + WorkDoneProgressOptions, }; pub fn server_capabilities() -> ServerCapabilities { @@ -60,7 +60,7 @@ pub fn server_capabilities() -> ServerCapabilities { execute_command_provider: None, workspace: None, call_hierarchy_provider: Some(CallHierarchyServerCapability::Simple(true)), - semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensOptions( + semantic_tokens_provider: Some( SemanticTokensOptions { legend: SemanticTokensLegend { token_types: semantic_tokens::supported_token_types().iter().cloned().collect(), @@ -71,9 +71,11 @@ pub fn server_capabilities() -> ServerCapabilities { }, document_provider: Some(SemanticTokensDocumentProvider::Bool(true)), - ..SemanticTokensOptions::default() - }, - )), + range_provider: Some(true), + work_done_progress_options: Default::default(), + } + .into(), + ), experimental: Default::default(), } } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 6e9e604a6..2b25f5443 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -527,8 +527,9 @@ fn on_request( .on::(handlers::handle_call_hierarchy_prepare)? .on::(handlers::handle_call_hierarchy_incoming)? .on::(handlers::handle_call_hierarchy_outgoing)? - .on::(handlers::handle_ssr)? .on::(handlers::handle_semantic_tokens)? + .on::(handlers::handle_semantic_tokens_range)? + .on::(handlers::handle_ssr)? .finish(); Ok(()) } diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index e13e7c95a..267edd578 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -17,8 +17,8 @@ use lsp_types::{ Diagnostic, DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokenModifier, SemanticTokenType, - SemanticTokens, SemanticTokensParams, SemanticTokensResult, SymbolInformation, - TextDocumentIdentifier, TextEdit, WorkspaceEdit, + SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, + SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit, }; use ra_ide::{ AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, @@ -1092,3 +1092,25 @@ pub fn handle_semantic_tokens( Ok(Some(tokens.into())) } + +pub fn handle_semantic_tokens_range( + world: WorldSnapshot, + params: SemanticTokensRangeParams, +) -> Result> { + let _p = profile("handle_semantic_tokens_range"); + + let frange = (¶ms.text_document, params.range).try_conv_with(&world)?; + let line_index = world.analysis().file_line_index(frange.file_id)?; + + let mut builder = SemanticTokensBuilder::default(); + + for h in world.analysis().highlight_range(frange)?.into_iter() { + let type_and_modifiers: (SemanticTokenType, Vec) = h.tag.conv(); + let (token_type, token_modifiers) = type_and_modifiers.conv(); + builder.push(h.range.conv_with(&line_index), token_type, token_modifiers); + } + + let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; + + Ok(Some(tokens.into())) +} diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index 3734899bc..642ac41ac 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs @@ -12,9 +12,10 @@ pub use lsp_types::{ DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, SelectionRange, - SelectionRangeParams, SemanticTokensParams, SemanticTokensResult, ServerCapabilities, - ShowMessageParams, SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, - TextEdit, WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, + SelectionRangeParams, SemanticTokensParams, SemanticTokensRangeParams, + SemanticTokensRangeResult, SemanticTokensResult, ServerCapabilities, ShowMessageParams, + SignatureHelp, SymbolKind, TextDocumentEdit, TextDocumentPositionParams, TextEdit, + WorkDoneProgressParams, WorkspaceEdit, WorkspaceSymbolParams, }; pub enum AnalyzerStatus {} -- cgit v1.2.3 From fa355d6339d7b5ccfd4b1a96f035a4366e8152fe Mon Sep 17 00:00:00 2001 From: kjeremy Date: Tue, 25 Feb 2020 14:44:47 -0500 Subject: Rename back to highlight and check event's again highlight range --- crates/ra_ide/src/lib.rs | 8 ++- crates/ra_ide/src/syntax_highlighting.rs | 86 +++++++++++++++++++------------- 2 files changed, 54 insertions(+), 40 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index c02bb08a0..d22870669 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -425,16 +425,14 @@ impl Analysis { self.with_db(|db| runnables::runnables(db, file_id)) } - /// Computes syntax highlighting for the given file. + /// Computes syntax highlighting for the given file pub fn highlight(&self, file_id: FileId) -> Cancelable> { - self.with_db(|db| syntax_highlighting::highlight(db, file_id)) + self.with_db(|db| syntax_highlighting::highlight(db, file_id, None)) } /// Computes syntax highlighting for the given file range. pub fn highlight_range(&self, frange: FileRange) -> Cancelable> { - self.with_db(|db| { - syntax_highlighting::highlight_range(db, frange.file_id, Some(frange.range)) - }) + self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range))) } /// Computes syntax highlighting for the given file. diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 22c84561f..9bc3ad448 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -67,17 +67,12 @@ fn is_control_keyword(kind: SyntaxKind) -> bool { } } -pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec { - let _p = profile("highlight"); - highlight_range(db, file_id, None) -} - -pub(crate) fn highlight_range( +pub(crate) fn highlight( db: &RootDatabase, file_id: FileId, range: Option, ) -> Vec { - let _p = profile("highlight_range"); + let _p = profile("highlight"); let parse = db.parse(file_id); let root = parse.tree().syntax().clone(); @@ -89,31 +84,56 @@ pub(crate) fn highlight_range( let mut in_macro_call = None; - // Determine the root based on the range - let root = match range { - Some(range) => match root.covering_element(range) { + // Determine the root based on the given range. + let (root, highlight_range) = if let Some(range) = range { + let root = match root.covering_element(range) { NodeOrToken::Node(node) => node, NodeOrToken::Token(token) => token.parent(), - }, - None => root, + }; + (root, range) + } else { + (root.clone(), root.text_range()) }; for event in root.preorder_with_tokens() { match event { - WalkEvent::Enter(node) => match node.kind() { - MACRO_CALL => { - in_macro_call = Some(node.clone()); - if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { - res.push(HighlightedRange { range, tag: tags::MACRO, binding_hash: None }); - } + WalkEvent::Enter(node) => { + if node.text_range().intersection(&highlight_range).is_none() { + continue; } - _ if in_macro_call.is_some() => { - if let Some(token) = node.as_token() { - if let Some((tag, binding_hash)) = highlight_token_tree( + + match node.kind() { + MACRO_CALL => { + in_macro_call = Some(node.clone()); + if let Some(range) = highlight_macro(InFile::new(file_id.into(), node)) { + res.push(HighlightedRange { + range, + tag: tags::MACRO, + binding_hash: None, + }); + } + } + _ if in_macro_call.is_some() => { + if let Some(token) = node.as_token() { + if let Some((tag, binding_hash)) = highlight_token_tree( + &mut sb, + &analyzer, + &mut bindings_shadow_count, + InFile::new(file_id.into(), token.clone()), + ) { + res.push(HighlightedRange { + range: node.text_range(), + tag, + binding_hash, + }); + } + } + } + _ => { + if let Some((tag, binding_hash)) = highlight_node( &mut sb, - &analyzer, &mut bindings_shadow_count, - InFile::new(file_id.into(), token.clone()), + InFile::new(file_id.into(), node.clone()), ) { res.push(HighlightedRange { range: node.text_range(), @@ -123,17 +143,12 @@ pub(crate) fn highlight_range( } } } - _ => { - if let Some((tag, binding_hash)) = highlight_node( - &mut sb, - &mut bindings_shadow_count, - InFile::new(file_id.into(), node.clone()), - ) { - res.push(HighlightedRange { range: node.text_range(), tag, binding_hash }); - } - } - }, + } WalkEvent::Leave(node) => { + if node.text_range().intersection(&highlight_range).is_none() { + continue; + } + if let Some(m) = in_macro_call.as_ref() { if *m == node { in_macro_call = None; @@ -284,7 +299,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo ) } - let mut ranges = highlight(db, file_id); + let mut ranges = highlight(db, file_id, None); ranges.sort_by_key(|it| it.range.start()); // quick non-optimal heuristic to intersect token ranges and highlighted ranges let mut frontier = 0; @@ -509,10 +524,11 @@ fn bar() { }"#, ); + // The "x" let highlights = &analysis .highlight_range(FileRange { file_id, - range: TextRange::offset_len(82.into(), 1.into()), // "x" + range: TextRange::offset_len(82.into(), 1.into()), }) .unwrap(); -- cgit v1.2.3