Skip to content

Commit

Permalink
LS: Project model - initial struct (#6718)
Browse files Browse the repository at this point in the history
  • Loading branch information
piotmag769 authored Nov 25, 2024
1 parent 8776352 commit 84986d5
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 98 deletions.
19 changes: 9 additions & 10 deletions crates/cairo-lang-language-server/src/lang/db/swapper.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::HashSet;
use std::panic::{AssertUnwindSafe, catch_unwind};
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, SystemTime};

Expand All @@ -17,9 +16,10 @@ use crate::config::Config;
use crate::lang::db::AnalysisDatabase;
use crate::lang::lsp::LsProtoGroup;
use crate::lang::proc_macros::db::ProcMacroGroup;
use crate::project::ProjectController;
use crate::server::client::Notifier;
use crate::toolchain::scarb::ScarbToolchain;
use crate::{Backend, Tricks, env_config};
use crate::{Tricks, env_config};

/// Swaps entire [`AnalysisDatabase`] with empty one periodically.
///
Expand Down Expand Up @@ -60,7 +60,7 @@ impl AnalysisDatabaseSwapper {
config: &Config,
tricks: &Tricks,
notifier: &Notifier,
loaded_scarb_manifests: &mut HashSet<PathBuf>,
project_controller: &mut ProjectController,
) {
let Ok(elapsed) = self.last_replace.elapsed() else {
warn!("system time went backwards, skipping db swap");
Expand All @@ -76,7 +76,7 @@ impl AnalysisDatabaseSwapper {
return;
}

self.swap(db, open_files, config, tricks, notifier, loaded_scarb_manifests)
self.swap(db, open_files, config, tricks, notifier, project_controller)
}

/// Swaps the database.
Expand All @@ -88,10 +88,10 @@ impl AnalysisDatabaseSwapper {
config: &Config,
tricks: &Tricks,
notifier: &Notifier,
loaded_scarb_manifests: &mut HashSet<PathBuf>,
project_controller: &mut ProjectController,
) {
let Ok(new_db) = catch_unwind(AssertUnwindSafe(|| {
loaded_scarb_manifests.clear();
project_controller.clear_loaded_workspaces();

let mut new_db = AnalysisDatabase::new(tricks);
self.migrate_proc_macro_state(&mut new_db, db);
Expand All @@ -101,7 +101,7 @@ impl AnalysisDatabaseSwapper {
open_files,
config,
notifier,
loaded_scarb_manifests,
project_controller,
);
new_db
})) else {
Expand Down Expand Up @@ -160,21 +160,20 @@ impl AnalysisDatabaseSwapper {
open_files: &HashSet<Url>,
config: &Config,
notifier: &Notifier,
loaded_scarb_manifests: &mut HashSet<PathBuf>,
project_controller: &mut ProjectController,
) {
for uri in open_files {
let Ok(file_path) = uri.to_file_path() else {
// We are only interested in physical files.
continue;
};

Backend::detect_crate_for(
project_controller.detect_crate_for(
new_db,
&self.scarb_toolchain,
config,
&file_path,
notifier,
loaded_scarb_manifests,
);
}
}
Expand Down
89 changes: 6 additions & 83 deletions crates/cairo-lang-language-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,45 +38,34 @@
//! }
//! ```
use std::collections::HashSet;
use std::panic::RefUnwindSafe;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::process::ExitCode;
use std::time::SystemTime;
use std::{io, panic};

use anyhow::{Context, Result};
use cairo_lang_compiler::db::validate_corelib;
use cairo_lang_compiler::project::{setup_project, update_crate_roots_from_project_config};
use anyhow::Result;
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::FileLongId;
use cairo_lang_project::ProjectConfig;
use cairo_lang_semantic::plugin::PluginSuite;
use crossbeam::select;
use lsp_server::Message;
use lsp_types::RegistrationParams;
use salsa::{Database, Durability};
use tracing::{debug, error, info, trace, warn};
use tracing::{debug, error, info};

use crate::config::Config;
use crate::lang::db::AnalysisDatabase;
use crate::lang::lsp::LsProtoGroup;
use crate::lang::proc_macros::controller::ProcMacroChannelsReceivers;
use crate::lsp::capabilities::server::{
collect_dynamic_registrations, collect_server_capabilities,
};
use crate::lsp::ext::{CorelibVersionMismatch, ScarbMetadataFailed};
use crate::lsp::result::LSPResult;
use crate::project::ProjectManifestPath;
use crate::project::scarb::{get_workspace_members_manifests, update_crate_roots};
use crate::project::unmanaged_core_crate::try_to_init_unmanaged_core;
use crate::server::client::{Notifier, Requester, Responder};
use crate::server::connection::{Connection, ConnectionInitializer};
use crate::server::panic::is_cancelled;
use crate::server::schedule::thread::JoinHandle;
use crate::server::schedule::{Scheduler, Task, event_loop_thread};
use crate::state::State;
use crate::toolchain::scarb::ScarbToolchain;

mod config;
mod env_config;
Expand Down Expand Up @@ -408,7 +397,7 @@ impl Backend {
&state.config,
&state.tricks,
&notifier,
&mut state.loaded_scarb_manifests,
&mut state.project_controller,
);
}

Expand All @@ -417,90 +406,24 @@ impl Backend {
state.diagnostics_controller.refresh(state);
}

/// Tries to detect the crate root the config that contains a cairo file, and add it to the
/// system.
#[tracing::instrument(skip_all)]
fn detect_crate_for(
db: &mut AnalysisDatabase,
scarb_toolchain: &ScarbToolchain,
config: &Config,
file_path: &Path,
notifier: &Notifier,
loaded_scarb_manifests: &mut HashSet<PathBuf>,
) {
match ProjectManifestPath::discover(file_path) {
Some(ProjectManifestPath::Scarb(manifest_path)) => {
if loaded_scarb_manifests.contains(&manifest_path) {
trace!("scarb project is already loaded: {}", manifest_path.display());
return;
}

let metadata = scarb_toolchain
.metadata(&manifest_path)
.with_context(|| {
format!("failed to refresh scarb workspace: {}", manifest_path.display())
})
.inspect_err(|err| {
warn!("{err:?}");
notifier.notify::<ScarbMetadataFailed>(());
})
.ok();

if let Some(metadata) = metadata {
loaded_scarb_manifests.extend(get_workspace_members_manifests(&metadata));
update_crate_roots(&metadata, db);
} else {
// Try to set up a corelib at least.
try_to_init_unmanaged_core(db, config, scarb_toolchain);
}

if let Err(result) = validate_corelib(db) {
notifier.notify::<CorelibVersionMismatch>(result.to_string());
}
}

Some(ProjectManifestPath::CairoProject(config_path)) => {
// The base path of ProjectConfig must be absolute to ensure that all paths in Salsa
// DB will also be absolute.
assert!(config_path.is_absolute());

try_to_init_unmanaged_core(db, config, scarb_toolchain);

if let Ok(config) = ProjectConfig::from_file(&config_path) {
update_crate_roots_from_project_config(db, &config);
};
}

None => {
try_to_init_unmanaged_core(db, config, scarb_toolchain);

if let Err(err) = setup_project(&mut *db, file_path) {
let file_path_s = file_path.to_string_lossy();
error!("error loading file {file_path_s} as a single crate: {err}");
}
}
}
}

/// Reload crate detection for all open files.
fn reload(
state: &mut State,
notifier: &Notifier,
requester: &mut Requester<'_>,
) -> LSPResult<()> {
state.loaded_scarb_manifests.clear();
state.project_controller.clear_loaded_workspaces();
state.config.reload(requester, &state.client_capabilities)?;

for uri in state.open_files.iter() {
let Some(file_id) = state.db.file_for_url(uri) else { continue };
if let FileLongId::OnDisk(file_path) = state.db.lookup_intern_file(file_id) {
Backend::detect_crate_for(
state.project_controller.detect_crate_for(
&mut state.db,
&state.scarb_toolchain,
&state.config,
&file_path,
notifier,
&mut state.loaded_scarb_manifests,
);
}
}
Expand Down
96 changes: 96 additions & 0 deletions crates/cairo-lang-language-server/src/project/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,104 @@
use std::collections::HashSet;
use std::path::{Path, PathBuf};

use anyhow::Context;
use cairo_lang_compiler::db::validate_corelib;
use cairo_lang_compiler::project::{setup_project, update_crate_roots_from_project_config};
use cairo_lang_project::ProjectConfig;
use tracing::{error, trace, warn};

pub use self::crate_data::Crate;
pub use self::project_manifest_path::*;
use crate::config::Config;
use crate::lang::db::AnalysisDatabase;
use crate::lsp::ext::{CorelibVersionMismatch, ScarbMetadataFailed};
use crate::project::scarb::{get_workspace_members_manifests, update_crate_roots};
use crate::project::unmanaged_core_crate::try_to_init_unmanaged_core;
use crate::server::client::Notifier;
use crate::state::Owned;
use crate::toolchain::scarb::ScarbToolchain;

mod crate_data;
mod project_manifest_path;
// TODO(mkaput): These two are `pub` temporarily.
pub(crate) mod scarb;
pub(crate) mod unmanaged_core_crate;

pub struct ProjectController {
loaded_scarb_manifests: Owned<HashSet<PathBuf>>,
}

impl ProjectController {
pub fn new() -> Self {
ProjectController { loaded_scarb_manifests: Default::default() }
}

/// Tries to detect the crate root the config that contains a cairo file, and add it to the
/// system.
#[tracing::instrument(skip_all)]
pub fn detect_crate_for(
&mut self,
db: &mut AnalysisDatabase,
scarb_toolchain: &ScarbToolchain,
config: &Config,
file_path: &Path,
notifier: &Notifier,
) {
match ProjectManifestPath::discover(file_path) {
Some(ProjectManifestPath::Scarb(manifest_path)) => {
if self.loaded_scarb_manifests.contains(&manifest_path) {
trace!("scarb project is already loaded: {}", manifest_path.display());
return;
}

let metadata = scarb_toolchain
.metadata(&manifest_path)
.with_context(|| {
format!("failed to refresh scarb workspace: {}", manifest_path.display())
})
.inspect_err(|err| {
warn!("{err:?}");
notifier.notify::<ScarbMetadataFailed>(());
})
.ok();

if let Some(metadata) = metadata {
self.loaded_scarb_manifests.extend(get_workspace_members_manifests(&metadata));
update_crate_roots(&metadata, db);
} else {
// Try to set up a corelib at least.
try_to_init_unmanaged_core(db, config, scarb_toolchain);
}

if let Err(result) = validate_corelib(db) {
notifier.notify::<CorelibVersionMismatch>(result.to_string());
}
}

Some(ProjectManifestPath::CairoProject(config_path)) => {
// The base path of ProjectConfig must be absolute to ensure that all paths in Salsa
// DB will also be absolute.
assert!(config_path.is_absolute());

try_to_init_unmanaged_core(db, config, scarb_toolchain);

if let Ok(config) = ProjectConfig::from_file(&config_path) {
update_crate_roots_from_project_config(db, &config);
};
}

None => {
try_to_init_unmanaged_core(db, config, scarb_toolchain);

if let Err(err) = setup_project(&mut *db, file_path) {
let file_path_s = file_path.to_string_lossy();
error!("error loading file {file_path_s} as a single crate: {err}");
}
}
}
}

pub fn clear_loaded_workspaces(&mut self) {
self.loaded_scarb_manifests.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,12 @@ impl SyncNotificationHandler for DidOpenTextDocument {
if uri.scheme() == "file" {
let Ok(path) = uri.to_file_path() else { return Ok(()) };

Backend::detect_crate_for(
state.project_controller.detect_crate_for(
&mut state.db,
&state.scarb_toolchain,
&state.config,
&path,
&notifier,
&mut state.loaded_scarb_manifests,
);
}

Expand Down
Loading

0 comments on commit 84986d5

Please sign in to comment.