aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ssr
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ssr')
-rw-r--r--crates/ra_ssr/src/lib.rs30
-rw-r--r--crates/ra_ssr/src/matching.rs4
-rw-r--r--crates/ra_ssr/src/tests.rs42
3 files changed, 54 insertions, 22 deletions
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;
13#[cfg(test)] 13#[cfg(test)]
14mod tests; 14mod tests;
15 15
16use crate::errors::bail;
16pub use crate::errors::SsrError; 17pub use crate::errors::SsrError;
17pub use crate::matching::Match; 18pub use crate::matching::Match;
18use crate::matching::MatchFailureReason; 19use crate::matching::MatchFailureReason;
19use hir::Semantics; 20use hir::Semantics;
20use ra_db::{FileId, FileRange}; 21use ra_db::{FileId, FilePosition, FileRange};
21use ra_ide_db::source_change::SourceFileEdit; 22use ra_ide_db::source_change::SourceFileEdit;
22use ra_syntax::{ast, AstNode, SyntaxNode, TextRange}; 23use ra_syntax::{ast, AstNode, SyntaxNode, TextRange};
23use rustc_hash::FxHashMap; 24use rustc_hash::FxHashMap;
@@ -51,10 +52,35 @@ pub struct MatchFinder<'db> {
51} 52}
52 53
53impl<'db> MatchFinder<'db> { 54impl<'db> MatchFinder<'db> {
54 pub fn new(db: &'db ra_ide_db::RootDatabase) -> MatchFinder<'db> { 55 /// Constructs a new instance where names will be looked up as if they appeared at
56 /// `lookup_context`.
57 pub fn in_context(
58 db: &'db ra_ide_db::RootDatabase,
59 _lookup_context: FilePosition,
60 ) -> MatchFinder<'db> {
61 // FIXME: Use lookup_context
55 MatchFinder { sema: Semantics::new(db), rules: Vec::new() } 62 MatchFinder { sema: Semantics::new(db), rules: Vec::new() }
56 } 63 }
57 64
65 /// Constructs an instance using the start of the first file in `db` as the lookup context.
66 pub fn at_first_file(db: &'db ra_ide_db::RootDatabase) -> Result<MatchFinder<'db>, SsrError> {
67 use ra_db::SourceDatabaseExt;
68 use ra_ide_db::symbol_index::SymbolsDatabase;
69 if let Some(first_file_id) = db
70 .local_roots()
71 .iter()
72 .next()
73 .and_then(|root| db.source_root(root.clone()).iter().next())
74 {
75 Ok(MatchFinder::in_context(
76 db,
77 FilePosition { file_id: first_file_id, offset: 0.into() },
78 ))
79 } else {
80 bail!("No files to search");
81 }
82 }
83
58 /// Adds a rule to be applied. The order in which rules are added matters. Earlier rules take 84 /// Adds a rule to be applied. The order in which rules are added matters. Earlier rules take
59 /// precedence. If a node is matched by an earlier rule, then later rules won't be permitted to 85 /// precedence. If a node is matched by an earlier rule, then later rules won't be permitted to
60 /// match to it. 86 /// 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 {
576 let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap(); 576 let rule: SsrRule = "foo($x) ==>> bar($x)".parse().unwrap();
577 let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }"; 577 let input = "fn foo() {} fn bar() {} fn main() { foo(1+2); }";
578 578
579 let (db, _) = crate::tests::single_file(input); 579 let (db, position) = crate::tests::single_file(input);
580 let mut match_finder = MatchFinder::new(&db); 580 let mut match_finder = MatchFinder::in_context(&db, position);
581 match_finder.add_rule(rule); 581 match_finder.add_rule(rule);
582 let matches = match_finder.matches(); 582 let matches = match_finder.matches();
583 assert_eq!(matches.matches.len(), 1); 583 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 @@
1use crate::{MatchFinder, SsrRule}; 1use crate::{MatchFinder, SsrRule};
2use expect::{expect, Expect}; 2use expect::{expect, Expect};
3use ra_db::{salsa::Durability, FileId, SourceDatabaseExt}; 3use ra_db::{salsa::Durability, FileId, FilePosition, SourceDatabaseExt};
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
5use std::sync::Arc; 5use std::sync::Arc;
6use test_utils::mark; 6use test_utils::mark;
@@ -59,15 +59,21 @@ fn parser_undefined_placeholder_in_replacement() {
59 ); 59 );
60} 60}
61 61
62pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FileId) { 62/// `code` may optionally contain a cursor marker `<|>`. If it doesn't, then the position will be
63/// the start of the file.
64pub(crate) fn single_file(code: &str) -> (ra_ide_db::RootDatabase, FilePosition) {
63 use ra_db::fixture::WithFixture; 65 use ra_db::fixture::WithFixture;
64 use ra_ide_db::symbol_index::SymbolsDatabase; 66 use ra_ide_db::symbol_index::SymbolsDatabase;
65 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code); 67 let (mut db, position) = if code.contains(test_utils::CURSOR_MARKER) {
66 let mut db = db; 68 ra_ide_db::RootDatabase::with_position(code)
69 } else {
70 let (db, file_id) = ra_ide_db::RootDatabase::with_single_file(code);
71 (db, FilePosition { file_id, offset: 0.into() })
72 };
67 let mut local_roots = FxHashSet::default(); 73 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ra_db::fixture::WORKSPACE); 74 local_roots.insert(ra_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 75 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70 (db, file_id) 76 (db, position)
71} 77}
72 78
73fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) { 79fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) {
@@ -75,8 +81,8 @@ fn assert_ssr_transform(rule: &str, input: &str, expected: Expect) {
75} 81}
76 82
77fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { 83fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) {
78 let (db, file_id) = single_file(input); 84 let (db, position) = single_file(input);
79 let mut match_finder = MatchFinder::new(&db); 85 let mut match_finder = MatchFinder::in_context(&db, position);
80 for rule in rules { 86 for rule in rules {
81 let rule: SsrRule = rule.parse().unwrap(); 87 let rule: SsrRule = rule.parse().unwrap();
82 match_finder.add_rule(rule); 88 match_finder.add_rule(rule);
@@ -85,10 +91,10 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) {
85 if edits.is_empty() { 91 if edits.is_empty() {
86 panic!("No edits were made"); 92 panic!("No edits were made");
87 } 93 }
88 assert_eq!(edits[0].file_id, file_id); 94 assert_eq!(edits[0].file_id, position.file_id);
89 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters 95 // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters
90 // stuff. 96 // stuff.
91 let mut actual = db.file_text(file_id).to_string(); 97 let mut actual = db.file_text(position.file_id).to_string();
92 edits[0].edit.apply(&mut actual); 98 edits[0].edit.apply(&mut actual);
93 expected.assert_eq(&actual); 99 expected.assert_eq(&actual);
94} 100}
@@ -106,34 +112,34 @@ fn print_match_debug_info(match_finder: &MatchFinder, file_id: FileId, snippet:
106} 112}
107 113
108fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { 114fn assert_matches(pattern: &str, code: &str, expected: &[&str]) {
109 let (db, file_id) = single_file(code); 115 let (db, position) = single_file(code);
110 let mut match_finder = MatchFinder::new(&db); 116 let mut match_finder = MatchFinder::in_context(&db, position);
111 match_finder.add_search_pattern(pattern.parse().unwrap()); 117 match_finder.add_search_pattern(pattern.parse().unwrap());
112 let matched_strings: Vec<String> = 118 let matched_strings: Vec<String> =
113 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); 119 match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect();
114 if matched_strings != expected && !expected.is_empty() { 120 if matched_strings != expected && !expected.is_empty() {
115 print_match_debug_info(&match_finder, file_id, &expected[0]); 121 print_match_debug_info(&match_finder, position.file_id, &expected[0]);
116 } 122 }
117 assert_eq!(matched_strings, expected); 123 assert_eq!(matched_strings, expected);
118} 124}
119 125
120fn assert_no_match(pattern: &str, code: &str) { 126fn assert_no_match(pattern: &str, code: &str) {
121 let (db, file_id) = single_file(code); 127 let (db, position) = single_file(code);
122 let mut match_finder = MatchFinder::new(&db); 128 let mut match_finder = MatchFinder::in_context(&db, position);
123 match_finder.add_search_pattern(pattern.parse().unwrap()); 129 match_finder.add_search_pattern(pattern.parse().unwrap());
124 let matches = match_finder.matches().flattened().matches; 130 let matches = match_finder.matches().flattened().matches;
125 if !matches.is_empty() { 131 if !matches.is_empty() {
126 print_match_debug_info(&match_finder, file_id, &matches[0].matched_text()); 132 print_match_debug_info(&match_finder, position.file_id, &matches[0].matched_text());
127 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches); 133 panic!("Got {} matches when we expected none: {:#?}", matches.len(), matches);
128 } 134 }
129} 135}
130 136
131fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) { 137fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expected_reason: &str) {
132 let (db, file_id) = single_file(code); 138 let (db, position) = single_file(code);
133 let mut match_finder = MatchFinder::new(&db); 139 let mut match_finder = MatchFinder::in_context(&db, position);
134 match_finder.add_search_pattern(pattern.parse().unwrap()); 140 match_finder.add_search_pattern(pattern.parse().unwrap());
135 let mut reasons = Vec::new(); 141 let mut reasons = Vec::new();
136 for d in match_finder.debug_where_text_equal(file_id, snippet) { 142 for d in match_finder.debug_where_text_equal(position.file_id, snippet) {
137 if let Some(reason) = d.match_failure_reason() { 143 if let Some(reason) = d.match_failure_reason() {
138 reasons.push(reason.to_owned()); 144 reasons.push(reason.to_owned());
139 } 145 }