-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create new project action in searcher. (enso-org/ide#1566)
Original commit: enso-org/ide@5adc952
- Loading branch information
Showing
20 changed files
with
2,381 additions
and
1,752 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
//! IDE controller | ||
//! | ||
//! The IDE controller expose functionality bound to the application as a whole, not to specific | ||
//! component or opened project. | ||
pub mod desktop; | ||
pub mod plain; | ||
|
||
use crate::prelude::*; | ||
|
||
use crate::notification; | ||
|
||
use mockall::automock; | ||
use parser::Parser; | ||
|
||
|
||
|
||
// ============================ | ||
// === Status Notifications === | ||
// ============================ | ||
|
||
/// The handle used to pair the ProcessStarted and ProcessFinished notifications. | ||
pub type BackgroundTaskHandle = usize; | ||
|
||
/// A notification which should be displayed to the User on the status bar. | ||
#[allow(missing_docs)] | ||
#[derive(Clone,Debug)] | ||
pub enum StatusNotification { | ||
/// Notification about single event, should be logged in an event log window. | ||
Event { label:String }, | ||
/// Notification about new background task done in IDE (like compiling library). | ||
BackgroundTaskStarted { label:String, handle: BackgroundTaskHandle }, | ||
/// Notification that some task notified in [`BackgroundTaskStarted`] has been finished. | ||
BackgroundTaskFinished { handle:BackgroundTaskHandle }, | ||
} | ||
|
||
/// A publisher for status notification events. | ||
#[derive(Clone,CloneRef,Debug,Default)] | ||
pub struct StatusNotificationPublisher { | ||
publisher : notification::Publisher<StatusNotification>, | ||
next_process_handle : Rc<Cell<usize>>, | ||
} | ||
|
||
impl StatusNotificationPublisher { | ||
/// Constructor. | ||
pub fn new() -> Self { default() } | ||
|
||
/// Publish a new status event (see [`StatusNotification::Event`]) | ||
pub fn publish_event(&self, label:impl Into<String>) { | ||
let label = label.into(); | ||
let notification = StatusNotification::Event {label}; | ||
executor::global::spawn(self.publisher.publish(notification)); | ||
} | ||
|
||
/// Publish a notification about new process (see [`StatusNotification::ProcessStarted`]). | ||
/// | ||
/// Returns the handle to be used when notifying about process finishing. | ||
pub fn publish_background_task(&self, label:impl Into<String>) -> BackgroundTaskHandle { | ||
let label = label.into(); | ||
let handle = self.next_process_handle.get(); | ||
self.next_process_handle.set(handle + 1); | ||
let notification = StatusNotification::BackgroundTaskStarted {label,handle}; | ||
executor::global::spawn(self.publisher.publish(notification)); | ||
handle | ||
} | ||
|
||
/// Publish a notfication that process has finished (see [`StatusNotification::ProcessFinished`]) | ||
pub fn published_background_task_finished(&self, handle:BackgroundTaskHandle) { | ||
let notification = StatusNotification::BackgroundTaskFinished {handle}; | ||
executor::global::spawn(self.publisher.publish(notification)); | ||
} | ||
|
||
/// The asynchronous stream of published notifications. | ||
pub fn subscribe(&self) -> impl Stream<Item=StatusNotification> { | ||
self.publisher.subscribe() | ||
} | ||
} | ||
|
||
|
||
|
||
// ==================== | ||
// === Notification === | ||
// ==================== | ||
|
||
/// Notification of IDE Controller. | ||
/// | ||
/// In contrast to [`StatusNotification`], which is a notification from any application part to | ||
/// be delivered to User (displayed on some event log or status bar), this is a notification to be | ||
/// used internally in code. | ||
#[derive(Copy,Clone,Debug)] | ||
pub enum Notification { | ||
/// User created a new project. The new project is opened in IDE. | ||
NewProjectCreated | ||
} | ||
|
||
|
||
|
||
// =========== | ||
// === API === | ||
// =========== | ||
|
||
/// The API of all project management operations. | ||
/// | ||
/// It is a separate trait, because those methods are not supported in some environments (see also | ||
/// [`API::manage_projects`]). | ||
pub trait ManagingProjectAPI { | ||
|
||
/// Create a new unnamed project and open it in the IDE. | ||
fn create_new_project(&self) -> BoxFuture<FallibleResult>; | ||
} | ||
|
||
/// The API of IDE Controller. | ||
#[automock] | ||
pub trait API:Debug { | ||
/// The model of currently opened project. | ||
/// | ||
/// IDE can have only one project opened at a time. | ||
fn current_project(&self) -> model::Project; | ||
|
||
/// Getter of Status Notification Publisher. | ||
fn status_notifications(&self) -> &StatusNotificationPublisher; | ||
|
||
/// The Parser Handle. | ||
fn parser(&self) -> &Parser; | ||
|
||
/// Subscribe the controller notifications. | ||
fn subscribe(&self) -> StaticBoxStream<Notification>; | ||
|
||
/// Return the Managing Project API. | ||
/// | ||
/// It may be some delegated object or just the reference to self. | ||
// Automock macro does not work without explicit lifetimes here. | ||
#[allow(clippy::needless_lifetimes)] | ||
fn manage_projects<'a>(&'a self) -> FallibleResult<&'a dyn ManagingProjectAPI>; | ||
} | ||
|
||
/// A polymorphic handle of IDE controller. | ||
pub type Ide = Rc<dyn API>; | ||
|
||
/// The IDE Controller for desktop environments. | ||
pub type Desktop = desktop::Handle; | ||
|
||
/// The Plain IDE controller with a single project and no possibility to change it. | ||
pub type Plain = plain::Handle; | ||
|
||
impl Debug for MockAPI { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f,"Mocked Ide Controller") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
//! The Desktop IDE Controller | ||
//! | ||
//! See [`crate::controller::ide`] for more detailed description of IDE Controller API. | ||
use crate::prelude::*; | ||
|
||
use crate::controller::ide::API; | ||
use crate::controller::ide::ManagingProjectAPI; | ||
use crate::controller::ide::StatusNotificationPublisher; | ||
use crate::controller::ide::Notification; | ||
use crate::controller::project::ENGINE_VERSION_FOR_NEW_PROJECTS; | ||
use crate::ide::initializer; | ||
use crate::notification; | ||
|
||
use enso_protocol::project_manager; | ||
use enso_protocol::project_manager::MissingComponentAction; | ||
use enso_protocol::project_manager::ProjectName; | ||
use parser::Parser; | ||
|
||
|
||
|
||
// ================= | ||
// === Constants === | ||
// ================= | ||
|
||
const UNNAMED_PROJECT_NAME:&str = "Unnamed"; | ||
|
||
|
||
|
||
// ============================= | ||
// === The Controller Handle === | ||
// ============================= | ||
|
||
/// The Desktop IDE Controller handle. | ||
/// | ||
/// The desktop controller has access to the Project Manager, and thus is able to perform all | ||
/// project management operations. | ||
#[derive(Clone,CloneRef,Derivative)] | ||
#[derivative(Debug)] | ||
pub struct Handle { | ||
logger : Logger, | ||
current_project : Rc<CloneRefCell<model::Project>>, | ||
#[derivative(Debug="ignore")] | ||
project_manager : Rc<dyn project_manager::API>, | ||
status_notifications : StatusNotificationPublisher, | ||
parser : Parser, | ||
notifications : notification::Publisher<Notification>, | ||
} | ||
|
||
impl Handle { | ||
/// Create a project controller handle with already loaded project model. | ||
pub fn new_with_project | ||
(project_manager:Rc<dyn project_manager::API>, initial_project:model::Project) -> Self { | ||
let logger = Logger::new("controller::ide::Desktop"); | ||
let current_project = Rc::new(CloneRefCell::new(initial_project)); | ||
let status_notifications = default(); | ||
let parser = Parser::new_or_panic(); | ||
let notifications = default(); | ||
Self {logger,current_project,project_manager,status_notifications,parser,notifications} | ||
} | ||
|
||
/// Create a project controller handle which opens the project with the given name, or creates it | ||
/// if it does not exist. | ||
pub async fn new_with_opened_project | ||
(project_manager:Rc<dyn project_manager::API>, name:ProjectName) -> FallibleResult<Self> { | ||
// TODO[ao]: Reuse of initializer used in previous code design. It should be soon replaced | ||
// anyway, because we will soon resign from the "open or create" approach when opening | ||
// IDE. See https://github.com/enso-org/ide/issues/1492 for details. | ||
let initializer = initializer::WithProjectManager::new(project_manager.clone_ref(),name); | ||
let model = initializer.initialize_project_model().await?; | ||
Ok(Self::new_with_project(project_manager,model)) | ||
} | ||
} | ||
|
||
impl API for Handle { | ||
fn current_project (&self) -> model::Project { self.current_project.get() } | ||
fn status_notifications(&self) -> &StatusNotificationPublisher { &self.status_notifications } | ||
fn parser (&self) -> &Parser { &self.parser } | ||
|
||
fn subscribe(&self) -> StaticBoxStream<Notification> { | ||
self.notifications.subscribe().boxed_local() | ||
} | ||
|
||
fn manage_projects(&self) -> FallibleResult<&dyn ManagingProjectAPI> { | ||
Ok(self) | ||
} | ||
} | ||
|
||
impl ManagingProjectAPI for Handle { | ||
fn create_new_project(&self) -> BoxFuture<FallibleResult> { | ||
async move { | ||
use model::project::Synchronized as Project; | ||
|
||
let list = self.project_manager.list_projects(&None).await?; | ||
let names:HashSet<String> = list.projects.into_iter().map(|p| p.name.into()).collect(); | ||
let without_suffix = UNNAMED_PROJECT_NAME.to_owned(); | ||
let with_suffix = (1..).map(|i| format!("{}_{}", UNNAMED_PROJECT_NAME, i)); | ||
let mut candidates = std::iter::once(without_suffix).chain(with_suffix); | ||
// The iterator have no end, so we can safely unwrap. | ||
let name = candidates.find(|c| names.contains(c)).unwrap(); | ||
let version = Some(ENGINE_VERSION_FOR_NEW_PROJECTS.to_owned()); | ||
let action = MissingComponentAction::Install; | ||
|
||
let create_result = self.project_manager.create_project(&name,&version,&action).await?; | ||
let new_project_id = create_result.project_id; | ||
let project_mgr = self.project_manager.clone_ref(); | ||
let new_project = Project::new_opened(&self.logger,project_mgr,new_project_id,name); | ||
self.current_project.set(new_project.await?); | ||
executor::global::spawn(self.notifications.publish(Notification::NewProjectCreated)); | ||
Ok(()) | ||
}.boxed_local() | ||
} | ||
} |
Oops, something went wrong.