Skip to content

Commit

Permalink
LS: Sort projects by specificity
Browse files Browse the repository at this point in the history
commit-id:81b8f857
  • Loading branch information
mkaput committed Jul 3, 2024
1 parent 26db807 commit 262d066
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ impl Project for CairoProject {
vec![&self.project_path]
}

fn main_manifest_file(&self) -> &Path {
&self.project_path
}

fn reload(&mut self) {
let project_config = ProjectConfig::from_file(&self.project_path)
.with_context(|| {
Expand Down
62 changes: 58 additions & 4 deletions crates/cairo-lang-language-server/src/project/manager.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::cmp::Ordering;
use std::fmt;
use std::path::Path;

Expand Down Expand Up @@ -34,13 +35,11 @@ use crate::toolchain::scarb::ScarbToolchain;
/// As an output of processing these events, the manager updates the analysis database inputs with
/// the new state of projects, via the [`ProjectManager::apply_db_changes`] method.
pub struct ProjectManager {
// FIXME(mkaput): Projects must be sorted from the most specific to the least specific,
// so that linear scanning for project related to a file would be correct.
// FIXME(mkaput): Scarb workspace have an ability to self-modify their root path, and thus
// it is possible, that duplicate projects could happen during the lifetime of the server.
// These need to be deduplicated.
/// List of loaded projects.
projects: Vec<Box<dyn Project>>,
projects: ProjectsCollection,

/// The unmanaged `core` crate manager.
unmanaged_core_crate: UnmanagedCoreCrate,
Expand Down Expand Up @@ -70,7 +69,7 @@ impl ProjectManager {
Some(manifest_path) => {
debug!("loading project: {}", manifest_path.as_path().display());
let project = self.initialize_project(manifest_path);
self.projects.push(project);
self.projects.insert(project);
}
None if is_cairo_file(path) => {
// TODO(mkaput): Implement detached files.
Expand Down Expand Up @@ -142,3 +141,58 @@ impl fmt::Debug for ProjectManager {
fn is_cairo_file(path: &Path) -> bool {
path.extension().map_or(false, |ext| ext == CAIRO_FILE_EXTENSION)
}

/// A list of loaded projects, sorted by project specificity.
///
/// Project specificity is determined by its main manifest file path.
/// Projects that are nested in another project directory are considered more specific.
#[derive(Default)]
struct ProjectsCollection(Vec<Box<dyn Project>>);

impl ProjectsCollection {
/// Inserts an element to the collection, ensuring the sorting invariant is preserved.
fn insert(&mut self, project: Box<dyn Project>) {
self.0.push(project);
self.0.sort_unstable_by(|a, b| {
let a = a.main_manifest_file();
let a_dir = a.parent().unwrap_or(a);
let b = b.main_manifest_file();
let b_dir = b.parent().unwrap_or(b);
if a_dir.starts_with(b_dir) {
Ordering::Less
} else if b_dir.starts_with(a_dir) {
Ordering::Greater
} else {
a.cmp(b)
}
})
}

/// Returns an iterator over the list.
fn iter(&self) -> std::slice::Iter<'_, Box<dyn Project>> {
self.0.iter()
}

/// Returns an iterator that allows modifying each value.
fn iter_mut(&mut self) -> std::slice::IterMut<'_, Box<dyn Project>> {
self.0.iter_mut()
}
}

impl<'a> IntoIterator for &'a ProjectsCollection {
type Item = &'a Box<dyn Project>;
type IntoIter = std::slice::Iter<'a, Box<dyn Project>>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a> IntoIterator for &'a mut ProjectsCollection {
type Item = &'a mut Box<dyn Project>;
type IntoIter = std::slice::IterMut<'a, Box<dyn Project>>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
3 changes: 3 additions & 0 deletions crates/cairo-lang-language-server/src/project/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ trait Project: Debug + Send {
/// This list may also include lockfiles.
fn manifest_files(&self) -> Vec<&Path>;

/// Gets the main manifest file of this project.
fn main_manifest_file(&self) -> &Path;

/// Forces the project to reload its state.
fn reload(&mut self);

Expand Down
7 changes: 7 additions & 0 deletions crates/cairo-lang-language-server/src/project/scarb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ impl Project for ScarbWorkspace {
.collect()
}

fn main_manifest_file(&self) -> &Path {
match &self.metadata {
Some(metadata) => metadata.workspace.manifest_path.as_std_path(),
None => &self.manifest_path,
}
}

fn reload(&mut self) {
self.do_reload();
}
Expand Down
2 changes: 1 addition & 1 deletion crates/cairo-lang-language-server/tests/e2e/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::support::normalize::normalize;
use crate::support::sandbox;

cairo_lang_test_utils::test_file_test!(
hover,
project,
"tests/test_data/project",
{
cairo_projects: "cairo_projects.txt",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ project2/subproject/src/lib.cairo
project_path: "[ROOT]/project1/cairo_project.toml",
},
CairoProject {
project_path: "[ROOT]/project2/cairo_project.toml",
project_path: "[ROOT]/project2/subproject/cairo_project.toml",
},
CairoProject {
project_path: "[ROOT]/project2/subproject/cairo_project.toml",
project_path: "[ROOT]/project2/cairo_project.toml",
},
]
]

0 comments on commit 262d066

Please sign in to comment.