Skip to content

Commit

Permalink
Merge branch 'develop' into wip/procrat/shift-space-for-fullscreen-vi…
Browse files Browse the repository at this point in the history
…z-6260

* develop:
  Add verbose logs mention to CONTRIBUTING.md (#6703)
  Hide "shared with" column on local backend (#6684)
  Fix top bar styles (#6695)
  Fix opening cloud projects (#6683)
  Show spinner when opening/creating a project (#6321)
  Fix logic for enabling dashboard (#6696)
  Create unique atom getter suggestions (#6694)
  Ensure slow shutdown of LS always kicks off hooks (#6665)
  Fix `--startup.project` to bypass opening dashboard (#6671)
  • Loading branch information
Procrat committed May 16, 2023
2 parents cc7daae + f38033b commit 07e7d46
Show file tree
Hide file tree
Showing 29 changed files with 498 additions and 452 deletions.
65 changes: 49 additions & 16 deletions app/gui/src/controller/ide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use crate::prelude::*;

use crate::config::ProjectToOpen;

use double_representation::name::project;
use mockall::automock;
use parser::Parser;
Expand Down Expand Up @@ -102,9 +104,7 @@ impl StatusNotificationPublisher {
/// used internally in code.
#[derive(Copy, Clone, Debug)]
pub enum Notification {
/// User created a new project. The new project is opened in IDE.
NewProjectCreated,
/// User opened an existing project.
/// User opened a new or existing project.
ProjectOpened,
/// User closed the project.
ProjectClosed,
Expand All @@ -118,10 +118,12 @@ pub enum Notification {

// === Errors ===

#[allow(missing_docs)]
/// Error raised when a project with given name or ID was not found.
#[derive(Clone, Debug, Fail)]
#[fail(display = "Project with name \"{}\" not found.", 0)]
struct ProjectNotFound(String);
#[fail(display = "Project '{}' was not found.", project)]
pub struct ProjectNotFound {
project: ProjectToOpen,
}


// === Managing API ===
Expand All @@ -131,11 +133,16 @@ struct ProjectNotFound(String);
/// 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.
/// Create a new project and open it in the IDE.
///
/// `name` is an optional project name. It overrides the name of the template if given.
/// `template` is an optional project template name. Available template names are defined in
/// `lib/scala/pkg/src/main/scala/org/enso/pkg/Template.scala`.
fn create_new_project(&self, template: Option<project::Template>) -> BoxFuture<FallibleResult>;
fn create_new_project(
&self,
name: Option<String>,
template: Option<project::Template>,
) -> BoxFuture<FallibleResult>;

/// Return a list of existing projects.
fn list_projects(&self) -> BoxFuture<FallibleResult<Vec<ProjectMetadata>>>;
Expand All @@ -150,18 +157,44 @@ pub trait ManagingProjectAPI {
/// and then for the project opening.
fn open_project_by_name(&self, name: String) -> BoxFuture<FallibleResult> {
async move {
let projects = self.list_projects().await?;
let mut projects = projects.into_iter();
let project = projects.find(|project| project.name.as_ref() == name);
let uuid = project.map(|project| project.id);
if let Some(uuid) = uuid {
self.open_project(uuid).await
} else {
Err(ProjectNotFound(name).into())
let project_id = self.find_project(&ProjectToOpen::Name(name.into())).await?;
self.open_project(project_id).await
}
.boxed_local()
}

/// Open a project by name or ID. If no project with the given name exists, it will be created.
fn open_or_create_project(&self, project_to_open: ProjectToOpen) -> BoxFuture<FallibleResult> {
async move {
match self.find_project(&project_to_open).await {
Ok(project_id) => self.open_project(project_id).await,
Err(error) =>
if let ProjectToOpen::Name(name) = project_to_open {
info!("Attempting to create project with name '{name}'.");
self.create_new_project(Some(name.to_string()), None).await
} else {
Err(error)
},
}
}
.boxed_local()
}

/// Find a project by name or ID.
fn find_project<'a: 'c, 'b: 'c, 'c>(
&'a self,
project_to_open: &'b ProjectToOpen,
) -> BoxFuture<'c, FallibleResult<Uuid>> {
async move {
self.list_projects()
.await?
.into_iter()
.find(|project_metadata| project_to_open.matches(project_metadata))
.map(|metadata| metadata.id)
.ok_or_else(|| ProjectNotFound { project: project_to_open.clone() }.into())
}
.boxed_local()
}
}


Expand Down
74 changes: 16 additions & 58 deletions app/gui/src/controller/ide/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
use crate::prelude::*;

use crate::config::ProjectToOpen;
use crate::controller::ide::ManagingProjectAPI;
use crate::controller::ide::Notification;
use crate::controller::ide::StatusNotificationPublisher;
use crate::controller::ide::API;
use crate::ide::initializer;

use double_representation::name::project;
use engine_protocol::project_manager;
Expand Down Expand Up @@ -49,53 +47,16 @@ pub struct Handle {
}

impl Handle {
/// Create IDE controller. If `maybe_project_name` is `Some`, a project with provided name will
/// be opened. Otherwise controller will be used for project manager operations by Welcome
/// Screen.
pub async fn new(
project_manager: Rc<dyn project_manager::API>,
project_to_open: Option<ProjectToOpen>,
) -> FallibleResult<Self> {
let project = match project_to_open {
Some(project_to_open) =>
Some(Self::init_project_model(project_manager.clone_ref(), project_to_open).await?),
None => None,
};
Ok(Self::new_with_project_model(project_manager, project))
}

/// Create IDE controller with prepared project model. If `project` is `None`,
/// `API::current_project` returns `None` as well.
pub fn new_with_project_model(
project_manager: Rc<dyn project_manager::API>,
project: Option<model::Project>,
) -> Self {
let current_project = Rc::new(CloneCell::new(project));
let status_notifications = default();
let parser = Parser::new();
let notifications = default();
let component_browser_private_entries_visibility_flag = default();
Self {
current_project,
/// Create IDE controller.
pub fn new(project_manager: Rc<dyn project_manager::API>) -> FallibleResult<Self> {
Ok(Self {
current_project: default(),
project_manager,
status_notifications,
parser,
notifications,
component_browser_private_entries_visibility_flag,
}
}

/// Open project with provided name.
async fn init_project_model(
project_manager: Rc<dyn project_manager::API>,
project_to_open: ProjectToOpen,
) -> FallibleResult<model::Project> {
// 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, project_to_open);
let model = initializer.initialize_project_model().await?;
Ok(model)
status_notifications: default(),
parser: default(),
notifications: default(),
component_browser_private_entries_visibility_flag: default(),
})
}
}

Expand Down Expand Up @@ -133,14 +94,16 @@ impl API for Handle {

impl ManagingProjectAPI for Handle {
#[profile(Objective)]
fn create_new_project(&self, template: Option<project::Template>) -> BoxFuture<FallibleResult> {
fn create_new_project(
&self,
name: Option<String>,
template: Option<project::Template>,
) -> BoxFuture<FallibleResult> {
async move {
use model::project::Synchronized as Project;

let list = self.project_manager.list_projects(&None).await?;
let existing_names: HashSet<_> =
list.projects.into_iter().map(|p| p.name.into()).collect();
let name = make_project_name(&template);
let name = name.unwrap_or_else(|| make_project_name(&template));
let name = choose_unique_project_name(&existing_names, &name);
let name = ProjectName::new_unchecked(name);
let version = &enso_config::ARGS.groups.engine.options.preferred_version.value;
Expand All @@ -151,12 +114,7 @@ impl ManagingProjectAPI for Handle {
.project_manager
.create_project(&name, &template.map(|t| t.into()), &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(project_mgr, new_project_id);
self.current_project.set(Some(new_project.await?));
self.notifications.notify(Notification::NewProjectCreated);
Ok(())
self.open_project(create_result.project_id).await
}
.boxed_local()
}
Expand Down
33 changes: 0 additions & 33 deletions app/gui/src/controller/searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -679,33 +679,6 @@ impl Searcher {
Mode::NewNode { .. } => self.add_example(&example).map(Some),
_ => Err(CannotExecuteWhenEditingNode.into()),
},
Action::ProjectManagement(action) => {
match self.ide.manage_projects() {
Ok(_) => {
let ide = self.ide.clone_ref();
executor::global::spawn(async move {
// We checked that manage_projects returns Some just a moment ago, so
// unwrapping is safe.
let manage_projects = ide.manage_projects().unwrap();
let result = match action {
action::ProjectManagement::CreateNewProject =>
manage_projects.create_new_project(None),
action::ProjectManagement::OpenProject { id, .. } =>
manage_projects.open_project(*id),
};
if let Err(err) = result.await {
error!("Error when creating new project: {err}");
}
});
Ok(None)
}
Err(err) => Err(NotSupported {
action_label: Action::ProjectManagement(action).to_string(),
reason: err,
}
.into()),
}
}
}
}

Expand Down Expand Up @@ -1001,12 +974,6 @@ impl Searcher {
let mut actions = action::ListWithSearchResultBuilder::new();
let (libraries_icon, default_icon) =
action::hardcoded::ICONS.with(|i| (i.libraries.clone_ref(), i.default.clone_ref()));
if should_add_additional_entries && self.ide.manage_projects().is_ok() {
let mut root_cat = actions.add_root_category("Projects", default_icon.clone_ref());
let category = root_cat.add_category("Projects", default_icon.clone_ref());
let create_project = action::ProjectManagement::CreateNewProject;
category.add_action(Action::ProjectManagement(create_project));
}
let mut libraries_root_cat =
actions.add_root_category("Libraries", libraries_icon.clone_ref());
if should_add_additional_entries {
Expand Down
14 changes: 0 additions & 14 deletions app/gui/src/controller/searcher/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,6 @@ impl Suggestion {
/// Action of adding example code.
pub type Example = Rc<model::suggestion_database::Example>;

/// A variants of project management actions. See also [`Action`].
#[allow(missing_docs)]
#[derive(Clone, CloneRef, Debug, Eq, PartialEq)]
pub enum ProjectManagement {
CreateNewProject,
OpenProject { id: Immutable<Uuid>, name: ImString },
}

/// A single action on the Searcher list. See also `controller::searcher::Searcher` docs.
#[derive(Clone, CloneRef, Debug, PartialEq)]
pub enum Action {
Expand All @@ -84,8 +76,6 @@ pub enum Action {
/// Add to the current module a new function with example code, and a new node in
/// current scene calling that function.
Example(Example),
/// The project management operation: creating or opening, projects.
ProjectManagement(ProjectManagement),
// In the future, other action types will be added (like module/method management, etc.).
}

Expand All @@ -101,10 +91,6 @@ impl Display for Action {
Self::Suggestion(Suggestion::Hardcoded(suggestion)) =>
Display::fmt(&suggestion.name, f),
Self::Example(example) => write!(f, "Example: {}", example.name),
Self::ProjectManagement(ProjectManagement::CreateNewProject) =>
write!(f, "New Project"),
Self::ProjectManagement(ProjectManagement::OpenProject { name, .. }) =>
Display::fmt(name, f),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion app/gui/src/ide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::prelude::*;

use crate::config::ProjectToOpen;
use crate::presenter::Presenter;

use analytics::AnonymousData;
Expand Down Expand Up @@ -90,6 +91,11 @@ impl Ide {
}
}
}

/// Open a project by name or ID. If no project with the given name exists, it will be created.
pub fn open_or_create_project(&self, project: ProjectToOpen) {
self.presenter.open_or_create_project(project)
}
}

/// A reduced version of [`Ide`] structure, representing an application which failed to initialize.
Expand All @@ -101,7 +107,6 @@ pub struct FailedIde {
pub view: ide_view::root::View,
}


/// The Path of the module initially opened after opening project in IDE.
pub fn initial_module_path(project: &model::Project) -> model::module::Path {
project.main_module_path()
Expand Down
Loading

0 comments on commit 07e7d46

Please sign in to comment.