aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/mock_analysis.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/mock_analysis.rs')
-rw-r--r--crates/ra_ide/src/mock_analysis.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs
new file mode 100644
index 000000000..bf8a54932
--- /dev/null
+++ b/crates/ra_ide/src/mock_analysis.rs
@@ -0,0 +1,149 @@
1//! FIXME: write short doc here
2
3use std::sync::Arc;
4
5use ra_cfg::CfgOptions;
6use ra_db::{Env, RelativePathBuf};
7use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
8
9use crate::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition,
11 FileRange, SourceRootId,
12};
13
14/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
15/// from a set of in-memory files.
16#[derive(Debug, Default)]
17pub struct MockAnalysis {
18 files: Vec<(String, String)>,
19}
20
21impl MockAnalysis {
22 pub fn new() -> MockAnalysis {
23 MockAnalysis::default()
24 }
25 /// Creates `MockAnalysis` using a fixture data in the following format:
26 ///
27 /// ```not_rust
28 /// //- /main.rs
29 /// mod foo;
30 /// fn main() {}
31 ///
32 /// //- /foo.rs
33 /// struct Baz;
34 /// ```
35 pub fn with_files(fixture: &str) -> MockAnalysis {
36 let mut res = MockAnalysis::new();
37 for entry in parse_fixture(fixture) {
38 res.add_file(&entry.meta, &entry.text);
39 }
40 res
41 }
42
43 /// Same as `with_files`, but requires that a single file contains a `<|>` marker,
44 /// whose position is also returned.
45 pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) {
46 let mut position = None;
47 let mut res = MockAnalysis::new();
48 for entry in parse_fixture(fixture) {
49 if entry.text.contains(CURSOR_MARKER) {
50 assert!(position.is_none(), "only one marker (<|>) per fixture is allowed");
51 position = Some(res.add_file_with_position(&entry.meta, &entry.text));
52 } else {
53 res.add_file(&entry.meta, &entry.text);
54 }
55 }
56 let position = position.expect("expected a marker (<|>)");
57 (res, position)
58 }
59
60 pub fn add_file(&mut self, path: &str, text: &str) -> FileId {
61 let file_id = FileId((self.files.len() + 1) as u32);
62 self.files.push((path.to_string(), text.to_string()));
63 file_id
64 }
65 pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition {
66 let (offset, text) = extract_offset(text);
67 let file_id = FileId((self.files.len() + 1) as u32);
68 self.files.push((path.to_string(), text));
69 FilePosition { file_id, offset }
70 }
71 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
72 let (range, text) = extract_range(text);
73 let file_id = FileId((self.files.len() + 1) as u32);
74 self.files.push((path.to_string(), text));
75 FileRange { file_id, range }
76 }
77 pub fn id_of(&self, path: &str) -> FileId {
78 let (idx, _) = self
79 .files
80 .iter()
81 .enumerate()
82 .find(|(_, (p, _text))| path == p)
83 .expect("no file in this mock");
84 FileId(idx as u32 + 1)
85 }
86 pub fn analysis_host(self) -> AnalysisHost {
87 let mut host = AnalysisHost::default();
88 let source_root = SourceRootId(0);
89 let mut change = AnalysisChange::new();
90 change.add_root(source_root, true);
91 let mut crate_graph = CrateGraph::default();
92 let mut root_crate = None;
93 for (i, (path, contents)) in self.files.into_iter().enumerate() {
94 assert!(path.starts_with('/'));
95 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
96 let file_id = FileId(i as u32 + 1);
97 let cfg_options = CfgOptions::default();
98 if path == "/lib.rs" || path == "/main.rs" {
99 root_crate = Some(crate_graph.add_crate_root(
100 file_id,
101 Edition2018,
102 cfg_options,
103 Env::default(),
104 ));
105 } else if path.ends_with("/lib.rs") {
106 let other_crate =
107 crate_graph.add_crate_root(file_id, Edition2018, cfg_options, Env::default());
108 let crate_name = path.parent().unwrap().file_name().unwrap();
109 if let Some(root_crate) = root_crate {
110 crate_graph.add_dep(root_crate, crate_name.into(), other_crate).unwrap();
111 }
112 }
113 change.add_file(source_root, file_id, path, Arc::new(contents));
114 }
115 change.set_crate_graph(crate_graph);
116 host.apply_change(change);
117 host
118 }
119 pub fn analysis(self) -> Analysis {
120 self.analysis_host().analysis()
121 }
122}
123
124/// Creates analysis from a multi-file fixture, returns positions marked with <|>.
125pub fn analysis_and_position(fixture: &str) -> (Analysis, FilePosition) {
126 let (mock, position) = MockAnalysis::with_files_and_position(fixture);
127 (mock.analysis(), position)
128}
129
130/// Creates analysis for a single file.
131pub fn single_file(code: &str) -> (Analysis, FileId) {
132 let mut mock = MockAnalysis::new();
133 let file_id = mock.add_file("/main.rs", code);
134 (mock.analysis(), file_id)
135}
136
137/// Creates analysis for a single file, returns position marked with <|>.
138pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) {
139 let mut mock = MockAnalysis::new();
140 let pos = mock.add_file_with_position("/main.rs", code);
141 (mock.analysis(), pos)
142}
143
144/// Creates analysis for a single file, returns range marked with a pair of <|>.
145pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) {
146 let mut mock = MockAnalysis::new();
147 let pos = mock.add_file_with_range("/main.rs", code);
148 (mock.analysis(), pos)
149}