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 From c2d3203d0c708dc2ccb7c0d017ae876180c0e5a8 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Sun, 3 Mar 2019 21:21:40 +0200 Subject: Add vscode support for range in SyntaxTreeParams This enables the client to use a command to either show the live-updating version of the syntax tree for the current file. Or optionally when a selected range is provided, we then provide a snapshot of the syntax tree for the range. --- editors/code/package.json | 2 +- editors/code/src/commands/syntaxTree.ts | 44 ++++++++++++++++++++++++++------- editors/code/src/extension.ts | 6 ++++- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index d4ce2ae2c..fda411810 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -75,7 +75,7 @@ "commands": [ { "command": "rust-analyzer.syntaxTree", - "title": "Show syntax tree for current file", + "title": "Show Syntax Tree", "category": "Rust Analyzer" }, { diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts index c0baf08c5..7200ae823 100644 --- a/editors/code/src/commands/syntaxTree.ts +++ b/editors/code/src/commands/syntaxTree.ts @@ -1,5 +1,5 @@ import * as vscode from 'vscode'; -import { TextDocumentIdentifier } from 'vscode-languageclient'; +import { Range, TextDocumentIdentifier } from 'vscode-languageclient'; import { Server } from '../server'; @@ -17,8 +17,21 @@ export class TextDocumentContentProvider if (editor == null) { return ''; } + + let range: Range | undefined; + + // When the range based query is enabled we take the range of the selection + if (uri.query === 'range=true') { + range = editor.selection.isEmpty + ? undefined + : Server.client.code2ProtocolConverter.asRange( + editor.selection + ); + } + const request: SyntaxTreeParams = { - textDocument: { uri: editor.document.uri.toString() } + textDocument: { uri: editor.document.uri.toString() }, + range }; return Server.client.sendRequest( 'rust-analyzer/syntaxTree', @@ -33,6 +46,7 @@ export class TextDocumentContentProvider interface SyntaxTreeParams { textDocument: TextDocumentIdentifier; + range?: Range; } type SyntaxTreeResult = string; @@ -40,11 +54,23 @@ type SyntaxTreeResult = string; // Opens the virtual file that will show the syntax tree // // The contents of the file come from the `TextDocumentContentProvider` -export async function handle() { - const document = await vscode.workspace.openTextDocument(syntaxTreeUri); - return vscode.window.showTextDocument( - document, - vscode.ViewColumn.Two, - true - ); +export function createHandle(provider: TextDocumentContentProvider) { + return async () => { + const editor = vscode.window.activeTextEditor; + const rangeEnabled = !!(editor && !editor.selection.isEmpty); + + const uri = rangeEnabled + ? vscode.Uri.parse(`${syntaxTreeUri.toString()}?range=true`) + : syntaxTreeUri; + + const document = await vscode.workspace.openTextDocument(uri); + + provider.eventEmitter.fire(uri); + + return vscode.window.showTextDocument( + document, + vscode.ViewColumn.Two, + true + ); + }; } diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 8b332eeb2..5134bb258 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -52,7 +52,6 @@ export function activate(context: vscode.ExtensionContext) { registerCommand('rust-analyzer.collectGarbage', () => Server.client.sendRequest('rust-analyzer/collectGarbage', null) ); - registerCommand('rust-analyzer.syntaxTree', commands.syntaxTree.handle); registerCommand( 'rust-analyzer.extendSelection', commands.extendSelection.handle @@ -109,6 +108,11 @@ export function activate(context: vscode.ExtensionContext) { ) ); + registerCommand( + 'rust-analyzer.syntaxTree', + commands.syntaxTree.createHandle(textDocumentContentProvider) + ); + vscode.workspace.onDidChangeTextDocument( events.changeTextDocument.createHandler(textDocumentContentProvider), null, -- cgit v1.2.3 From 1b4e0ec1c868c7f2a0eef1e59bfa382db85a6900 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Sun, 3 Mar 2019 21:54:51 +0200 Subject: Rename syntaxtree text provider to SyntaxTreeContentProvider --- editors/code/src/commands/syntaxTree.ts | 4 ++-- editors/code/src/events/change_text_document.ts | 10 ++++------ editors/code/src/extension.ts | 10 +++++----- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/editors/code/src/commands/syntaxTree.ts b/editors/code/src/commands/syntaxTree.ts index 7200ae823..2f50fe14b 100644 --- a/editors/code/src/commands/syntaxTree.ts +++ b/editors/code/src/commands/syntaxTree.ts @@ -5,7 +5,7 @@ import { Server } from '../server'; export const syntaxTreeUri = vscode.Uri.parse('rust-analyzer://syntaxtree'); -export class TextDocumentContentProvider +export class SyntaxTreeContentProvider implements vscode.TextDocumentContentProvider { public eventEmitter = new vscode.EventEmitter(); public syntaxTree: string = 'Not available'; @@ -54,7 +54,7 @@ type SyntaxTreeResult = string; // Opens the virtual file that will show the syntax tree // // The contents of the file come from the `TextDocumentContentProvider` -export function createHandle(provider: TextDocumentContentProvider) { +export function createHandle(provider: SyntaxTreeContentProvider) { return async () => { const editor = vscode.window.activeTextEditor; const rangeEnabled = !!(editor && !editor.selection.isEmpty); diff --git a/editors/code/src/events/change_text_document.ts b/editors/code/src/events/change_text_document.ts index 6be057245..89488bc61 100644 --- a/editors/code/src/events/change_text_document.ts +++ b/editors/code/src/events/change_text_document.ts @@ -1,20 +1,18 @@ import * as vscode from 'vscode'; import { - syntaxTreeUri, - TextDocumentContentProvider + SyntaxTreeContentProvider, + syntaxTreeUri } from '../commands/syntaxTree'; -export function createHandler( - textDocumentContentProvider: TextDocumentContentProvider -) { +export function createHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { return (event: vscode.TextDocumentChangeEvent) => { const doc = event.document; if (doc.languageId !== 'rust') { return; } afterLs(() => { - textDocumentContentProvider.eventEmitter.fire(syntaxTreeUri); + syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri); }); }; } diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 5134bb258..894334c55 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; import * as commands from './commands'; -import { TextDocumentContentProvider } from './commands/syntaxTree'; +import { SyntaxTreeContentProvider } from './commands/syntaxTree'; import * as events from './events'; import * as notifications from './notifications'; import { Server } from './server'; @@ -100,21 +100,21 @@ export function activate(context: vscode.ExtensionContext) { events.changeActiveTextEditor.handle ); - const textDocumentContentProvider = new TextDocumentContentProvider(); + const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); disposeOnDeactivation( vscode.workspace.registerTextDocumentContentProvider( 'rust-analyzer', - textDocumentContentProvider + syntaxTreeContentProvider ) ); registerCommand( 'rust-analyzer.syntaxTree', - commands.syntaxTree.createHandle(textDocumentContentProvider) + commands.syntaxTree.createHandle(syntaxTreeContentProvider) ); vscode.workspace.onDidChangeTextDocument( - events.changeTextDocument.createHandler(textDocumentContentProvider), + events.changeTextDocument.createHandler(syntaxTreeContentProvider), null, context.subscriptions ); -- cgit v1.2.3 From 0db95fc812d2c839e847527b774dfda170266cec Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Sun, 3 Mar 2019 22:03:37 +0200 Subject: Allow syntax tree to update when changing files Previously when using the file based syntax tree, it would not update until a change had been made in the new file. Now we automatically update the syntax tree to match the current file. --- .../code/src/events/change_active_text_editor.ts | 39 +++++++++++++--------- editors/code/src/extension.ts | 4 +-- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/editors/code/src/events/change_active_text_editor.ts b/editors/code/src/events/change_active_text_editor.ts index af295b2ec..64be56225 100644 --- a/editors/code/src/events/change_active_text_editor.ts +++ b/editors/code/src/events/change_active_text_editor.ts @@ -1,23 +1,32 @@ import { TextEditor } from 'vscode'; import { TextDocumentIdentifier } from 'vscode-languageclient'; +import { + SyntaxTreeContentProvider, + syntaxTreeUri +} from '../commands/syntaxTree'; import { Decoration } from '../highlighting'; import { Server } from '../server'; -export async function handle(editor: TextEditor | undefined) { - if ( - !Server.config.highlightingOn || - !editor || - editor.document.languageId !== 'rust' - ) { - return; - } - const params: TextDocumentIdentifier = { - uri: editor.document.uri.toString() +export function makeHandler(syntaxTreeProvider: SyntaxTreeContentProvider) { + return async function handle(editor: TextEditor | undefined) { + if (!editor || editor.document.languageId !== 'rust') { + return; + } + + syntaxTreeProvider.eventEmitter.fire(syntaxTreeUri); + + if (!Server.config.highlightingOn) { + return; + } + + const params: TextDocumentIdentifier = { + uri: editor.document.uri.toString() + }; + const decorations = await Server.client.sendRequest( + 'rust-analyzer/decorationsRequest', + params + ); + Server.highlighter.setHighlights(editor, decorations); }; - const decorations = await Server.client.sendRequest( - 'rust-analyzer/decorationsRequest', - params - ); - Server.highlighter.setHighlights(editor, decorations); } diff --git a/editors/code/src/extension.ts b/editors/code/src/extension.ts index 894334c55..941beba18 100644 --- a/editors/code/src/extension.ts +++ b/editors/code/src/extension.ts @@ -94,13 +94,13 @@ export function activate(context: vscode.ExtensionContext) { notifications.publishDecorations.handle ] ]; + const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); // The events below are plain old javascript events, triggered and handled by vscode vscode.window.onDidChangeActiveTextEditor( - events.changeActiveTextEditor.handle + events.changeActiveTextEditor.makeHandler(syntaxTreeContentProvider) ); - const syntaxTreeContentProvider = new SyntaxTreeContentProvider(); disposeOnDeactivation( vscode.workspace.registerTextDocumentContentProvider( 'rust-analyzer', -- cgit v1.2.3 From 16ecd276f036de9b5dccdbcce55b25a2a5699385 Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Mon, 4 Mar 2019 08:54:54 +0200 Subject: Implement syntax tree support for syntax inside string This allows us to select a string or portions of it and try parsing it as rust syntax. This is mostly helpful when developing tests where the test itself contains some rust syntax as a string. --- crates/ra_ide_api/src/lib.rs | 11 +--- crates/ra_ide_api/src/syntax_tree.rs | 85 +++++++++++++++++++++++++ crates/ra_ide_api/tests/test/main.rs | 118 +++++++++++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 8 deletions(-) create mode 100644 crates/ra_ide_api/src/syntax_tree.rs diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 3e7cfbb54..b8a4adbce 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -32,13 +32,14 @@ mod references; mod impls; mod assists; mod diagnostics; +mod syntax_tree; #[cfg(test)] mod marks; use std::sync::Arc; -use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit, AstNode, algo}; +use ra_syntax::{SourceFile, TreeArc, TextRange, TextUnit}; use ra_text_edit::TextEdit; use ra_db::{ SourceDatabase, CheckCanceled, @@ -246,13 +247,7 @@ 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, 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() - } + syntax_tree::syntax_tree(&self.db, file_id, text_range) } /// Returns an edit to remove all newlines in the range, cleaning up minor diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs new file mode 100644 index 000000000..cdee63d59 --- /dev/null +++ b/crates/ra_ide_api/src/syntax_tree.rs @@ -0,0 +1,85 @@ +use ra_db::SourceDatabase; +use crate::db::RootDatabase; +use ra_syntax::{ + SourceFile, SyntaxNode, TextRange, AstNode, + algo::{self, visit::{visitor, Visitor}}, ast::{self, AstToken} +}; + +pub use ra_db::FileId; + +pub(crate) fn syntax_tree( + db: &RootDatabase, + file_id: FileId, + text_range: Option, +) -> String { + if let Some(text_range) = text_range { + let file = db.parse(file_id); + let node = algo::find_covering_node(file.syntax(), text_range); + + if let Some(tree) = syntax_tree_for_string(node, text_range) { + return tree; + } + + node.debug_dump() + } else { + db.parse(file_id).syntax().debug_dump() + } +} + +/// Attempts parsing the selected contents of a string literal +/// as rust syntax and returns its syntax tree +fn syntax_tree_for_string(node: &SyntaxNode, text_range: TextRange) -> Option { + // When the range is inside a string + // we'll attempt parsing it as rust syntax + // to provide the syntax tree of the contents of the string + visitor() + .visit(|node: &ast::String| syntax_tree_for_token(node, text_range)) + .visit(|node: &ast::RawString| syntax_tree_for_token(node, text_range)) + .accept(node)? +} + +fn syntax_tree_for_token(node: &T, text_range: TextRange) -> Option { + // Range of the full node + let node_range = node.syntax().range(); + let text = node.text().to_string(); + + // We start at some point inside the node + // Either we have selected the whole string + // or our selection is inside it + let start = text_range.start() - node_range.start(); + + // how many characters we have selected + let len = text_range.len().to_usize(); + + let node_len = node_range.len().to_usize(); + + let start = start.to_usize(); + + // We want to cap our length + let len = len.min(node_len); + + // Ensure our slice is inside the actual string + let end = if start + len < text.len() { start + len } else { text.len() - start }; + + let text = &text[start..end]; + + // Remove possible extra string quotes from the start + // and the end of the string + let text = text + .trim_start_matches('r') + .trim_start_matches('#') + .trim_start_matches('"') + .trim_end_matches('#') + .trim_end_matches('"') + .trim(); + + let parsed = SourceFile::parse(&text); + + // If the "file" parsed without errors, + // return its syntax + if parsed.errors().is_empty() { + return Some(parsed.syntax().debug_dump()); + } + + None +} diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index b0c80e255..0f0766f62 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs @@ -272,3 +272,121 @@ EXPR_STMT@[16; 58) .trim() ); } + +#[test] +fn test_syntax_tree_inside_string() { + let (analysis, range) = single_file_with_range( + r#"fn test() { + assert!(" +<|>fn foo() { +}<|> +fn bar() { +} + ", ""); +}"# + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + 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; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r#" +<|>fn foo() { +}<|> +fn bar() { +} + "#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 12) + FN_DEF@[0; 12) + 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; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) +"# + .trim() + ); + + // With a raw string + let (analysis, range) = single_file_with_range( + r###"fn test() { + assert!(r<|>#" +fn foo() { +} +fn bar() { +}"<|>#, ""); +}"### + .trim(), + ); + let syn = analysis.syntax_tree(range.file_id, Some(range.range)); + assert_eq!( + syn.trim(), + r#" +SOURCE_FILE@[0; 25) + FN_DEF@[0; 12) + 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; 12) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + R_CURLY@[11; 12) + WHITESPACE@[12; 13) + FN_DEF@[13; 25) + FN_KW@[13; 15) + WHITESPACE@[15; 16) + NAME@[16; 19) + IDENT@[16; 19) "bar" + PARAM_LIST@[19; 21) + L_PAREN@[19; 20) + R_PAREN@[20; 21) + WHITESPACE@[21; 22) + BLOCK@[22; 25) + L_CURLY@[22; 23) + WHITESPACE@[23; 24) + R_CURLY@[24; 25) + +"# + .trim() + ); +} -- cgit v1.2.3 From 1ef2c0613134633ef0fe0d515f7d416e482f07fb Mon Sep 17 00:00:00 2001 From: Ville Penttinen Date: Mon, 4 Mar 2019 09:19:46 +0200 Subject: Allow syntax strings to contain test markers We simply remove all the CUSTOM_MARKERS before attempting to parse the file. This allows for the syntax selection to work with most of the test strings. --- crates/ra_ide_api/src/syntax_tree.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index cdee63d59..bbe9222b4 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs @@ -71,7 +71,9 @@ fn syntax_tree_for_token(node: &T, text_range: TextRange) -> Option .trim_start_matches('"') .trim_end_matches('#') .trim_end_matches('"') - .trim(); + .trim() + // Remove custom markers + .replace("<|>", ""); let parsed = SourceFile::parse(&text); -- cgit v1.2.3