Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read packages source when library name is different from package name #469

Merged
merged 10 commits into from
Jan 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "racer"
version = "1.1.0"
version = "1.2.0"
license = "MIT"
description = "Code completion for Rust"
authors = ["Phil Dawes <[email protected]>"]
Expand All @@ -25,5 +25,8 @@ env_logger = "~0.3.2"
typed-arena = "~1.0.1"
clap = "~1.5.3"

[dev-dependencies]
test_fixtures = { path = "src/test_fixtures" }

[features]
nightly = []
2 changes: 1 addition & 1 deletion src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ fn build_cli<'a, 'b, 'c, 'd, 'e, 'f>() -> App<'a, 'b, 'c, 'd, 'e, 'f> {
// than the less verbose "Usage String" method...faster, meaning runtime speed since that's
// extremely important here
App::new("racer")
.version("v1.1.0")
.version("v1.2.0")
.author("Phil Dawes")
.about("A Rust code completion utility")
.settings(&[AppSettings::GlobalVersion,
Expand Down
195 changes: 142 additions & 53 deletions src/racer/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@ use std::fs::File;
use std::io::Read;
use std::env;
use std::path::{Path,PathBuf};
use std::collections::BTreeMap;
use util::{path_exists, is_dir};
use std::fs::{read_dir};
use toml;

#[derive(Debug)]
struct PackageInfo {
name: String,
version: Option<String>,
source: Option<PathBuf>
}

// otry is 'option try'
macro_rules! otry {
($e:expr) => (match $e { Some(e) => e, None => return None })
Expand Down Expand Up @@ -61,45 +69,102 @@ fn empty_if_no_branch() {
}

fn find_src_via_lockfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
let mut file = otry2!(File::open(cargofile));
if let Some(packages) = get_cargo_packages(cargofile) {
for package in packages {
if let Some(package_source) = package.source.clone() {
if let Some(tomlfile) = find_cargo_tomlfile(package_source.as_path()) {
let package_name = get_package_name(tomlfile.as_path());

debug!("find_src_via_lockfile package_name: {}", package_name);

if package_name == kratename {
return package.source;
}
}
}
}
}
None
}

fn parse_toml_file(toml_file: &Path) -> Option<BTreeMap<String, toml::Value>> {
let mut file = otry2!(File::open(toml_file));
let mut string = String::new();
otry2!(file.read_to_string(&mut string));
let mut parser = toml::Parser::new(&string);
let lock_table = parser.parse().unwrap();

debug!("find_src_via_lockfile found lock table {:?}", lock_table);
parser.parse()
}

fn get_cargo_packages(cargofile: &Path) -> Option<Vec<PackageInfo>> {
let lock_table = parse_toml_file(cargofile).unwrap();

let t = match lock_table.get("package") {
debug!("get_cargo_packages found lock_table {:?}", lock_table);

let packages_array = match lock_table.get("package") {
Some(&toml::Value::Array(ref t1)) => t1,
_ => return None
};

for item in t {
if let toml::Value::Table(ref t) = *item {
if let Some(&toml::Value::String(ref name)) = t.get("name") {
if name.replace("-", "_") == kratename {
debug!("found matching crate {:?}", t);
let version = otry!(getstr(t, "version"));
let source = otry!(getstr(t, "source"));

if Some("registry") == source.split("+").nth(0) {
return get_versioned_cratefile(name, &version, cargofile);
} else if Some("git") == source.split("+").nth(0) {
let sha1 = otry!(source.split("#").last());
let mut result = Vec::new();

for package_element in packages_array {
if let &toml::Value::Table(ref package_table) = package_element {
if let Some(&toml::Value::String(ref package_name)) = package_table.get("name") {
let package_version = otry!(getstr(package_table, "version"));
let package_source = otry!(getstr(package_table, "source"));

let package_source = match package_source.split("+").nth(0) {
Some("registry") => {
get_versioned_cratefile(package_name, &package_version, cargofile)
},
Some("git") => {
let sha1 = otry!(package_source.split("#").last());
let mut d = otry!(get_cargo_rootdir(cargofile));
let branch = get_branch_from_source(&source);
let branch = get_branch_from_source(&package_source);
d.push("git");
d.push("checkouts");
d = otry!(find_git_src_dir(d, name, &sha1, branch));
d = otry!(find_git_src_dir(d, package_name, &sha1, branch));
d.push("src");
d.push("lib.rs");
return Some(d);
}
}

Some(d)
},
_ => return None
};

result.push(PackageInfo{
name: package_name.to_owned(),
version: Some(package_version),
source: package_source
});
}
}
}
None
Some(result)
}

fn get_package_name(cargofile: &Path) -> String {
let lock_table = parse_toml_file(cargofile).unwrap();

debug!("get_package_name found lock_table {:?}", lock_table);

let mut name = String::new();

if let Some(&toml::Value::Table(ref lib_table)) = lock_table.get("lib") {
if let Some(&toml::Value::String(ref package_name)) = lib_table.get("name") {
name.push_str(package_name);
return name;
}
}

if let Some(&toml::Value::Table(ref package_table)) = lock_table.get("package") {
if let Some(&toml::Value::String(ref package_name)) = package_table.get("name") {
name.push_str(package_name);
}
}

name
}

fn get_cargo_rootdir(cargofile: &Path) -> Option<PathBuf> {
Expand Down Expand Up @@ -231,13 +296,7 @@ fn get_versioned_cratefile(kratename: &str, version: &str, cargofile: &Path) ->
fn find_src_via_tomlfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
// only look for 'path' references here.
// We find the git and crates.io stuff via the lockfile

let mut file = otry2!(File::open(cargofile));
let mut string = String::new();
otry2!(file.read_to_string(&mut string));
let mut parser = toml::Parser::new(&string);
let table = otry!(parser.parse());

let table = parse_toml_file(cargofile).unwrap();

// is it this lib? (e.g. you're searching from tests to find the main library crate)
{
Expand Down Expand Up @@ -273,36 +332,66 @@ fn find_src_via_tomlfile(kratename: &str, cargofile: &Path) -> Option<PathBuf> {
}

// otherwise search the dependencies
let t = match table.get("dependencies") {
let local_packages = get_local_packages(&table, cargofile, "dependencies").unwrap();
let local_packages_dev = get_local_packages(&table, cargofile, "dev-dependencies").unwrap();

debug!("find_src_via_tomlfile found local packages: {:?}", local_packages);
debug!("find_src_via_tomlfile found local packages dev: {:?}", local_packages_dev);

for package in local_packages.iter().chain(local_packages_dev.iter()) {
if let Some(package_source) = package.source.clone() {
if let Some(tomlfile) = find_cargo_tomlfile(package_source.as_path()) {
let package_name = get_package_name(tomlfile.as_path());

debug!("find_src_via_tomlfile package_name: {}", package_name);

if package_name == kratename {
return Some(package_source);
}
}
}
}

None
}

fn get_local_packages(table: &BTreeMap<String, toml::Value>, cargofile: &Path, section_name: &str) -> Option<Vec<PackageInfo>> {
debug!("get_local_packages found table {:?}", table);

let t = match table.get(section_name) {
Some(&toml::Value::Table(ref t)) => t,
_ => return None
};

let mut name = kratename;
let value = if kratename.contains('_') {
t.iter().find(|&(k, _)| k.replace("-", "_") == name).map(|(k,v)| {
name = k;
v
})
} else {
t.get(kratename)
};
let mut result = Vec::new();

match value {
Some(&toml::Value::Table(ref t)) => {
// local directory
let relative_path = otry!(getstr(t, "path"));
Some(otry!(cargofile.parent())
.join(relative_path)
.join("src")
.join("lib.rs"))
},
Some(&toml::Value::String(ref version)) => {
// versioned crate
get_versioned_cratefile(name, version, cargofile)
}
_ => None
for (package_name, value) in t.iter() {
let mut package_version = None;

let package_source = match *value {
toml::Value::Table(ref t) => {
// local directory
let relative_path = otry!(getstr(t, "path"));
Some(otry!(cargofile.parent())
.join(relative_path)
.join("src")
.join("lib.rs"))
},
toml::Value::String(ref version) => {
// versioned crate
package_version = Some(version.to_owned());
get_versioned_cratefile(package_name, version, cargofile)
}
_ => continue
};

result.push(PackageInfo {
name: package_name.to_owned(),
version: package_version,
source: package_source
});
}
Some(result)
}

fn find_cratesio_src_dirs(d: PathBuf) -> Vec<PathBuf> {
Expand Down
8 changes: 8 additions & 0 deletions src/test_fixtures/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "test_fixtures"
version = "0.1.0"
authors = ["jaxx"]

[lib]
name = "fixtures"
path = "src/lib.rs"
4 changes: 4 additions & 0 deletions src/test_fixtures/src/foo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

pub fn test() {
println!("Hello from test function");
}
2 changes: 2 additions & 0 deletions src/test_fixtures/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

pub mod foo;
Loading