Skip to content

Commit

Permalink
LS: Extract Crate type for describing input to the analysis db
Browse files Browse the repository at this point in the history
commit-id:a865f380
  • Loading branch information
mkaput committed Jul 10, 2024
1 parent 8aa1b60 commit 3dc6fe9
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 45 deletions.
69 changes: 69 additions & 0 deletions crates/cairo-lang-language-server/src/project/crate_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use std::path::PathBuf;
use std::sync::Arc;

use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::ModuleId;
use cairo_lang_filesystem::db::{
AsFilesGroupMut, CrateConfiguration, CrateSettings, FilesGroupEx, CORELIB_CRATE_NAME,
};
use cairo_lang_filesystem::ids::{CrateId, CrateLongId, Directory};
use cairo_lang_utils::Intern;
use smol_str::SmolStr;

use crate::lang::db::AnalysisDatabase;

/// A complete set of information needed to set up a real crate in the analysis database.
#[derive(Debug, PartialEq, Eq)]
pub struct Crate {
/// Crate name.
pub name: SmolStr,

/// The root directory of the crate.
///
/// This path **must** be absolute,
/// so it can be safely used as a `FileId` in the analysis database.
pub root: PathBuf,

/// Custom stem of crate main file name, if it is not `lib.cairo`.
///
/// This is used to generate a virtual lib file for crates without a root `lib.cairo`.
pub custom_main_file_stem: Option<SmolStr>,

/// Crate settings.
pub settings: CrateSettings,
}

impl Crate {
/// Applies this crate to the [`AnalysisDatabase`].
pub fn apply(&self, db: &mut AnalysisDatabase) {
let crate_id = CrateLongId::Real(self.name.clone()).intern(db);

let crate_configuration = CrateConfiguration {
root: Directory::Real(self.root.clone()),
settings: self.settings.clone(),
};
db.set_crate_config(crate_id, Some(crate_configuration));

if let Some(file_stem) = &self.custom_main_file_stem {
inject_virtual_wrapper_lib(db, crate_id, file_stem);
}
}

/// States whether this is the `core` crate.
pub fn is_core(&self) -> bool {
self.name == CORELIB_CRATE_NAME
}
}

/// Generate a wrapper lib file for a compilation unit without a root `lib.cairo`.
///
/// This approach allows compiling crates that do not define `lib.cairo` file. For example, single
/// file crates can be created this way. The actual single file module is defined as `mod` item in
/// created lib file.
fn inject_virtual_wrapper_lib(db: &mut AnalysisDatabase, crate_id: CrateId, file_stem: &str) {
let module_id = ModuleId::CrateRoot(crate_id);
let file_id = db.module_main_file(module_id).unwrap();
// Inject virtual lib file wrapper.
db.as_files_group_mut()
.override_file_content(file_id, Some(Arc::new(format!("mod {file_stem};"))));
}
1 change: 1 addition & 0 deletions crates/cairo-lang-language-server/src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub use self::project_manifest_path::*;

mod crate_data;
mod project_manifest_path;
// TODO(mkaput): These two are `pub` temporarily.
pub(crate) mod scarb;
Expand Down
59 changes: 14 additions & 45 deletions crates/cairo-lang-language-server/src/project/scarb/db.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
use std::fs;
use std::path::Path;
use std::sync::Arc;

use anyhow::{bail, ensure, Context, Result};
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::ModuleId;
use cairo_lang_filesystem::db::{
AsFilesGroupMut, CrateConfiguration, CrateSettings, Edition, ExperimentalFeaturesConfig,
FilesGroupEx, CORELIB_CRATE_NAME,
};
use cairo_lang_filesystem::ids::{CrateId, CrateLongId, Directory};
use cairo_lang_utils::Intern;
use cairo_lang_filesystem::db::{CrateSettings, Edition, ExperimentalFeaturesConfig};
use scarb_metadata::{Metadata, PackageMetadata};
use tracing::{debug, warn};

use crate::lang::db::AnalysisDatabase;
use crate::project::crate_data::Crate;

/// Updates crate roots in the database with the information from Scarb metadata.
///
Expand All @@ -30,14 +23,7 @@ use crate::lang::db::AnalysisDatabase;
// causes overriding of the crate within single call of this function. This is an UX problem, for
// which we do not know the solution yet.
pub fn update_crate_roots(metadata: &Metadata, db: &mut AnalysisDatabase) {
#[derive(Debug)]
struct Root<'a> {
crate_long_id: CrateLongId,
crate_configuration: CrateConfiguration,
non_lib_source_mod: Option<&'a str>,
}

let mut crate_roots = Vec::<Root<'_>>::new();
let mut crates = Vec::<Crate>::new();
for compilation_unit in &metadata.compilation_units {
if compilation_unit.target.kind == "cairo-plugin" {
debug!("skipping cairo plugin compilation unit: {}", compilation_unit.id);
Expand All @@ -46,7 +32,6 @@ pub fn update_crate_roots(metadata: &Metadata, db: &mut AnalysisDatabase) {

for component in &compilation_unit.components {
let crate_name = component.name.as_str();
let crate_long_id = CrateLongId::Real(crate_name.into());

let package = metadata.packages.iter().find(|package| package.id == component.package);
if package.is_none() {
Expand All @@ -73,28 +58,25 @@ pub fn update_crate_roots(metadata: &Metadata, db: &mut AnalysisDatabase) {
experimental_features: scarb_package_experimental_features(&package),
};

let crate_configuration =
CrateConfiguration { root: Directory::Real(root.into()), settings };
let custom_main_file_stem = (file_stem != "lib").then_some(file_stem.into());

let non_lib_source_mod = (file_stem != "lib").then_some(file_stem);

crate_roots.push(Root { crate_long_id, crate_configuration, non_lib_source_mod });
crates.push(Crate {
name: crate_name.into(),
root: root.into(),
custom_main_file_stem,
settings,
});
}
}

debug!("updating crate roots from scarb metadata: {crate_roots:#?}");
debug!("updating crate roots from scarb metadata: {crates:#?}");

if !crate_roots.iter().any(|r| r.crate_long_id.name() == CORELIB_CRATE_NAME) {
if !crates.iter().any(Crate::is_core) {
warn!("core crate is missing in scarb metadata, did not initialize it");
}

for Root { crate_long_id, crate_configuration, non_lib_source_mod } in crate_roots {
let crate_id = crate_long_id.intern(db);
db.set_crate_config(crate_id, Some(crate_configuration));

if let Some(file_stem) = non_lib_source_mod {
inject_virtual_wrapper_lib(crate_id, file_stem, db);
}
for cr in crates {
cr.apply(db);
}
}

Expand Down Expand Up @@ -186,16 +168,3 @@ fn scarb_package_experimental_features(
coupons: contains("coupons"),
}
}

/// Generate a wrapper lib file for a compilation unit without a root `lib.cairo`.
///
/// This approach allows compiling crates that do not define `lib.cairo` file. For example, single
/// file crates can be created this way. The actual single file module is defined as `mod` item in
/// created lib file.
fn inject_virtual_wrapper_lib(crate_id: CrateId, file_stem: &str, db: &mut AnalysisDatabase) {
let module_id = ModuleId::CrateRoot(crate_id);
let file_id = db.module_main_file(module_id).unwrap();
// Inject virtual lib file wrapper.
db.as_files_group_mut()
.override_file_content(file_id, Some(Arc::new(format!("mod {file_stem};"))));
}

0 comments on commit 3dc6fe9

Please sign in to comment.