From 3975952601888d9f77e466c12e8e389748984b33 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Wed, 22 Jul 2020 15:00:28 +1000 Subject: SSR: Pass current file position through to SSR code. In a subsequent commit, it will be used for resolving paths. --- crates/ra_ssr/src/lib.rs | 30 ++++++++++++++++++++++++++++-- crates/ra_ssr/src/matching.rs | 4 ++-- crates/ra_ssr/src/tests.rs | 42 ++++++++++++++++++++++++------------------ 3 files changed, 54 insertions(+), 22 deletions(-) (limited to 'crates/ra_ssr') diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index 6d578610b..a0a5c9762 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs @@ -13,11 +13,12 @@ mod errors; #[cfg(test)] mod tests; +use crate::errors::bail; pub use crate::errors::SsrError; pub use crate::matching::Match; use crate::matching::MatchFailureReason; use hir::Semantics; -use ra_db::{FileId, FileRange}; +use ra_db::{FileId, FilePosition, FileRange}; use ra_ide_db::source_change::SourceFileEdit; use ra_syntax::{ast, AstNode, SyntaxNode, TextRange}; use rustc_hash::FxHashMap; @@ -51,10 +52,35 @@ pub struct MatchFinder<'db> { } impl<'db> MatchFinder<'db> { - pub fn new(db: &'db ra_ide_db::RootDatabase) -> MatchFinder<'db> { + /// Constructs a new instance where names will be looked up as if they appeared at + /// `lookup_context`. + pub fn in_context( + db: &'db ra_ide_db::RootDatabase, + _lookup_context: FilePosition, + ) -> MatchFinder<'db> { + // FIXME: Use lookup_context MatchFinder { sema: Semantics::new(db), rules: Vec::new() } } + /// Constructs an instance using the start of the first file in `db` as the lookup context. + pub fn at_first_file(db: &'db ra_ide_db::RootDatabase) -> Result, SsrError> { + use ra_db::SourceDatabaseExt; + use ra_ide_db::symbol_index::SymbolsDatabase; + if let Some(first_file_id) = db + .local_roots() + .iter() + .next() + .and_then(|root| db.source_root(root.clone()).iter().next()) + { + Ok(MatchFinder::in_context( + db, + FilePosition { file_id: first_file_id, offset: 0.into() }, + )) + } else { + bail!("No files to search"); + } + } + /// Adds a rule to be applied. The order in which rules are added matters. Earlier rules take /// precedence. If a node is matched by an earlier rule, then later rules won't be permitted to /// match to it. diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs index 005569f6f..a43d57c34 100644 --- a/crates/ra_ssr/src/matching.rs +++ b/crates/ra_ssr/src/matching.rs @@ -576,8 +576,8 @@ mod tests { let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap(); let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }"; - let (db, _) = crate::tests::single_file(input); - let mut match_finder = MatchFinder::new(&db); + let (db, position) = crate::tests::single_file(input); + let mut match_finder = MatchFinder::in_context(&db, position); match_finder.add_rule(rule); let matches = match_finder.matches(); assert_eq!(matches.matches.len(), 1); diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 523035b31..63d527894 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs @@ -1,6 +1,6 @@ use crate::{MatchFinder, SsrRule}; use expect::{expect, Expect}; -use ra_db::{salsa::Durability, FileId, SourceDatabaseExt}; +use ra_db::{salsa::Durability, FileId, FilePosition, SourceDatabaseExt}; use rustc_hash::FxHashSet; use std::sync::Arc; use test_utils::mark; @@ -59,15 +59,21 @@ fn parser_undefined_placeholder_in_replacement() { ); } -pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FileId) { +/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be +/// the start of the file. +pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FilePosition) { use ra_db::fixture::WithFixture; use ra_ide_db::symbol_index::SymbolsDatabase; - let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code); - let mut db = db; + let (mut db, position) = if code.contains(test_utils::CURSOR_MARKER) { + ra_ide_db::RootDatabase::with_position(code) + } else { + let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code); + (db, FilePosition { file_id, offset: 0.into() }) + }; let mut local_roots = FxHashSet::default(); local_roots.insert(ra_db::fixture::WORKSPACE); db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); - (db, file_id) + (db, position) } fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { @@ -75,8 +81,8 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { } fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { - let (db, file_id) = single_file(input); - let mut match_finder = MatchFinder::new(&db); + let (db, position) = single_file(input); + let mut match_finder = MatchFinder::in_context(&db, position); for rule in rules { let rule: SsrRule = rule.parse().unwrap(); match_finder.add_rule(rule); @@ -85,10 +91,10 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { if edits.is_empty() { panic!("No edits were made"); } - assert_eq!(edits[0].file_id, file_id); + assert_eq!(edits[0].file_id, position.file_id); // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters // stuff. - let mut actual = db.file_text(file_id).to_string(); + let mut actual = db.file_text(position.file_id).to_string(); edits[0].edit.apply(&mut actual); expected.assert_eq(&actual); } @@ -106,34 +112,34 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet: } fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { - let (db, file_id) = single_file(code); - let mut match_finder = MatchFinder::new(&db); + let (db, position) = single_file(code); + let mut match_finder = MatchFinder::in_context(&db, position); match_finder.add_search_pattern(pattern.parse().unwrap()); let matched_strings: Vec = match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); if matched_strings != expected && !expected.is_empty() { - print_match_debug_info(&match_finder, file_id, &expected[0]); + print_match_debug_info(&match_finder, position.file_id, &expected[0]); } assert_eq!(matched_strings, expected); } fn assert_no_match(pattern: &str, code: &str) { - let (db, file_id) = single_file(code); - let mut match_finder = MatchFinder::new(&db); + let (db, position) = single_file(code); + let mut match_finder = MatchFinder::in_context(&db, position); match_finder.add_search_pattern(pattern.parse().unwrap()); let matches = match_finder.matches().flattened().matches; if !matches.is_empty() { - print_match_debug_info(&match_finder, file_id, &matches[0].matched_text()); + print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text()); panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches); } } fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { - let (db, file_id) = single_file(code); - let mut match_finder = MatchFinder::new(&db); + let (db, position) = single_file(code); + let mut match_finder = MatchFinder::in_context(&db, position); match_finder.add_search_pattern(pattern.parse().unwrap()); let mut reasons = Vec::new(); - for d in match_finder.debug_where_text_equal(file_id, snippet) { + for d in match_finder.debug_where_text_equal(position.file_id, snippet) { if let Some(reason) = d.match_failure_reason() { reasons.push(reason.to_owned()); } -- cgit v1.2.3