Skip to content

Commit 3600378

Browse files
committed
Parse DESCRIPTION files
1 parent 6fd8e7f commit 3600378

File tree

6 files changed

+516
-194
lines changed

6 files changed

+516
-194
lines changed

crates/ark/src/lsp/diagnostics.rs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use std::collections::BTreeMap;
99
use std::collections::HashMap;
1010
use std::collections::HashSet;
11+
use std::sync::Arc;
1112

1213
use anyhow::bail;
1314
use anyhow::Result;
@@ -28,6 +29,7 @@ use crate::lsp::documents::Document;
2829
use crate::lsp::encoding::convert_tree_sitter_range_to_lsp_range;
2930
use crate::lsp::indexer;
3031
use crate::lsp::inputs::library::Library;
32+
use crate::lsp::inputs::package::Package;
3133
use crate::lsp::state::WorldState;
3234
use crate::lsp::traits::node::NodeExt;
3335
use crate::lsp::traits::rope::RopeExt;
@@ -826,24 +828,91 @@ fn handle_package_attach_call(node: Node, context: &mut DiagnosticContext) -> an
826828
};
827829

828830
let package_name = value.get_identifier_or_string_text(context.contents)?;
831+
let attach_pos = node.end_position();
829832

830-
// Insert exports for the attached package
831-
if let Some(package) = context.library.get(&package_name) {
832-
for symbol in &package.namespace.exports {
833-
let pos = node.end_position();
834-
context
835-
.library_symbols
836-
.entry(pos)
837-
.or_insert_with(HashSet::new)
838-
.insert(symbol.clone());
833+
let package = match insert_package_exports(&package_name, attach_pos, context) {
834+
Ok(package) => package,
835+
Err(err) => {
836+
lsp::log_warn!("{err:?}");
837+
return Ok(());
838+
},
839+
};
840+
841+
// Also attach packages from `Depends` field
842+
let mut attach_dependencies = package.description.depends.clone();
843+
844+
// Special handling for the tidyverse and tidymodels packages. Hard-coded
845+
// for now but in the future, this should probably be expressed as a
846+
// `DESCRIPTION` field like `Config/Needs/attach`.
847+
let attach_field = match package.description.name.as_str() {
848+
// https://github.com/tidyverse/tidyverse/blob/0231aafb/R/attach.R#L1
849+
"tidyverse" => {
850+
vec![
851+
"dplyr",
852+
"readr",
853+
"forcats",
854+
"stringr",
855+
"ggplot2",
856+
"tibble",
857+
"lubridate",
858+
"tidyr",
859+
"purrr",
860+
]
861+
},
862+
// https://github.com/tidymodels/tidymodels/blob/aa3f82cf/R/attach.R#L1
863+
"tidymodels" => {
864+
vec![
865+
"broom",
866+
"dials",
867+
"dplyr",
868+
"ggplot2",
869+
"infer",
870+
"modeldata",
871+
"parsnip",
872+
"purrr",
873+
"recipes",
874+
"rsample",
875+
"tibble",
876+
"tidyr",
877+
"tune",
878+
"workflows",
879+
"workflowsets",
880+
"yardstick",
881+
]
882+
},
883+
_ => vec![],
884+
};
885+
attach_dependencies.extend(attach_field.into_iter().map(String::from));
886+
887+
for package_name in attach_dependencies {
888+
if let Err(err) = insert_package_exports(&package_name, attach_pos, context) {
889+
lsp::log_warn!("{err:?}");
839890
}
840-
} else {
841-
lsp::log_warn!("Can't get exports from package {package_name} because it is not installed.")
842891
}
843892

844893
Ok(())
845894
}
846895

896+
fn insert_package_exports(
897+
package_name: &str,
898+
attach_pos: Point,
899+
context: &mut DiagnosticContext,
900+
) -> anyhow::Result<Arc<Package>> {
901+
let Some(package) = context.library.get(package_name) else {
902+
return Err(anyhow::anyhow!(
903+
"Can't get exports from package {package_name} because it is not installed."
904+
));
905+
};
906+
907+
context
908+
.library_symbols
909+
.entry(attach_pos)
910+
.or_default()
911+
.extend(package.namespace.exports.iter().cloned());
912+
913+
Ok(package)
914+
}
915+
847916
fn recurse_subset_or_subset2(
848917
node: Node,
849918
context: &mut DiagnosticContext,
@@ -1030,9 +1099,10 @@ mod tests {
10301099
use crate::lsp::diagnostics::generate_diagnostics;
10311100
use crate::lsp::documents::Document;
10321101
use crate::lsp::inputs::library::Library;
1033-
use crate::lsp::inputs::package::Description;
1034-
use crate::lsp::inputs::package::Namespace;
10351102
use crate::lsp::inputs::package::Package;
1103+
use crate::lsp::inputs::package_description::Dcf;
1104+
use crate::lsp::inputs::package_description::Description;
1105+
use crate::lsp::inputs::package_namespace::Namespace;
10361106
use crate::lsp::state::WorldState;
10371107
use crate::r_task;
10381108

@@ -1532,6 +1602,7 @@ foo
15321602
name: "mockpkg".to_string(),
15331603
version: "1.0.0".to_string(),
15341604
depends: vec![],
1605+
fields: Dcf::new(),
15351606
};
15361607
let package = Package {
15371608
path: PathBuf::from("/mock/path"),
@@ -1613,6 +1684,7 @@ foo
16131684
name: "pkg1".to_string(),
16141685
version: "1.0.0".to_string(),
16151686
depends: vec![],
1687+
fields: Dcf::new(),
16161688
};
16171689
let package1 = Package {
16181690
path: PathBuf::from("/mock/path1"),
@@ -1630,6 +1702,7 @@ foo
16301702
name: "pkg2".to_string(),
16311703
version: "1.0.0".to_string(),
16321704
depends: vec![],
1705+
fields: Dcf::new(),
16331706
};
16341707
let package2 = Package {
16351708
path: PathBuf::from("/mock/path2"),
@@ -1689,6 +1762,7 @@ foo
16891762
name: "pkg".to_string(),
16901763
version: "1.0.0".to_string(),
16911764
depends: vec![],
1765+
fields: Dcf::new(),
16921766
};
16931767
let package = Package {
16941768
path: PathBuf::from("/mock/path"),

crates/ark/src/lsp/inputs/library.rs

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,12 @@
55
//
66

77
use std::collections::HashMap;
8-
use std::fs;
98
use std::path::PathBuf;
109
use std::sync::Arc;
1110
use std::sync::RwLock;
1211

1312
use super::package::Package;
1413
use crate::lsp;
15-
use crate::lsp::inputs::package::Description;
16-
use crate::lsp::inputs::package::Namespace;
1714

1815
/// Lazily manages a list of known R packages by name
1916
#[derive(Default, Clone, Debug)]
@@ -71,33 +68,11 @@ impl Library {
7168

7269
fn load_package(&self, name: &str) -> anyhow::Result<Option<Package>> {
7370
for lib_path in self.library_paths.iter() {
74-
let package_path = lib_path.join(name);
75-
let description_path = package_path.join("DESCRIPTION");
76-
let namespace_path = package_path.join("NAMESPACE");
77-
78-
// Only consider libraries that have a folder named after the
79-
// requested package and that contains a description file
80-
if !description_path.is_file() {
81-
continue;
71+
match Package::load(&lib_path, name) {
72+
Ok(Some(pkg)) => return Ok(Some(pkg)),
73+
Ok(None) => (),
74+
Err(err) => lsp::log_warn!("Can't load package: {err:?}"),
8275
}
83-
84-
// TODO
85-
// let _desc_contents = fs::read_to_string(&description_path)?;
86-
87-
let description = Description {
88-
name: name.to_string(),
89-
version: "unknown".to_string(),
90-
depends: vec![],
91-
};
92-
93-
let namespace_contents = fs::read_to_string(&namespace_path)?;
94-
let namespace = Namespace::parse(&namespace_contents)?;
95-
96-
return Ok(Some(Package {
97-
path: package_path,
98-
description,
99-
namespace,
100-
}));
10176
}
10277

10378
Ok(None)

crates/ark/src/lsp/inputs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
pub mod library;
99
pub mod package;
10+
pub mod package_description;
11+
pub mod package_namespace;
1012
pub mod source_root;

0 commit comments

Comments
 (0)