Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit 7369b33

Browse files
committed
Introduce project model
1 parent a528e2f commit 7369b33

File tree

5 files changed

+225
-8
lines changed

5 files changed

+225
-8
lines changed

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ languageserver-types = "0.45"
2424
lazy_static = "1"
2525
log = "0.4"
2626
num_cpus = "1"
27-
racer = "2.1.2"
27+
racer = "2.1.3"
2828
rayon = "1"
2929
rls-analysis = "0.14"
3030
rls-blacklist = "0.1.2"

src/actions/requests.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,9 @@ impl RequestAction for Definition {
241241
let racer_receiver = {
242242
if ctx.config.lock().unwrap().goto_def_racer_fallback {
243243
Some(work_pool::receive_from_thread(move || {
244-
let cache = racer_cache(vfs);
245-
let session = racer::Session::new(&cache);
244+
let cache = racer_cache(vfs.clone());
245+
let project_model = crate::project_model::cargo_project_model(vfs);
246+
let session = racer::Session::with_project_model(&cache, project_model);
246247
let location = pos_to_racer_location(params.position);
247248

248249
racer::find_definition(file_path, location, &session)
@@ -325,8 +326,9 @@ impl RequestAction for Completion {
325326
let vfs = ctx.vfs;
326327
let file_path = parse_file_path!(&params.text_document.uri, "complete")?;
327328

328-
let cache = racer_cache(vfs);
329-
let session = racer::Session::new(&cache);
329+
let cache = racer_cache(vfs.clone());
330+
let project_model = crate::project_model::cargo_project_model(vfs);
331+
let session = racer::Session::with_project_model(&cache, project_model);
330332

331333
let location = pos_to_racer_location(params.position);
332334
let results = racer::complete_from_file(file_path, location, &session);

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub mod config;
4848
pub mod lsp_data;
4949
pub mod server;
5050
pub mod concurrency;
51+
pub mod project_model;
5152

5253
#[cfg(test)]
5354
mod test;

src/project_model.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/// This module should represent the RLS view of the Cargo project model.
2+
/// At the time of writing though it is very limited, does not contain
3+
/// the project model per se and implements the minimal amount of functionality
4+
/// required to make the racer work
5+
6+
7+
use std::{
8+
collections::{
9+
HashSet,
10+
hash_map::{self, HashMap},
11+
},
12+
sync::Arc,
13+
path::{Path, PathBuf},
14+
cell::RefCell,
15+
rc::Rc,
16+
};
17+
use log::{log, info, warn, debug};
18+
use rls_vfs::{Vfs, FileContents};
19+
use cargo::{
20+
Config,
21+
ops,
22+
util::{errors::CargoResult, important_paths::find_root_manifest_for_wd, toml},
23+
core::{
24+
Workspace, PackageSet, PackageId, registry::PackageRegistry,
25+
resolver::{EncodableResolve, Method, Resolve},
26+
}
27+
};
28+
use racer;
29+
30+
pub fn cargo_project_model(vfs: Arc<Vfs>) -> Box<dyn racer::ProjectModelProvider> {
31+
Box::new(CargoProjectModel {
32+
vfs,
33+
cached_lockfile: Default::default(),
34+
cached_deps: Default::default(),
35+
})
36+
}
37+
38+
struct CargoProjectModel {
39+
vfs: Arc<Vfs>,
40+
/// Cached lockfiles (path to Cargo.lock -> Resolve)
41+
cached_lockfile: RefCell<HashMap<PathBuf, Rc<Resolve>>>,
42+
/// Cached dependencie (path to Cargo.toml -> Depedencies)
43+
cached_deps: RefCell<HashMap<PathBuf, Rc<Dependencies>>>,
44+
}
45+
46+
macro_rules! cargo_try {
47+
($r:expr) => {
48+
match $r {
49+
Ok(val) => val,
50+
Err(err) => {
51+
warn!("Error in cargo: {}", err);
52+
return None;
53+
}
54+
}
55+
};
56+
}
57+
58+
impl racer::ProjectModelProvider for CargoProjectModel {
59+
fn discover_project_manifest(&self, path: &Path) -> Option<PathBuf> {
60+
let path = cargo_try!(find_root_manifest_for_wd(path));
61+
Some(path)
62+
}
63+
fn resolve_dependency(&self, manifest: &Path, libname: &str) -> Option<PathBuf> {
64+
let deps = match self.get_deps(manifest) {
65+
Some(deps) => {
66+
debug!("[resolve_dependency] cache exists for manifest");
67+
deps
68+
}
69+
None => {
70+
// cache doesn't exist
71+
// calculating depedencies can be bottleneck we use info! here(kngwyu)
72+
info!("[resolve_dependency] cache doesn't exist");
73+
let deps_map = self.resolve_dependencies(&manifest)?;
74+
self.cache_deps(manifest, deps_map)
75+
}
76+
};
77+
deps.get_src_path(libname)
78+
}
79+
}
80+
81+
impl CargoProjectModel {
82+
pub(crate) fn load_lockfile(
83+
&self,
84+
path: &Path,
85+
resolver: &dyn Fn(&str) -> Option<Resolve>,
86+
) -> Option<Rc<Resolve>>
87+
{
88+
match self.cached_lockfile.borrow_mut().entry(path.to_owned()) {
89+
hash_map::Entry::Occupied(occupied) => Some(Rc::clone(occupied.get())),
90+
hash_map::Entry::Vacant(vacant) => {
91+
let contents = match self.vfs.load_file(path) {
92+
Ok(FileContents::Text(f)) => f,
93+
Ok(_) => return None,
94+
Err(e) => {
95+
debug!(
96+
"[CargoProjectModel::load_lock_file] Failed to load {}: {}",
97+
path.display(),
98+
e
99+
);
100+
return None;
101+
}
102+
};
103+
resolver(&contents)
104+
.map(|res| Rc::clone(vacant.insert(Rc::new(res))))
105+
}
106+
}
107+
}
108+
109+
fn resolve_dependencies(&self, manifest: &Path) -> Option<HashMap<String, PathBuf>> {
110+
let mut config = cargo_try!(Config::default());
111+
// frozen=true, locked=true
112+
config.configure(0, Some(true), &None, true, true, &None, &[]).ok()?;
113+
let ws = cargo_try!(Workspace::new(&manifest, &config));
114+
// get resolve from lock file
115+
let lock_path = ws.root().to_owned().join("Cargo.lock");
116+
let lock_file = self.load_lockfile(&lock_path, &|lockfile| {
117+
let resolve = cargo_try!(toml::parse(&lockfile, &lock_path, ws.config()));
118+
let v: EncodableResolve = cargo_try!(resolve.try_into());
119+
Some(cargo_try!(v.into_resolve(&ws)))
120+
});
121+
// then resolve precisely and add overrides
122+
let mut registry = cargo_try!(PackageRegistry::new(ws.config()));
123+
let resolve = cargo_try!(match lock_file {
124+
Some(prev) => resolve_with_prev(&mut registry, &ws, Some(&*prev)),
125+
None => resolve_with_prev(&mut registry, &ws, None),
126+
});
127+
let packages = get_resolved_packages(&resolve, registry);
128+
// we have caches for each crates, so only need depth1 depedencies(= dependencies in Cargo.toml)
129+
let depth1_dependencies = match ws.current_opt() {
130+
Some(cur) => cur.dependencies().iter().map(|p| p.name()).collect(),
131+
None => HashSet::new(),
132+
};
133+
let current_pkg = ws.current().map(|pkg| pkg.name());
134+
let is_current_pkg = |name| {
135+
if let Ok(n) = current_pkg {
136+
n == name
137+
} else {
138+
false
139+
}
140+
};
141+
let deps_map = packages
142+
.package_ids()
143+
.filter_map(|package_id| {
144+
let pkg = packages.get(package_id).ok()?;
145+
let pkg_name = pkg.name();
146+
// for examples/ or tests/ dir, we have to handle current package specially
147+
if !is_current_pkg(pkg_name) && !depth1_dependencies.contains(&pkg.name()) {
148+
return None;
149+
}
150+
let targets = pkg.manifest().targets();
151+
// we only need library target
152+
let lib_target = targets.into_iter().find(|target| target.is_lib())?;
153+
// crate_name returns target.name.replace("-", "_")
154+
let crate_name = lib_target.crate_name();
155+
let src_path = lib_target.src_path().to_owned();
156+
Some((crate_name, src_path))
157+
})
158+
.collect();
159+
Some(deps_map)
160+
}
161+
162+
fn get_deps(&self, manifest: &Path) -> Option<Rc<Dependencies>> {
163+
let deps = self.cached_deps.borrow();
164+
deps.get(manifest).map(|rc| Rc::clone(&rc))
165+
}
166+
167+
fn cache_deps(&self, manifest: &Path, cache: HashMap<String, PathBuf>) -> Rc<Dependencies> {
168+
let manifest = manifest.to_owned();
169+
let deps = Rc::new(Dependencies { inner: cache });
170+
self.cached_deps.borrow_mut().insert(manifest, deps.clone());
171+
deps
172+
}
173+
}
174+
175+
/// dependencies info of a package
176+
#[derive(Clone, Debug, Default)]
177+
struct Dependencies {
178+
/// dependencies of a package(library name -> src_path)
179+
inner: HashMap<String, PathBuf>,
180+
}
181+
182+
impl Dependencies {
183+
/// Get src path from a library name.
184+
/// e.g. from query string `bit_set` it returns
185+
/// `~/.cargo/registry/src/github.1485827954.workers.dev-1ecc6299db9ec823/bit-set-0.4.0`
186+
fn get_src_path(&self, query: &str) -> Option<PathBuf> {
187+
let p = self.inner.get(query)?;
188+
Some(p.to_owned())
189+
}
190+
}
191+
192+
// wrapper of resolve_with_previous
193+
fn resolve_with_prev<'cfg>(
194+
registry: &mut PackageRegistry<'cfg>,
195+
ws: &Workspace<'cfg>,
196+
prev: Option<&Resolve>,
197+
) -> CargoResult<Resolve> {
198+
ops::resolve_with_previous(
199+
registry,
200+
ws,
201+
Method::Everything,
202+
prev,
203+
None,
204+
&[],
205+
true,
206+
false,
207+
)
208+
}
209+
210+
// until cargo 0.30 is released
211+
fn get_resolved_packages<'a>(resolve: &Resolve, registry: PackageRegistry<'a>) -> PackageSet<'a> {
212+
let ids: Vec<PackageId> = resolve.iter().cloned().collect();
213+
registry.get(&ids)
214+
}

0 commit comments

Comments
 (0)