diff options
Diffstat (limited to 'crates')
73 files changed, 869 insertions, 1131 deletions
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml index a82e1761c..17b04182f 100644 --- a/crates/ra_analysis/Cargo.toml +++ b/crates/ra_analysis/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "ra_analysis" | 3 | name = "ra_analysis" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
@@ -14,7 +15,7 @@ fst = "0.3.1" | |||
14 | im = "12.0.0" | 15 | im = "12.0.0" |
15 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_editor = { path = "../ra_editor" } | 17 | ra_editor = { path = "../ra_editor" } |
17 | salsa = { path = "../salsa" } | 18 | salsa = "0.5.0" |
18 | rustc-hash = "1.0" | 19 | rustc-hash = "1.0" |
19 | 20 | ||
20 | [dev-dependencies] | 21 | [dev-dependencies] |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs new file mode 100644 index 000000000..c69577233 --- /dev/null +++ b/crates/ra_analysis/src/db.rs | |||
@@ -0,0 +1,117 @@ | |||
1 | use std::{ | ||
2 | fmt, | ||
3 | sync::Arc, | ||
4 | hash::{Hash, Hasher}, | ||
5 | }; | ||
6 | use salsa; | ||
7 | use rustc_hash::FxHashSet; | ||
8 | use ra_syntax::File; | ||
9 | use ra_editor::{LineIndex}; | ||
10 | use crate::{ | ||
11 | symbol_index::SymbolIndex, | ||
12 | module_map::{ModulesDatabase, ModuleTreeQuery, ModuleDescriptorQuery}, | ||
13 | FileId, FileResolverImp, | ||
14 | }; | ||
15 | |||
16 | #[derive(Default)] | ||
17 | pub(crate) struct RootDatabase { | ||
18 | runtime: salsa::runtime::Runtime<RootDatabase>, | ||
19 | } | ||
20 | |||
21 | impl fmt::Debug for RootDatabase { | ||
22 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
23 | fmt.write_str("RootDatabase { ... }") | ||
24 | } | ||
25 | } | ||
26 | |||
27 | impl salsa::Database for RootDatabase { | ||
28 | fn salsa_runtime(&self) -> &salsa::runtime::Runtime<RootDatabase> { | ||
29 | &self.runtime | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl salsa::ParallelDatabase for RootDatabase { | ||
34 | fn fork(&self) -> Self { | ||
35 | RootDatabase { | ||
36 | runtime: self.runtime.fork(), | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl Clone for RootDatabase { | ||
42 | fn clone(&self) -> RootDatabase { | ||
43 | salsa::ParallelDatabase::fork(self) | ||
44 | } | ||
45 | } | ||
46 | |||
47 | salsa::database_storage! { | ||
48 | pub(crate) struct RootDatabaseStorage for RootDatabase { | ||
49 | impl FilesDatabase { | ||
50 | fn file_text() for FileTextQuery; | ||
51 | fn file_set() for FileSetQuery; | ||
52 | } | ||
53 | impl SyntaxDatabase { | ||
54 | fn file_syntax() for FileSyntaxQuery; | ||
55 | fn file_lines() for FileLinesQuery; | ||
56 | fn file_symbols() for FileSymbolsQuery; | ||
57 | } | ||
58 | impl ModulesDatabase { | ||
59 | fn module_tree() for ModuleTreeQuery; | ||
60 | fn module_descriptor() for ModuleDescriptorQuery; | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | salsa::query_group! { | ||
66 | pub(crate) trait FilesDatabase: salsa::Database { | ||
67 | fn file_text(file_id: FileId) -> Arc<String> { | ||
68 | type FileTextQuery; | ||
69 | storage input; | ||
70 | } | ||
71 | fn file_set(key: ()) -> Arc<FileSet> { | ||
72 | type FileSetQuery; | ||
73 | storage input; | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #[derive(Default, Debug, PartialEq, Eq)] | ||
79 | pub(crate) struct FileSet { | ||
80 | pub(crate) files: FxHashSet<FileId>, | ||
81 | pub(crate) resolver: FileResolverImp, | ||
82 | } | ||
83 | |||
84 | impl Hash for FileSet { | ||
85 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
86 | let mut files = self.files.iter().cloned().collect::<Vec<_>>(); | ||
87 | files.sort(); | ||
88 | files.hash(hasher); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | salsa::query_group! { | ||
93 | pub(crate) trait SyntaxDatabase: FilesDatabase { | ||
94 | fn file_syntax(file_id: FileId) -> File { | ||
95 | type FileSyntaxQuery; | ||
96 | } | ||
97 | fn file_lines(file_id: FileId) -> Arc<LineIndex> { | ||
98 | type FileLinesQuery; | ||
99 | } | ||
100 | fn file_symbols(file_id: FileId) -> Arc<SymbolIndex> { | ||
101 | type FileSymbolsQuery; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> File { | ||
107 | let text = db.file_text(file_id); | ||
108 | File::parse(&*text) | ||
109 | } | ||
110 | fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> { | ||
111 | let text = db.file_text(file_id); | ||
112 | Arc::new(LineIndex::new(&*text)) | ||
113 | } | ||
114 | fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<SymbolIndex> { | ||
115 | let syntax = db.file_syntax(file_id); | ||
116 | Arc::new(SymbolIndex::for_file(file_id, syntax)) | ||
117 | } | ||
diff --git a/crates/ra_analysis/src/db/imp.rs b/crates/ra_analysis/src/db/imp.rs deleted file mode 100644 index 36f6cf290..000000000 --- a/crates/ra_analysis/src/db/imp.rs +++ /dev/null | |||
@@ -1,153 +0,0 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | any::Any, | ||
4 | hash::{Hash, Hasher}, | ||
5 | collections::hash_map::{DefaultHasher}, | ||
6 | iter, | ||
7 | }; | ||
8 | use rustc_hash::FxHashMap; | ||
9 | use salsa; | ||
10 | use {FileId, imp::FileResolverImp}; | ||
11 | use super::{State, Query, QueryCtx}; | ||
12 | |||
13 | pub(super) type Data = Arc<Any + Send + Sync + 'static>; | ||
14 | |||
15 | #[derive(Debug)] | ||
16 | pub(super) struct Db { | ||
17 | names: Arc<FxHashMap<salsa::QueryTypeId, &'static str>>, | ||
18 | pub(super) imp: salsa::Db<State, Data>, | ||
19 | } | ||
20 | |||
21 | impl Db { | ||
22 | pub(super) fn new(mut reg: QueryRegistry) -> Db { | ||
23 | let config = reg.config.take().unwrap(); | ||
24 | Db { | ||
25 | names: Arc::new(reg.names), | ||
26 | imp: salsa::Db::new(config, State::default()) | ||
27 | } | ||
28 | } | ||
29 | pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db { | ||
30 | let names = self.names.clone(); | ||
31 | let mut invalidations = salsa::Invalidations::new(); | ||
32 | invalidations.invalidate(FILE_TEXT, changed_files.iter().map(hash).map(salsa::InputFingerprint)); | ||
33 | if resolver_changed { | ||
34 | invalidations.invalidate(FILE_SET, iter::once(salsa::InputFingerprint(hash(&())))); | ||
35 | } else { | ||
36 | invalidations.invalidate(FILE_SET, iter::empty()); | ||
37 | } | ||
38 | let imp = self.imp.with_ground_data( | ||
39 | new_state, | ||
40 | invalidations, | ||
41 | ); | ||
42 | Db { names, imp } | ||
43 | } | ||
44 | pub(super) fn extract_trace(&self, ctx: &salsa::QueryCtx<State, Data>) -> Vec<&'static str> { | ||
45 | ctx.trace().into_iter().map(|it| self.names[&it]).collect() | ||
46 | } | ||
47 | } | ||
48 | |||
49 | pub(crate) trait EvalQuery { | ||
50 | type Params; | ||
51 | type Output; | ||
52 | fn query_type(&self) -> salsa::QueryTypeId; | ||
53 | fn f(&self) -> salsa::QueryFn<State, Data>; | ||
54 | fn get(&self, &QueryCtx, Self::Params) -> Arc<Self::Output>; | ||
55 | } | ||
56 | |||
57 | impl<T, R> EvalQuery for Query<T, R> | ||
58 | where | ||
59 | T: Hash + Send + Sync + 'static, | ||
60 | R: Hash + Send + Sync + 'static, | ||
61 | { | ||
62 | type Params = T; | ||
63 | type Output = R; | ||
64 | fn query_type(&self) -> salsa::QueryTypeId { | ||
65 | salsa::QueryTypeId(self.0) | ||
66 | } | ||
67 | fn f(&self) -> salsa::QueryFn<State, Data> { | ||
68 | let f = self.1; | ||
69 | Box::new(move |ctx, data| { | ||
70 | let ctx = QueryCtx { imp: ctx }; | ||
71 | let data: &T = data.downcast_ref().unwrap(); | ||
72 | let res = f(ctx, data); | ||
73 | let h = hash(&res); | ||
74 | (Arc::new(res), salsa::OutputFingerprint(h)) | ||
75 | }) | ||
76 | } | ||
77 | fn get(&self, ctx: &QueryCtx, params: Self::Params) -> Arc<Self::Output> { | ||
78 | let query_id = salsa::QueryId( | ||
79 | self.query_type(), | ||
80 | salsa::InputFingerprint(hash(¶ms)), | ||
81 | ); | ||
82 | let res = ctx.imp.get(query_id, Arc::new(params)); | ||
83 | res.downcast().unwrap() | ||
84 | } | ||
85 | } | ||
86 | |||
87 | pub(super) struct QueryRegistry { | ||
88 | config: Option<salsa::QueryConfig<State, Data>>, | ||
89 | names: FxHashMap<salsa::QueryTypeId, &'static str>, | ||
90 | } | ||
91 | |||
92 | impl QueryRegistry { | ||
93 | pub(super) fn new() -> QueryRegistry { | ||
94 | let mut config = salsa::QueryConfig::<State, Data>::new(); | ||
95 | config = config.with_ground_query( | ||
96 | FILE_TEXT, Box::new(|state, params| { | ||
97 | let file_id: &FileId = params.downcast_ref().unwrap(); | ||
98 | let res = state.file_map[file_id].clone(); | ||
99 | let fingerprint = salsa::OutputFingerprint(hash(&res)); | ||
100 | (res, fingerprint) | ||
101 | }) | ||
102 | ); | ||
103 | config = config.with_ground_query( | ||
104 | FILE_SET, Box::new(|state, _params| { | ||
105 | let file_ids: Vec<FileId> = state.file_map.keys().cloned().collect(); | ||
106 | let hash = hash(&file_ids); | ||
107 | let file_resolver = state.file_resolver.clone(); | ||
108 | let res = (file_ids, file_resolver); | ||
109 | let fingerprint = salsa::OutputFingerprint(hash); | ||
110 | (Arc::new(res), fingerprint) | ||
111 | }) | ||
112 | ); | ||
113 | let mut names = FxHashMap::default(); | ||
114 | names.insert(FILE_TEXT, "FILE_TEXT"); | ||
115 | names.insert(FILE_SET, "FILE_SET"); | ||
116 | QueryRegistry { config: Some(config), names } | ||
117 | } | ||
118 | pub(super) fn add<Q: EvalQuery>(&mut self, q: Q, name: &'static str) { | ||
119 | let id = q.query_type(); | ||
120 | let prev = self.names.insert(id, name); | ||
121 | assert!(prev.is_none(), "duplicate query: {:?}", id); | ||
122 | let config = self.config.take().unwrap(); | ||
123 | let config = config.with_query(id, q.f()); | ||
124 | self.config= Some(config); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | fn hash<T: Hash>(x: &T) -> u64 { | ||
129 | let mut hasher = DefaultHasher::new(); | ||
130 | x.hash(&mut hasher); | ||
131 | hasher.finish() | ||
132 | } | ||
133 | |||
134 | const FILE_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(0); | ||
135 | pub(super) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> { | ||
136 | let query_id = salsa::QueryId( | ||
137 | FILE_TEXT, | ||
138 | salsa::InputFingerprint(hash(&file_id)), | ||
139 | ); | ||
140 | let res = ctx.imp.get(query_id, Arc::new(file_id)); | ||
141 | res.downcast().unwrap() | ||
142 | } | ||
143 | |||
144 | const FILE_SET: salsa::QueryTypeId = salsa::QueryTypeId(1); | ||
145 | pub(super) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> { | ||
146 | let query_id = salsa::QueryId( | ||
147 | FILE_SET, | ||
148 | salsa::InputFingerprint(hash(&())), | ||
149 | ); | ||
150 | let res = ctx.imp.get(query_id, Arc::new(())); | ||
151 | res.downcast().unwrap() | ||
152 | } | ||
153 | |||
diff --git a/crates/ra_analysis/src/db/mod.rs b/crates/ra_analysis/src/db/mod.rs deleted file mode 100644 index 22769d112..000000000 --- a/crates/ra_analysis/src/db/mod.rs +++ /dev/null | |||
@@ -1,85 +0,0 @@ | |||
1 | mod imp; | ||
2 | |||
3 | use std::{ | ||
4 | sync::Arc, | ||
5 | }; | ||
6 | use im; | ||
7 | use salsa; | ||
8 | use {FileId, imp::FileResolverImp}; | ||
9 | |||
10 | #[derive(Debug, Default, Clone)] | ||
11 | pub(crate) struct State { | ||
12 | pub(crate) file_map: im::HashMap<FileId, Arc<String>>, | ||
13 | pub(crate) file_resolver: FileResolverImp | ||
14 | } | ||
15 | |||
16 | #[derive(Debug)] | ||
17 | pub(crate) struct Db { | ||
18 | imp: imp::Db, | ||
19 | } | ||
20 | |||
21 | #[derive(Clone, Copy)] | ||
22 | pub(crate) struct QueryCtx<'a> { | ||
23 | imp: &'a salsa::QueryCtx<State, imp::Data>, | ||
24 | } | ||
25 | |||
26 | pub(crate) struct Query<T, R>(pub(crate) u16, pub(crate) fn(QueryCtx, &T) -> R); | ||
27 | |||
28 | pub(crate) struct QueryRegistry { | ||
29 | imp: imp::QueryRegistry, | ||
30 | } | ||
31 | |||
32 | impl Default for Db { | ||
33 | fn default() -> Db { | ||
34 | Db::new() | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl Db { | ||
39 | pub(crate) fn new() -> Db { | ||
40 | let reg = QueryRegistry::new(); | ||
41 | Db { imp: imp::Db::new(reg.imp) } | ||
42 | } | ||
43 | pub(crate) fn state(&self) -> &State { | ||
44 | self.imp.imp.ground_data() | ||
45 | } | ||
46 | pub(crate) fn with_changes(&self, new_state: State, changed_files: &[FileId], resolver_changed: bool) -> Db { | ||
47 | Db { imp: self.imp.with_changes(new_state, changed_files, resolver_changed) } | ||
48 | } | ||
49 | pub(crate) fn make_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> R { | ||
50 | let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; | ||
51 | f(ctx) | ||
52 | } | ||
53 | #[allow(unused)] | ||
54 | pub(crate) fn trace_query<F: FnOnce(QueryCtx) -> R, R>(&self, f: F) -> (R, Vec<&'static str>) { | ||
55 | let ctx = QueryCtx { imp: &self.imp.imp.query_ctx() }; | ||
56 | let res = f(ctx); | ||
57 | let trace = self.imp.extract_trace(ctx.imp); | ||
58 | (res, trace) | ||
59 | } | ||
60 | } | ||
61 | |||
62 | impl<'a> QueryCtx<'a> { | ||
63 | pub(crate) fn get<Q: imp::EvalQuery>(&self, q: Q, params: Q::Params) -> Arc<Q::Output> { | ||
64 | q.get(self, params) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> { | ||
69 | imp::file_text(ctx, file_id) | ||
70 | } | ||
71 | |||
72 | pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> { | ||
73 | imp::file_set(ctx) | ||
74 | } | ||
75 | impl QueryRegistry { | ||
76 | fn new() -> QueryRegistry { | ||
77 | let mut reg = QueryRegistry { imp: imp::QueryRegistry::new() }; | ||
78 | ::queries::register_queries(&mut reg); | ||
79 | ::module_map::register_queries(&mut reg); | ||
80 | reg | ||
81 | } | ||
82 | pub(crate) fn add<Q: imp::EvalQuery>(&mut self, q: Q, name: &'static str) { | ||
83 | self.imp.add(q, name) | ||
84 | } | ||
85 | } | ||
diff --git a/crates/ra_analysis/src/descriptors.rs b/crates/ra_analysis/src/descriptors.rs index 0731b5572..8d9f38ca5 100644 --- a/crates/ra_analysis/src/descriptors.rs +++ b/crates/ra_analysis/src/descriptors.rs | |||
@@ -4,14 +4,15 @@ use std::{ | |||
4 | use relative_path::RelativePathBuf; | 4 | use relative_path::RelativePathBuf; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | SmolStr, | 6 | SmolStr, |
7 | ast::{self, NameOwner}, | 7 | ast::{self, NameOwner, AstNode}, |
8 | text_utils::is_subrange | ||
8 | }; | 9 | }; |
9 | use { | 10 | use crate::{ |
10 | FileId, | 11 | FileId, |
11 | imp::FileResolverImp, | 12 | imp::FileResolverImp, |
12 | }; | 13 | }; |
13 | 14 | ||
14 | #[derive(Debug, Hash)] | 15 | #[derive(Debug, PartialEq, Eq, Hash)] |
15 | pub struct ModuleDescriptor { | 16 | pub struct ModuleDescriptor { |
16 | pub submodules: Vec<Submodule> | 17 | pub submodules: Vec<Submodule> |
17 | } | 18 | } |
@@ -42,7 +43,7 @@ pub struct Submodule { | |||
42 | pub name: SmolStr, | 43 | pub name: SmolStr, |
43 | } | 44 | } |
44 | 45 | ||
45 | #[derive(Hash, Debug)] | 46 | #[derive(Debug, PartialEq, Eq, Hash)] |
46 | pub(crate) struct ModuleTreeDescriptor { | 47 | pub(crate) struct ModuleTreeDescriptor { |
47 | nodes: Vec<NodeData>, | 48 | nodes: Vec<NodeData>, |
48 | links: Vec<LinkData>, | 49 | links: Vec<LinkData>, |
@@ -51,7 +52,7 @@ pub(crate) struct ModuleTreeDescriptor { | |||
51 | 52 | ||
52 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 53 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] |
53 | struct Node(usize); | 54 | struct Node(usize); |
54 | #[derive(Hash, Debug)] | 55 | #[derive(Hash, Debug, PartialEq, Eq)] |
55 | struct NodeData { | 56 | struct NodeData { |
56 | file_id: FileId, | 57 | file_id: FileId, |
57 | links: Vec<Link>, | 58 | links: Vec<Link>, |
@@ -60,7 +61,7 @@ struct NodeData { | |||
60 | 61 | ||
61 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | 62 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] |
62 | pub(crate) struct Link(usize); | 63 | pub(crate) struct Link(usize); |
63 | #[derive(Hash, Debug)] | 64 | #[derive(Hash, Debug, PartialEq, Eq)] |
64 | struct LinkData { | 65 | struct LinkData { |
65 | owner: Node, | 66 | owner: Node, |
66 | name: SmolStr, | 67 | name: SmolStr, |
@@ -69,7 +70,7 @@ struct LinkData { | |||
69 | } | 70 | } |
70 | 71 | ||
71 | 72 | ||
72 | #[derive(Clone, Debug, Hash)] | 73 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
73 | pub enum Problem { | 74 | pub enum Problem { |
74 | UnresolvedModule { | 75 | UnresolvedModule { |
75 | candidate: RelativePathBuf, | 76 | candidate: RelativePathBuf, |
@@ -218,3 +219,56 @@ fn resolve_submodule( | |||
218 | } | 219 | } |
219 | (points_to, problem) | 220 | (points_to, problem) |
220 | } | 221 | } |
222 | |||
223 | #[derive(Debug, Clone)] | ||
224 | pub struct FnDescriptor { | ||
225 | pub name: String, | ||
226 | pub label : String, | ||
227 | pub ret_type: Option<String>, | ||
228 | pub params: Vec<String>, | ||
229 | } | ||
230 | |||
231 | impl FnDescriptor { | ||
232 | pub fn new(node: ast::FnDef) -> Option<Self> { | ||
233 | let name = node.name()?.text().to_string(); | ||
234 | |||
235 | // Strip the body out for the label. | ||
236 | let label : String = if let Some(body) = node.body() { | ||
237 | let body_range = body.syntax().range(); | ||
238 | let label : String = node.syntax().children() | ||
239 | .filter(|child| !is_subrange(body_range, child.range())) | ||
240 | .map(|node| node.text().to_string()) | ||
241 | .collect(); | ||
242 | label | ||
243 | } else { | ||
244 | node.syntax().text().to_string() | ||
245 | }; | ||
246 | |||
247 | let params = FnDescriptor::param_list(node); | ||
248 | let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); | ||
249 | |||
250 | Some(FnDescriptor { | ||
251 | name, | ||
252 | ret_type, | ||
253 | params, | ||
254 | label | ||
255 | }) | ||
256 | } | ||
257 | |||
258 | fn param_list(node: ast::FnDef) -> Vec<String> { | ||
259 | let mut res = vec![]; | ||
260 | if let Some(param_list) = node.param_list() { | ||
261 | if let Some(self_param) = param_list.self_param() { | ||
262 | res.push(self_param.syntax().text().to_string()) | ||
263 | } | ||
264 | |||
265 | // Maybe use param.pat here? See if we can just extract the name? | ||
266 | //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); | ||
267 | res.extend(param_list.params() | ||
268 | .filter_map(|p| p.pat()) | ||
269 | .map(|pat| pat.syntax().text().to_string()) | ||
270 | ); | ||
271 | } | ||
272 | res | ||
273 | } | ||
274 | } | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 47bc0032b..5efcaeca0 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | sync::{ | 2 | sync::{ |
3 | Arc, | 3 | Arc, |
4 | atomic::{AtomicBool, Ordering::SeqCst}, | ||
5 | }, | 4 | }, |
5 | hash::{Hash, Hasher}, | ||
6 | fmt, | 6 | fmt, |
7 | collections::VecDeque, | 7 | collections::VecDeque, |
8 | iter, | 8 | iter, |
@@ -12,24 +12,38 @@ use relative_path::RelativePath; | |||
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; | 13 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | TextUnit, TextRange, SmolStr, File, AstNode, | 15 | TextUnit, TextRange, SmolStr, File, AstNode, SyntaxNodeRef, |
16 | SyntaxKind::*, | 16 | SyntaxKind::*, |
17 | ast::{self, NameOwner}, | 17 | ast::{self, NameOwner, ArgListOwner, Expr}, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | use { | 20 | use crate::{ |
21 | FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, | 21 | FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, |
22 | JobToken, CrateGraph, CrateId, | 22 | JobToken, CrateGraph, CrateId, |
23 | roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, | 23 | roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, |
24 | descriptors::{ModuleTreeDescriptor, Problem}, | 24 | descriptors::{FnDescriptor, ModuleTreeDescriptor, Problem}, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | |||
28 | #[derive(Clone, Debug)] | 27 | #[derive(Clone, Debug)] |
29 | pub(crate) struct FileResolverImp { | 28 | pub(crate) struct FileResolverImp { |
30 | inner: Arc<FileResolver> | 29 | inner: Arc<FileResolver> |
31 | } | 30 | } |
32 | 31 | ||
32 | impl PartialEq for FileResolverImp { | ||
33 | fn eq(&self, other: &FileResolverImp) -> bool { | ||
34 | self.inner() == other.inner() | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl Eq for FileResolverImp { | ||
39 | } | ||
40 | |||
41 | impl Hash for FileResolverImp { | ||
42 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
43 | self.inner().hash(hasher); | ||
44 | } | ||
45 | } | ||
46 | |||
33 | impl FileResolverImp { | 47 | impl FileResolverImp { |
34 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { | 48 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { |
35 | FileResolverImp { inner } | 49 | FileResolverImp { inner } |
@@ -40,6 +54,9 @@ impl FileResolverImp { | |||
40 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | 54 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { |
41 | self.inner.resolve(file_id, path) | 55 | self.inner.resolve(file_id, path) |
42 | } | 56 | } |
57 | fn inner(&self) -> *const FileResolver { | ||
58 | &*self.inner | ||
59 | } | ||
43 | } | 60 | } |
44 | 61 | ||
45 | impl Default for FileResolverImp { | 62 | impl Default for FileResolverImp { |
@@ -60,29 +77,27 @@ impl Default for FileResolverImp { | |||
60 | 77 | ||
61 | #[derive(Debug)] | 78 | #[derive(Debug)] |
62 | pub(crate) struct AnalysisHostImpl { | 79 | pub(crate) struct AnalysisHostImpl { |
63 | data: Arc<WorldData> | 80 | data: WorldData |
64 | } | 81 | } |
65 | 82 | ||
66 | impl AnalysisHostImpl { | 83 | impl AnalysisHostImpl { |
67 | pub fn new() -> AnalysisHostImpl { | 84 | pub fn new() -> AnalysisHostImpl { |
68 | AnalysisHostImpl { | 85 | AnalysisHostImpl { |
69 | data: Arc::new(WorldData::default()), | 86 | data: WorldData::default(), |
70 | } | 87 | } |
71 | } | 88 | } |
72 | pub fn analysis(&self) -> AnalysisImpl { | 89 | pub fn analysis(&self) -> AnalysisImpl { |
73 | AnalysisImpl { | 90 | AnalysisImpl { |
74 | needs_reindex: AtomicBool::new(false), | ||
75 | data: self.data.clone(), | 91 | data: self.data.clone(), |
76 | } | 92 | } |
77 | } | 93 | } |
78 | pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { | 94 | pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { |
79 | let data = self.data_mut(); | 95 | self.data_mut() |
80 | data.root = Arc::new(data.root.apply_changes(changes, None)); | 96 | .root.apply_changes(changes, None); |
81 | } | 97 | } |
82 | pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { | 98 | pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { |
83 | let data = self.data_mut(); | 99 | self.data_mut() |
84 | data.file_resolver = resolver.clone(); | 100 | .root.apply_changes(&mut iter::empty(), Some(resolver)); |
85 | data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver))); | ||
86 | } | 101 | } |
87 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | 102 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { |
88 | let mut visited = FxHashSet::default(); | 103 | let mut visited = FxHashSet::default(); |
@@ -97,34 +112,24 @@ impl AnalysisHostImpl { | |||
97 | self.data_mut().libs.push(Arc::new(root)); | 112 | self.data_mut().libs.push(Arc::new(root)); |
98 | } | 113 | } |
99 | fn data_mut(&mut self) -> &mut WorldData { | 114 | fn data_mut(&mut self) -> &mut WorldData { |
100 | Arc::make_mut(&mut self.data) | 115 | &mut self.data |
101 | } | 116 | } |
102 | } | 117 | } |
103 | 118 | ||
104 | pub(crate) struct AnalysisImpl { | 119 | pub(crate) struct AnalysisImpl { |
105 | needs_reindex: AtomicBool, | 120 | data: WorldData, |
106 | data: Arc<WorldData>, | ||
107 | } | 121 | } |
108 | 122 | ||
109 | impl fmt::Debug for AnalysisImpl { | 123 | impl fmt::Debug for AnalysisImpl { |
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 124 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
111 | (&*self.data).fmt(f) | 125 | self.data.fmt(f) |
112 | } | ||
113 | } | ||
114 | |||
115 | impl Clone for AnalysisImpl { | ||
116 | fn clone(&self) -> AnalysisImpl { | ||
117 | AnalysisImpl { | ||
118 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
119 | data: Arc::clone(&self.data), | ||
120 | } | ||
121 | } | 126 | } |
122 | } | 127 | } |
123 | 128 | ||
124 | impl AnalysisImpl { | 129 | impl AnalysisImpl { |
125 | fn root(&self, file_id: FileId) -> &SourceRoot { | 130 | fn root(&self, file_id: FileId) -> &SourceRoot { |
126 | if self.data.root.contains(file_id) { | 131 | if self.data.root.contains(file_id) { |
127 | return &*self.data.root; | 132 | return &self.data.root; |
128 | } | 133 | } |
129 | &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() | 134 | &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() |
130 | } | 135 | } |
@@ -306,6 +311,68 @@ impl AnalysisImpl { | |||
306 | .collect() | 311 | .collect() |
307 | } | 312 | } |
308 | 313 | ||
314 | pub fn resolve_callable(&self, file_id: FileId, offset: TextUnit, token: &JobToken) | ||
315 | -> Option<(FnDescriptor, Option<usize>)> { | ||
316 | |||
317 | let root = self.root(file_id); | ||
318 | let file = root.syntax(file_id); | ||
319 | let syntax = file.syntax(); | ||
320 | |||
321 | // Find the calling expression and it's NameRef | ||
322 | let calling_node = FnCallNode::with_node(syntax, offset)?; | ||
323 | let name_ref = calling_node.name_ref()?; | ||
324 | |||
325 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | ||
326 | let file_symbols = self.index_resolve(name_ref, token); | ||
327 | for (_, fs) in file_symbols { | ||
328 | if fs.kind == FN_DEF { | ||
329 | if let Some(fn_def) = find_node_at_offset(syntax, fs.node_range.start()) { | ||
330 | if let Some(descriptor) = FnDescriptor::new(fn_def) { | ||
331 | // If we have a calling expression let's find which argument we are on | ||
332 | let mut current_parameter = None; | ||
333 | |||
334 | let num_params = descriptor.params.len(); | ||
335 | let has_self = fn_def.param_list() | ||
336 | .and_then(|l| l.self_param()) | ||
337 | .is_some(); | ||
338 | |||
339 | if num_params == 1 { | ||
340 | if !has_self { | ||
341 | current_parameter = Some(1); | ||
342 | } | ||
343 | } else if num_params > 1 { | ||
344 | // Count how many parameters into the call we are. | ||
345 | // TODO: This is best effort for now and should be fixed at some point. | ||
346 | // It may be better to see where we are in the arg_list and then check | ||
347 | // where offset is in that list (or beyond). | ||
348 | // Revisit this after we get documentation comments in. | ||
349 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
350 | let start = arg_list.syntax().range().start(); | ||
351 | |||
352 | let range_search = TextRange::from_to(start, offset); | ||
353 | let mut commas: usize = arg_list.syntax().text() | ||
354 | .slice(range_search).to_string() | ||
355 | .matches(",") | ||
356 | .count(); | ||
357 | |||
358 | // If we have a method call eat the first param since it's just self. | ||
359 | if has_self { | ||
360 | commas = commas + 1; | ||
361 | } | ||
362 | |||
363 | current_parameter = Some(commas); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | return Some((descriptor, current_parameter)); | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | None | ||
374 | } | ||
375 | |||
309 | fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { | 376 | fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { |
310 | let name = name_ref.text(); | 377 | let name = name_ref.text(); |
311 | let mut query = Query::new(name.to_string()); | 378 | let mut query = Query::new(name.to_string()); |
@@ -325,9 +392,8 @@ impl AnalysisImpl { | |||
325 | 392 | ||
326 | #[derive(Default, Clone, Debug)] | 393 | #[derive(Default, Clone, Debug)] |
327 | struct WorldData { | 394 | struct WorldData { |
328 | file_resolver: FileResolverImp, | ||
329 | crate_graph: CrateGraph, | 395 | crate_graph: CrateGraph, |
330 | root: Arc<WritableSourceRoot>, | 396 | root: WritableSourceRoot, |
331 | libs: Vec<Arc<ReadonlySourceRoot>>, | 397 | libs: Vec<Arc<ReadonlySourceRoot>>, |
332 | } | 398 | } |
333 | 399 | ||
@@ -355,3 +421,46 @@ impl CrateGraph { | |||
355 | Some(crate_id) | 421 | Some(crate_id) |
356 | } | 422 | } |
357 | } | 423 | } |
424 | |||
425 | enum FnCallNode<'a> { | ||
426 | CallExpr(ast::CallExpr<'a>), | ||
427 | MethodCallExpr(ast::MethodCallExpr<'a>) | ||
428 | } | ||
429 | |||
430 | impl<'a> FnCallNode<'a> { | ||
431 | pub fn with_node(syntax: SyntaxNodeRef, offset: TextUnit) -> Option<FnCallNode> { | ||
432 | if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { | ||
433 | return Some(FnCallNode::CallExpr(expr)); | ||
434 | } | ||
435 | if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) { | ||
436 | return Some(FnCallNode::MethodCallExpr(expr)); | ||
437 | } | ||
438 | None | ||
439 | } | ||
440 | |||
441 | pub fn name_ref(&self) -> Option<ast::NameRef> { | ||
442 | match *self { | ||
443 | FnCallNode::CallExpr(call_expr) => { | ||
444 | Some(match call_expr.expr()? { | ||
445 | Expr::PathExpr(path_expr) => { | ||
446 | path_expr.path()?.segment()?.name_ref()? | ||
447 | }, | ||
448 | _ => return None | ||
449 | }) | ||
450 | }, | ||
451 | |||
452 | FnCallNode::MethodCallExpr(call_expr) => { | ||
453 | call_expr.syntax().children() | ||
454 | .filter_map(ast::NameRef::cast) | ||
455 | .nth(0) | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | |||
460 | pub fn arg_list(&self) -> Option<ast::ArgList> { | ||
461 | match *self { | ||
462 | FnCallNode::CallExpr(expr) => expr.arg_list(), | ||
463 | FnCallNode::MethodCallExpr(expr) => expr.arg_list() | ||
464 | } | ||
465 | } | ||
466 | } | ||
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 849fd93e4..d8b355a81 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -19,7 +19,6 @@ mod imp; | |||
19 | mod job; | 19 | mod job; |
20 | mod roots; | 20 | mod roots; |
21 | mod db; | 21 | mod db; |
22 | mod queries; | ||
23 | mod descriptors; | 22 | mod descriptors; |
24 | 23 | ||
25 | use std::{ | 24 | use std::{ |
@@ -29,15 +28,18 @@ use std::{ | |||
29 | 28 | ||
30 | use relative_path::{RelativePath, RelativePathBuf}; | 29 | use relative_path::{RelativePath, RelativePathBuf}; |
31 | use ra_syntax::{File, TextRange, TextUnit, AtomEdit}; | 30 | use ra_syntax::{File, TextRange, TextUnit, AtomEdit}; |
32 | use imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp}; | ||
33 | use rustc_hash::FxHashMap; | 31 | use rustc_hash::FxHashMap; |
32 | use crate::imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp}; | ||
34 | 33 | ||
35 | pub use ra_editor::{ | 34 | pub use ra_editor::{ |
36 | StructureNode, LineIndex, FileSymbol, | 35 | StructureNode, LineIndex, FileSymbol, |
37 | Runnable, RunnableKind, HighlightedRange, CompletionItem, | 36 | Runnable, RunnableKind, HighlightedRange, CompletionItem, |
38 | Fold, FoldKind | 37 | Fold, FoldKind |
39 | }; | 38 | }; |
40 | pub use job::{JobToken, JobHandle}; | 39 | pub use crate::{ |
40 | job::{JobToken, JobHandle}, | ||
41 | descriptors::FnDescriptor, | ||
42 | }; | ||
41 | 43 | ||
42 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 44 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
43 | pub struct FileId(pub u32); | 45 | pub struct FileId(pub u32); |
@@ -159,7 +161,7 @@ impl Query { | |||
159 | } | 161 | } |
160 | } | 162 | } |
161 | 163 | ||
162 | #[derive(Clone, Debug)] | 164 | #[derive(Debug)] |
163 | pub struct Analysis { | 165 | pub struct Analysis { |
164 | imp: AnalysisImpl | 166 | imp: AnalysisImpl |
165 | } | 167 | } |
@@ -236,6 +238,11 @@ impl Analysis { | |||
236 | let file = self.imp.file_syntax(file_id); | 238 | let file = self.imp.file_syntax(file_id); |
237 | ra_editor::folding_ranges(&file) | 239 | ra_editor::folding_ranges(&file) |
238 | } | 240 | } |
241 | |||
242 | pub fn resolve_callable(&self, file_id: FileId, offset: TextUnit, token: &JobToken) | ||
243 | -> Option<(FnDescriptor, Option<usize>)> { | ||
244 | self.imp.resolve_callable(file_id, offset, token) | ||
245 | } | ||
239 | } | 246 | } |
240 | 247 | ||
241 | #[derive(Debug)] | 248 | #[derive(Debug)] |
@@ -250,3 +257,9 @@ impl LibraryData { | |||
250 | LibraryData { root } | 257 | LibraryData { root } |
251 | } | 258 | } |
252 | } | 259 | } |
260 | |||
261 | #[test] | ||
262 | fn analysis_is_send() { | ||
263 | fn is_send<T: Send>() {} | ||
264 | is_send::<Analysis>(); | ||
265 | } | ||
diff --git a/crates/ra_analysis/src/module_map.rs b/crates/ra_analysis/src/module_map.rs index a21f55fff..c1799e3d4 100644 --- a/crates/ra_analysis/src/module_map.rs +++ b/crates/ra_analysis/src/module_map.rs | |||
@@ -1,157 +1,34 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | use { | 2 | use crate::{ |
3 | FileId, | 3 | FileId, |
4 | db::{ | 4 | db::{SyntaxDatabase}, |
5 | Query, QueryRegistry, QueryCtx, | ||
6 | file_set | ||
7 | }, | ||
8 | queries::file_syntax, | ||
9 | descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, | 5 | descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, |
10 | }; | 6 | }; |
11 | 7 | ||
12 | pub(crate) fn register_queries(reg: &mut QueryRegistry) { | 8 | salsa::query_group! { |
13 | reg.add(MODULE_DESCR, "MODULE_DESCR"); | 9 | pub(crate) trait ModulesDatabase: SyntaxDatabase { |
14 | reg.add(MODULE_TREE, "MODULE_TREE"); | 10 | fn module_tree(key: ()) -> Arc<ModuleTreeDescriptor> { |
15 | } | 11 | type ModuleTreeQuery; |
16 | |||
17 | pub(crate) fn module_tree(ctx: QueryCtx) -> Arc<ModuleTreeDescriptor> { | ||
18 | ctx.get(MODULE_TREE, ()) | ||
19 | } | ||
20 | |||
21 | const MODULE_DESCR: Query<FileId, ModuleDescriptor> = Query(30, |ctx, &file_id| { | ||
22 | let file = file_syntax(ctx, file_id); | ||
23 | ModuleDescriptor::new(file.ast()) | ||
24 | }); | ||
25 | |||
26 | const MODULE_TREE: Query<(), ModuleTreeDescriptor> = Query(31, |ctx, _| { | ||
27 | let file_set = file_set(ctx); | ||
28 | let mut files = Vec::new(); | ||
29 | for &file_id in file_set.0.iter() { | ||
30 | let module_descr = ctx.get(MODULE_DESCR, file_id); | ||
31 | files.push((file_id, module_descr)); | ||
32 | } | ||
33 | ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.1) | ||
34 | }); | ||
35 | |||
36 | #[cfg(test)] | ||
37 | mod tests { | ||
38 | use std::collections::HashMap; | ||
39 | use im; | ||
40 | use relative_path::{RelativePath, RelativePathBuf}; | ||
41 | use { | ||
42 | db::{Db}, | ||
43 | imp::FileResolverImp, | ||
44 | FileId, FileResolver, | ||
45 | }; | ||
46 | use super::*; | ||
47 | |||
48 | #[derive(Debug)] | ||
49 | struct FileMap(im::HashMap<FileId, RelativePathBuf>); | ||
50 | |||
51 | impl FileResolver for FileMap { | ||
52 | fn file_stem(&self, file_id: FileId) -> String { | ||
53 | self.0[&file_id].file_stem().unwrap().to_string() | ||
54 | } | 12 | } |
55 | fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> { | 13 | fn module_descriptor(file_id: FileId) -> Arc<ModuleDescriptor> { |
56 | let path = self.0[&file_id].join(rel).normalize(); | 14 | type ModuleDescriptorQuery; |
57 | self.0.iter() | ||
58 | .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path)) | ||
59 | .next() | ||
60 | } | 15 | } |
61 | } | 16 | } |
17 | } | ||
62 | 18 | ||
63 | struct Fixture { | ||
64 | next_file_id: u32, | ||
65 | fm: im::HashMap<FileId, RelativePathBuf>, | ||
66 | db: Db, | ||
67 | } | ||
68 | |||
69 | impl Fixture { | ||
70 | fn new() -> Fixture { | ||
71 | Fixture { | ||
72 | next_file_id: 1, | ||
73 | fm: im::HashMap::new(), | ||
74 | db: Db::new(), | ||
75 | } | ||
76 | } | ||
77 | fn add_file(&mut self, path: &str, text: &str) -> FileId { | ||
78 | assert!(path.starts_with("/")); | ||
79 | let file_id = FileId(self.next_file_id); | ||
80 | self.next_file_id += 1; | ||
81 | self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); | ||
82 | let mut new_state = self.db.state().clone(); | ||
83 | new_state.file_map.insert(file_id, Arc::new(text.to_string())); | ||
84 | new_state.file_resolver = FileResolverImp::new( | ||
85 | Arc::new(FileMap(self.fm.clone())) | ||
86 | ); | ||
87 | self.db = self.db.with_changes(new_state, &[file_id], true); | ||
88 | file_id | ||
89 | } | ||
90 | fn remove_file(&mut self, file_id: FileId) { | ||
91 | self.fm.remove(&file_id); | ||
92 | let mut new_state = self.db.state().clone(); | ||
93 | new_state.file_map.remove(&file_id); | ||
94 | new_state.file_resolver = FileResolverImp::new( | ||
95 | Arc::new(FileMap(self.fm.clone())) | ||
96 | ); | ||
97 | self.db = self.db.with_changes(new_state, &[file_id], true); | ||
98 | } | ||
99 | fn change_file(&mut self, file_id: FileId, new_text: &str) { | ||
100 | let mut new_state = self.db.state().clone(); | ||
101 | new_state.file_map.insert(file_id, Arc::new(new_text.to_string())); | ||
102 | self.db = self.db.with_changes(new_state, &[file_id], false); | ||
103 | } | ||
104 | fn check_parent_modules( | ||
105 | &self, | ||
106 | file_id: FileId, | ||
107 | expected: &[FileId], | ||
108 | queries: &[(&'static str, u64)] | ||
109 | ) { | ||
110 | let (tree, events) = self.db.trace_query(|ctx| module_tree(ctx)); | ||
111 | let actual = tree.parent_modules(file_id) | ||
112 | .into_iter() | ||
113 | .map(|link| link.owner(&tree)) | ||
114 | .collect::<Vec<_>>(); | ||
115 | assert_eq!(actual.as_slice(), expected); | ||
116 | let mut counts = HashMap::new(); | ||
117 | events.into_iter() | ||
118 | .for_each(|event| *counts.entry(event).or_insert(0) += 1); | ||
119 | for &(query_id, expected_count) in queries.iter() { | ||
120 | let actual_count = *counts.get(&query_id).unwrap_or(&0); | ||
121 | assert_eq!( | ||
122 | actual_count, | ||
123 | expected_count, | ||
124 | "counts for {} differ", | ||
125 | query_id, | ||
126 | ) | ||
127 | } | ||
128 | |||
129 | } | ||
130 | } | ||
131 | |||
132 | #[test] | ||
133 | fn test_parent_module() { | ||
134 | let mut f = Fixture::new(); | ||
135 | let foo = f.add_file("/foo.rs", ""); | ||
136 | f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); | ||
137 | |||
138 | let lib = f.add_file("/lib.rs", "mod foo;"); | ||
139 | f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); | ||
140 | f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 0)]); | ||
141 | |||
142 | f.change_file(lib, ""); | ||
143 | f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); | ||
144 | |||
145 | f.change_file(lib, "mod foo;"); | ||
146 | f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); | ||
147 | |||
148 | f.change_file(lib, "mod bar;"); | ||
149 | f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 1)]); | ||
150 | 19 | ||
151 | f.change_file(lib, "mod foo;"); | 20 | fn module_descriptor(db: &impl ModulesDatabase, file_id: FileId) -> Arc<ModuleDescriptor> { |
152 | f.check_parent_modules(foo, &[lib], &[("MODULE_DESCR", 1)]); | 21 | let file = db.file_syntax(file_id); |
22 | Arc::new(ModuleDescriptor::new(file.ast())) | ||
23 | } | ||
153 | 24 | ||
154 | f.remove_file(lib); | 25 | fn module_tree(db: &impl ModulesDatabase, (): ()) -> Arc<ModuleTreeDescriptor> { |
155 | f.check_parent_modules(foo, &[], &[("MODULE_DESCR", 0)]); | 26 | let file_set = db.file_set(()); |
27 | let mut files = Vec::new(); | ||
28 | for &file_id in file_set.files.iter() { | ||
29 | let module_descr = db.module_descriptor(file_id); | ||
30 | files.push((file_id, module_descr)); | ||
156 | } | 31 | } |
32 | let res = ModuleTreeDescriptor::new(files.iter().map(|(file_id, descr)| (*file_id, &**descr)), &file_set.resolver); | ||
33 | Arc::new(res) | ||
157 | } | 34 | } |
diff --git a/crates/ra_analysis/src/queries.rs b/crates/ra_analysis/src/queries.rs deleted file mode 100644 index 062a2f420..000000000 --- a/crates/ra_analysis/src/queries.rs +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | use ra_syntax::File; | ||
3 | use ra_editor::LineIndex; | ||
4 | use { | ||
5 | FileId, | ||
6 | db::{Query, QueryCtx, QueryRegistry}, | ||
7 | symbol_index::SymbolIndex, | ||
8 | }; | ||
9 | |||
10 | pub(crate) use db::{file_text, file_set}; | ||
11 | |||
12 | pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File { | ||
13 | (&*ctx.get(FILE_SYNTAX, file_id)).clone() | ||
14 | } | ||
15 | pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc<LineIndex> { | ||
16 | ctx.get(FILE_LINES, file_id) | ||
17 | } | ||
18 | pub(crate) fn file_symbols(ctx: QueryCtx, file_id: FileId) -> Arc<SymbolIndex> { | ||
19 | ctx.get(FILE_SYMBOLS, file_id) | ||
20 | } | ||
21 | |||
22 | const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| { | ||
23 | let text = file_text(ctx, *file_id); | ||
24 | File::parse(&*text) | ||
25 | }); | ||
26 | const FILE_LINES: Query<FileId, LineIndex> = Query(17, |ctx, file_id: &FileId| { | ||
27 | let text = file_text(ctx, *file_id); | ||
28 | LineIndex::new(&*text) | ||
29 | }); | ||
30 | const FILE_SYMBOLS: Query<FileId, SymbolIndex> = Query(18, |ctx, file_id: &FileId| { | ||
31 | let syntax = file_syntax(ctx, *file_id); | ||
32 | SymbolIndex::for_file(*file_id, syntax) | ||
33 | }); | ||
34 | |||
35 | pub(crate) fn register_queries(reg: &mut QueryRegistry) { | ||
36 | reg.add(FILE_SYNTAX, "FILE_SYNTAX"); | ||
37 | reg.add(FILE_LINES, "FILE_LINES"); | ||
38 | reg.add(FILE_SYMBOLS, "FILE_SYMBOLS"); | ||
39 | } | ||
diff --git a/crates/ra_analysis/src/roots.rs b/crates/ra_analysis/src/roots.rs index 32a8c5bd0..76bcecd38 100644 --- a/crates/ra_analysis/src/roots.rs +++ b/crates/ra_analysis/src/roots.rs | |||
@@ -5,16 +5,18 @@ use std::{ | |||
5 | 5 | ||
6 | use once_cell::sync::OnceCell; | 6 | use once_cell::sync::OnceCell; |
7 | use rayon::prelude::*; | 7 | use rayon::prelude::*; |
8 | use rustc_hash::FxHashMap; | 8 | use salsa::Database; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
9 | use ra_editor::LineIndex; | 10 | use ra_editor::LineIndex; |
10 | use ra_syntax::File; | 11 | use ra_syntax::File; |
11 | 12 | ||
12 | use { | 13 | use crate::{ |
13 | FileId, | 14 | FileId, |
14 | imp::FileResolverImp, | 15 | imp::FileResolverImp, |
15 | symbol_index::SymbolIndex, | 16 | symbol_index::SymbolIndex, |
16 | descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, | 17 | descriptors::{ModuleDescriptor, ModuleTreeDescriptor}, |
17 | db::Db, | 18 | db::{self, FilesDatabase, SyntaxDatabase}, |
19 | module_map::ModulesDatabase, | ||
18 | }; | 20 | }; |
19 | 21 | ||
20 | pub(crate) trait SourceRoot { | 22 | pub(crate) trait SourceRoot { |
@@ -25,62 +27,68 @@ pub(crate) trait SourceRoot { | |||
25 | fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>); | 27 | fn symbols(&self, acc: &mut Vec<Arc<SymbolIndex>>); |
26 | } | 28 | } |
27 | 29 | ||
28 | #[derive(Default, Debug)] | 30 | #[derive(Default, Debug, Clone)] |
29 | pub(crate) struct WritableSourceRoot { | 31 | pub(crate) struct WritableSourceRoot { |
30 | db: Db, | 32 | db: db::RootDatabase, |
31 | } | 33 | } |
32 | 34 | ||
33 | impl WritableSourceRoot { | 35 | impl WritableSourceRoot { |
34 | pub fn apply_changes( | 36 | pub fn apply_changes( |
35 | &self, | 37 | &mut self, |
36 | changes: &mut dyn Iterator<Item=(FileId, Option<String>)>, | 38 | changes: &mut dyn Iterator<Item=(FileId, Option<String>)>, |
37 | file_resolver: Option<FileResolverImp>, | 39 | file_resolver: Option<FileResolverImp>, |
38 | ) -> WritableSourceRoot { | 40 | ) { |
39 | let resolver_changed = file_resolver.is_some(); | 41 | let mut changed = FxHashSet::default(); |
40 | let mut changed_files = Vec::new(); | 42 | let mut removed = FxHashSet::default(); |
41 | let mut new_state = self.db.state().clone(); | ||
42 | |||
43 | for (file_id, text) in changes { | 43 | for (file_id, text) in changes { |
44 | changed_files.push(file_id); | ||
45 | match text { | 44 | match text { |
46 | Some(text) => { | ||
47 | new_state.file_map.insert(file_id, Arc::new(text)); | ||
48 | }, | ||
49 | None => { | 45 | None => { |
50 | new_state.file_map.remove(&file_id); | 46 | removed.insert(file_id); |
47 | } | ||
48 | Some(text) => { | ||
49 | self.db.query(db::FileTextQuery) | ||
50 | .set(file_id, Arc::new(text)); | ||
51 | changed.insert(file_id); | ||
51 | } | 52 | } |
52 | } | 53 | } |
53 | } | 54 | } |
54 | if let Some(file_resolver) = file_resolver { | 55 | let file_set = self.db.file_set(()); |
55 | new_state.file_resolver = file_resolver | 56 | let mut files: FxHashSet<FileId> = file_set |
56 | } | 57 | .files |
57 | WritableSourceRoot { | 58 | .clone(); |
58 | db: self.db.with_changes(new_state, &changed_files, resolver_changed) | 59 | for file_id in removed { |
60 | files.remove(&file_id); | ||
59 | } | 61 | } |
62 | files.extend(changed); | ||
63 | let resolver = file_resolver.unwrap_or_else(|| file_set.resolver.clone()); | ||
64 | self.db.query(db::FileSetQuery) | ||
65 | .set((), Arc::new(db::FileSet { files, resolver })); | ||
60 | } | 66 | } |
61 | } | 67 | } |
62 | 68 | ||
63 | impl SourceRoot for WritableSourceRoot { | 69 | impl SourceRoot for WritableSourceRoot { |
64 | fn module_tree(&self) -> Arc<ModuleTreeDescriptor> { | 70 | fn module_tree(&self) -> Arc<ModuleTreeDescriptor> { |
65 | self.db.make_query(::module_map::module_tree) | 71 | self.db.module_tree(()) |
66 | } | 72 | } |
67 | |||
68 | fn contains(&self, file_id: FileId) -> bool { | 73 | fn contains(&self, file_id: FileId) -> bool { |
69 | self.db.state().file_map.contains_key(&file_id) | 74 | self.db.file_set(()) |
75 | .files | ||
76 | .contains(&file_id) | ||
70 | } | 77 | } |
71 | fn lines(&self, file_id: FileId) -> Arc<LineIndex> { | 78 | fn lines(&self, file_id: FileId) -> Arc<LineIndex> { |
72 | self.db.make_query(|ctx| ::queries::file_lines(ctx, file_id)) | 79 | self.db.file_lines(file_id) |
73 | } | 80 | } |
74 | fn syntax(&self, file_id: FileId) -> File { | 81 | fn syntax(&self, file_id: FileId) -> File { |
75 | self.db.make_query(|ctx| ::queries::file_syntax(ctx, file_id)) | 82 | self.db.file_syntax(file_id) |
76 | } | 83 | } |
77 | fn symbols<'a>(&'a self, acc: &mut Vec<Arc<SymbolIndex>>) { | 84 | fn symbols<'a>(&'a self, acc: &mut Vec<Arc<SymbolIndex>>) { |
78 | self.db.make_query(|ctx| { | 85 | let db = &self.db; |
79 | let file_set = ::queries::file_set(ctx); | 86 | let symbols = db.file_set(()); |
80 | let syms = file_set.0.iter() | 87 | let symbols = symbols |
81 | .map(|file_id| ::queries::file_symbols(ctx, *file_id)); | 88 | .files |
82 | acc.extend(syms); | 89 | .iter() |
83 | }); | 90 | .map(|&file_id| db.file_symbols(file_id)); |
91 | acc.extend(symbols); | ||
84 | } | 92 | } |
85 | } | 93 | } |
86 | 94 | ||
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs index ffbb6a29f..54672fde4 100644 --- a/crates/ra_analysis/src/symbol_index.rs +++ b/crates/ra_analysis/src/symbol_index.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | use fst::{self, Streamer}; | 10 | use fst::{self, Streamer}; |
11 | use rayon::prelude::*; | 11 | use rayon::prelude::*; |
12 | use {Query, FileId, JobToken}; | 12 | use crate::{Query, FileId, JobToken}; |
13 | 13 | ||
14 | #[derive(Debug)] | 14 | #[derive(Debug)] |
15 | pub(crate) struct SymbolIndex { | 15 | pub(crate) struct SymbolIndex { |
@@ -17,6 +17,15 @@ pub(crate) struct SymbolIndex { | |||
17 | map: fst::Map, | 17 | map: fst::Map, |
18 | } | 18 | } |
19 | 19 | ||
20 | impl PartialEq for SymbolIndex { | ||
21 | fn eq(&self, other: &SymbolIndex) -> bool { | ||
22 | self.symbols == other.symbols | ||
23 | } | ||
24 | } | ||
25 | |||
26 | impl Eq for SymbolIndex { | ||
27 | } | ||
28 | |||
20 | impl Hash for SymbolIndex { | 29 | impl Hash for SymbolIndex { |
21 | fn hash<H: Hasher>(&self, hasher: &mut H) { | 30 | fn hash<H: Hasher>(&self, hasher: &mut H) { |
22 | self.symbols.hash(hasher) | 31 | self.symbols.hash(hasher) |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index a886cd0ff..2d3679fa9 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -1,6 +1,8 @@ | |||
1 | extern crate relative_path; | 1 | extern crate relative_path; |
2 | extern crate ra_analysis; | 2 | extern crate ra_analysis; |
3 | extern crate rustc_hash; | 3 | extern crate rustc_hash; |
4 | extern crate ra_editor; | ||
5 | extern crate ra_syntax; | ||
4 | extern crate test_utils; | 6 | extern crate test_utils; |
5 | 7 | ||
6 | use std::{ | 8 | use std::{ |
@@ -9,8 +11,8 @@ use std::{ | |||
9 | 11 | ||
10 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
11 | use relative_path::{RelativePath, RelativePathBuf}; | 13 | use relative_path::{RelativePath, RelativePathBuf}; |
12 | use ra_analysis::{Analysis, AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId}; | 14 | use ra_analysis::{Analysis, AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId, FnDescriptor}; |
13 | use test_utils::assert_eq_dbg; | 15 | use test_utils::{assert_eq_dbg, extract_offset}; |
14 | 16 | ||
15 | #[derive(Debug)] | 17 | #[derive(Debug)] |
16 | struct FileMap(Vec<(FileId, RelativePathBuf)>); | 18 | struct FileMap(Vec<(FileId, RelativePathBuf)>); |
@@ -39,7 +41,7 @@ impl FileResolver for FileMap { | |||
39 | } | 41 | } |
40 | } | 42 | } |
41 | 43 | ||
42 | fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost { | 44 | fn analysis_host(files: &[(&str, &str)]) -> AnalysisHost { |
43 | let mut host = AnalysisHost::new(); | 45 | let mut host = AnalysisHost::new(); |
44 | let mut file_map = Vec::new(); | 46 | let mut file_map = Vec::new(); |
45 | for (id, &(path, contents)) in files.iter().enumerate() { | 47 | for (id, &(path, contents)) in files.iter().enumerate() { |
@@ -53,10 +55,20 @@ fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost | |||
53 | host | 55 | host |
54 | } | 56 | } |
55 | 57 | ||
56 | fn analysis(files: &'static [(&'static str, &'static str)]) -> Analysis { | 58 | fn analysis(files: &[(&str, &str)]) -> Analysis { |
57 | analysis_host(files).analysis() | 59 | analysis_host(files).analysis() |
58 | } | 60 | } |
59 | 61 | ||
62 | fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) { | ||
63 | let (offset, code) = extract_offset(text); | ||
64 | let code = code.as_str(); | ||
65 | |||
66 | let (_handle, token) = JobHandle::new(); | ||
67 | let snap = analysis(&[("/lib.rs", code)]); | ||
68 | |||
69 | snap.resolve_callable(FileId(1), offset, &token).unwrap() | ||
70 | } | ||
71 | |||
60 | #[test] | 72 | #[test] |
61 | fn test_resolve_module() { | 73 | fn test_resolve_module() { |
62 | let snap = analysis(&[ | 74 | let snap = analysis(&[ |
@@ -145,3 +157,85 @@ fn test_resolve_crate_root() { | |||
145 | vec![CrateId(1)], | 157 | vec![CrateId(1)], |
146 | ); | 158 | ); |
147 | } | 159 | } |
160 | |||
161 | #[test] | ||
162 | fn test_fn_signature_two_args_first() { | ||
163 | let (desc, param) = get_signature( | ||
164 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
165 | fn bar() { foo(<|>3, ); }"#); | ||
166 | |||
167 | assert_eq!(desc.name, "foo".to_string()); | ||
168 | assert_eq!(desc.params, vec!("x".to_string(),"y".to_string())); | ||
169 | assert_eq!(desc.ret_type, Some("-> u32".into())); | ||
170 | assert_eq!(param, Some(0)); | ||
171 | } | ||
172 | |||
173 | #[test] | ||
174 | fn test_fn_signature_two_args_second() { | ||
175 | let (desc, param) = get_signature( | ||
176 | r#"fn foo(x: u32, y: u32) -> u32 {x + y} | ||
177 | fn bar() { foo(3, <|>); }"#); | ||
178 | |||
179 | assert_eq!(desc.name, "foo".to_string()); | ||
180 | assert_eq!(desc.params, vec!("x".to_string(),"y".to_string())); | ||
181 | assert_eq!(desc.ret_type, Some("-> u32".into())); | ||
182 | assert_eq!(param, Some(1)); | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn test_fn_signature_for_impl() { | ||
187 | let (desc, param) = get_signature( | ||
188 | r#"struct F; impl F { pub fn new() { F{}} } | ||
189 | fn bar() {let _ : F = F::new(<|>);}"#); | ||
190 | |||
191 | assert_eq!(desc.name, "new".to_string()); | ||
192 | assert_eq!(desc.params, Vec::<String>::new()); | ||
193 | assert_eq!(desc.ret_type, None); | ||
194 | assert_eq!(param, None); | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_fn_signature_for_method_self() { | ||
199 | let (desc, param) = get_signature( | ||
200 | r#"struct F; | ||
201 | impl F { | ||
202 | pub fn new() -> F{ | ||
203 | F{} | ||
204 | } | ||
205 | |||
206 | pub fn do_it(&self) {} | ||
207 | } | ||
208 | |||
209 | fn bar() { | ||
210 | let f : F = F::new(); | ||
211 | f.do_it(<|>); | ||
212 | }"#); | ||
213 | |||
214 | assert_eq!(desc.name, "do_it".to_string()); | ||
215 | assert_eq!(desc.params, vec!["&self".to_string()]); | ||
216 | assert_eq!(desc.ret_type, None); | ||
217 | assert_eq!(param, None); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn test_fn_signature_for_method_with_arg() { | ||
222 | let (desc, param) = get_signature( | ||
223 | r#"struct F; | ||
224 | impl F { | ||
225 | pub fn new() -> F{ | ||
226 | F{} | ||
227 | } | ||
228 | |||
229 | pub fn do_it(&self, x: i32) {} | ||
230 | } | ||
231 | |||
232 | fn bar() { | ||
233 | let f : F = F::new(); | ||
234 | f.do_it(<|>); | ||
235 | }"#); | ||
236 | |||
237 | assert_eq!(desc.name, "do_it".to_string()); | ||
238 | assert_eq!(desc.params, vec!["&self".to_string(), "x".to_string()]); | ||
239 | assert_eq!(desc.ret_type, None); | ||
240 | assert_eq!(param, Some(1)); | ||
241 | } | ||
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index 5e7bf3ed4..0b8d6f3dd 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "ra_cli" | 3 | name = "ra_cli" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
diff --git a/crates/ra_editor/Cargo.toml b/crates/ra_editor/Cargo.toml index 91cefc8d7..7791da156 100644 --- a/crates/ra_editor/Cargo.toml +++ b/crates/ra_editor/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "ra_editor" | 3 | name = "ra_editor" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs index 216d592ff..7b0a48c81 100644 --- a/crates/ra_editor/src/code_actions.rs +++ b/crates/ra_editor/src/code_actions.rs | |||
@@ -11,7 +11,7 @@ use ra_syntax::{ | |||
11 | }, | 11 | }, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use {EditBuilder, Edit, find_node_at_offset}; | 14 | use crate::{EditBuilder, Edit, find_node_at_offset}; |
15 | 15 | ||
16 | #[derive(Debug)] | 16 | #[derive(Debug)] |
17 | pub struct LocalEdit { | 17 | pub struct LocalEdit { |
@@ -136,7 +136,7 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta | |||
136 | #[cfg(test)] | 136 | #[cfg(test)] |
137 | mod tests { | 137 | mod tests { |
138 | use super::*; | 138 | use super::*; |
139 | use test_utils::{check_action, check_action_range}; | 139 | use crate::test_utils::{check_action, check_action_range}; |
140 | 140 | ||
141 | #[test] | 141 | #[test] |
142 | fn test_swap_comma() { | 142 | fn test_swap_comma() { |
diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs index 20b8484b3..b6095dca9 100644 --- a/crates/ra_editor/src/completion.rs +++ b/crates/ra_editor/src/completion.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | text_utils::is_subrange, | 9 | text_utils::is_subrange, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use { | 12 | use crate::{ |
13 | AtomEdit, find_node_at_offset, | 13 | AtomEdit, find_node_at_offset, |
14 | scope::{FnScopes, ModuleScope}, | 14 | scope::{FnScopes, ModuleScope}, |
15 | }; | 15 | }; |
diff --git a/crates/ra_editor/src/edit.rs b/crates/ra_editor/src/edit.rs index 2839ac20a..46e687319 100644 --- a/crates/ra_editor/src/edit.rs +++ b/crates/ra_editor/src/edit.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {TextRange, TextUnit}; | 1 | use crate::{TextRange, TextUnit}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AtomEdit, | 3 | AtomEdit, |
4 | text_utils::contains_offset_nonstrict, | 4 | text_utils::contains_offset_nonstrict, |
diff --git a/crates/ra_editor/src/folding_ranges.rs b/crates/ra_editor/src/folding_ranges.rs index 3aabd54ae..a1699d449 100644 --- a/crates/ra_editor/src/folding_ranges.rs +++ b/crates/ra_editor/src/folding_ranges.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast, | ||
5 | AstNode, | ||
4 | File, TextRange, SyntaxNodeRef, | 6 | File, TextRange, SyntaxNodeRef, |
5 | SyntaxKind, | 7 | SyntaxKind::{self, *}, |
6 | Direction, | 8 | Direction, |
7 | }; | 9 | }; |
8 | 10 | ||
@@ -20,67 +22,97 @@ pub struct Fold { | |||
20 | 22 | ||
21 | pub fn folding_ranges(file: &File) -> Vec<Fold> { | 23 | pub fn folding_ranges(file: &File) -> Vec<Fold> { |
22 | let mut res = vec![]; | 24 | let mut res = vec![]; |
23 | let mut visited = FxHashSet::default(); | 25 | let mut visited_comments = FxHashSet::default(); |
24 | 26 | ||
25 | for node in file.syntax().descendants() { | 27 | for node in file.syntax().descendants() { |
26 | if visited.contains(&node) { | 28 | // Fold items that span multiple lines |
29 | if let Some(kind) = fold_kind(node.kind()) { | ||
30 | if has_newline(node) { | ||
31 | res.push(Fold { range: node.range(), kind }); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | // Also fold groups of comments | ||
36 | if visited_comments.contains(&node) { | ||
27 | continue; | 37 | continue; |
28 | } | 38 | } |
39 | if node.kind() == COMMENT { | ||
40 | contiguous_range_for_comment(node, &mut visited_comments) | ||
41 | .map(|range| res.push(Fold { range, kind: FoldKind::Comment })); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | res | ||
46 | } | ||
29 | 47 | ||
30 | let range_and_kind = match node.kind() { | 48 | fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> { |
31 | SyntaxKind::COMMENT => ( | 49 | match kind { |
32 | contiguous_range_for(SyntaxKind::COMMENT, node, &mut visited), | 50 | COMMENT => Some(FoldKind::Comment), |
33 | Some(FoldKind::Comment), | 51 | USE_ITEM => Some(FoldKind::Imports), |
34 | ), | 52 | _ => None |
35 | SyntaxKind::USE_ITEM => ( | 53 | } |
36 | contiguous_range_for(SyntaxKind::USE_ITEM, node, &mut visited), | 54 | } |
37 | Some(FoldKind::Imports), | 55 | |
38 | ), | 56 | fn has_newline( |
39 | _ => (None, None), | 57 | node: SyntaxNodeRef, |
40 | }; | 58 | ) -> bool { |
41 | 59 | for descendant in node.descendants() { | |
42 | match range_and_kind { | 60 | if let Some(ws) = ast::Whitespace::cast(descendant) { |
43 | (Some(range), Some(kind)) => { | 61 | if ws.has_newlines() { |
44 | res.push(Fold { | 62 | return true; |
45 | range: range, | 63 | } |
46 | kind: kind | 64 | } else if let Some(comment) = ast::Comment::cast(descendant) { |
47 | }); | 65 | if comment.has_newlines() { |
66 | return true; | ||
48 | } | 67 | } |
49 | _ => {} | ||
50 | } | 68 | } |
51 | } | 69 | } |
52 | 70 | ||
53 | res | 71 | false |
54 | } | 72 | } |
55 | 73 | ||
56 | fn contiguous_range_for<'a>( | 74 | fn contiguous_range_for_comment<'a>( |
57 | kind: SyntaxKind, | 75 | first: SyntaxNodeRef<'a>, |
58 | node: SyntaxNodeRef<'a>, | ||
59 | visited: &mut FxHashSet<SyntaxNodeRef<'a>>, | 76 | visited: &mut FxHashSet<SyntaxNodeRef<'a>>, |
60 | ) -> Option<TextRange> { | 77 | ) -> Option<TextRange> { |
61 | visited.insert(node); | 78 | visited.insert(first); |
62 | 79 | ||
63 | let left = node; | 80 | // Only fold comments of the same flavor |
64 | let mut right = node; | 81 | let group_flavor = ast::Comment::cast(first)?.flavor(); |
65 | for node in node.siblings(Direction::Next) { | 82 | |
66 | visited.insert(node); | 83 | let mut last = first; |
67 | match node.kind() { | 84 | for node in first.siblings(Direction::Next) { |
68 | SyntaxKind::WHITESPACE if !node.leaf_text().unwrap().as_str().contains("\n\n") => (), | 85 | if let Some(ws) = ast::Whitespace::cast(node) { |
69 | k => { | 86 | // There is a blank line, which means the group ends here |
70 | if k == kind { | 87 | if ws.count_newlines_lazy().take(2).count() == 2 { |
71 | right = node | 88 | break; |
72 | } else { | 89 | } |
73 | break; | 90 | |
74 | } | 91 | // Ignore whitespace without blank lines |
92 | continue; | ||
93 | } | ||
94 | |||
95 | match ast::Comment::cast(node) { | ||
96 | Some(next_comment) if next_comment.flavor() == group_flavor => { | ||
97 | visited.insert(node); | ||
98 | last = node; | ||
99 | } | ||
100 | // The comment group ends because either: | ||
101 | // * An element of a different kind was reached | ||
102 | // * A comment of a different flavor was reached | ||
103 | _ => { | ||
104 | break | ||
75 | } | 105 | } |
76 | } | 106 | } |
77 | } | 107 | } |
78 | if left != right { | 108 | |
109 | if first != last { | ||
79 | Some(TextRange::from_to( | 110 | Some(TextRange::from_to( |
80 | left.range().start(), | 111 | first.range().start(), |
81 | right.range().end(), | 112 | last.range().end(), |
82 | )) | 113 | )) |
83 | } else { | 114 | } else { |
115 | // The group consists of only one element, therefore it cannot be folded | ||
84 | None | 116 | None |
85 | } | 117 | } |
86 | } | 118 | } |
@@ -88,52 +120,65 @@ fn contiguous_range_for<'a>( | |||
88 | #[cfg(test)] | 120 | #[cfg(test)] |
89 | mod tests { | 121 | mod tests { |
90 | use super::*; | 122 | use super::*; |
123 | use test_utils::extract_ranges; | ||
124 | |||
125 | fn do_check(text: &str, fold_kinds: &[FoldKind]) { | ||
126 | let (ranges, text) = extract_ranges(text); | ||
127 | let file = File::parse(&text); | ||
128 | let folds = folding_ranges(&file); | ||
129 | |||
130 | assert_eq!(folds.len(), ranges.len()); | ||
131 | for ((fold, range), fold_kind) in folds.into_iter().zip(ranges.into_iter()).zip(fold_kinds.into_iter()) { | ||
132 | assert_eq!(fold.range.start(), range.start()); | ||
133 | assert_eq!(fold.range.end(), range.end()); | ||
134 | assert_eq!(&fold.kind, fold_kind); | ||
135 | } | ||
136 | } | ||
91 | 137 | ||
92 | #[test] | 138 | #[test] |
93 | fn test_fold_comments() { | 139 | fn test_fold_comments() { |
94 | let text = r#" | 140 | let text = r#" |
95 | // Hello | 141 | <|>// Hello |
96 | // this is a multiline | 142 | // this is a multiline |
97 | // comment | 143 | // comment |
98 | // | 144 | //<|> |
99 | 145 | ||
100 | // But this is not | 146 | // But this is not |
101 | 147 | ||
102 | fn main() { | 148 | fn main() { |
103 | // We should | 149 | <|>// We should |
104 | // also | 150 | // also |
105 | // fold | 151 | // fold |
106 | // this one. | 152 | // this one.<|> |
153 | <|>//! But this one is different | ||
154 | //! because it has another flavor<|> | ||
155 | <|>/* As does this | ||
156 | multiline comment */<|> | ||
107 | }"#; | 157 | }"#; |
108 | 158 | ||
109 | let file = File::parse(&text); | 159 | let fold_kinds = &[ |
110 | let folds = folding_ranges(&file); | 160 | FoldKind::Comment, |
111 | assert_eq!(folds.len(), 2); | 161 | FoldKind::Comment, |
112 | assert_eq!(folds[0].range.start(), 1.into()); | 162 | FoldKind::Comment, |
113 | assert_eq!(folds[0].range.end(), 46.into()); | 163 | FoldKind::Comment, |
114 | assert_eq!(folds[0].kind, FoldKind::Comment); | 164 | ]; |
115 | 165 | do_check(text, fold_kinds); | |
116 | assert_eq!(folds[1].range.start(), 84.into()); | ||
117 | assert_eq!(folds[1].range.end(), 137.into()); | ||
118 | assert_eq!(folds[1].kind, FoldKind::Comment); | ||
119 | } | 166 | } |
120 | 167 | ||
121 | #[test] | 168 | #[test] |
122 | fn test_fold_imports() { | 169 | fn test_fold_imports() { |
123 | let text = r#" | 170 | let text = r#" |
124 | use std::str; | 171 | <|>use std::{ |
125 | use std::vec; | 172 | str, |
126 | use std::io as iop; | 173 | vec, |
174 | io as iop | ||
175 | };<|> | ||
127 | 176 | ||
128 | fn main() { | 177 | fn main() { |
129 | }"#; | 178 | }"#; |
130 | 179 | ||
131 | let file = File::parse(&text); | 180 | let folds = &[FoldKind::Imports]; |
132 | let folds = folding_ranges(&file); | 181 | do_check(text, folds); |
133 | assert_eq!(folds.len(), 1); | ||
134 | assert_eq!(folds[0].range.start(), 1.into()); | ||
135 | assert_eq!(folds[0].range.end(), 48.into()); | ||
136 | assert_eq!(folds[0].kind, FoldKind::Imports); | ||
137 | } | 182 | } |
138 | 183 | ||
139 | 184 | ||
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index 710afc65d..bd61fd191 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -164,7 +164,7 @@ pub fn resolve_local_name(file: &File, offset: TextUnit, name_ref: ast::NameRef) | |||
164 | #[cfg(test)] | 164 | #[cfg(test)] |
165 | mod tests { | 165 | mod tests { |
166 | use super::*; | 166 | use super::*; |
167 | use test_utils::{assert_eq_dbg, extract_offset, add_cursor}; | 167 | use crate::test_utils::{assert_eq_dbg, extract_offset, add_cursor}; |
168 | 168 | ||
169 | #[test] | 169 | #[test] |
170 | fn test_highlighting() { | 170 | fn test_highlighting() { |
diff --git a/crates/ra_editor/src/line_index.rs b/crates/ra_editor/src/line_index.rs index 9cd8da3a8..95d64b8a8 100644 --- a/crates/ra_editor/src/line_index.rs +++ b/crates/ra_editor/src/line_index.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use superslice::Ext; | 1 | use superslice::Ext; |
2 | use ::TextUnit; | 2 | use crate::TextUnit; |
3 | 3 | ||
4 | #[derive(Clone, Debug, Hash)] | 4 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
5 | pub struct LineIndex { | 5 | pub struct LineIndex { |
6 | newlines: Vec<TextUnit>, | 6 | newlines: Vec<TextUnit>, |
7 | } | 7 | } |
diff --git a/crates/ra_editor/src/scope/fn_scope.rs b/crates/ra_editor/src/scope/fn_scope.rs index 9a48bda02..99d698b60 100644 --- a/crates/ra_editor/src/scope/fn_scope.rs +++ b/crates/ra_editor/src/scope/fn_scope.rs | |||
@@ -174,7 +174,7 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
174 | } | 174 | } |
175 | } | 175 | } |
176 | ast::Expr::LambdaExpr(e) => { | 176 | ast::Expr::LambdaExpr(e) => { |
177 | let mut scope = scopes.new_scope(scope); | 177 | let scope = scopes.new_scope(scope); |
178 | scopes.add_params_bindings(scope, e.param_list()); | 178 | scopes.add_params_bindings(scope, e.param_list()); |
179 | if let Some(body) = e.body() { | 179 | if let Some(body) = e.body() { |
180 | scopes.set_scope(body.syntax(), scope); | 180 | scopes.set_scope(body.syntax(), scope); |
@@ -256,7 +256,7 @@ pub fn resolve_local_name<'a>(name_ref: ast::NameRef, scopes: &'a FnScopes) -> O | |||
256 | mod tests { | 256 | mod tests { |
257 | use super::*; | 257 | use super::*; |
258 | use ra_syntax::File; | 258 | use ra_syntax::File; |
259 | use {find_node_at_offset, test_utils::extract_offset}; | 259 | use crate::{find_node_at_offset, test_utils::extract_offset}; |
260 | 260 | ||
261 | fn do_check(code: &str, expected: &[&str]) { | 261 | fn do_check(code: &str, expected: &[&str]) { |
262 | let (off, code) = extract_offset(code); | 262 | let (off, code) = extract_offset(code); |
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs index e5cc5ca28..d9e4b2df7 100644 --- a/crates/ra_editor/src/symbols.rs +++ b/crates/ra_editor/src/symbols.rs | |||
@@ -6,7 +6,7 @@ use ra_syntax::{ | |||
6 | walk::{walk, WalkEvent}, | 6 | walk::{walk, WalkEvent}, |
7 | }, | 7 | }, |
8 | }; | 8 | }; |
9 | use TextRange; | 9 | use crate::TextRange; |
10 | 10 | ||
11 | #[derive(Debug, Clone)] | 11 | #[derive(Debug, Clone)] |
12 | pub struct StructureNode { | 12 | pub struct StructureNode { |
@@ -17,7 +17,7 @@ pub struct StructureNode { | |||
17 | pub kind: SyntaxKind, | 17 | pub kind: SyntaxKind, |
18 | } | 18 | } |
19 | 19 | ||
20 | #[derive(Debug, Clone, Hash)] | 20 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
21 | pub struct FileSymbol { | 21 | pub struct FileSymbol { |
22 | pub name: SmolStr, | 22 | pub name: SmolStr, |
23 | pub node_range: TextRange, | 23 | pub node_range: TextRange, |
diff --git a/crates/ra_editor/src/test_utils.rs b/crates/ra_editor/src/test_utils.rs index c4ea4db6c..49eb530d5 100644 --- a/crates/ra_editor/src/test_utils.rs +++ b/crates/ra_editor/src/test_utils.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{File, TextUnit, TextRange}; | 1 | use ra_syntax::{File, TextUnit, TextRange}; |
2 | pub use _test_utils::*; | 2 | pub use crate::_test_utils::*; |
3 | use LocalEdit; | 3 | use crate::LocalEdit; |
4 | 4 | ||
5 | pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( | 5 | pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( |
6 | before: &str, | 6 | before: &str, |
diff --git a/crates/ra_editor/src/typing.rs b/crates/ra_editor/src/typing.rs index 1dc658f9b..542b9e10b 100644 --- a/crates/ra_editor/src/typing.rs +++ b/crates/ra_editor/src/typing.rs | |||
@@ -10,7 +10,7 @@ use ra_syntax::{ | |||
10 | SyntaxKind::*, | 10 | SyntaxKind::*, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use {LocalEdit, EditBuilder, find_node_at_offset}; | 13 | use crate::{LocalEdit, EditBuilder, find_node_at_offset}; |
14 | 14 | ||
15 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { | 15 | pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { |
16 | let range = if range.is_empty() { | 16 | let range = if range.is_empty() { |
@@ -244,7 +244,7 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str { | |||
244 | #[cfg(test)] | 244 | #[cfg(test)] |
245 | mod tests { | 245 | mod tests { |
246 | use super::*; | 246 | use super::*; |
247 | use test_utils::{check_action, extract_range, extract_offset, add_cursor}; | 247 | use crate::test_utils::{check_action, extract_range, extract_offset, add_cursor}; |
248 | 248 | ||
249 | fn check_join_lines(before: &str, after: &str) { | 249 | fn check_join_lines(before: &str, after: &str) { |
250 | check_action(before, after, |file, offset| { | 250 | check_action(before, after, |file, offset| { |
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 1fe6b2ebe..2b3257117 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "ra_lsp_server" | 3 | name = "ra_lsp_server" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index 3c628f29c..5598ec75f 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs | |||
@@ -7,6 +7,7 @@ use languageserver_types::{ | |||
7 | TextDocumentSyncKind, | 7 | TextDocumentSyncKind, |
8 | ExecuteCommandOptions, | 8 | ExecuteCommandOptions, |
9 | CompletionOptions, | 9 | CompletionOptions, |
10 | SignatureHelpOptions, | ||
10 | DocumentOnTypeFormattingOptions, | 11 | DocumentOnTypeFormattingOptions, |
11 | }; | 12 | }; |
12 | 13 | ||
@@ -26,7 +27,9 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
26 | resolve_provider: None, | 27 | resolve_provider: None, |
27 | trigger_characters: None, | 28 | trigger_characters: None, |
28 | }), | 29 | }), |
29 | signature_help_provider: None, | 30 | signature_help_provider: Some(SignatureHelpOptions { |
31 | trigger_characters: Some(vec!["(".to_string(), ",".to_string()]) | ||
32 | }), | ||
30 | definition_provider: Some(true), | 33 | definition_provider: Some(true), |
31 | type_definition_provider: None, | 34 | type_definition_provider: None, |
32 | implementation_provider: None, | 35 | implementation_provider: None, |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 08a656569..a75b160c5 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -7,7 +7,7 @@ use ra_editor::{LineIndex, LineCol, Edit, AtomEdit}; | |||
7 | use ra_syntax::{SyntaxKind, TextUnit, TextRange}; | 7 | use ra_syntax::{SyntaxKind, TextUnit, TextRange}; |
8 | use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; | 8 | use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; |
9 | 9 | ||
10 | use { | 10 | use crate::{ |
11 | Result, | 11 | Result, |
12 | server_world::ServerWorld, | 12 | server_world::ServerWorld, |
13 | req, | 13 | req, |
@@ -299,7 +299,7 @@ pub fn to_location( | |||
299 | Ok(loc) | 299 | Ok(loc) |
300 | } | 300 | } |
301 | 301 | ||
302 | pub trait MapConvWith<'a>: Sized { | 302 | pub trait MapConvWith<'a>: Sized + 'a { |
303 | type Ctx; | 303 | type Ctx; |
304 | type Output; | 304 | type Output; |
305 | 305 | ||
@@ -309,7 +309,7 @@ pub trait MapConvWith<'a>: Sized { | |||
309 | } | 309 | } |
310 | 310 | ||
311 | impl<'a, I> MapConvWith<'a> for I | 311 | impl<'a, I> MapConvWith<'a> for I |
312 | where I: Iterator, | 312 | where I: Iterator + 'a, |
313 | I::Item: ConvWith | 313 | I::Item: ConvWith |
314 | { | 314 | { |
315 | type Ctx = <I::Item as ConvWith>::Ctx; | 315 | type Ctx = <I::Item as ConvWith>::Ctx; |
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index 60652d55e..7224b1476 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs | |||
@@ -34,5 +34,7 @@ mod project_model; | |||
34 | pub mod thread_watcher; | 34 | pub mod thread_watcher; |
35 | 35 | ||
36 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 36 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
37 | pub use caps::server_capabilities; | 37 | pub use crate::{ |
38 | pub use main_loop::main_loop; | 38 | main_loop::main_loop, |
39 | caps::server_capabilities, | ||
40 | }; | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index ab8be15e9..5acb39b60 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -13,7 +13,7 @@ use ra_syntax::{ | |||
13 | text_utils::contains_offset_nonstrict | 13 | text_utils::contains_offset_nonstrict |
14 | }; | 14 | }; |
15 | 15 | ||
16 | use ::{ | 16 | use crate::{ |
17 | req::{self, Decoration}, Result, | 17 | req::{self, Decoration}, Result, |
18 | conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location}, | 18 | conv::{Conv, ConvWith, TryConvWith, MapConvWith, to_location}, |
19 | server_world::ServerWorld, | 19 | server_world::ServerWorld, |
@@ -411,6 +411,42 @@ pub fn handle_folding_range( | |||
411 | Ok(res) | 411 | Ok(res) |
412 | } | 412 | } |
413 | 413 | ||
414 | pub fn handle_signature_help( | ||
415 | world: ServerWorld, | ||
416 | params: req::TextDocumentPositionParams, | ||
417 | token: JobToken, | ||
418 | ) -> Result<Option<req::SignatureHelp>> { | ||
419 | use languageserver_types::{ParameterInformation, SignatureInformation}; | ||
420 | |||
421 | let file_id = params.text_document.try_conv_with(&world)?; | ||
422 | let line_index = world.analysis().file_line_index(file_id); | ||
423 | let offset = params.position.conv_with(&line_index); | ||
424 | |||
425 | if let Some((descriptor, active_param)) = world.analysis().resolve_callable(file_id, offset, &token) { | ||
426 | let parameters : Vec<ParameterInformation> = | ||
427 | descriptor.params.iter().map(|param| | ||
428 | ParameterInformation { | ||
429 | label: param.clone(), | ||
430 | documentation: None | ||
431 | } | ||
432 | ).collect(); | ||
433 | |||
434 | let sig_info = SignatureInformation { | ||
435 | label: descriptor.label, | ||
436 | documentation: None, | ||
437 | parameters: Some(parameters) | ||
438 | }; | ||
439 | |||
440 | Ok(Some(req::SignatureHelp { | ||
441 | signatures: vec![sig_info], | ||
442 | active_signature: Some(0), | ||
443 | active_parameter: active_param.map(|a| a as u64) | ||
444 | })) | ||
445 | } else { | ||
446 | Ok(None) | ||
447 | } | ||
448 | } | ||
449 | |||
414 | pub fn handle_code_action( | 450 | pub fn handle_code_action( |
415 | world: ServerWorld, | 451 | world: ServerWorld, |
416 | params: req::CodeActionParams, | 452 | params: req::CodeActionParams, |
@@ -442,7 +478,7 @@ pub fn handle_code_action( | |||
442 | } | 478 | } |
443 | 479 | ||
444 | pub fn publish_diagnostics( | 480 | pub fn publish_diagnostics( |
445 | world: ServerWorld, | 481 | world: &ServerWorld, |
446 | file_id: FileId, | 482 | file_id: FileId, |
447 | ) -> Result<req::PublishDiagnosticsParams> { | 483 | ) -> Result<req::PublishDiagnosticsParams> { |
448 | let uri = world.file_id_to_uri(file_id)?; | 484 | let uri = world.file_id_to_uri(file_id)?; |
@@ -461,7 +497,7 @@ pub fn publish_diagnostics( | |||
461 | } | 497 | } |
462 | 498 | ||
463 | pub fn publish_decorations( | 499 | pub fn publish_decorations( |
464 | world: ServerWorld, | 500 | world: &ServerWorld, |
465 | file_id: FileId, | 501 | file_id: FileId, |
466 | ) -> Result<req::PublishDecorationsParams> { | 502 | ) -> Result<req::PublishDecorationsParams> { |
467 | let uri = world.file_id_to_uri(file_id)?; | 503 | let uri = world.file_id_to_uri(file_id)?; |
diff --git a/crates/ra_lsp_server/src/main_loop/mod.rs b/crates/ra_lsp_server/src/main_loop/mod.rs index 402615e42..cf2477cb5 100644 --- a/crates/ra_lsp_server/src/main_loop/mod.rs +++ b/crates/ra_lsp_server/src/main_loop/mod.rs | |||
@@ -16,7 +16,7 @@ use gen_lsp_server::{ | |||
16 | }; | 16 | }; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | 18 | ||
19 | use { | 19 | use crate::{ |
20 | req, | 20 | req, |
21 | Result, | 21 | Result, |
22 | vfs::{self, FileEvent}, | 22 | vfs::{self, FileEvent}, |
@@ -255,6 +255,7 @@ fn on_request( | |||
255 | .on::<req::Completion>(handlers::handle_completion)? | 255 | .on::<req::Completion>(handlers::handle_completion)? |
256 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? | 256 | .on::<req::CodeActionRequest>(handlers::handle_code_action)? |
257 | .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? | 257 | .on::<req::FoldingRangeRequest>(handlers::handle_folding_range)? |
258 | .on::<req::SignatureHelpRequest>(handlers::handle_signature_help)? | ||
258 | .finish(); | 259 | .finish(); |
259 | match req { | 260 | match req { |
260 | Ok((id, handle)) => { | 261 | Ok((id, handle)) => { |
@@ -390,7 +391,7 @@ fn update_file_notifications_on_threadpool( | |||
390 | ) { | 391 | ) { |
391 | pool.spawn(move || { | 392 | pool.spawn(move || { |
392 | for file_id in subscriptions { | 393 | for file_id in subscriptions { |
393 | match handlers::publish_diagnostics(world.clone(), file_id) { | 394 | match handlers::publish_diagnostics(&world, file_id) { |
394 | Err(e) => { | 395 | Err(e) => { |
395 | error!("failed to compute diagnostics: {:?}", e) | 396 | error!("failed to compute diagnostics: {:?}", e) |
396 | } | 397 | } |
@@ -399,7 +400,7 @@ fn update_file_notifications_on_threadpool( | |||
399 | sender.send(Task::Notify(not)); | 400 | sender.send(Task::Notify(not)); |
400 | } | 401 | } |
401 | } | 402 | } |
402 | match handlers::publish_decorations(world.clone(), file_id) { | 403 | match handlers::publish_decorations(&world, file_id) { |
403 | Err(e) => { | 404 | Err(e) => { |
404 | error!("failed to compute decorations: {:?}", e) | 405 | error!("failed to compute decorations: {:?}", e) |
405 | } | 406 | } |
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 43e4fd654..c144d9596 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs | |||
@@ -5,7 +5,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
5 | use cargo_metadata::{metadata_run, CargoOpt}; | 5 | use cargo_metadata::{metadata_run, CargoOpt}; |
6 | use ra_syntax::SmolStr; | 6 | use ra_syntax::SmolStr; |
7 | 7 | ||
8 | use { | 8 | use crate::{ |
9 | Result, | 9 | Result, |
10 | thread_watcher::{Worker, ThreadWatcher}, | 10 | thread_watcher::{Worker, ThreadWatcher}, |
11 | }; | 11 | }; |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index f80957589..1630edf7f 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -14,6 +14,7 @@ pub use languageserver_types::{ | |||
14 | CompletionParams, CompletionResponse, | 14 | CompletionParams, CompletionResponse, |
15 | DocumentOnTypeFormattingParams, | 15 | DocumentOnTypeFormattingParams, |
16 | TextDocumentEdit, | 16 | TextDocumentEdit, |
17 | SignatureHelp, Hover | ||
17 | }; | 18 | }; |
18 | 19 | ||
19 | pub enum SyntaxTree {} | 20 | pub enum SyntaxTree {} |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index c4cdf83d4..9b3013ae8 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -8,7 +8,7 @@ use rustc_hash::FxHashMap; | |||
8 | use languageserver_types::Url; | 8 | use languageserver_types::Url; |
9 | use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; | 9 | use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; |
10 | 10 | ||
11 | use { | 11 | use crate::{ |
12 | Result, | 12 | Result, |
13 | path_map::{PathMap, Root}, | 13 | path_map::{PathMap, Root}, |
14 | vfs::{FileEvent, FileEventKind}, | 14 | vfs::{FileEvent, FileEventKind}, |
@@ -23,7 +23,6 @@ pub struct ServerWorldState { | |||
23 | pub mem_map: FxHashMap<FileId, Option<String>>, | 23 | pub mem_map: FxHashMap<FileId, Option<String>>, |
24 | } | 24 | } |
25 | 25 | ||
26 | #[derive(Clone)] | ||
27 | pub struct ServerWorld { | 26 | pub struct ServerWorld { |
28 | pub workspaces: Arc<Vec<CargoWorkspace>>, | 27 | pub workspaces: Arc<Vec<CargoWorkspace>>, |
29 | pub analysis: Analysis, | 28 | pub analysis: Analysis, |
diff --git a/crates/ra_lsp_server/src/thread_watcher.rs b/crates/ra_lsp_server/src/thread_watcher.rs index 86a3a91e0..3257effcb 100644 --- a/crates/ra_lsp_server/src/thread_watcher.rs +++ b/crates/ra_lsp_server/src/thread_watcher.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::thread; | 1 | use std::thread; |
2 | use crossbeam_channel::{bounded, unbounded, Sender, Receiver}; | 2 | use crossbeam_channel::{bounded, unbounded, Sender, Receiver}; |
3 | use drop_bomb::DropBomb; | 3 | use drop_bomb::DropBomb; |
4 | use Result; | 4 | use crate::Result; |
5 | 5 | ||
6 | pub struct Worker<I, O> { | 6 | pub struct Worker<I, O> { |
7 | pub inp: Sender<I>, | 7 | pub inp: Sender<I>, |
diff --git a/crates/ra_lsp_server/src/vfs.rs b/crates/ra_lsp_server/src/vfs.rs index a1c1783f2..d8f9b1aac 100644 --- a/crates/ra_lsp_server/src/vfs.rs +++ b/crates/ra_lsp_server/src/vfs.rs | |||
@@ -5,7 +5,7 @@ use std::{ | |||
5 | 5 | ||
6 | use walkdir::WalkDir; | 6 | use walkdir::WalkDir; |
7 | 7 | ||
8 | use { | 8 | use crate::{ |
9 | thread_watcher::{Worker, ThreadWatcher}, | 9 | thread_watcher::{Worker, ThreadWatcher}, |
10 | }; | 10 | }; |
11 | 11 | ||
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index dced45f55..7265b5999 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs | |||
@@ -12,7 +12,7 @@ mod support; | |||
12 | 12 | ||
13 | use ra_lsp_server::req::{Runnables, RunnablesParams}; | 13 | use ra_lsp_server::req::{Runnables, RunnablesParams}; |
14 | 14 | ||
15 | use support::project; | 15 | use crate::support::project; |
16 | 16 | ||
17 | 17 | ||
18 | const LOG: &'static str = ""; | 18 | const LOG: &'static str = ""; |
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 8fe2aa816..d1339f62f 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs | |||
@@ -25,7 +25,7 @@ use ra_lsp_server::{main_loop, req, thread_watcher::{ThreadWatcher, Worker}}; | |||
25 | 25 | ||
26 | pub fn project(fixture: &str) -> Server { | 26 | pub fn project(fixture: &str) -> Server { |
27 | static INIT: Once = Once::new(); | 27 | static INIT: Once = Once::new(); |
28 | INIT.call_once(|| Logger::with_env_or_str(::LOG).start().unwrap()); | 28 | INIT.call_once(|| Logger::with_env_or_str(crate::LOG).start().unwrap()); |
29 | 29 | ||
30 | let tmp_dir = TempDir::new("test-project") | 30 | let tmp_dir = TempDir::new("test-project") |
31 | .unwrap(); | 31 | .unwrap(); |
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index 6345e4725..34bb1c591 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "ra_syntax" | 3 | name = "ra_syntax" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
diff --git a/crates/ra_syntax/src/algo/mod.rs b/crates/ra_syntax/src/algo/mod.rs index a6678093d..e686a5704 100644 --- a/crates/ra_syntax/src/algo/mod.rs +++ b/crates/ra_syntax/src/algo/mod.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | pub mod walk; | 1 | pub mod walk; |
2 | pub mod visit; | 2 | pub mod visit; |
3 | 3 | ||
4 | use { | 4 | use crate::{ |
5 | SyntaxNodeRef, TextUnit, TextRange, | 5 | SyntaxNodeRef, TextUnit, TextRange, |
6 | text_utils::{contains_offset_nonstrict, is_subrange}, | 6 | text_utils::{contains_offset_nonstrict, is_subrange}, |
7 | }; | 7 | }; |
diff --git a/crates/ra_syntax/src/algo/visit.rs b/crates/ra_syntax/src/algo/visit.rs index 9f1c127c7..1ae988a87 100644 --- a/crates/ra_syntax/src/algo/visit.rs +++ b/crates/ra_syntax/src/algo/visit.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use std::marker::PhantomData; | 1 | use std::marker::PhantomData; |
2 | use {SyntaxNodeRef, AstNode}; | 2 | use crate::{SyntaxNodeRef, AstNode}; |
3 | 3 | ||
4 | 4 | ||
5 | pub fn visitor<'a, T>() -> impl Visitor<'a, Output=T> { | 5 | pub fn visitor<'a, T>() -> impl Visitor<'a, Output=T> { |
diff --git a/crates/ra_syntax/src/algo/walk.rs b/crates/ra_syntax/src/algo/walk.rs index 8e294d965..d34415626 100644 --- a/crates/ra_syntax/src/algo/walk.rs +++ b/crates/ra_syntax/src/algo/walk.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use { | 1 | use crate::{ |
2 | SyntaxNodeRef, | 2 | SyntaxNodeRef, |
3 | algo::generate, | 3 | algo::generate, |
4 | }; | 4 | }; |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index ef7b5b1a1..160d186b8 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | // This file is automatically generated based on the file `./generated.rs.tera` when `cargo gen-kinds` is run | 1 | // This file is automatically generated based on the file `./generated.rs.tera` when `cargo gen-kinds` is run |
2 | // Do not edit manually | 2 | // Do not edit manually |
3 | 3 | ||
4 | use { | 4 | use crate::{ |
5 | ast, | 5 | ast, |
6 | SyntaxNodeRef, AstNode, | 6 | SyntaxNodeRef, AstNode, |
7 | SyntaxKind::*, | 7 | SyntaxKind::*, |
@@ -1387,7 +1387,11 @@ impl<'a> AstNode<'a> for PathExpr<'a> { | |||
1387 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | 1387 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } |
1388 | } | 1388 | } |
1389 | 1389 | ||
1390 | impl<'a> PathExpr<'a> {} | 1390 | impl<'a> PathExpr<'a> { |
1391 | pub fn path(self) -> Option<Path<'a>> { | ||
1392 | super::child_opt(self) | ||
1393 | } | ||
1394 | } | ||
1391 | 1395 | ||
1392 | // PathPat | 1396 | // PathPat |
1393 | #[derive(Debug, Clone, Copy)] | 1397 | #[derive(Debug, Clone, Copy)] |
@@ -2193,3 +2197,21 @@ impl<'a> WhileExpr<'a> { | |||
2193 | } | 2197 | } |
2194 | } | 2198 | } |
2195 | 2199 | ||
2200 | // Whitespace | ||
2201 | #[derive(Debug, Clone, Copy)] | ||
2202 | pub struct Whitespace<'a> { | ||
2203 | syntax: SyntaxNodeRef<'a>, | ||
2204 | } | ||
2205 | |||
2206 | impl<'a> AstNode<'a> for Whitespace<'a> { | ||
2207 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
2208 | match syntax.kind() { | ||
2209 | WHITESPACE => Some(Whitespace { syntax }), | ||
2210 | _ => None, | ||
2211 | } | ||
2212 | } | ||
2213 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
2214 | } | ||
2215 | |||
2216 | impl<'a> Whitespace<'a> {} | ||
2217 | |||
diff --git a/crates/ra_syntax/src/ast/generated.rs.tera b/crates/ra_syntax/src/ast/generated.rs.tera index ffa9c4134..5cb7a35ed 100644 --- a/crates/ra_syntax/src/ast/generated.rs.tera +++ b/crates/ra_syntax/src/ast/generated.rs.tera | |||
@@ -3,7 +3,7 @@ the below applies to the result of this template | |||
3 | #}// This file is automatically generated based on the file `./generated.rs.tera` when `cargo gen-kinds` is run | 3 | #}// This file is automatically generated based on the file `./generated.rs.tera` when `cargo gen-kinds` is run |
4 | // Do not edit manually | 4 | // Do not edit manually |
5 | 5 | ||
6 | use { | 6 | use crate::{ |
7 | ast, | 7 | ast, |
8 | SyntaxNodeRef, AstNode, | 8 | SyntaxNodeRef, AstNode, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs index 10dac72e5..88193a1ed 100644 --- a/crates/ra_syntax/src/ast/mod.rs +++ b/crates/ra_syntax/src/ast/mod.rs | |||
@@ -4,7 +4,7 @@ use std::marker::PhantomData; | |||
4 | 4 | ||
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | 6 | ||
7 | use { | 7 | use crate::{ |
8 | SmolStr, SyntaxNodeRef, SyntaxKind::*, | 8 | SmolStr, SyntaxNodeRef, SyntaxKind::*, |
9 | yellow::{RefRoot, SyntaxNodeChildren}, | 9 | yellow::{RefRoot, SyntaxNodeChildren}, |
10 | }; | 10 | }; |
@@ -100,8 +100,8 @@ impl<'a> Lifetime<'a> { | |||
100 | } | 100 | } |
101 | 101 | ||
102 | impl<'a> Comment<'a> { | 102 | impl<'a> Comment<'a> { |
103 | pub fn text(&self) -> SmolStr { | 103 | pub fn text(&self) -> &SmolStr { |
104 | self.syntax().leaf_text().unwrap().clone() | 104 | self.syntax().leaf_text().unwrap() |
105 | } | 105 | } |
106 | 106 | ||
107 | pub fn flavor(&self) -> CommentFlavor { | 107 | pub fn flavor(&self) -> CommentFlavor { |
@@ -120,9 +120,17 @@ impl<'a> Comment<'a> { | |||
120 | pub fn prefix(&self) -> &'static str { | 120 | pub fn prefix(&self) -> &'static str { |
121 | self.flavor().prefix() | 121 | self.flavor().prefix() |
122 | } | 122 | } |
123 | |||
124 | pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> { | ||
125 | self.text().chars().filter(|&c| c == '\n').map(|_| &()) | ||
126 | } | ||
127 | |||
128 | pub fn has_newlines(&self) -> bool { | ||
129 | self.count_newlines_lazy().count() > 0 | ||
130 | } | ||
123 | } | 131 | } |
124 | 132 | ||
125 | #[derive(Debug)] | 133 | #[derive(Debug, PartialEq, Eq)] |
126 | pub enum CommentFlavor { | 134 | pub enum CommentFlavor { |
127 | Line, | 135 | Line, |
128 | Doc, | 136 | Doc, |
@@ -142,6 +150,20 @@ impl CommentFlavor { | |||
142 | } | 150 | } |
143 | } | 151 | } |
144 | 152 | ||
153 | impl<'a> Whitespace<'a> { | ||
154 | pub fn text(&self) -> &SmolStr { | ||
155 | &self.syntax().leaf_text().unwrap() | ||
156 | } | ||
157 | |||
158 | pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> { | ||
159 | self.text().chars().filter(|&c| c == '\n').map(|_| &()) | ||
160 | } | ||
161 | |||
162 | pub fn has_newlines(&self) -> bool { | ||
163 | self.count_newlines_lazy().count() > 0 | ||
164 | } | ||
165 | } | ||
166 | |||
145 | impl<'a> Name<'a> { | 167 | impl<'a> Name<'a> { |
146 | pub fn text(&self) -> SmolStr { | 168 | pub fn text(&self) -> SmolStr { |
147 | let ident = self.syntax().first_child() | 169 | let ident = self.syntax().first_child() |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 9da0c2c13..ea8063d3b 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -342,7 +342,7 @@ Grammar( | |||
342 | "TupleExpr": (), | 342 | "TupleExpr": (), |
343 | "ArrayExpr": (), | 343 | "ArrayExpr": (), |
344 | "ParenExpr": (), | 344 | "ParenExpr": (), |
345 | "PathExpr": (), | 345 | "PathExpr": (options: ["Path"]), |
346 | "LambdaExpr": ( | 346 | "LambdaExpr": ( |
347 | options: [ | 347 | options: [ |
348 | "ParamList", | 348 | "ParamList", |
@@ -538,5 +538,6 @@ Grammar( | |||
538 | options: [ "NameRef" ] | 538 | options: [ "NameRef" ] |
539 | ), | 539 | ), |
540 | "Comment": (), | 540 | "Comment": (), |
541 | "Whitespace": (), | ||
541 | }, | 542 | }, |
542 | ) | 543 | ) |
diff --git a/crates/ra_syntax/src/grammar/expressions/atom.rs b/crates/ra_syntax/src/grammar/expressions/atom.rs index a720d255f..e21de68c5 100644 --- a/crates/ra_syntax/src/grammar/expressions/atom.rs +++ b/crates/ra_syntax/src/grammar/expressions/atom.rs | |||
@@ -30,7 +30,7 @@ pub(super) const ATOM_EXPR_FIRST: TokenSet = | |||
30 | token_set_union![ | 30 | token_set_union![ |
31 | LITERAL_FIRST, | 31 | LITERAL_FIRST, |
32 | token_set![L_CURLY, L_PAREN, L_BRACK, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, | 32 | token_set![L_CURLY, L_PAREN, L_BRACK, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, |
33 | RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], | 33 | RETURN_KW, IDENT, SELF_KW, SUPER_KW, CRATE_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ], |
34 | ]; | 34 | ]; |
35 | 35 | ||
36 | const EXPR_RECOVERY_SET: TokenSet = | 36 | const EXPR_RECOVERY_SET: TokenSet = |
diff --git a/crates/ra_syntax/src/grammar/mod.rs b/crates/ra_syntax/src/grammar/mod.rs index 2cb11dc1e..1199ba230 100644 --- a/crates/ra_syntax/src/grammar/mod.rs +++ b/crates/ra_syntax/src/grammar/mod.rs | |||
@@ -31,7 +31,7 @@ mod type_args; | |||
31 | mod type_params; | 31 | mod type_params; |
32 | mod types; | 32 | mod types; |
33 | 33 | ||
34 | use { | 34 | use crate::{ |
35 | token_set::TokenSet, | 35 | token_set::TokenSet, |
36 | parser_api::{Marker, CompletedMarker, Parser}, | 36 | parser_api::{Marker, CompletedMarker, Parser}, |
37 | SyntaxKind::{self, *}, | 37 | SyntaxKind::{self, *}, |
diff --git a/crates/ra_syntax/src/lexer/comments.rs b/crates/ra_syntax/src/lexer/comments.rs index eb417c2dc..afe6886a1 100644 --- a/crates/ra_syntax/src/lexer/comments.rs +++ b/crates/ra_syntax/src/lexer/comments.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use lexer::ptr::Ptr; | 1 | use crate::lexer::ptr::Ptr; |
2 | 2 | ||
3 | use SyntaxKind::{self, *}; | 3 | use crate::SyntaxKind::{self, *}; |
4 | 4 | ||
5 | pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool { | 5 | pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool { |
6 | if ptr.at_str("!/") { | 6 | if ptr.at_str("!/") { |
diff --git a/crates/ra_syntax/src/lexer/mod.rs b/crates/ra_syntax/src/lexer/mod.rs index 3e11db88b..9dc0b63d6 100644 --- a/crates/ra_syntax/src/lexer/mod.rs +++ b/crates/ra_syntax/src/lexer/mod.rs | |||
@@ -4,7 +4,7 @@ mod numbers; | |||
4 | mod ptr; | 4 | mod ptr; |
5 | mod strings; | 5 | mod strings; |
6 | 6 | ||
7 | use { | 7 | use crate::{ |
8 | SyntaxKind::{self, *}, | 8 | SyntaxKind::{self, *}, |
9 | TextUnit, | 9 | TextUnit, |
10 | }; | 10 | }; |
diff --git a/crates/ra_syntax/src/lexer/numbers.rs b/crates/ra_syntax/src/lexer/numbers.rs index 22e7d4e99..46daf5e52 100644 --- a/crates/ra_syntax/src/lexer/numbers.rs +++ b/crates/ra_syntax/src/lexer/numbers.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use lexer::classes::*; | 1 | use crate::lexer::classes::*; |
2 | use lexer::ptr::Ptr; | 2 | use crate::lexer::ptr::Ptr; |
3 | 3 | ||
4 | use SyntaxKind::{self, *}; | 4 | use crate::SyntaxKind::{self, *}; |
5 | 5 | ||
6 | pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind { | 6 | pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind { |
7 | if c == '0' { | 7 | if c == '0' { |
diff --git a/crates/ra_syntax/src/lexer/ptr.rs b/crates/ra_syntax/src/lexer/ptr.rs index c9a5354ea..c4708cb1c 100644 --- a/crates/ra_syntax/src/lexer/ptr.rs +++ b/crates/ra_syntax/src/lexer/ptr.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use TextUnit; | 1 | use crate::TextUnit; |
2 | 2 | ||
3 | use std::str::Chars; | 3 | use std::str::Chars; |
4 | 4 | ||
diff --git a/crates/ra_syntax/src/lexer/strings.rs b/crates/ra_syntax/src/lexer/strings.rs index 5ff483d14..bceacdcac 100644 --- a/crates/ra_syntax/src/lexer/strings.rs +++ b/crates/ra_syntax/src/lexer/strings.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use SyntaxKind::{self, *}; | 1 | use crate::SyntaxKind::{self, *}; |
2 | 2 | ||
3 | use lexer::ptr::Ptr; | 3 | use crate::lexer::ptr::Ptr; |
4 | 4 | ||
5 | pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool { | 5 | pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool { |
6 | match (c, c1, c2) { | 6 | match (c, c1, c2) { |
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 703469629..7eba5ee61 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -46,7 +46,7 @@ mod yellow; | |||
46 | pub mod utils; | 46 | pub mod utils; |
47 | pub mod text_utils; | 47 | pub mod text_utils; |
48 | 48 | ||
49 | pub use { | 49 | pub use crate::{ |
50 | rowan::{SmolStr, TextRange, TextUnit}, | 50 | rowan::{SmolStr, TextRange, TextUnit}, |
51 | ast::AstNode, | 51 | ast::AstNode, |
52 | lexer::{tokenize, Token}, | 52 | lexer::{tokenize, Token}, |
@@ -55,11 +55,11 @@ pub use { | |||
55 | reparsing::AtomEdit, | 55 | reparsing::AtomEdit, |
56 | }; | 56 | }; |
57 | 57 | ||
58 | use { | 58 | use crate::{ |
59 | yellow::{GreenNode}, | 59 | yellow::{GreenNode}, |
60 | }; | 60 | }; |
61 | 61 | ||
62 | #[derive(Clone, Debug, Hash)] | 62 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
63 | pub struct File { | 63 | pub struct File { |
64 | root: SyntaxNode | 64 | root: SyntaxNode |
65 | } | 65 | } |
diff --git a/crates/ra_syntax/src/parser_api.rs b/crates/ra_syntax/src/parser_api.rs index 772d753af..cc23bb75e 100644 --- a/crates/ra_syntax/src/parser_api.rs +++ b/crates/ra_syntax/src/parser_api.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use { | 1 | use crate::{ |
2 | token_set::TokenSet, | 2 | token_set::TokenSet, |
3 | parser_impl::ParserImpl, | 3 | parser_impl::ParserImpl, |
4 | SyntaxKind::{self, ERROR}, | 4 | SyntaxKind::{self, ERROR}, |
diff --git a/crates/ra_syntax/src/parser_impl/event.rs b/crates/ra_syntax/src/parser_impl/event.rs index 95e5ce4cc..928d2cc7a 100644 --- a/crates/ra_syntax/src/parser_impl/event.rs +++ b/crates/ra_syntax/src/parser_impl/event.rs | |||
@@ -8,7 +8,7 @@ | |||
8 | //! `start node`, `finish node`, and `FileBuilder` converts | 8 | //! `start node`, `finish node`, and `FileBuilder` converts |
9 | //! this stream to a real tree. | 9 | //! this stream to a real tree. |
10 | use std::mem; | 10 | use std::mem; |
11 | use { | 11 | use crate::{ |
12 | TextUnit, TextRange, SmolStr, | 12 | TextUnit, TextRange, SmolStr, |
13 | lexer::Token, | 13 | lexer::Token, |
14 | parser_impl::Sink, | 14 | parser_impl::Sink, |
diff --git a/crates/ra_syntax/src/parser_impl/input.rs b/crates/ra_syntax/src/parser_impl/input.rs index c0fe4d488..ac6d900d8 100644 --- a/crates/ra_syntax/src/parser_impl/input.rs +++ b/crates/ra_syntax/src/parser_impl/input.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {lexer::Token, SyntaxKind, SyntaxKind::EOF, TextRange, TextUnit}; | 1 | use crate::{lexer::Token, SyntaxKind, SyntaxKind::EOF, TextRange, TextUnit}; |
2 | 2 | ||
3 | use std::ops::{Add, AddAssign}; | 3 | use std::ops::{Add, AddAssign}; |
4 | 4 | ||
diff --git a/crates/ra_syntax/src/parser_impl/mod.rs b/crates/ra_syntax/src/parser_impl/mod.rs index 8d74cef0e..c2a6448e7 100644 --- a/crates/ra_syntax/src/parser_impl/mod.rs +++ b/crates/ra_syntax/src/parser_impl/mod.rs | |||
@@ -3,7 +3,7 @@ mod input; | |||
3 | 3 | ||
4 | use std::cell::Cell; | 4 | use std::cell::Cell; |
5 | 5 | ||
6 | use { | 6 | use crate::{ |
7 | TextUnit, SmolStr, | 7 | TextUnit, SmolStr, |
8 | lexer::Token, | 8 | lexer::Token, |
9 | parser_api::Parser, | 9 | parser_api::Parser, |
@@ -13,7 +13,7 @@ use { | |||
13 | }, | 13 | }, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | use SyntaxKind::{self, EOF, TOMBSTONE}; | 16 | use crate::SyntaxKind::{self, EOF, TOMBSTONE}; |
17 | 17 | ||
18 | pub(crate) trait Sink { | 18 | pub(crate) trait Sink { |
19 | type Tree; | 19 | type Tree; |
diff --git a/crates/ra_syntax/src/reparsing.rs b/crates/ra_syntax/src/reparsing.rs index d8b6a6a10..16272fe88 100644 --- a/crates/ra_syntax/src/reparsing.rs +++ b/crates/ra_syntax/src/reparsing.rs | |||
@@ -1,14 +1,14 @@ | |||
1 | use algo; | 1 | use crate::algo; |
2 | use grammar; | 2 | use crate::grammar; |
3 | use lexer::{tokenize, Token}; | 3 | use crate::lexer::{tokenize, Token}; |
4 | use yellow::{self, GreenNode, SyntaxNodeRef, SyntaxError}; | 4 | use crate::yellow::{self, GreenNode, SyntaxNodeRef, SyntaxError}; |
5 | use parser_impl; | 5 | use crate::parser_impl; |
6 | use parser_api::Parser; | 6 | use crate::parser_api::Parser; |
7 | use { | 7 | use crate::{ |
8 | TextUnit, TextRange, | 8 | TextUnit, TextRange, |
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | }; | 10 | }; |
11 | use text_utils::replace_range; | 11 | use crate::text_utils::replace_range; |
12 | 12 | ||
13 | #[derive(Debug, Clone)] | 13 | #[derive(Debug, Clone)] |
14 | pub struct AtomEdit { | 14 | pub struct AtomEdit { |
diff --git a/crates/ra_syntax/src/syntax_kinds/mod.rs b/crates/ra_syntax/src/syntax_kinds/mod.rs index 332cd13ac..3041e5633 100644 --- a/crates/ra_syntax/src/syntax_kinds/mod.rs +++ b/crates/ra_syntax/src/syntax_kinds/mod.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | mod generated; | 1 | mod generated; |
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | use SyntaxKind::*; | 4 | use crate::SyntaxKind::*; |
5 | 5 | ||
6 | pub use self::generated::SyntaxKind; | 6 | pub use self::generated::SyntaxKind; |
7 | 7 | ||
diff --git a/crates/ra_syntax/src/text_utils.rs b/crates/ra_syntax/src/text_utils.rs index 58ae1e43e..adf26ef30 100644 --- a/crates/ra_syntax/src/text_utils.rs +++ b/crates/ra_syntax/src/text_utils.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use {TextRange, TextUnit}; | 1 | use crate::{TextRange, TextUnit}; |
2 | 2 | ||
3 | pub fn contains_offset_nonstrict(range: TextRange, offset: TextUnit) -> bool { | 3 | pub fn contains_offset_nonstrict(range: TextRange, offset: TextUnit) -> bool { |
4 | range.start() <= offset && offset <= range.end() | 4 | range.start() <= offset && offset <= range.end() |
diff --git a/crates/ra_syntax/src/token_set.rs b/crates/ra_syntax/src/token_set.rs index c83fba81b..d407dfa48 100644 --- a/crates/ra_syntax/src/token_set.rs +++ b/crates/ra_syntax/src/token_set.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use SyntaxKind; | 1 | use crate::SyntaxKind; |
2 | 2 | ||
3 | #[derive(Clone, Copy)] | 3 | #[derive(Clone, Copy)] |
4 | pub(crate) struct TokenSet(pub(crate) u128); | 4 | pub(crate) struct TokenSet(pub(crate) u128); |
@@ -29,7 +29,7 @@ macro_rules! token_set_union { | |||
29 | 29 | ||
30 | #[test] | 30 | #[test] |
31 | fn token_set_works_for_tokens() { | 31 | fn token_set_works_for_tokens() { |
32 | use SyntaxKind::*; | 32 | use crate::SyntaxKind::*; |
33 | let ts = token_set! { EOF, SHEBANG }; | 33 | let ts = token_set! { EOF, SHEBANG }; |
34 | assert!(ts.contains(EOF)); | 34 | assert!(ts.contains(EOF)); |
35 | assert!(ts.contains(SHEBANG)); | 35 | assert!(ts.contains(SHEBANG)); |
diff --git a/crates/ra_syntax/src/utils.rs b/crates/ra_syntax/src/utils.rs index e274f7471..df1f4b372 100644 --- a/crates/ra_syntax/src/utils.rs +++ b/crates/ra_syntax/src/utils.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use std::fmt::Write; | 1 | use std::fmt::Write; |
2 | use { | 2 | use crate::{ |
3 | algo::walk::{walk, WalkEvent}, | 3 | algo::walk::{walk, WalkEvent}, |
4 | SyntaxKind, File, SyntaxNodeRef | 4 | SyntaxKind, File, SyntaxNodeRef |
5 | }; | 5 | }; |
diff --git a/crates/ra_syntax/src/yellow/builder.rs b/crates/ra_syntax/src/yellow/builder.rs index c307b2bd0..67a1a382b 100644 --- a/crates/ra_syntax/src/yellow/builder.rs +++ b/crates/ra_syntax/src/yellow/builder.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use rowan::GreenNodeBuilder; | 1 | use rowan::GreenNodeBuilder; |
2 | use { | 2 | use crate::{ |
3 | TextUnit, SmolStr, | 3 | TextUnit, SmolStr, |
4 | parser_impl::Sink, | 4 | parser_impl::Sink, |
5 | yellow::{GreenNode, SyntaxError, RaTypes}, | 5 | yellow::{GreenNode, SyntaxError, RaTypes}, |
diff --git a/crates/ra_syntax/src/yellow/mod.rs b/crates/ra_syntax/src/yellow/mod.rs index 710320f47..ab9bca0f0 100644 --- a/crates/ra_syntax/src/yellow/mod.rs +++ b/crates/ra_syntax/src/yellow/mod.rs | |||
@@ -6,7 +6,7 @@ use std::{ | |||
6 | hash::{Hash, Hasher}, | 6 | hash::{Hash, Hasher}, |
7 | }; | 7 | }; |
8 | use rowan::Types; | 8 | use rowan::Types; |
9 | use {SyntaxKind, TextUnit, TextRange, SmolStr}; | 9 | use crate::{SyntaxKind, TextUnit, TextRange, SmolStr}; |
10 | use self::syntax_text::SyntaxText; | 10 | use self::syntax_text::SyntaxText; |
11 | 11 | ||
12 | pub use rowan::{TreeRoot}; | 12 | pub use rowan::{TreeRoot}; |
@@ -70,16 +70,16 @@ impl<'a> SyntaxNodeRef<'a> { | |||
70 | self.0.leaf_text() | 70 | self.0.leaf_text() |
71 | } | 71 | } |
72 | pub fn ancestors(self) -> impl Iterator<Item=SyntaxNodeRef<'a>> { | 72 | pub fn ancestors(self) -> impl Iterator<Item=SyntaxNodeRef<'a>> { |
73 | ::algo::generate(Some(self), |&node| node.parent()) | 73 | crate::algo::generate(Some(self), |&node| node.parent()) |
74 | } | 74 | } |
75 | pub fn descendants(self) -> impl Iterator<Item=SyntaxNodeRef<'a>> { | 75 | pub fn descendants(self) -> impl Iterator<Item=SyntaxNodeRef<'a>> { |
76 | ::algo::walk::walk(self).filter_map(|event| match event { | 76 | crate::algo::walk::walk(self).filter_map(|event| match event { |
77 | ::algo::walk::WalkEvent::Enter(node) => Some(node), | 77 | crate::algo::walk::WalkEvent::Enter(node) => Some(node), |
78 | ::algo::walk::WalkEvent::Exit(_) => None, | 78 | crate::algo::walk::WalkEvent::Exit(_) => None, |
79 | }) | 79 | }) |
80 | } | 80 | } |
81 | pub fn siblings(self, direction: Direction) -> impl Iterator<Item=SyntaxNodeRef<'a>> { | 81 | pub fn siblings(self, direction: Direction) -> impl Iterator<Item=SyntaxNodeRef<'a>> { |
82 | ::algo::generate(Some(self), move |&node| match direction { | 82 | crate::algo::generate(Some(self), move |&node| match direction { |
83 | Direction::Next => node.next_sibling(), | 83 | Direction::Next => node.next_sibling(), |
84 | Direction::Prev => node.prev_sibling(), | 84 | Direction::Prev => node.prev_sibling(), |
85 | }) | 85 | }) |
@@ -156,7 +156,7 @@ impl<R: TreeRoot<RaTypes>> Iterator for SyntaxNodeChildren<R> { | |||
156 | 156 | ||
157 | 157 | ||
158 | fn has_short_text(kind: SyntaxKind) -> bool { | 158 | fn has_short_text(kind: SyntaxKind) -> bool { |
159 | use SyntaxKind::*; | 159 | use crate::SyntaxKind::*; |
160 | match kind { | 160 | match kind { |
161 | IDENT | LIFETIME | INT_NUMBER | FLOAT_NUMBER => true, | 161 | IDENT | LIFETIME | INT_NUMBER | FLOAT_NUMBER => true, |
162 | _ => false, | 162 | _ => false, |
diff --git a/crates/ra_syntax/src/yellow/syntax_text.rs b/crates/ra_syntax/src/yellow/syntax_text.rs index 0db1049de..ae33b993d 100644 --- a/crates/ra_syntax/src/yellow/syntax_text.rs +++ b/crates/ra_syntax/src/yellow/syntax_text.rs | |||
@@ -2,7 +2,7 @@ use std::{ | |||
2 | fmt, ops, | 2 | fmt, ops, |
3 | }; | 3 | }; |
4 | 4 | ||
5 | use { | 5 | use crate::{ |
6 | SyntaxNodeRef, TextRange, TextUnit, | 6 | SyntaxNodeRef, TextRange, TextUnit, |
7 | text_utils::{intersect, contains_offset_nonstrict}, | 7 | text_utils::{intersect, contains_offset_nonstrict}, |
8 | }; | 8 | }; |
diff --git a/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.rs b/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.rs new file mode 100644 index 000000000..f1ed30220 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | fn main() { | ||
2 | make_query(crate::module_map::module_tree); | ||
3 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.txt b/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.txt new file mode 100644 index 000000000..364315180 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.txt | |||
@@ -0,0 +1,41 @@ | |||
1 | ROOT@[0; 62) | ||
2 | FN_DEF@[0; 61) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 7) | ||
6 | IDENT@[3; 7) "main" | ||
7 | PARAM_LIST@[7; 9) | ||
8 | L_PAREN@[7; 8) | ||
9 | R_PAREN@[8; 9) | ||
10 | WHITESPACE@[9; 10) | ||
11 | BLOCK@[10; 61) | ||
12 | L_CURLY@[10; 11) | ||
13 | WHITESPACE@[11; 16) | ||
14 | EXPR_STMT@[16; 59) | ||
15 | CALL_EXPR@[16; 58) | ||
16 | PATH_EXPR@[16; 26) | ||
17 | PATH@[16; 26) | ||
18 | PATH_SEGMENT@[16; 26) | ||
19 | NAME_REF@[16; 26) | ||
20 | IDENT@[16; 26) "make_query" | ||
21 | ARG_LIST@[26; 58) | ||
22 | L_PAREN@[26; 27) | ||
23 | PATH_EXPR@[27; 57) | ||
24 | PATH@[27; 57) | ||
25 | PATH@[27; 44) | ||
26 | PATH@[27; 32) | ||
27 | PATH_SEGMENT@[27; 32) | ||
28 | CRATE_KW@[27; 32) | ||
29 | COLONCOLON@[32; 34) | ||
30 | PATH_SEGMENT@[34; 44) | ||
31 | NAME_REF@[34; 44) | ||
32 | IDENT@[34; 44) "module_map" | ||
33 | COLONCOLON@[44; 46) | ||
34 | PATH_SEGMENT@[46; 57) | ||
35 | NAME_REF@[46; 57) | ||
36 | IDENT@[46; 57) "module_tree" | ||
37 | R_PAREN@[57; 58) | ||
38 | SEMI@[58; 59) | ||
39 | WHITESPACE@[59; 60) | ||
40 | R_CURLY@[60; 61) | ||
41 | WHITESPACE@[61; 62) | ||
diff --git a/crates/salsa/Cargo.toml b/crates/salsa/Cargo.toml deleted file mode 100644 index 9eb83234f..000000000 --- a/crates/salsa/Cargo.toml +++ /dev/null | |||
@@ -1,8 +0,0 @@ | |||
1 | [package] | ||
2 | name = "salsa" | ||
3 | version = "0.1.0" | ||
4 | authors = ["Aleksey Kladov <[email protected]>"] | ||
5 | |||
6 | [dependencies] | ||
7 | parking_lot = "0.6.3" | ||
8 | im = "12.0.0" | ||
diff --git a/crates/salsa/src/lib.rs b/crates/salsa/src/lib.rs deleted file mode 100644 index 35deed374..000000000 --- a/crates/salsa/src/lib.rs +++ /dev/null | |||
@@ -1,293 +0,0 @@ | |||
1 | extern crate im; | ||
2 | extern crate parking_lot; | ||
3 | |||
4 | use std::{ | ||
5 | sync::Arc, | ||
6 | collections::{HashSet, HashMap}, | ||
7 | cell::RefCell, | ||
8 | }; | ||
9 | use parking_lot::Mutex; | ||
10 | |||
11 | pub type GroundQueryFn<T, D> = Box<Fn(&T, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; | ||
12 | pub type QueryFn<T, D> = Box<Fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>; | ||
13 | |||
14 | #[derive(Debug)] | ||
15 | pub struct Db<T, D> { | ||
16 | db: Arc<DbState<T, D>>, | ||
17 | query_config: Arc<QueryConfig<T, D>>, | ||
18 | } | ||
19 | |||
20 | pub struct QueryConfig<T, D> { | ||
21 | ground_fn: HashMap<QueryTypeId, GroundQueryFn<T, D>>, | ||
22 | query_fn: HashMap<QueryTypeId, QueryFn<T, D>>, | ||
23 | } | ||
24 | |||
25 | impl<T, D> ::std::fmt::Debug for QueryConfig<T, D> { | ||
26 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { | ||
27 | ::std::fmt::Display::fmt("QueryConfig { ... }", f) | ||
28 | } | ||
29 | } | ||
30 | |||
31 | #[derive(Debug)] | ||
32 | struct DbState<T, D> { | ||
33 | ground_data: T, | ||
34 | gen: Gen, | ||
35 | graph: Mutex<im::HashMap<QueryId, (Gen, Arc<QueryRecord<D>>)>>, | ||
36 | } | ||
37 | |||
38 | #[derive(Debug)] | ||
39 | struct QueryRecord<D> { | ||
40 | params: D, | ||
41 | output: D, | ||
42 | output_fingerprint: OutputFingerprint, | ||
43 | deps: Vec<(QueryId, OutputFingerprint)>, | ||
44 | } | ||
45 | |||
46 | impl<T, D> DbState<T, D> { | ||
47 | fn record( | ||
48 | &self, | ||
49 | query_id: QueryId, | ||
50 | params: D, | ||
51 | output: D, | ||
52 | output_fingerprint: OutputFingerprint, | ||
53 | deps: Vec<(QueryId, OutputFingerprint)>, | ||
54 | ) { | ||
55 | let gen = self.gen; | ||
56 | let record = QueryRecord { | ||
57 | params, | ||
58 | output, | ||
59 | output_fingerprint, | ||
60 | deps, | ||
61 | }; | ||
62 | self.graph.lock().insert(query_id, (gen, Arc::new(record))); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | impl<T, D> QueryConfig<T, D> { | ||
67 | pub fn new() -> Self { | ||
68 | QueryConfig { | ||
69 | ground_fn: HashMap::new(), | ||
70 | query_fn: HashMap::new(), | ||
71 | } | ||
72 | } | ||
73 | pub fn with_ground_query( | ||
74 | mut self, | ||
75 | query_type: QueryTypeId, | ||
76 | query_fn: GroundQueryFn<T, D> | ||
77 | ) -> Self { | ||
78 | let prev = self.ground_fn.insert(query_type, query_fn); | ||
79 | assert!(prev.is_none()); | ||
80 | self | ||
81 | } | ||
82 | pub fn with_query( | ||
83 | mut self, | ||
84 | query_type: QueryTypeId, | ||
85 | query_fn: QueryFn<T, D>, | ||
86 | ) -> Self { | ||
87 | let prev = self.query_fn.insert(query_type, query_fn); | ||
88 | assert!(prev.is_none()); | ||
89 | self | ||
90 | } | ||
91 | } | ||
92 | |||
93 | pub struct QueryCtx<T, D> { | ||
94 | db: Arc<DbState<T, D>>, | ||
95 | query_config: Arc<QueryConfig<T, D>>, | ||
96 | stack: RefCell<Vec<Vec<(QueryId, OutputFingerprint)>>>, | ||
97 | executed: RefCell<Vec<QueryTypeId>>, | ||
98 | } | ||
99 | |||
100 | impl<T, D> QueryCtx<T, D> | ||
101 | where | ||
102 | D: Clone | ||
103 | { | ||
104 | fn new(db: &Db<T, D>) -> QueryCtx<T, D> { | ||
105 | QueryCtx { | ||
106 | db: Arc::clone(&db.db), | ||
107 | query_config: Arc::clone(&db.query_config), | ||
108 | stack: RefCell::new(vec![Vec::new()]), | ||
109 | executed: RefCell::new(Vec::new()), | ||
110 | } | ||
111 | } | ||
112 | pub fn get( | ||
113 | &self, | ||
114 | query_id: QueryId, | ||
115 | params: D, | ||
116 | ) -> D { | ||
117 | let (res, output_fingerprint) = self.get_inner(query_id, params); | ||
118 | self.record_dep(query_id, output_fingerprint); | ||
119 | res | ||
120 | } | ||
121 | pub fn trace(&self) -> Vec<QueryTypeId> { | ||
122 | ::std::mem::replace(&mut *self.executed.borrow_mut(), Vec::new()) | ||
123 | } | ||
124 | |||
125 | fn get_inner( | ||
126 | &self, | ||
127 | query_id: QueryId, | ||
128 | params: D, | ||
129 | ) -> (D, OutputFingerprint) { | ||
130 | let (gen, record) = { | ||
131 | let guard = self.db.graph.lock(); | ||
132 | match guard.get(&query_id).map(|it| it.clone()){ | ||
133 | None => { | ||
134 | drop(guard); | ||
135 | return self.force(query_id, params); | ||
136 | }, | ||
137 | Some(it) => it, | ||
138 | } | ||
139 | }; | ||
140 | if gen == self.db.gen { | ||
141 | return (record.output.clone(), record.output_fingerprint) | ||
142 | } | ||
143 | if self.query_config.ground_fn.contains_key(&query_id.0) { | ||
144 | let (invalidated, record) = { | ||
145 | let guard = self.db.graph.lock(); | ||
146 | let (gen, ref record) = guard[&query_id]; | ||
147 | (gen == INVALIDATED, record.clone()) | ||
148 | }; | ||
149 | if invalidated { | ||
150 | return self.force(query_id, params); | ||
151 | } else { | ||
152 | return (record.output.clone(), record.output_fingerprint); | ||
153 | } | ||
154 | } | ||
155 | for (dep_query_id, prev_fingerprint) in record.deps.iter().cloned() { | ||
156 | let dep_params: D = { | ||
157 | let guard = self.db.graph.lock(); | ||
158 | guard[&dep_query_id] | ||
159 | .1 | ||
160 | .params | ||
161 | .clone() | ||
162 | }; | ||
163 | if prev_fingerprint != self.get_inner(dep_query_id, dep_params).1 { | ||
164 | return self.force(query_id, params) | ||
165 | } | ||
166 | } | ||
167 | let gen = self.db.gen; | ||
168 | { | ||
169 | let mut guard = self.db.graph.lock(); | ||
170 | guard[&query_id].0 = gen; | ||
171 | } | ||
172 | (record.output.clone(), record.output_fingerprint) | ||
173 | } | ||
174 | fn force( | ||
175 | &self, | ||
176 | query_id: QueryId, | ||
177 | params: D, | ||
178 | ) -> (D, OutputFingerprint) { | ||
179 | self.executed.borrow_mut().push(query_id.0); | ||
180 | self.stack.borrow_mut().push(Vec::new()); | ||
181 | |||
182 | let (res, output_fingerprint) = if let Some(f) = self.query_config.ground_fn.get(&query_id.0) { | ||
183 | f(&self.db.ground_data, ¶ms) | ||
184 | } else if let Some(f) = self.query_config.query_fn.get(&query_id.0) { | ||
185 | f(self, ¶ms) | ||
186 | } else { | ||
187 | panic!("unknown query type: {:?}", query_id.0); | ||
188 | }; | ||
189 | |||
190 | let res: D = res.into(); | ||
191 | |||
192 | let deps = self.stack.borrow_mut().pop().unwrap(); | ||
193 | self.db.record(query_id, params, res.clone(), output_fingerprint, deps); | ||
194 | (res, output_fingerprint) | ||
195 | } | ||
196 | fn record_dep( | ||
197 | &self, | ||
198 | query_id: QueryId, | ||
199 | output_fingerprint: OutputFingerprint, | ||
200 | ) -> () { | ||
201 | let mut stack = self.stack.borrow_mut(); | ||
202 | let deps = stack.last_mut().unwrap(); | ||
203 | deps.push((query_id, output_fingerprint)) | ||
204 | } | ||
205 | } | ||
206 | |||
207 | pub struct Invalidations { | ||
208 | types: HashSet<QueryTypeId>, | ||
209 | ids: Vec<QueryId>, | ||
210 | } | ||
211 | |||
212 | impl Invalidations { | ||
213 | pub fn new() -> Invalidations { | ||
214 | Invalidations { | ||
215 | types: HashSet::new(), | ||
216 | ids: Vec::new(), | ||
217 | } | ||
218 | } | ||
219 | pub fn invalidate( | ||
220 | &mut self, | ||
221 | query_type: QueryTypeId, | ||
222 | params: impl Iterator<Item=InputFingerprint>, | ||
223 | ) { | ||
224 | self.types.insert(query_type); | ||
225 | self.ids.extend(params.map(|it| QueryId(query_type, it))) | ||
226 | } | ||
227 | } | ||
228 | |||
229 | impl<T, D> Db<T, D> | ||
230 | where | ||
231 | D: Clone | ||
232 | { | ||
233 | pub fn new(query_config: QueryConfig<T, D>, ground_data: T) -> Db<T, D> { | ||
234 | Db { | ||
235 | db: Arc::new(DbState { ground_data, gen: Gen(0), graph: Default::default() }), | ||
236 | query_config: Arc::new(query_config), | ||
237 | } | ||
238 | } | ||
239 | pub fn ground_data(&self) -> &T { | ||
240 | &self.db.ground_data | ||
241 | } | ||
242 | pub fn with_ground_data( | ||
243 | &self, | ||
244 | ground_data: T, | ||
245 | invalidations: Invalidations, | ||
246 | ) -> Db<T, D> { | ||
247 | for id in self.query_config.ground_fn.keys() { | ||
248 | assert!( | ||
249 | invalidations.types.contains(id), | ||
250 | "all ground queries must be invalidated" | ||
251 | ); | ||
252 | } | ||
253 | |||
254 | let gen = Gen(self.db.gen.0 + 1); | ||
255 | let mut graph = self.db.graph.lock().clone(); | ||
256 | for id in invalidations.ids { | ||
257 | if let Some((gen, _)) = graph.get_mut(&id) { | ||
258 | *gen = INVALIDATED; | ||
259 | } | ||
260 | } | ||
261 | let graph = Mutex::new(graph); | ||
262 | Db { | ||
263 | db: Arc::new(DbState { ground_data, gen, graph }), | ||
264 | query_config: Arc::clone(&self.query_config) | ||
265 | } | ||
266 | } | ||
267 | pub fn query_ctx(&self) -> QueryCtx<T, D> { | ||
268 | QueryCtx::new(self) | ||
269 | } | ||
270 | pub fn get( | ||
271 | &self, | ||
272 | query_id: QueryId, | ||
273 | params: D, | ||
274 | ) -> (D, Vec<QueryTypeId>) { | ||
275 | let ctx = self.query_ctx(); | ||
276 | let res = ctx.get(query_id, params.into()); | ||
277 | let executed = ::std::mem::replace(&mut *ctx.executed.borrow_mut(), Vec::new()); | ||
278 | (res, executed) | ||
279 | } | ||
280 | } | ||
281 | |||
282 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
283 | struct Gen(u64); | ||
284 | const INVALIDATED: Gen = Gen(!0); | ||
285 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
286 | pub struct InputFingerprint(pub u64); | ||
287 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
288 | pub struct OutputFingerprint(pub u64); | ||
289 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
290 | pub struct QueryTypeId(pub u16); | ||
291 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
292 | pub struct QueryId(pub QueryTypeId, pub InputFingerprint); | ||
293 | |||
diff --git a/crates/salsa/tests/integration.rs b/crates/salsa/tests/integration.rs deleted file mode 100644 index aed9219be..000000000 --- a/crates/salsa/tests/integration.rs +++ /dev/null | |||
@@ -1,170 +0,0 @@ | |||
1 | extern crate salsa; | ||
2 | use std::{ | ||
3 | iter::once, | ||
4 | sync::Arc, | ||
5 | collections::hash_map::{HashMap, DefaultHasher}, | ||
6 | any::Any, | ||
7 | hash::{Hash, Hasher}, | ||
8 | }; | ||
9 | |||
10 | type State = HashMap<u32, String>; | ||
11 | type Data = Arc<Any + Send + Sync + 'static>; | ||
12 | const GET_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(1); | ||
13 | const GET_FILES: salsa::QueryTypeId = salsa::QueryTypeId(2); | ||
14 | const FILE_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(3); | ||
15 | const TOTAL_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(4); | ||
16 | |||
17 | fn mk_ground_query<T, R>( | ||
18 | state: &State, | ||
19 | params: &Data, | ||
20 | f: fn(&State, &T) -> R, | ||
21 | ) -> (Data, salsa::OutputFingerprint) | ||
22 | where | ||
23 | T: 'static, | ||
24 | R: Hash + Send + Sync + 'static, | ||
25 | { | ||
26 | let params = params.downcast_ref().unwrap(); | ||
27 | let result = f(state, params); | ||
28 | let fingerprint = o_print(&result); | ||
29 | (Arc::new(result), fingerprint) | ||
30 | } | ||
31 | |||
32 | fn get<T, R>(db: &salsa::Db<State, Data>, query_type: salsa::QueryTypeId, param: T) -> (Arc<R>, Vec<salsa::QueryTypeId>) | ||
33 | where | ||
34 | T: Hash + Send + Sync + 'static, | ||
35 | R: Send + Sync + 'static, | ||
36 | { | ||
37 | let i_print = i_print(¶m); | ||
38 | let param = Arc::new(param); | ||
39 | let (res, trace) = db.get(salsa::QueryId(query_type, i_print), param); | ||
40 | (res.downcast().unwrap(), trace) | ||
41 | } | ||
42 | |||
43 | struct QueryCtx<'a>(&'a salsa::QueryCtx<State, Data>); | ||
44 | |||
45 | impl<'a> QueryCtx<'a> { | ||
46 | fn get_text(&self, id: u32) -> Arc<String> { | ||
47 | let i_print = i_print(&id); | ||
48 | let text = self.0.get(salsa::QueryId(GET_TEXT, i_print), Arc::new(id)); | ||
49 | text.downcast().unwrap() | ||
50 | } | ||
51 | fn get_files(&self) -> Arc<Vec<u32>> { | ||
52 | let i_print = i_print(&()); | ||
53 | let files = self.0.get(salsa::QueryId(GET_FILES, i_print), Arc::new(())); | ||
54 | let res = files.downcast().unwrap(); | ||
55 | res | ||
56 | } | ||
57 | fn get_n_lines(&self, id: u32) -> usize { | ||
58 | let i_print = i_print(&id); | ||
59 | let n_lines = self.0.get(salsa::QueryId(FILE_NEWLINES, i_print), Arc::new(id)); | ||
60 | *n_lines.downcast().unwrap() | ||
61 | } | ||
62 | } | ||
63 | |||
64 | fn mk_query<T, R>( | ||
65 | query_ctx: &salsa::QueryCtx<State, Data>, | ||
66 | params: &Data, | ||
67 | f: fn(QueryCtx, &T) -> R, | ||
68 | ) -> (Data, salsa::OutputFingerprint) | ||
69 | where | ||
70 | T: 'static, | ||
71 | R: Hash + Send + Sync + 'static, | ||
72 | { | ||
73 | let params: &T = params.downcast_ref().unwrap(); | ||
74 | let query_ctx = QueryCtx(query_ctx); | ||
75 | let result = f(query_ctx, params); | ||
76 | let fingerprint = o_print(&result); | ||
77 | (Arc::new(result), fingerprint) | ||
78 | } | ||
79 | |||
80 | fn mk_queries() -> salsa::QueryConfig<State, Data> { | ||
81 | salsa::QueryConfig::<State, Data>::new() | ||
82 | .with_ground_query(GET_TEXT, Box::new(|state, id| { | ||
83 | mk_ground_query::<u32, String>(state, id, |state, id| state[id].clone()) | ||
84 | })) | ||
85 | .with_ground_query(GET_FILES, Box::new(|state, id| { | ||
86 | mk_ground_query::<(), Vec<u32>>(state, id, |state, &()| state.keys().cloned().collect()) | ||
87 | })) | ||
88 | .with_query(FILE_NEWLINES, Box::new(|query_ctx, id| { | ||
89 | mk_query(query_ctx, id, |query_ctx, &id| { | ||
90 | let text = query_ctx.get_text(id); | ||
91 | text.lines().count() | ||
92 | }) | ||
93 | })) | ||
94 | .with_query(TOTAL_NEWLINES, Box::new(|query_ctx, id| { | ||
95 | mk_query(query_ctx, id, |query_ctx, &()| { | ||
96 | let mut total = 0; | ||
97 | for &id in query_ctx.get_files().iter() { | ||
98 | total += query_ctx.get_n_lines(id) | ||
99 | } | ||
100 | total | ||
101 | }) | ||
102 | })) | ||
103 | } | ||
104 | |||
105 | #[test] | ||
106 | fn test_number_of_lines() { | ||
107 | let mut state = State::new(); | ||
108 | let db = salsa::Db::new(mk_queries(), state.clone()); | ||
109 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
110 | assert_eq!(*newlines, 0); | ||
111 | assert_eq!(trace.len(), 2); | ||
112 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
113 | assert_eq!(*newlines, 0); | ||
114 | assert_eq!(trace.len(), 0); | ||
115 | |||
116 | state.insert(1, "hello\nworld".to_string()); | ||
117 | let mut inv = salsa::Invalidations::new(); | ||
118 | inv.invalidate(GET_TEXT, once(i_print(&1u32))); | ||
119 | inv.invalidate(GET_FILES, once(i_print(&()))); | ||
120 | let db = db.with_ground_data(state.clone(), inv); | ||
121 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
122 | assert_eq!(*newlines, 2); | ||
123 | assert_eq!(trace.len(), 4); | ||
124 | |||
125 | state.insert(2, "spam\neggs".to_string()); | ||
126 | let mut inv = salsa::Invalidations::new(); | ||
127 | inv.invalidate(GET_TEXT, once(i_print(&2u32))); | ||
128 | inv.invalidate(GET_FILES, once(i_print(&()))); | ||
129 | let db = db.with_ground_data(state.clone(), inv); | ||
130 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
131 | assert_eq!(*newlines, 4); | ||
132 | assert_eq!(trace.len(), 4); | ||
133 | |||
134 | let mut invs = vec![]; | ||
135 | for i in 0..10 { | ||
136 | let id = i + 10; | ||
137 | invs.push(i_print(&id)); | ||
138 | state.insert(id, "spam".to_string()); | ||
139 | } | ||
140 | let mut inv = salsa::Invalidations::new(); | ||
141 | inv.invalidate(GET_TEXT, invs.into_iter()); | ||
142 | inv.invalidate(GET_FILES, once(i_print(&()))); | ||
143 | let db = db.with_ground_data(state.clone(), inv); | ||
144 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
145 | assert_eq!(*newlines, 14); | ||
146 | assert_eq!(trace.len(), 22); | ||
147 | |||
148 | state.insert(15, String::new()); | ||
149 | let mut inv = salsa::Invalidations::new(); | ||
150 | inv.invalidate(GET_TEXT, once(i_print(&15u32))); | ||
151 | inv.invalidate(GET_FILES, once(i_print(&()))); | ||
152 | let db = db.with_ground_data(state.clone(), inv); | ||
153 | let (newlines, trace) = get::<(), usize>(&db, TOTAL_NEWLINES, ()); | ||
154 | assert_eq!(*newlines, 13); | ||
155 | assert_eq!(trace.len(), 4); | ||
156 | } | ||
157 | |||
158 | fn o_print<T: Hash>(x: &T) -> salsa::OutputFingerprint { | ||
159 | let mut hasher = DefaultHasher::new(); | ||
160 | x.hash(&mut hasher); | ||
161 | let hash = hasher.finish(); | ||
162 | salsa::OutputFingerprint(hash) | ||
163 | } | ||
164 | |||
165 | fn i_print<T: Hash>(x: &T) -> salsa::InputFingerprint { | ||
166 | let mut hasher = DefaultHasher::new(); | ||
167 | x.hash(&mut hasher); | ||
168 | let hash = hasher.finish(); | ||
169 | salsa::InputFingerprint(hash) | ||
170 | } | ||
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 41316581e..fe0998ab8 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "test_utils" | 3 | name = "test_utils" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 068eb80ce..ee73153f0 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -38,22 +38,44 @@ pub fn assert_eq_dbg(expected: &str, actual: &impl fmt::Debug) { | |||
38 | } | 38 | } |
39 | 39 | ||
40 | pub fn extract_offset(text: &str) -> (TextUnit, String) { | 40 | pub fn extract_offset(text: &str) -> (TextUnit, String) { |
41 | let cursor = "<|>"; | 41 | match try_extract_offset(text) { |
42 | let cursor_pos = match text.find(cursor) { | ||
43 | None => panic!("text should contain cursor marker"), | 42 | None => panic!("text should contain cursor marker"), |
44 | Some(pos) => pos, | 43 | Some(result) => result, |
45 | }; | 44 | } |
45 | } | ||
46 | |||
47 | pub fn try_extract_offset(text: &str) -> Option<(TextUnit, String)> { | ||
48 | let cursor = "<|>"; | ||
49 | let cursor_pos = text.find(cursor)?; | ||
46 | let mut new_text = String::with_capacity(text.len() - cursor.len()); | 50 | let mut new_text = String::with_capacity(text.len() - cursor.len()); |
47 | new_text.push_str(&text[..cursor_pos]); | 51 | new_text.push_str(&text[..cursor_pos]); |
48 | new_text.push_str(&text[cursor_pos + cursor.len()..]); | 52 | new_text.push_str(&text[cursor_pos + cursor.len()..]); |
49 | let cursor_pos = TextUnit::from(cursor_pos as u32); | 53 | let cursor_pos = TextUnit::from(cursor_pos as u32); |
50 | (cursor_pos, new_text) | 54 | Some((cursor_pos, new_text)) |
51 | } | 55 | } |
52 | 56 | ||
53 | pub fn extract_range(text: &str) -> (TextRange, String) { | 57 | pub fn extract_range(text: &str) -> (TextRange, String) { |
54 | let (start, text) = extract_offset(text); | 58 | match try_extract_range(text) { |
55 | let (end, text) = extract_offset(&text); | 59 | None => panic!("text should contain cursor marker"), |
56 | (TextRange::from_to(start, end), text) | 60 | Some(result) => result, |
61 | } | ||
62 | } | ||
63 | |||
64 | pub fn try_extract_range(text: &str) -> Option<(TextRange, String)> { | ||
65 | let (start, text) = try_extract_offset(text)?; | ||
66 | let (end, text) = try_extract_offset(&text)?; | ||
67 | Some((TextRange::from_to(start, end), text)) | ||
68 | } | ||
69 | |||
70 | pub fn extract_ranges(text: &str) -> (Vec<TextRange>, String) { | ||
71 | let mut ranges = Vec::new(); | ||
72 | let mut text = String::from(text); | ||
73 | while let Some((range, new_text)) = try_extract_range(&text) { | ||
74 | text = new_text; | ||
75 | ranges.push(range); | ||
76 | } | ||
77 | |||
78 | (ranges, text) | ||
57 | } | 79 | } |
58 | 80 | ||
59 | pub fn add_cursor(text: &str, offset: TextUnit) -> String { | 81 | pub fn add_cursor(text: &str, offset: TextUnit) -> String { |
diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml index d03910986..e2fecc60d 100644 --- a/crates/tools/Cargo.toml +++ b/crates/tools/Cargo.toml | |||
@@ -1,4 +1,5 @@ | |||
1 | [package] | 1 | [package] |
2 | edition = "2018" | ||
2 | name = "tools" | 3 | name = "tools" |
3 | version = "0.1.0" | 4 | version = "0.1.0" |
4 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |