From ac52d9a1f1a94e2c836c8a04a316f6454936a79a Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Sun, 3 Mar 2019 12:02:55 +0200 Subject: Add optional range parameter to SyntaxTreeParams When range is provided, instead of showing the syntax for the whole file, we'll show the syntax tree for the given range. --- crates/ra_ide_api/src/lib.rs | 12 ++- crates/ra_ide_api/tests/test/main.rs | 136 ++++++++++++++++++++++++- crates/ra_lsp_server/src/main_loop/handlers.rs | 4 +- crates/ra_lsp_server/src/req.rs | 1 + 4 files changed, 148 insertions(+), 5 deletions(-) diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 6546d0644..3e7cfbb54 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -38,7 +38,7 @@ mod marks; use std::sync::Arc; -use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode}; +use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode, algo}; use ra_text_edit::TextEdit; use ra_db::{ SourceDatabase, CheckCanceled, @@ -245,8 +245,14 @@ impl Analysis { /// Returns a syntax tree represented as `String`, for debug purposes. // FIXME: use a better name here. - pub fn syntax_tree(&self, file_id: FileId) -> String { - self.db.parse(file_id).syntax().debug_dump() + pub fn syntax_tree(&self, file_id: FileId, text_range: Option) -> String { + if let Some(text_range) = text_range { + let file = self.db.parse(file_id); + let node = algo::find_covering_node(file.syntax(), text_range); + node.debug_dump() + } else { + self.db.parse(file_id).syntax().debug_dump() + } } /// Returns an edit to remove all newlines in the range, cleaning up minor diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index ff1a0e46b..b0c80e255 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs @@ -1,6 +1,6 @@ use insta::assert_debug_snapshot_matches; use ra_ide_api::{ - mock_analysis::{single_file, single_file_with_position, MockAnalysis}, + mock_analysis::{single_file, single_file_with_position, single_file_with_range, MockAnalysis}, AnalysisChange, CrateGraph, Edition::Edition2018, Query, NavigationTarget, ReferenceSearchResult, }; @@ -138,3 +138,137 @@ mod foo { assert_eq!(s.name(), "FooInner"); assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); } + +#[test] +fn test_syntax_tree_without_range() { + // Basic syntax + let (analysis, file_id) = single_file(r#"fn foo() {}"#); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 11) + FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, file_id) = single_file( + r#" +fn test() { + assert!(" + fn foo() { + } + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(file_id, None); + + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 60) + FN_DEF@[0; 60) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 7) + IDENT@[3; 7) "test" + PARAM_LIST@[7; 9) + L_PAREN@[7; 8) + R_PAREN@[8; 9) + WHITESPACE@[9; 10) + BLOCK@[10; 60) + L_CURLY@[10; 11) + WHITESPACE@[11; 16) + EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + WHITESPACE@[58; 59) + R_CURLY@[59; 60) + "# + .trim() + ); +} + +#[test] +fn test_syntax_tree_with_range() { + let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +FN_DEF@[0; 11) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 11) + L_CURLY@[9; 10) + R_CURLY@[10; 11) + "# + .trim() + ); + + let (analysis, range) = single_file_with_range( + r#"fn test() { + <|>assert!(" + fn foo() { + } + ", "");<|> +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + + assert_eq!( + syn.trim(), + r#" +EXPR_STMT@[16; 58) + MACRO_CALL@[16; 57) + PATH@[16; 22) + PATH_SEGMENT@[16; 22) + NAME_REF@[16; 22) + IDENT@[16; 22) "assert" + EXCL@[22; 23) + TOKEN_TREE@[23; 57) + L_PAREN@[23; 24) + STRING@[24; 52) + COMMA@[52; 53) + WHITESPACE@[53; 54) + STRING@[54; 56) + R_PAREN@[56; 57) + SEMI@[57; 58) + "# + .trim() + ); +} diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index dce6fcc67..89e96a33a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -32,7 +32,9 @@ pub fn handle_analyzer_status(world: ServerWorld, _: ()) -> Result { pub fn handle_syntax_tree(world: ServerWorld, params: req::SyntaxTreeParams) -> Result { let id = params.text_document.try_conv_with(&world)?; - let res = world.analysis().syntax_tree(id); + let line_index = world.analysis().file_line_index(id); + let text_range = params.range.map(|p| p.conv_with(&line_index)); + let res = world.analysis().syntax_tree(id, text_range); Ok(res) } diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index e224ede80..5c589f969 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -39,6 +39,7 @@ impl Request for SyntaxTree { #[serde(rename_all = "camelCase")] pub struct SyntaxTreeParams { pub text_document: TextDocumentIdentifier, + pub range: Option, } pub enum ExtendSelection {} -- cgit v1.2.3