Skip to content
This repository has been archived by the owner on Dec 28, 2021. It is now read-only.

Create new project action in searcher. #1566

Merged
merged 25 commits into from
May 21, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
074aefe
WIP
farmaazon Apr 27, 2021
ca89039
WIP
farmaazon Apr 27, 2021
c44ad20
Refactored project setup
farmaazon May 12, 2021
ec93597
Create new project action
farmaazon May 12, 2021
1be198c
Fixes
farmaazon May 12, 2021
1b11b70
Code cleanup
farmaazon May 13, 2021
6df60f5
Self-review
farmaazon May 13, 2021
99f32f1
Merge remote-tracking branch 'origin/develop' into wip/ao/create-new-…
farmaazon May 13, 2021
9a65231
Fix compilation
farmaazon May 14, 2021
679d654
Update src/rust/ide/src/controller/ide/desktop.rs
farmaazon May 17, 2021
a0ee9e5
Update src/rust/ide/src/controller/ide/desktop.rs
farmaazon May 17, 2021
589a403
Update src/rust/ide/src/controller/ide/desktop.rs
farmaazon May 17, 2021
5148154
Update src/rust/ide/src/controller/project.rs
farmaazon May 17, 2021
58b10ec
Update src/rust/ide/src/controller/ide/plain.rs
farmaazon May 17, 2021
b929f64
Update src/rust/ide/src/controller/project.rs
farmaazon May 17, 2021
75d5366
Update src/rust/ide/src/controller/project.rs
farmaazon May 17, 2021
384b9a0
Fixes
farmaazon May 14, 2021
b675ee4
Applying @mwu-two review
farmaazon May 17, 2021
ab849cb
Merge remote-tracking branch 'origin/develop' into wip/ao/create-new-…
farmaazon May 17, 2021
245c21f
Applying @wdanilo review
farmaazon May 18, 2021
84921d7
Merge remote-tracking branch 'origin/develop' into wip/ao/create-new-…
farmaazon May 21, 2021
a58d8bc
Linter fixes
farmaazon May 21, 2021
9b9c14d
Merge branch 'develop' into wip/ao/create-new-project2
farmaazon May 21, 2021
ccda769
prettify CHANGELOG
farmaazon May 21, 2021
c264a2c
Fix tests
farmaazon May 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub type Event = json_rpc::handler::Event<Notification>;
// ============

/// A path is a representation of a path relative to a specified content root.
// FIXME [mwu] Consider rename to something like `FilePath`, see https://github.com/enso-org/enso/issues/708
#[derive(Clone,Debug,Serialize,Deserialize,Hash,PartialEq,Eq)]
#[serde(rename_all="camelCase")]
pub struct Path {
Expand Down
10 changes: 9 additions & 1 deletion src/rust/ide/lib/enso-protocol/src/project_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl Display for IpWithSocket {
}

/// Project name.
#[derive(Debug,Display,Clone,Serialize,Deserialize,From,PartialEq,Shrinkwrap)]
#[derive(Clone,Debug,Deserialize,Display,Eq,From,Hash,PartialEq,Serialize,Shrinkwrap)]
#[shrinkwrap(mutable)]
pub struct ProjectName(pub String);

Expand All @@ -111,6 +111,14 @@ impl ProjectName {
}
}

impl AsRef<str> for ProjectName {
fn as_ref(&self) -> &str { &self.0 }
}

impl From<ProjectName> for String {
fn from(name:ProjectName) -> Self { name.0 }
}

/// Project information, such as name, its id and last time it was opened.
#[derive(Debug,Clone,Serialize,Deserialize,PartialEq)]
pub struct ProjectMetadata {
Expand Down
5 changes: 5 additions & 0 deletions src/rust/ide/src/controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@
//!
//! Controllers store their handles using `utils::cell` handle types to ensure
//! that mutable state is safely accessed.
//TODO check if this docs is still valid

pub mod graph;
pub mod ide;
pub mod module;
pub mod text;
pub mod project;
pub mod visualization;
pub mod searcher;

pub use graph::Handle as Graph;
pub use graph::executed::Handle as ExecutedGraph;
pub use ide::Handle as Ide;
pub use module::Handle as Module;
pub use project::Handle as Project;
pub use text::Handle as Text;
pub use visualization::Handle as Visualization;
pub use searcher::Searcher;
Expand Down
88 changes: 88 additions & 0 deletions src/rust/ide/src/controller/ide.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
pub mod desktop;
pub mod cloud;

use crate::prelude::*;

use crate::notification;



// ============================
// === Status Notifications ===
// ============================

pub type ProcessHandle = usize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the name Process here, initially I thought about different kind of process.
Perhaps something more along lines of "Job", "Task", "BackgroundAction", etc?


#[derive(Clone,Debug)]
pub enum StatusNotification {
Event { label:String },
ProcessStarted { label:String, handle:ProcessHandle },
ProcessFinished { handle:ProcessHandle },
}

#[derive(Clone,CloneRef,Debug,Default)]
pub struct StatusNotifications {
publisher : notification::Publisher<StatusNotification>,
next_process_handle : Rc<Cell<usize>>,
}

impl StatusNotifications {
pub fn new() -> Self { default() }

pub fn publish_event(&self, label:impl Into<String>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd bikeshed a few more names here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand this comment

let label = label.into();
let notification = StatusNotification::Event {label};
executor::global::spawn(self.publisher.publish(notification));
}

pub fn publish_process(&self, label:impl Into<String>) -> ProcessHandle {
let label = label.into();
let handle = self.next_process_handle.get();
self.next_process_handle.set(handle + 1);
let notification = StatusNotification::ProcessStarted {label,handle};
executor::global::spawn(self.publisher.publish(notification));
handle
}

pub fn published_process_finished(&self, handle:ProcessHandle) {
let notification = StatusNotification::ProcessFinished {handle};
executor::global::spawn(self.publisher.publish(notification));
}

pub fn subscribe(&self) -> impl Stream<Item=StatusNotification> {
self.publisher.subscribe()
}
}

// ====================
// === Notification ===
// ====================

#[derive(Copy,Clone,Debug)]
pub enum Notification {
NewProjectCreated
}



// ===========
// === API ===
// ===========

pub trait ManagingProjectAPI {
fn create_new_project<'a>(&'a self) -> BoxFuture<'a, FallibleResult>;
}

