diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_batch/src/lib.rs | 42 | ||||
-rw-r--r-- | crates/ra_batch/src/vfs_filter.rs | 108 | ||||
-rw-r--r-- | crates/ra_cli/src/analysis_bench.rs | 92 | ||||
-rw-r--r-- | crates/ra_cli/src/analysis_stats.rs | 19 | ||||
-rw-r--r-- | crates/ra_cli/src/main.rs | 47 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide_api/src/line_index.rs | 2 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 1 |
8 files changed, 232 insertions, 81 deletions
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs index fa244e86c..43d3fb7e3 100644 --- a/crates/ra_batch/src/lib.rs +++ b/crates/ra_batch/src/lib.rs | |||
@@ -8,7 +8,7 @@ use ra_db::{ | |||
8 | CrateGraph, FileId, SourceRootId, | 8 | CrateGraph, FileId, SourceRootId, |
9 | }; | 9 | }; |
10 | use ra_ide_api::{AnalysisHost, AnalysisChange}; | 10 | use ra_ide_api::{AnalysisHost, AnalysisChange}; |
11 | use ra_project_model::ProjectWorkspace; | 11 | use ra_project_model::{ProjectWorkspace, ProjectRoot}; |
12 | use ra_vfs::{Vfs, VfsChange}; | 12 | use ra_vfs::{Vfs, VfsChange}; |
13 | use vfs_filter::IncludeRustFiles; | 13 | use vfs_filter::IncludeRustFiles; |
14 | 14 | ||
@@ -21,13 +21,11 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId { | |||
21 | SourceRootId(r.0) | 21 | SourceRootId(r.0) |
22 | } | 22 | } |
23 | 23 | ||
24 | pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> { | 24 | pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, FxHashMap<SourceRootId, ProjectRoot>)> { |
25 | let root = std::env::current_dir()?.join(root); | 25 | let root = std::env::current_dir()?.join(root); |
26 | let ws = ProjectWorkspace::discover(root.as_ref())?; | 26 | let ws = ProjectWorkspace::discover(root.as_ref())?; |
27 | let mut roots = Vec::new(); | 27 | let project_roots = ws.to_roots(); |
28 | roots.push(IncludeRustFiles::member(root.clone())); | 28 | let (mut vfs, roots) = Vfs::new(IncludeRustFiles::from_roots(project_roots.clone()).collect()); |
29 | roots.extend(IncludeRustFiles::from_roots(ws.to_roots())); | ||
30 | let (mut vfs, roots) = Vfs::new(roots); | ||
31 | let crate_graph = ws.to_crate_graph(&mut |path: &Path| { | 29 | let crate_graph = ws.to_crate_graph(&mut |path: &Path| { |
32 | let vfs_file = vfs.load(path); | 30 | let vfs_file = vfs.load(path); |
33 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 31 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); |
@@ -35,17 +33,27 @@ pub fn load_cargo(root: &Path) -> Result<(AnalysisHost, Vec<SourceRootId>)> { | |||
35 | }); | 33 | }); |
36 | log::debug!("crate graph: {:?}", crate_graph); | 34 | log::debug!("crate graph: {:?}", crate_graph); |
37 | 35 | ||
38 | let local_roots = roots | 36 | let source_roots = roots |
39 | .into_iter() | 37 | .iter() |
40 | .filter(|r| vfs.root2path(*r).starts_with(&root)) | 38 | .map(|&vfs_root| { |
41 | .map(vfs_root_to_id) | 39 | let source_root_id = vfs_root_to_id(vfs_root); |
42 | .collect(); | 40 | let project_root = project_roots |
43 | 41 | .iter() | |
44 | let host = load(root.as_path(), crate_graph, &mut vfs); | 42 | .find(|it| it.path() == &vfs.root2path(vfs_root)) |
45 | Ok((host, local_roots)) | 43 | .unwrap() |
44 | .clone(); | ||
45 | (source_root_id, project_root) | ||
46 | }) | ||
47 | .collect::<FxHashMap<_, _>>(); | ||
48 | let host = load(&source_roots, crate_graph, &mut vfs); | ||
49 | Ok((host, source_roots)) | ||
46 | } | 50 | } |
47 | 51 | ||
48 | pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> AnalysisHost { | 52 | pub fn load( |
53 | source_roots: &FxHashMap<SourceRootId, ProjectRoot>, | ||
54 | crate_graph: CrateGraph, | ||
55 | vfs: &mut Vfs, | ||
56 | ) -> AnalysisHost { | ||
49 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); | 57 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); |
50 | let mut host = AnalysisHost::new(lru_cap); | 58 | let mut host = AnalysisHost::new(lru_cap); |
51 | let mut analysis_change = AnalysisChange::new(); | 59 | let mut analysis_change = AnalysisChange::new(); |
@@ -60,8 +68,8 @@ pub fn load(project_root: &Path, crate_graph: CrateGraph, vfs: &mut Vfs) -> Anal | |||
60 | for change in vfs.commit_changes() { | 68 | for change in vfs.commit_changes() { |
61 | match change { | 69 | match change { |
62 | VfsChange::AddRoot { root, files } => { | 70 | VfsChange::AddRoot { root, files } => { |
63 | let is_local = vfs.root2path(root).starts_with(&project_root); | ||
64 | let source_root_id = vfs_root_to_id(root); | 71 | let source_root_id = vfs_root_to_id(root); |
72 | let is_local = source_roots[&source_root_id].is_member(); | ||
65 | log::debug!( | 73 | log::debug!( |
66 | "loaded source root {:?} with path {:?}", | 74 | "loaded source root {:?} with path {:?}", |
67 | source_root_id, | 75 | source_root_id, |
@@ -106,7 +114,7 @@ mod tests { | |||
106 | let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); | 114 | let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); |
107 | let (host, roots) = load_cargo(path).unwrap(); | 115 | let (host, roots) = load_cargo(path).unwrap(); |
108 | let mut n_crates = 0; | 116 | let mut n_crates = 0; |
109 | for root in roots { | 117 | for (root, _) in roots { |
110 | for _krate in Crate::source_root_crates(host.raw_database(), root) { | 118 | for _krate in Crate::source_root_crates(host.raw_database(), root) { |
111 | n_crates += 1; | 119 | n_crates += 1; |
112 | } | 120 | } |
diff --git a/crates/ra_batch/src/vfs_filter.rs b/crates/ra_batch/src/vfs_filter.rs index dd20c1203..8552ac999 100644 --- a/crates/ra_batch/src/vfs_filter.rs +++ b/crates/ra_batch/src/vfs_filter.rs | |||
@@ -1,54 +1,54 @@ | |||
1 | use std::path::PathBuf; | 1 | use std::path::PathBuf; |
2 | use ra_project_model::ProjectRoot; | 2 | use ra_project_model::ProjectRoot; |
3 | use ra_vfs::{RootEntry, Filter, RelativePath}; | 3 | use ra_vfs::{RootEntry, Filter, RelativePath}; |
4 | 4 | ||
5 | /// `IncludeRustFiles` is used to convert | 5 | /// `IncludeRustFiles` is used to convert |
6 | /// from `ProjectRoot` to `RootEntry` for VFS | 6 | /// from `ProjectRoot` to `RootEntry` for VFS |
7 | pub struct IncludeRustFiles { | 7 | pub struct IncludeRustFiles { |
8 | root: ProjectRoot, | 8 | root: ProjectRoot, |
9 | } | 9 | } |
10 | 10 | ||
11 | impl IncludeRustFiles { | 11 | impl IncludeRustFiles { |
12 | pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry> | 12 | pub fn from_roots<R>(roots: R) -> impl Iterator<Item = RootEntry> |
13 | where | 13 | where |
14 | R: IntoIterator<Item = ProjectRoot>, | 14 | R: IntoIterator<Item = ProjectRoot>, |
15 | { | 15 | { |
16 | roots.into_iter().map(IncludeRustFiles::from_root) | 16 | roots.into_iter().map(IncludeRustFiles::from_root) |
17 | } | 17 | } |
18 | 18 | ||
19 | pub fn from_root(root: ProjectRoot) -> RootEntry { | 19 | pub fn from_root(root: ProjectRoot) -> RootEntry { |
20 | IncludeRustFiles::from(root).into() | 20 | IncludeRustFiles::from(root).into() |
21 | } | 21 | } |
22 | 22 | ||
23 | #[allow(unused)] | 23 | #[allow(unused)] |
24 | pub fn external(path: PathBuf) -> RootEntry { | 24 | pub fn external(path: PathBuf) -> RootEntry { |
25 | IncludeRustFiles::from_root(ProjectRoot::new(path, false)) | 25 | IncludeRustFiles::from_root(ProjectRoot::new(path, false)) |
26 | } | 26 | } |
27 | 27 | ||
28 | pub fn member(path: PathBuf) -> RootEntry { | 28 | pub fn member(path: PathBuf) -> RootEntry { |
29 | IncludeRustFiles::from_root(ProjectRoot::new(path, true)) | 29 | IncludeRustFiles::from_root(ProjectRoot::new(path, true)) |
30 | } | 30 | } |
31 | } | 31 | } |
32 | 32 | ||
33 | impl Filter for IncludeRustFiles { | 33 | impl Filter for IncludeRustFiles { |
34 | fn include_dir(&self, dir_path: &RelativePath) -> bool { | 34 | fn include_dir(&self, dir_path: &RelativePath) -> bool { |
35 | self.root.include_dir(dir_path) | 35 | self.root.include_dir(dir_path) |
36 | } | 36 | } |
37 | 37 | ||
38 | fn include_file(&self, file_path: &RelativePath) -> bool { | 38 | fn include_file(&self, file_path: &RelativePath) -> bool { |
39 | self.root.include_file(file_path) | 39 | self.root.include_file(file_path) |
40 | } | 40 | } |
41 | } | 41 | } |
42 | 42 | ||
43 | impl std::convert::From<ProjectRoot> for IncludeRustFiles { | 43 | impl From<ProjectRoot> for IncludeRustFiles { |
44 | fn from(v: ProjectRoot) -> IncludeRustFiles { | 44 | fn from(v: ProjectRoot) -> IncludeRustFiles { |
45 | IncludeRustFiles { root: v } | 45 | IncludeRustFiles { root: v } |
46 | } | 46 | } |
47 | } | 47 | } |
48 | 48 | ||
49 | impl std::convert::From<IncludeRustFiles> for RootEntry { | 49 | impl From<IncludeRustFiles> for RootEntry { |
50 | fn from(v: IncludeRustFiles) -> RootEntry { | 50 | fn from(v: IncludeRustFiles) -> RootEntry { |
51 | let path = v.root.path().clone(); | 51 | let path = v.root.path().clone(); |
52 | RootEntry::new(path, Box::new(v)) | 52 | RootEntry::new(path, Box::new(v)) |
53 | } | 53 | } |
54 | } | 54 | } |
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs new file mode 100644 index 000000000..33d472838 --- /dev/null +++ b/crates/ra_cli/src/analysis_bench.rs | |||
@@ -0,0 +1,92 @@ | |||
1 | use std::{ | ||
2 | path::{PathBuf, Path}, | ||
3 | time::Instant, | ||
4 | }; | ||
5 | |||
6 | use ra_db::{SourceDatabase, salsa::Database}; | ||
7 | use ra_ide_api::{AnalysisHost, Analysis, LineCol, FilePosition}; | ||
8 | |||
9 | use crate::Result; | ||
10 | |||
11 | pub(crate) enum Op { | ||
12 | Highlight { path: PathBuf }, | ||
13 | Complete { path: PathBuf, line: u32, column: u32 }, | ||
14 | } | ||
15 | |||
16 | pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> { | ||
17 | let start = Instant::now(); | ||
18 | eprint!("loading: "); | ||
19 | let (host, roots) = ra_batch::load_cargo(path)?; | ||
20 | let db = host.raw_database(); | ||
21 | eprintln!("{:?}\n", start.elapsed()); | ||
22 | |||
23 | let file_id = { | ||
24 | let path = match &op { | ||
25 | Op::Highlight { path } => path, | ||
26 | Op::Complete { path, .. } => path, | ||
27 | }; | ||
28 | let path = std::env::current_dir()?.join(path).canonicalize()?; | ||
29 | roots | ||
30 | .iter() | ||
31 | .find_map(|(source_root_id, project_root)| { | ||
32 | if project_root.is_member() { | ||
33 | for (rel_path, file_id) in &db.source_root(*source_root_id).files { | ||
34 | let abs_path = rel_path.to_path(project_root.path()); | ||
35 | if abs_path == path { | ||
36 | return Some(*file_id); | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | None | ||
41 | }) | ||
42 | .ok_or_else(|| format!("Can't find {:?}", path))? | ||
43 | }; | ||
44 | |||
45 | match op { | ||
46 | Op::Highlight { .. } => { | ||
47 | let res = do_work(&host, |analysis| { | ||
48 | analysis.diagnostics(file_id).unwrap(); | ||
49 | analysis.highlight_as_html(file_id, false).unwrap() | ||
50 | }); | ||
51 | if verbose { | ||
52 | println!("\n{}", res); | ||
53 | } | ||
54 | } | ||
55 | Op::Complete { line, column, .. } => { | ||
56 | let offset = host | ||
57 | .analysis() | ||
58 | .file_line_index(file_id) | ||
59 | .offset(LineCol { line, col_utf16: column }); | ||
60 | let file_postion = FilePosition { file_id, offset }; | ||
61 | |||
62 | let res = do_work(&host, |analysis| analysis.completions(file_postion)); | ||
63 | if verbose { | ||
64 | println!("\n{:#?}", res); | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | Ok(()) | ||
69 | } | ||
70 | |||
71 | fn do_work<F: Fn(&Analysis) -> T, T>(host: &AnalysisHost, work: F) -> T { | ||
72 | { | ||
73 | let start = Instant::now(); | ||
74 | eprint!("from scratch: "); | ||
75 | work(&host.analysis()); | ||
76 | eprintln!("{:?}", start.elapsed()); | ||
77 | } | ||
78 | { | ||
79 | let start = Instant::now(); | ||
80 | eprint!("no change: "); | ||
81 | work(&host.analysis()); | ||
82 | eprintln!("{:?}", start.elapsed()); | ||
83 | } | ||
84 | { | ||
85 | let start = Instant::now(); | ||
86 | eprint!("trivial change: "); | ||
87 | host.raw_database().salsa_runtime().next_revision(); | ||
88 | let res = work(&host.analysis()); | ||
89 | eprintln!("{:?}", start.elapsed()); | ||
90 | res | ||
91 | } | ||
92 | } | ||
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs index d76c37d84..ed98fc7f6 100644 --- a/crates/ra_cli/src/analysis_stats.rs +++ b/crates/ra_cli/src/analysis_stats.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use std::{collections::HashSet, time::Instant, fmt::Write}; | 1 | use std::{collections::HashSet, time::Instant, fmt::Write, path::Path}; |
2 | 2 | ||
3 | use ra_db::SourceDatabase; | 3 | use ra_db::SourceDatabase; |
4 | use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; | 4 | use ra_hir::{Crate, ModuleDef, Ty, ImplItem, HasSource}; |
@@ -6,20 +6,23 @@ use ra_syntax::AstNode; | |||
6 | 6 | ||
7 | use crate::Result; | 7 | use crate::Result; |
8 | 8 | ||
9 | pub fn run(verbose: bool, path: &str, only: Option<&str>) -> Result<()> { | 9 | pub fn run(verbose: bool, path: &Path, only: Option<&str>) -> Result<()> { |
10 | let db_load_time = Instant::now(); | 10 | let db_load_time = Instant::now(); |
11 | let (host, roots) = ra_batch::load_cargo(path.as_ref())?; | 11 | let (host, roots) = ra_batch::load_cargo(path)?; |
12 | let db = host.raw_database(); | 12 | let db = host.raw_database(); |
13 | println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); | 13 | println!("Database loaded, {} roots, {:?}", roots.len(), db_load_time.elapsed()); |
14 | let analysis_time = Instant::now(); | 14 | let analysis_time = Instant::now(); |
15 | let mut num_crates = 0; | 15 | let mut num_crates = 0; |
16 | let mut visited_modules = HashSet::new(); | 16 | let mut visited_modules = HashSet::new(); |
17 | let mut visit_queue = Vec::new(); | 17 | let mut visit_queue = Vec::new(); |
18 | for root in roots { | 18 | for (source_root_id, project_root) in roots { |
19 | for krate in Crate::source_root_crates(db, root) { | 19 | if project_root.is_member() { |
20 | num_crates += 1; | 20 | for krate in Crate::source_root_crates(db, source_root_id) { |
21 | let module = krate.root_module(db).expect("crate in source root without root module"); | 21 | num_crates += 1; |
22 | visit_queue.push(module); | 22 | let module = |
23 | krate.root_module(db).expect("crate in source root without root module"); | ||
24 | visit_queue.push(module); | ||
25 | } | ||
23 | } | 26 | } |
24 | } | 27 | } |
25 | println!("Crates in this dir: {}", num_crates); | 28 | println!("Crates in this dir: {}", num_crates); |
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index 1db98aec1..5adf8b096 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | mod analysis_stats; | 1 | mod analysis_stats; |
2 | mod analysis_bench; | ||
2 | 3 | ||
3 | use std::{io::Read, error::Error}; | 4 | use std::{io::Read, error::Error}; |
4 | 5 | ||
@@ -26,6 +27,27 @@ fn main() -> Result<()> { | |||
26 | .arg(Arg::with_name("only").short("o").takes_value(true)) | 27 | .arg(Arg::with_name("only").short("o").takes_value(true)) |
27 | .arg(Arg::with_name("path")), | 28 | .arg(Arg::with_name("path")), |
28 | ) | 29 | ) |
30 | .subcommand( | ||
31 | SubCommand::with_name("analysis-bench") | ||
32 | .arg(Arg::with_name("verbose").short("v").long("verbose")) | ||
33 | .arg( | ||
34 | Arg::with_name("highlight") | ||
35 | .long("highlight") | ||
36 | .takes_value(true) | ||
37 | .conflicts_with("complete") | ||
38 | .value_name("PATH") | ||
39 | .help("highlight this file"), | ||
40 | ) | ||
41 | .arg( | ||
42 | Arg::with_name("complete") | ||
43 | .long("complete") | ||
44 | .takes_value(true) | ||
45 | .conflicts_with("highlight") | ||
46 | .value_name("PATH:LINE:COLUMN") | ||
47 | .help("compute completions at this location"), | ||
48 | ) | ||
49 | .arg(Arg::with_name("path").value_name("PATH").help("project to analyze")), | ||
50 | ) | ||
29 | .get_matches(); | 51 | .get_matches(); |
30 | match matches.subcommand() { | 52 | match matches.subcommand() { |
31 | ("parse", Some(matches)) => { | 53 | ("parse", Some(matches)) => { |
@@ -51,7 +73,25 @@ fn main() -> Result<()> { | |||
51 | let verbose = matches.is_present("verbose"); | 73 | let verbose = matches.is_present("verbose"); |
52 | let path = matches.value_of("path").unwrap_or(""); | 74 | let path = matches.value_of("path").unwrap_or(""); |
53 | let only = matches.value_of("only"); | 75 | let only = matches.value_of("only"); |
54 | analysis_stats::run(verbose, path, only)?; | 76 | analysis_stats::run(verbose, path.as_ref(), only)?; |
77 | } | ||
78 | ("analysis-bench", Some(matches)) => { | ||
79 | let verbose = matches.is_present("verbose"); | ||
80 | let path = matches.value_of("path").unwrap_or(""); | ||
81 | let op = if let Some(path) = matches.value_of("highlight") { | ||
82 | analysis_bench::Op::Highlight { path: path.into() } | ||
83 | } else if let Some(path_line_col) = matches.value_of("complete") { | ||
84 | let (path_line, column) = rsplit_at_char(path_line_col, ':')?; | ||
85 | let (path, line) = rsplit_at_char(path_line, ':')?; | ||
86 | analysis_bench::Op::Complete { | ||
87 | path: path.into(), | ||
88 | line: line.parse()?, | ||
89 | column: column.parse()?, | ||
90 | } | ||
91 | } else { | ||
92 | panic!("either --highlight or --complete must be set") | ||
93 | }; | ||
94 | analysis_bench::run(verbose, path.as_ref(), op)?; | ||
55 | } | 95 | } |
56 | _ => unreachable!(), | 96 | _ => unreachable!(), |
57 | } | 97 | } |
@@ -68,3 +108,8 @@ fn read_stdin() -> Result<String> { | |||
68 | std::io::stdin().read_to_string(&mut buff)?; | 108 | std::io::stdin().read_to_string(&mut buff)?; |
69 | Ok(buff) | 109 | Ok(buff) |
70 | } | 110 | } |
111 | |||
112 | fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> { | ||
113 | let idx = s.rfind(":").ok_or_else(|| format!("no `{}` in {}", c, s))?; | ||
114 | Ok((&s[..idx], &s[idx + 1..])) | ||
115 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index a68c5e2a5..e61d5627e 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -276,7 +276,7 @@ impl AnalysisHost { | |||
276 | pub fn collect_garbage(&mut self) { | 276 | pub fn collect_garbage(&mut self) { |
277 | self.db.collect_garbage(); | 277 | self.db.collect_garbage(); |
278 | } | 278 | } |
279 | pub fn raw_database(&self) -> &impl hir::db::HirDatabase { | 279 | pub fn raw_database(&self) -> &(impl hir::db::HirDatabase + salsa::Database) { |
280 | &self.db | 280 | &self.db |
281 | } | 281 | } |
282 | } | 282 | } |
diff --git a/crates/ra_ide_api/src/line_index.rs b/crates/ra_ide_api/src/line_index.rs index 087dfafed..a53cf9ee0 100644 --- a/crates/ra_ide_api/src/line_index.rs +++ b/crates/ra_ide_api/src/line_index.rs | |||
@@ -10,7 +10,9 @@ pub struct LineIndex { | |||
10 | 10 | ||
11 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 11 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
12 | pub struct LineCol { | 12 | pub struct LineCol { |
13 | /// Zero-based | ||
13 | pub line: u32, | 14 | pub line: u32, |
15 | /// Zero-based | ||
14 | pub col_utf16: u32, | 16 | pub col_utf16: u32, |
15 | } | 17 | } |
16 | 18 | ||
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a3af153f1..42156bea6 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -37,6 +37,7 @@ pub enum ProjectWorkspace { | |||
37 | /// `ProjectRoot` describes a workspace root folder. | 37 | /// `ProjectRoot` describes a workspace root folder. |
38 | /// Which may be an external dependency, or a member of | 38 | /// Which may be an external dependency, or a member of |
39 | /// the current workspace. | 39 | /// the current workspace. |
40 | #[derive(Clone)] | ||
40 | pub struct ProjectRoot { | 41 | pub struct ProjectRoot { |
41 | /// Path to the root folder | 42 | /// Path to the root folder |
42 | path: PathBuf, | 43 | path: PathBuf, |