aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_analysis/Cargo.toml3
-rw-r--r--crates/ra_analysis/src/db.rs117
-rw-r--r--crates/ra_analysis/src/db/imp.rs153
-rw-r--r--crates/ra_analysis/src/db/mod.rs85
-rw-r--r--crates/ra_analysis/src/descriptors.rs68
-rw-r--r--crates/ra_analysis/src/imp.rs169
-rw-r--r--crates/ra_analysis/src/lib.rs21
-rw-r--r--crates/ra_analysis/src/module_map.rs165
-rw-r--r--crates/ra_analysis/src/queries.rs39
-rw-r--r--crates/ra_analysis/src/roots.rs72
-rw-r--r--crates/ra_analysis/src/symbol_index.rs11
-rw-r--r--crates/ra_analysis/tests/tests.rs102
-rw-r--r--crates/ra_cli/Cargo.toml1
-rw-r--r--crates/ra_editor/Cargo.toml1
-rw-r--r--crates/ra_editor/src/code_actions.rs4
-rw-r--r--crates/ra_editor/src/completion.rs2
-rw-r--r--crates/ra_editor/src/edit.rs2
-rw-r--r--crates/ra_editor/src/folding_ranges.rs177
-rw-r--r--crates/ra_editor/src/lib.rs2
-rw-r--r--crates/ra_editor/src/line_index.rs4
-rw-r--r--crates/ra_editor/src/scope/fn_scope.rs4
-rw-r--r--crates/ra_editor/src/symbols.rs4
-rw-r--r--crates/ra_editor/src/test_utils.rs4
-rw-r--r--crates/ra_editor/src/typing.rs4
-rw-r--r--crates/ra_lsp_server/Cargo.toml1
-rw-r--r--crates/ra_lsp_server/src/caps.rs5
-rw-r--r--crates/ra_lsp_server/src/conv.rs6
-rw-r--r--crates/ra_lsp_server/src/lib.rs6
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs42
-rw-r--r--crates/ra_lsp_server/src/main_loop/mod.rs7
-rw-r--r--crates/ra_lsp_server/src/project_model.rs2
-rw-r--r--crates/ra_lsp_server/src/req.rs1
-rw-r--r--crates/ra_lsp_server/src/server_world.rs3
-rw-r--r--crates/ra_lsp_server/src/thread_watcher.rs2
-rw-r--r--crates/ra_lsp_server/src/vfs.rs2
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs2
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs2
-rw-r--r--crates/ra_syntax/Cargo.toml1
-rw-r--r--crates/ra_syntax/src/algo/mod.rs2
-rw-r--r--crates/ra_syntax/src/algo/visit.rs2
-rw-r--r--crates/ra_syntax/src/algo/walk.rs2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs26
-rw-r--r--crates/ra_syntax/src/ast/generated.rs.tera2
-rw-r--r--crates/ra_syntax/src/ast/mod.rs30
-rw-r--r--crates/ra_syntax/src/grammar.ron3
-rw-r--r--crates/ra_syntax/src/grammar/expressions/atom.rs2
-rw-r--r--crates/ra_syntax/src/grammar/mod.rs2
-rw-r--r--crates/ra_syntax/src/lexer/comments.rs4
-rw-r--r--crates/ra_syntax/src/lexer/mod.rs2
-rw-r--r--crates/ra_syntax/src/lexer/numbers.rs6
-rw-r--r--crates/ra_syntax/src/lexer/ptr.rs2
-rw-r--r--crates/ra_syntax/src/lexer/strings.rs4
-rw-r--r--crates/ra_syntax/src/lib.rs6
-rw-r--r--crates/ra_syntax/src/parser_api.rs2
-rw-r--r--crates/ra_syntax/src/parser_impl/event.rs2
-rw-r--r--crates/ra_syntax/src/parser_impl/input.rs2
-rw-r--r--crates/ra_syntax/src/parser_impl/mod.rs4
-rw-r--r--crates/ra_syntax/src/reparsing.rs16
-rw-r--r--crates/ra_syntax/src/syntax_kinds/mod.rs2
-rw-r--r--crates/ra_syntax/src/text_utils.rs2
-rw-r--r--crates/ra_syntax/src/token_set.rs4
-rw-r--r--crates/ra_syntax/src/utils.rs2
-rw-r--r--crates/ra_syntax/src/yellow/builder.rs2
-rw-r--r--crates/ra_syntax/src/yellow/mod.rs14
-rw-r--r--crates/ra_syntax/src/yellow/syntax_text.rs2
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.rs3
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0035_crate_path_in_call.txt41
-rw-r--r--crates/salsa/Cargo.toml8
-rw-r--r--crates/salsa/src/lib.rs293
-rw-r--r--crates/salsa/tests/integration.rs170
-rw-r--r--crates/test_utils/Cargo.toml1
-rw-r--r--crates/test_utils/src/lib.rs38
-rw-r--r--crates/tools/Cargo.toml1
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]
2edition = "2018"
2name = "ra_analysis" 3name = "ra_analysis"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
@@ -14,7 +15,7 @@ fst = "0.3.1"
14im = "12.0.0" 15im = "12.0.0"
15ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 17ra_editor = { path = "../ra_editor" }
17salsa = { path = "../salsa" } 18salsa = "0.5.0"
18rustc-hash = "1.0" 19rustc-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 @@
1use std::{
2 fmt,
3 sync::Arc,
4 hash::{Hash, Hasher},
5};
6use salsa;
7use rustc_hash::FxHashSet;
8use ra_syntax::File;
9use ra_editor::{LineIndex};
10use crate::{
11 symbol_index::SymbolIndex,
12 module_map::{ModulesDatabase, ModuleTreeQuery, ModuleDescriptorQuery},
13 FileId, FileResolverImp,
14};
15
16#[derive(Default)]
17pub(crate) struct RootDatabase {
18 runtime: salsa::runtime::Runtime<RootDatabase>,
19}
20
21impl fmt::Debug for RootDatabase {
22 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
23 fmt.write_str("RootDatabase { ... }")
24 }
25}
26
27impl salsa::Database for RootDatabase {
28 fn salsa_runtime(&self) -> &salsa::runtime::Runtime<RootDatabase> {
29 &self.runtime
30 }
31}
32
33impl salsa::ParallelDatabase for RootDatabase {
34 fn fork(&self) -> Self {
35 RootDatabase {
36 runtime: self.runtime.fork(),
37 }
38 }
39}
40
41impl Clone for RootDatabase {
42 fn clone(&self) -> RootDatabase {
43 salsa::ParallelDatabase::fork(self)
44 }
45}
46
47salsa::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
65salsa::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)]
79pub(crate) struct FileSet {
80 pub(crate) files: FxHashSet<FileId>,
81 pub(crate) resolver: FileResolverImp,
82}
83
84impl 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
92salsa::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
106fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> File {
107 let text = db.file_text(file_id);
108 File::parse(&*text)
109}
110fn 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}
114fn 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 @@
1use std::{
2 sync::Arc,
3 any::Any,
4 hash::{Hash, Hasher},
5 collections::hash_map::{DefaultHasher},
6 iter,
7};
8use rustc_hash::FxHashMap;
9use salsa;
10use {FileId, imp::FileResolverImp};
11use super::{State, Query, QueryCtx};
12
13pub(super) type Data = Arc<Any + Send + Sync + 'static>;
14
15#[derive(Debug)]
16pub(super) struct Db {
17 names: Arc<FxHashMap<salsa::QueryTypeId, &'static str>>,
18 pub(super) imp: salsa::Db<State, Data>,
19}
20
21impl 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
49pub(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
57impl<T, R> EvalQuery for Query<T, R>
58where
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(&params)),
81 );
82 let res = ctx.imp.get(query_id, Arc::new(params));
83 res.downcast().unwrap()
84 }
85}
86
87pub(super) struct QueryRegistry {
88 config: Option<salsa::QueryConfig<State, Data>>,
89 names: FxHashMap<salsa::QueryTypeId, &'static str>,
90}
91
92impl 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
128fn hash<T: Hash>(x: &T) -> u64 {
129 let mut hasher = DefaultHasher::new();
130 x.hash(&mut hasher);
131 hasher.finish()
132}
133
134const FILE_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(0);
135pub(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
144const FILE_SET: salsa::QueryTypeId = salsa::QueryTypeId(1);
145pub(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 @@
1mod imp;
2
3use std::{
4 sync::Arc,
5};
6use im;
7use salsa;
8use {FileId, imp::FileResolverImp};
9
10#[derive(Debug, Default, Clone)]
11pub(crate) struct State {
12 pub(crate) file_map: im::HashMap<FileId, Arc<String>>,
13 pub(crate) file_resolver: FileResolverImp
14}
15
16#[derive(Debug)]
17pub(crate) struct Db {
18 imp: imp::Db,
19}
20
21#[derive(Clone, Copy)]
22pub(crate) struct QueryCtx<'a> {
23 imp: &'a salsa::QueryCtx<State, imp::Data>,
24}
25
26pub(crate) struct Query<T, R>(pub(crate) u16, pub(crate) fn(QueryCtx, &T) -> R);
27
28pub(crate) struct QueryRegistry {
29 imp: imp::QueryRegistry,
30}
31
32impl Default for Db {
33 fn default() -> Db {
34 Db::new()
35 }
36}
37
38impl 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
62impl<'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
68pub(crate) fn file_text(ctx: QueryCtx, file_id: FileId) -> Arc<String> {
69 imp::file_text(ctx, file_id)
70}
71
72pub(crate) fn file_set(ctx: QueryCtx) -> Arc<(Vec<FileId>, FileResolverImp)> {
73 imp::file_set(ctx)
74}
75impl 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::{
4use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
5use ra_syntax::{ 5use ra_syntax::{
6 SmolStr, 6 SmolStr,
7 ast::{self, NameOwner}, 7 ast::{self, NameOwner, AstNode},
8 text_utils::is_subrange
8}; 9};
9use { 10use 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)]
15pub struct ModuleDescriptor { 16pub 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)]
46pub(crate) struct ModuleTreeDescriptor { 47pub(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)]
53struct Node(usize); 54struct Node(usize);
54#[derive(Hash, Debug)] 55#[derive(Hash, Debug, PartialEq, Eq)]
55struct NodeData { 56struct 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)]
62pub(crate) struct Link(usize); 63pub(crate) struct Link(usize);
63#[derive(Hash, Debug)] 64#[derive(Hash, Debug, PartialEq, Eq)]
64struct LinkData { 65struct 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)]
73pub enum Problem { 74pub 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)]
224pub struct FnDescriptor {
225 pub name: String,
226 pub label : String,
227 pub ret_type: Option<String>,
228 pub params: Vec<String>,
229}
230
231impl 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 @@
1use std::{ 1use 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;
12use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
13use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; 13use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name};
14use ra_syntax::{ 14use 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
20use { 20use 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)]
29pub(crate) struct FileResolverImp { 28pub(crate) struct FileResolverImp {
30 inner: Arc<FileResolver> 29 inner: Arc<FileResolver>
31} 30}
32 31
32impl PartialEq for FileResolverImp {
33 fn eq(&self, other: &FileResolverImp) -> bool {
34 self.inner() == other.inner()
35 }
36}
37
38impl Eq for FileResolverImp {
39}
40
41impl Hash for FileResolverImp {
42 fn hash<H: Hasher>(&self, hasher: &mut H) {
43 self.inner().hash(hasher);
44 }
45}
46
33impl FileResolverImp { 47impl 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
45impl Default for FileResolverImp { 62impl Default for FileResolverImp {
@@ -60,29 +77,27 @@ impl Default for FileResolverImp {
60 77
61#[derive(Debug)] 78#[derive(Debug)]
62pub(crate) struct AnalysisHostImpl { 79pub(crate) struct AnalysisHostImpl {
63 data: Arc<WorldData> 80 data: WorldData
64} 81}
65 82
66impl AnalysisHostImpl { 83impl 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
104pub(crate) struct AnalysisImpl { 119pub(crate) struct AnalysisImpl {
105 needs_reindex: AtomicBool, 120 data: WorldData,
106 data: Arc<WorldData>,
107} 121}
108 122
109impl fmt::Debug for AnalysisImpl { 123impl 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
115impl 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
124impl AnalysisImpl { 129impl 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)]
327struct WorldData { 394struct 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
425enum FnCallNode<'a> {
426 CallExpr(ast::CallExpr<'a>),
427 MethodCallExpr(ast::MethodCallExpr<'a>)
428}
429
430impl<'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;
19mod job; 19mod job;
20mod roots; 20mod roots;
21mod db; 21mod db;
22mod queries;
23mod descriptors; 22mod descriptors;
24 23
25use std::{ 24use std::{
@@ -29,15 +28,18 @@ use std::{
29 28
30use relative_path::{RelativePath, RelativePathBuf}; 29use relative_path::{RelativePath, RelativePathBuf};
31use ra_syntax::{File, TextRange, TextUnit, AtomEdit}; 30use ra_syntax::{File, TextRange, TextUnit, AtomEdit};
32use imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp};
33use rustc_hash::FxHashMap; 31use rustc_hash::FxHashMap;
32use crate::imp::{AnalysisImpl, AnalysisHostImpl, FileResolverImp};
34 33
35pub use ra_editor::{ 34pub 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};
40pub use job::{JobToken, JobHandle}; 39pub 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)]
43pub struct FileId(pub u32); 45pub struct FileId(pub u32);
@@ -159,7 +161,7 @@ impl Query {
159 } 161 }
160} 162}
161 163
162#[derive(Clone, Debug)] 164#[derive(Debug)]
163pub struct Analysis { 165pub 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]
262fn 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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2use { 2use 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
12pub(crate) fn register_queries(reg: &mut QueryRegistry) { 8salsa::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
17pub(crate) fn module_tree(ctx: QueryCtx) -> Arc<ModuleTreeDescriptor> {
18 ctx.get(MODULE_TREE, ())
19}
20
21const 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
26const 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)]
37mod 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;"); 20fn 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); 25fn 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 @@
1use std::sync::Arc;
2use ra_syntax::File;
3use ra_editor::LineIndex;
4use {
5 FileId,
6 db::{Query, QueryCtx, QueryRegistry},
7 symbol_index::SymbolIndex,
8};
9
10pub(crate) use db::{file_text, file_set};
11
12pub(crate) fn file_syntax(ctx: QueryCtx, file_id: FileId) -> File {
13 (&*ctx.get(FILE_SYNTAX, file_id)).clone()
14}
15pub(crate) fn file_lines(ctx: QueryCtx, file_id: FileId) -> Arc<LineIndex> {
16 ctx.get(FILE_LINES, file_id)
17}
18pub(crate) fn file_symbols(ctx: QueryCtx, file_id: FileId) -> Arc<SymbolIndex> {
19 ctx.get(FILE_SYMBOLS, file_id)
20}
21
22const FILE_SYNTAX: Query<FileId, File> = Query(16, |ctx, file_id: &FileId| {
23 let text = file_text(ctx, *file_id);
24 File::parse(&*text)
25});
26const FILE_LINES: Query<FileId, LineIndex> = Query(17, |ctx, file_id: &FileId| {
27 let text = file_text(ctx, *file_id);
28 LineIndex::new(&*text)
29});
30const 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
35pub(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
6use once_cell::sync::OnceCell; 6use once_cell::sync::OnceCell;
7use rayon::prelude::*; 7use rayon::prelude::*;
8use rustc_hash::FxHashMap; 8use salsa::Database;
9use rustc_hash::{FxHashMap, FxHashSet};
9use ra_editor::LineIndex; 10use ra_editor::LineIndex;
10use ra_syntax::File; 11use ra_syntax::File;
11 12
12use { 13use 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
20pub(crate) trait SourceRoot { 22pub(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)]
29pub(crate) struct WritableSourceRoot { 31pub(crate) struct WritableSourceRoot {
30 db: Db, 32 db: db::RootDatabase,
31} 33}
32 34
33impl WritableSourceRoot { 35impl 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
63impl SourceRoot for WritableSourceRoot { 69impl 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};
10use fst::{self, Streamer}; 10use fst::{self, Streamer};
11use rayon::prelude::*; 11use rayon::prelude::*;
12use {Query, FileId, JobToken}; 12use crate::{Query, FileId, JobToken};
13 13
14#[derive(Debug)] 14#[derive(Debug)]
15pub(crate) struct SymbolIndex { 15pub(crate) struct SymbolIndex {
@@ -17,6 +17,15 @@ pub(crate) struct SymbolIndex {
17 map: fst::Map, 17 map: fst::Map,
18} 18}
19 19
20impl PartialEq for SymbolIndex {
21 fn eq(&self, other: &SymbolIndex) -> bool {
22 self.symbols == other.symbols
23 }
24}
25
26impl Eq for SymbolIndex {
27}
28
20impl Hash for SymbolIndex { 29impl 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 @@
1extern crate relative_path; 1extern crate relative_path;
2extern crate ra_analysis; 2extern crate ra_analysis;
3extern crate rustc_hash; 3extern crate rustc_hash;
4extern crate ra_editor;
5extern crate ra_syntax;
4extern crate test_utils; 6extern crate test_utils;
5 7
6use std::{ 8use std::{
@@ -9,8 +11,8 @@ use std::{
9 11
10use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
11use relative_path::{RelativePath, RelativePathBuf}; 13use relative_path::{RelativePath, RelativePathBuf};
12use ra_analysis::{Analysis, AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId}; 14use ra_analysis::{Analysis, AnalysisHost, FileId, FileResolver, JobHandle, CrateGraph, CrateId, FnDescriptor};
13use test_utils::assert_eq_dbg; 15use test_utils::{assert_eq_dbg, extract_offset};
14 16
15#[derive(Debug)] 17#[derive(Debug)]
16struct FileMap(Vec<(FileId, RelativePathBuf)>); 18struct FileMap(Vec<(FileId, RelativePathBuf)>);
@@ -39,7 +41,7 @@ impl FileResolver for FileMap {
39 } 41 }
40} 42}
41 43
42fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost { 44fn 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
56fn analysis(files: &'static [(&'static str, &'static str)]) -> Analysis { 58fn analysis(files: &[(&str, &str)]) -> Analysis {
57 analysis_host(files).analysis() 59 analysis_host(files).analysis()
58} 60}
59 61
62fn 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]
61fn test_resolve_module() { 73fn 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]
162fn test_fn_signature_two_args_first() {
163 let (desc, param) = get_signature(
164r#"fn foo(x: u32, y: u32) -> u32 {x + y}
165fn 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]
174fn test_fn_signature_two_args_second() {
175 let (desc, param) = get_signature(
176 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
177fn 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]
186fn test_fn_signature_for_impl() {
187 let (desc, param) = get_signature(
188r#"struct F; impl F { pub fn new() { F{}} }
189fn 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]
198fn test_fn_signature_for_method_self() {
199 let (desc, param) = get_signature(
200r#"struct F;
201impl F {
202 pub fn new() -> F{
203 F{}
204 }
205
206 pub fn do_it(&self) {}
207}
208
209fn 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]
221fn test_fn_signature_for_method_with_arg() {
222 let (desc, param) = get_signature(
223r#"struct F;
224impl F {
225 pub fn new() -> F{
226 F{}
227 }
228
229 pub fn do_it(&self, x: i32) {}
230}
231
232fn 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]
2edition = "2018"
2name = "ra_cli" 3name = "ra_cli"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["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]
2edition = "2018"
2name = "ra_editor" 3name = "ra_editor"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["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
14use {EditBuilder, Edit, find_node_at_offset}; 14use crate::{EditBuilder, Edit, find_node_at_offset};
15 15
16#[derive(Debug)] 16#[derive(Debug)]
17pub struct LocalEdit { 17pub struct LocalEdit {
@@ -136,7 +136,7 @@ fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<Synta
136#[cfg(test)] 136#[cfg(test)]
137mod tests { 137mod 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
12use { 12use 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 @@
1use {TextRange, TextUnit}; 1use crate::{TextRange, TextUnit};
2use ra_syntax::{ 2use 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 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2 2
3use ra_syntax::{ 3use 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
21pub fn folding_ranges(file: &File) -> Vec<Fold> { 23pub 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() { 48fn 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 ), 56fn 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
56fn contiguous_range_for<'a>( 74fn 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)]
89mod tests { 121mod 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
102fn main() { 148fn 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#"
124use std::str; 171<|>use std::{
125use std::vec; 172 str,
126use std::io as iop; 173 vec,
174 io as iop
175};<|>
127 176
128fn main() { 177fn 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)]
165mod tests { 165mod 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 @@
1use superslice::Ext; 1use superslice::Ext;
2use ::TextUnit; 2use crate::TextUnit;
3 3
4#[derive(Clone, Debug, Hash)] 4#[derive(Clone, Debug, Hash, PartialEq, Eq)]
5pub struct LineIndex { 5pub 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
256mod tests { 256mod 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};
9use TextRange; 9use crate::TextRange;
10 10
11#[derive(Debug, Clone)] 11#[derive(Debug, Clone)]
12pub struct StructureNode { 12pub 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)]
21pub struct FileSymbol { 21pub 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 @@
1use ra_syntax::{File, TextUnit, TextRange}; 1use ra_syntax::{File, TextUnit, TextRange};
2pub use _test_utils::*; 2pub use crate::_test_utils::*;
3use LocalEdit; 3use crate::LocalEdit;
4 4
5pub fn check_action<F: Fn(&File, TextUnit) -> Option<LocalEdit>> ( 5pub 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
13use {LocalEdit, EditBuilder, find_node_at_offset}; 13use crate::{LocalEdit, EditBuilder, find_node_at_offset};
14 14
15pub fn join_lines(file: &File, range: TextRange) -> LocalEdit { 15pub 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)]
245mod tests { 245mod 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]
2edition = "2018"
2name = "ra_lsp_server" 3name = "ra_lsp_server"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["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};
7use ra_syntax::{SyntaxKind, TextUnit, TextRange}; 7use ra_syntax::{SyntaxKind, TextUnit, TextRange};
8use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit}; 8use ra_analysis::{FileId, SourceChange, SourceFileEdit, FileSystemEdit};
9 9
10use { 10use 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
302pub trait MapConvWith<'a>: Sized { 302pub 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
311impl<'a, I> MapConvWith<'a> for I 311impl<'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;
34pub mod thread_watcher; 34pub mod thread_watcher;
35 35
36pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 36pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
37pub use caps::server_capabilities; 37pub use crate::{
38pub 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
16use ::{ 16use 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
414pub 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
414pub fn handle_code_action( 450pub 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
444pub fn publish_diagnostics( 480pub 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
463pub fn publish_decorations( 499pub 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};
17use rustc_hash::FxHashMap; 17use rustc_hash::FxHashMap;
18 18
19use { 19use 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};
5use cargo_metadata::{metadata_run, CargoOpt}; 5use cargo_metadata::{metadata_run, CargoOpt};
6use ra_syntax::SmolStr; 6use ra_syntax::SmolStr;
7 7
8use { 8use 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
19pub enum SyntaxTree {} 20pub 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;
8use languageserver_types::Url; 8use languageserver_types::Url;
9use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver}; 9use ra_analysis::{FileId, AnalysisHost, Analysis, CrateGraph, CrateId, LibraryData, FileResolver};
10 10
11use { 11use 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)]
27pub struct ServerWorld { 26pub 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 @@
1use std::thread; 1use std::thread;
2use crossbeam_channel::{bounded, unbounded, Sender, Receiver}; 2use crossbeam_channel::{bounded, unbounded, Sender, Receiver};
3use drop_bomb::DropBomb; 3use drop_bomb::DropBomb;
4use Result; 4use crate::Result;
5 5
6pub struct Worker<I, O> { 6pub 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
6use walkdir::WalkDir; 6use walkdir::WalkDir;
7 7
8use { 8use 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
13use ra_lsp_server::req::{Runnables, RunnablesParams}; 13use ra_lsp_server::req::{Runnables, RunnablesParams};
14 14
15use support::project; 15use crate::support::project;
16 16
17 17
18const LOG: &'static str = ""; 18const 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
26pub fn project(fixture: &str) -> Server { 26pub 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]
2edition = "2018"
2name = "ra_syntax" 3name = "ra_syntax"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["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 @@
1pub mod walk; 1pub mod walk;
2pub mod visit; 2pub mod visit;
3 3
4use { 4use 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 @@
1use std::marker::PhantomData; 1use std::marker::PhantomData;
2use {SyntaxNodeRef, AstNode}; 2use crate::{SyntaxNodeRef, AstNode};
3 3
4 4
5pub fn visitor<'a, T>() -> impl Visitor<'a, Output=T> { 5pub 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 @@
1use { 1use 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
4use { 4use 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
1390impl<'a> PathExpr<'a> {} 1390impl<'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)]
2202pub struct Whitespace<'a> {
2203 syntax: SyntaxNodeRef<'a>,
2204}
2205
2206impl<'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
2216impl<'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
6use { 6use 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
5use itertools::Itertools; 5use itertools::Itertools;
6 6
7use { 7use 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
102impl<'a> Comment<'a> { 102impl<'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)]
126pub enum CommentFlavor { 134pub enum CommentFlavor {
127 Line, 135 Line,
128 Doc, 136 Doc,
@@ -142,6 +150,20 @@ impl CommentFlavor {
142 } 150 }
143} 151}
144 152
153impl<'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
145impl<'a> Name<'a> { 167impl<'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
36const EXPR_RECOVERY_SET: TokenSet = 36const 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;
31mod type_params; 31mod type_params;
32mod types; 32mod types;
33 33
34use { 34use 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 @@
1use lexer::ptr::Ptr; 1use crate::lexer::ptr::Ptr;
2 2
3use SyntaxKind::{self, *}; 3use crate::SyntaxKind::{self, *};
4 4
5pub(crate) fn scan_shebang(ptr: &mut Ptr) -> bool { 5pub(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;
4mod ptr; 4mod ptr;
5mod strings; 5mod strings;
6 6
7use { 7use 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 @@
1use lexer::classes::*; 1use crate::lexer::classes::*;
2use lexer::ptr::Ptr; 2use crate::lexer::ptr::Ptr;
3 3
4use SyntaxKind::{self, *}; 4use crate::SyntaxKind::{self, *};
5 5
6pub(crate) fn scan_number(c: char, ptr: &mut Ptr) -> SyntaxKind { 6pub(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 @@
1use TextUnit; 1use crate::TextUnit;
2 2
3use std::str::Chars; 3use 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 @@
1use SyntaxKind::{self, *}; 1use crate::SyntaxKind::{self, *};
2 2
3use lexer::ptr::Ptr; 3use crate::lexer::ptr::Ptr;
4 4
5pub(crate) fn is_string_literal_start(c: char, c1: Option<char>, c2: Option<char>) -> bool { 5pub(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;
46pub mod utils; 46pub mod utils;
47pub mod text_utils; 47pub mod text_utils;
48 48
49pub use { 49pub 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
58use { 58use crate::{
59 yellow::{GreenNode}, 59 yellow::{GreenNode},
60}; 60};
61 61
62#[derive(Clone, Debug, Hash)] 62#[derive(Clone, Debug, Hash, PartialEq, Eq)]
63pub struct File { 63pub 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 @@
1use { 1use 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.
10use std::mem; 10use std::mem;
11use { 11use 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 @@
1use {lexer::Token, SyntaxKind, SyntaxKind::EOF, TextRange, TextUnit}; 1use crate::{lexer::Token, SyntaxKind, SyntaxKind::EOF, TextRange, TextUnit};
2 2
3use std::ops::{Add, AddAssign}; 3use 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
4use std::cell::Cell; 4use std::cell::Cell;
5 5
6use { 6use 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
16use SyntaxKind::{self, EOF, TOMBSTONE}; 16use crate::SyntaxKind::{self, EOF, TOMBSTONE};
17 17
18pub(crate) trait Sink { 18pub(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 @@
1use algo; 1use crate::algo;
2use grammar; 2use crate::grammar;
3use lexer::{tokenize, Token}; 3use crate::lexer::{tokenize, Token};
4use yellow::{self, GreenNode, SyntaxNodeRef, SyntaxError}; 4use crate::yellow::{self, GreenNode, SyntaxNodeRef, SyntaxError};
5use parser_impl; 5use crate::parser_impl;
6use parser_api::Parser; 6use crate::parser_api::Parser;
7use { 7use crate::{
8 TextUnit, TextRange, 8 TextUnit, TextRange,
9 SyntaxKind::*, 9 SyntaxKind::*,
10}; 10};
11use text_utils::replace_range; 11use crate::text_utils::replace_range;
12 12
13#[derive(Debug, Clone)] 13#[derive(Debug, Clone)]
14pub struct AtomEdit { 14pub 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 @@
1mod generated; 1mod generated;
2 2
3use std::fmt; 3use std::fmt;
4use SyntaxKind::*; 4use crate::SyntaxKind::*;
5 5
6pub use self::generated::SyntaxKind; 6pub 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 @@
1use {TextRange, TextUnit}; 1use crate::{TextRange, TextUnit};
2 2
3pub fn contains_offset_nonstrict(range: TextRange, offset: TextUnit) -> bool { 3pub 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 @@
1use SyntaxKind; 1use crate::SyntaxKind;
2 2
3#[derive(Clone, Copy)] 3#[derive(Clone, Copy)]
4pub(crate) struct TokenSet(pub(crate) u128); 4pub(crate) struct TokenSet(pub(crate) u128);
@@ -29,7 +29,7 @@ macro_rules! token_set_union {
29 29
30#[test] 30#[test]
31fn token_set_works_for_tokens() { 31fn 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 @@
1use std::fmt::Write; 1use std::fmt::Write;
2use { 2use 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 @@
1use rowan::GreenNodeBuilder; 1use rowan::GreenNodeBuilder;
2use { 2use 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};
8use rowan::Types; 8use rowan::Types;
9use {SyntaxKind, TextUnit, TextRange, SmolStr}; 9use crate::{SyntaxKind, TextUnit, TextRange, SmolStr};
10use self::syntax_text::SyntaxText; 10use self::syntax_text::SyntaxText;
11 11
12pub use rowan::{TreeRoot}; 12pub 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
158fn has_short_text(kind: SyntaxKind) -> bool { 158fn 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
5use { 5use 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 @@
1fn 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 @@
1ROOT@[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]
2name = "salsa"
3version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"]
5
6[dependencies]
7parking_lot = "0.6.3"
8im = "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 @@
1extern crate im;
2extern crate parking_lot;
3
4use std::{
5 sync::Arc,
6 collections::{HashSet, HashMap},
7 cell::RefCell,
8};
9use parking_lot::Mutex;
10
11pub type GroundQueryFn<T, D> = Box<Fn(&T, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
12pub type QueryFn<T, D> = Box<Fn(&QueryCtx<T, D>, &D) -> (D, OutputFingerprint) + Send + Sync + 'static>;
13
14#[derive(Debug)]
15pub struct Db<T, D> {
16 db: Arc<DbState<T, D>>,
17 query_config: Arc<QueryConfig<T, D>>,
18}
19
20pub struct QueryConfig<T, D> {
21 ground_fn: HashMap<QueryTypeId, GroundQueryFn<T, D>>,
22 query_fn: HashMap<QueryTypeId, QueryFn<T, D>>,
23}
24
25impl<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)]
32struct 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)]
39struct QueryRecord<D> {
40 params: D,
41 output: D,
42 output_fingerprint: OutputFingerprint,
43 deps: Vec<(QueryId, OutputFingerprint)>,
44}
45
46impl<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
66impl<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
93pub 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
100impl<T, D> QueryCtx<T, D>
101where
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, &params)
184 } else if let Some(f) = self.query_config.query_fn.get(&query_id.0) {
185 f(self, &params)
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
207pub struct Invalidations {
208 types: HashSet<QueryTypeId>,
209 ids: Vec<QueryId>,
210}
211
212impl 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
229impl<T, D> Db<T, D>
230where
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)]
283struct Gen(u64);
284const INVALIDATED: Gen = Gen(!0);
285#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
286pub struct InputFingerprint(pub u64);
287#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
288pub struct OutputFingerprint(pub u64);
289#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
290pub struct QueryTypeId(pub u16);
291#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
292pub 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 @@
1extern crate salsa;
2use std::{
3 iter::once,
4 sync::Arc,
5 collections::hash_map::{HashMap, DefaultHasher},
6 any::Any,
7 hash::{Hash, Hasher},
8};
9
10type State = HashMap<u32, String>;
11type Data = Arc<Any + Send + Sync + 'static>;
12const GET_TEXT: salsa::QueryTypeId = salsa::QueryTypeId(1);
13const GET_FILES: salsa::QueryTypeId = salsa::QueryTypeId(2);
14const FILE_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(3);
15const TOTAL_NEWLINES: salsa::QueryTypeId = salsa::QueryTypeId(4);
16
17fn mk_ground_query<T, R>(
18 state: &State,
19 params: &Data,
20 f: fn(&State, &T) -> R,
21) -> (Data, salsa::OutputFingerprint)
22where
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
32fn get<T, R>(db: &salsa::Db<State, Data>, query_type: salsa::QueryTypeId, param: T) -> (Arc<R>, Vec<salsa::QueryTypeId>)
33where
34 T: Hash + Send + Sync + 'static,
35 R: Send + Sync + 'static,
36{
37 let i_print = i_print(&param);
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
43struct QueryCtx<'a>(&'a salsa::QueryCtx<State, Data>);
44
45impl<'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
64fn mk_query<T, R>(
65 query_ctx: &salsa::QueryCtx<State, Data>,
66 params: &Data,
67 f: fn(QueryCtx, &T) -> R,
68) -> (Data, salsa::OutputFingerprint)
69where
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
80fn 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]
106fn 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
158fn 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
165fn 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]
2edition = "2018"
2name = "test_utils" 3name = "test_utils"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["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
40pub fn extract_offset(text: &str) -> (TextUnit, String) { 40pub 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
47pub 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
53pub fn extract_range(text: &str) -> (TextRange, String) { 57pub 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
64pub 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
70pub 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
59pub fn add_cursor(text: &str, offset: TextUnit) -> String { 81pub 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]
2edition = "2018"
2name = "tools" 3name = "tools"
3version = "0.1.0" 4version = "0.1.0"
4authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]