pub trait API:Debug {
fn current_project(&self) -> model::Project;

fn status_notifications(&self) -> &StatusNotifications;

fn subscribe(&self) -> StaticBoxStream<Notification>;

fn manage_projects(&self) -> Option<&dyn ManagingProjectAPI>;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps IDE should also provide parser?


pub type Handle = Rc<dyn API>;
pub type Desktop = desktop::Handle;
pub type Cloud = cloud::Handle;
48 changes: 48 additions & 0 deletions src/rust/ide/src/controller/ide/cloud.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::prelude::*;
use crate::controller::ide::{ManagingProjectAPI, Notification};
use crate::controller::ide::StatusNotifications;

use enso_protocol::project_manager::ProjectName;
use flo_stream::Subscriber;



#[derive(Clone,CloneRef,Debug)]
pub struct Handle {
pub logger : Logger,
pub status_notifications : StatusNotifications,
pub project : model::Project,
}

impl Handle {
pub async fn new
(project_name:ProjectName, json_endpoint:String, binary_endpoint:String)
-> FallibleResult<Self> {
let logger = Logger::new("controller::ide::Cloud");
// TODO[ao]: we should think how to handle engine's versions in cloud.
// https://github.com/enso-org/ide/issues/1195
let version = semver::Version::parse(controller::project::ENGINE_VERSION_FOR_NEW_PROJECTS)?;
let project_id = default();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that project_id should ever be default.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add TODO and issue.

let project = model::project::Synchronized::new_connected
(&logger,None,json_endpoint,binary_endpoint,version,project_id,project_name).await?;
let status_notifications = default();
Ok(Self{logger,project,status_notifications})
}
}

impl controller::ide::API for Handle {
fn current_project(&self) -> model::Project {
self.project.clone_ref()
}

fn status_notifications(&self) -> &StatusNotifications { &self.status_notifications }

fn subscribe(&self) -> StaticBoxStream<Notification> {
futures::stream::empty().boxed_local()
}

fn manage_projects(&self) -> Option<&dyn ManagingProjectAPI> {
None
}
}

87 changes: 87 additions & 0 deletions src/rust/ide/src/controller/ide/desktop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use crate::prelude::*;

use crate::controller::ide::API;
use crate::controller::ide::ManagingProjectAPI;
use crate::controller::ide::StatusNotifications;
use crate::controller::ide::Notification;
use crate::ide;
use crate::notification;

use enso_protocol::project_manager;
use enso_protocol::project_manager::MissingComponentAction;
use enso_protocol::project_manager::ProjectName;



// =================
// === Constants ===
// =================

const UNNAMED_PROJECT_NAME:&str = "Unnamed";
farmaazon marked this conversation as resolved.
Show resolved Hide resolved



// =============================
// === The Controller Handle ===
// =============================

#[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 : StatusNotifications,
notifications : notification::Publisher<Notification>,
}

impl Handle {
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 notifications = default();
Self {logger,current_project,project_manager,status_notifications,notifications}
}

pub async fn new_with_opened_project(project_manager:Rc<dyn project_manager::API>, name:ProjectName) -> FallibleResult<Self> {
let initializer = ide::initializer::WithProjectManager::new(Logger::new("Handle::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) -> &StatusNotifications { &self.status_notifications }

fn subscribe(&self) -> StaticBoxStream<Notification> {
self.notifications.subscribe().boxed_local()
}

fn manage_projects (&self) -> Option<&dyn ManagingProjectAPI> {
Some(self)
}
}

impl ManagingProjectAPI for Handle {
fn create_new_project<'a>(&'a self) -> BoxFuture<'a, FallibleResult> {
async move {

let list = self.project_manager.list_projects(&None).await?;
let names:HashSet<ProjectName> = list.projects.into_iter().map(|p| p.name).collect();
let candidates_with_suffix = (1..).map(|i| format!("{}_{}", UNNAMED_PROJECT_NAME, i));
let candidates = std::iter::once(UNNAMED_PROJECT_NAME.to_owned()).chain(candidates_with_suffix);
let candidates = candidates.map(ProjectName);
let name = candidates.skip_while(|c| names.contains(c)).next().unwrap();
let version = Some(controller::project::ENGINE_VERSION_FOR_NEW_PROJECTS.to_owned());
let action = MissingComponentAction::Install;

let new_project = self.project_manager.create_project(name.deref(),&version,&action).await?.project_id;
self.current_project.set(model::project::Synchronized::new_opened(&self.logger,self.project_manager.clone_ref(),new_project,name).await?);
executor::global::spawn(self.notifications.publish(Notification::NewProjectCreated));
Ok(())
}.boxed_local()
}
}
Loading