Port recent_projects
to Zed2 (#3525)
This PR ports the `recent_projects` crate to Zed2 (`recent_projects2`). Absent from this PR is wiring up the "Recent Projects" item in the title bar. We'll come back to that soon. Release Notes: - N/A
This commit is contained in:
parent
1a747c8457
commit
a4b271e063
10 changed files with 432 additions and 5 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -1959,6 +1959,7 @@ dependencies = [
|
||||||
"postage",
|
"postage",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"project2",
|
"project2",
|
||||||
|
"recent_projects2",
|
||||||
"rich_text2",
|
"rich_text2",
|
||||||
"rpc2",
|
"rpc2",
|
||||||
"schemars",
|
"schemars",
|
||||||
|
@ -7307,6 +7308,28 @@ dependencies = [
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "recent_projects2"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"db",
|
||||||
|
"editor2",
|
||||||
|
"futures 0.3.28",
|
||||||
|
"fuzzy2",
|
||||||
|
"gpui2",
|
||||||
|
"language2",
|
||||||
|
"ordered-float 2.10.0",
|
||||||
|
"picker2",
|
||||||
|
"postage",
|
||||||
|
"settings2",
|
||||||
|
"smol",
|
||||||
|
"text2",
|
||||||
|
"theme2",
|
||||||
|
"ui2",
|
||||||
|
"util",
|
||||||
|
"workspace2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -11948,6 +11971,7 @@ dependencies = [
|
||||||
"project_panel2",
|
"project_panel2",
|
||||||
"quick_action_bar2",
|
"quick_action_bar2",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"recent_projects2",
|
||||||
"regex",
|
"regex",
|
||||||
"rope2",
|
"rope2",
|
||||||
"rpc2",
|
"rpc2",
|
||||||
|
|
|
@ -92,6 +92,7 @@ members = [
|
||||||
"crates/project_symbols",
|
"crates/project_symbols",
|
||||||
"crates/quick_action_bar2",
|
"crates/quick_action_bar2",
|
||||||
"crates/recent_projects",
|
"crates/recent_projects",
|
||||||
|
"crates/recent_projects2",
|
||||||
"crates/rope",
|
"crates/rope",
|
||||||
"crates/rpc",
|
"crates/rpc",
|
||||||
"crates/rpc2",
|
"crates/rpc2",
|
||||||
|
|
|
@ -41,7 +41,7 @@ notifications = { package = "notifications2", path = "../notifications2" }
|
||||||
rich_text = { package = "rich_text2", path = "../rich_text2" }
|
rich_text = { package = "rich_text2", path = "../rich_text2" }
|
||||||
picker = { package = "picker2", path = "../picker2" }
|
picker = { package = "picker2", path = "../picker2" }
|
||||||
project = { package = "project2", path = "../project2" }
|
project = { package = "project2", path = "../project2" }
|
||||||
# recent_projects = { path = "../recent_projects" }
|
recent_projects = { package = "recent_projects2", path = "../recent_projects2" }
|
||||||
rpc = { package ="rpc2", path = "../rpc2" }
|
rpc = { package ="rpc2", path = "../rpc2" }
|
||||||
settings = { package = "settings2", path = "../settings2" }
|
settings = { package = "settings2", path = "../settings2" }
|
||||||
feature_flags = { package = "feature_flags2", path = "../feature_flags2"}
|
feature_flags = { package = "feature_flags2", path = "../feature_flags2"}
|
||||||
|
|
31
crates/recent_projects2/Cargo.toml
Normal file
31
crates/recent_projects2/Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
[package]
|
||||||
|
name = "recent_projects2"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/recent_projects.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
db = { path = "../db" }
|
||||||
|
editor = { package = "editor2", path = "../editor2" }
|
||||||
|
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
|
||||||
|
gpui = { package = "gpui2", path = "../gpui2" }
|
||||||
|
language = { package = "language2", path = "../language2" }
|
||||||
|
picker = { package = "picker2", path = "../picker2" }
|
||||||
|
settings = { package = "settings2", path = "../settings2" }
|
||||||
|
text = { package = "text2", path = "../text2" }
|
||||||
|
util = { path = "../util"}
|
||||||
|
theme = { package = "theme2", path = "../theme2" }
|
||||||
|
ui = { package = "ui2", path = "../ui2" }
|
||||||
|
workspace = { package = "workspace2", path = "../workspace2" }
|
||||||
|
|
||||||
|
futures.workspace = true
|
||||||
|
ordered-float.workspace = true
|
||||||
|
postage.workspace = true
|
||||||
|
smol.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
|
131
crates/recent_projects2/src/highlighted_workspace_location.rs
Normal file
131
crates/recent_projects2/src/highlighted_workspace_location.rs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use fuzzy::StringMatch;
|
||||||
|
use ui::{prelude::*, HighlightedLabel};
|
||||||
|
use util::paths::PathExt;
|
||||||
|
use workspace::WorkspaceLocation;
|
||||||
|
|
||||||
|
#[derive(IntoElement)]
|
||||||
|
pub struct HighlightedText {
|
||||||
|
pub text: String,
|
||||||
|
pub highlight_positions: Vec<usize>,
|
||||||
|
char_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HighlightedText {
|
||||||
|
fn join(components: impl Iterator<Item = Self>, separator: &str) -> Self {
|
||||||
|
let mut char_count = 0;
|
||||||
|
let separator_char_count = separator.chars().count();
|
||||||
|
let mut text = String::new();
|
||||||
|
let mut highlight_positions = Vec::new();
|
||||||
|
for component in components {
|
||||||
|
if char_count != 0 {
|
||||||
|
text.push_str(separator);
|
||||||
|
char_count += separator_char_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
highlight_positions.extend(
|
||||||
|
component
|
||||||
|
.highlight_positions
|
||||||
|
.iter()
|
||||||
|
.map(|position| position + char_count),
|
||||||
|
);
|
||||||
|
text.push_str(&component.text);
|
||||||
|
char_count += component.text.chars().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
highlight_positions,
|
||||||
|
char_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderOnce for HighlightedText {
|
||||||
|
type Rendered = HighlightedLabel;
|
||||||
|
|
||||||
|
fn render(self, _cx: &mut WindowContext) -> Self::Rendered {
|
||||||
|
HighlightedLabel::new(self.text, self.highlight_positions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HighlightedWorkspaceLocation {
|
||||||
|
pub names: HighlightedText,
|
||||||
|
pub paths: Vec<HighlightedText>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HighlightedWorkspaceLocation {
|
||||||
|
pub fn new(string_match: &StringMatch, location: &WorkspaceLocation) -> Self {
|
||||||
|
let mut path_start_offset = 0;
|
||||||
|
let (names, paths): (Vec<_>, Vec<_>) = location
|
||||||
|
.paths()
|
||||||
|
.iter()
|
||||||
|
.map(|path| {
|
||||||
|
let path = path.compact();
|
||||||
|
let highlighted_text = Self::highlights_for_path(
|
||||||
|
path.as_ref(),
|
||||||
|
&string_match.positions,
|
||||||
|
path_start_offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
path_start_offset += highlighted_text.1.char_count;
|
||||||
|
|
||||||
|
highlighted_text
|
||||||
|
})
|
||||||
|
.unzip();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
names: HighlightedText::join(names.into_iter().filter_map(|name| name), ", "),
|
||||||
|
paths,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the highlighted text for the name and path
|
||||||
|
fn highlights_for_path(
|
||||||
|
path: &Path,
|
||||||
|
match_positions: &Vec<usize>,
|
||||||
|
path_start_offset: usize,
|
||||||
|
) -> (Option<HighlightedText>, HighlightedText) {
|
||||||
|
let path_string = path.to_string_lossy();
|
||||||
|
let path_char_count = path_string.chars().count();
|
||||||
|
// Get the subset of match highlight positions that line up with the given path.
|
||||||
|
// Also adjusts them to start at the path start
|
||||||
|
let path_positions = match_positions
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.skip_while(|position| *position < path_start_offset)
|
||||||
|
.take_while(|position| *position < path_start_offset + path_char_count)
|
||||||
|
.map(|position| position - path_start_offset)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Again subset the highlight positions to just those that line up with the file_name
|
||||||
|
// again adjusted to the start of the file_name
|
||||||
|
let file_name_text_and_positions = path.file_name().map(|file_name| {
|
||||||
|
let text = file_name.to_string_lossy();
|
||||||
|
let char_count = text.chars().count();
|
||||||
|
let file_name_start = path_char_count - char_count;
|
||||||
|
let highlight_positions = path_positions
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.skip_while(|position| *position < file_name_start)
|
||||||
|
.take_while(|position| *position < file_name_start + char_count)
|
||||||
|
.map(|position| position - file_name_start)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
HighlightedText {
|
||||||
|
text: text.to_string(),
|
||||||
|
highlight_positions,
|
||||||
|
char_count,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
file_name_text_and_positions,
|
||||||
|
HighlightedText {
|
||||||
|
text: path_string.to_string(),
|
||||||
|
highlight_positions: path_positions,
|
||||||
|
char_count: path_char_count,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
239
crates/recent_projects2/src/recent_projects.rs
Normal file
239
crates/recent_projects2/src/recent_projects.rs
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
mod highlighted_workspace_location;
|
||||||
|
|
||||||
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
|
use gpui::{
|
||||||
|
actions, AppContext, DismissEvent, Div, EventEmitter, FocusHandle, FocusableView, Result, Task,
|
||||||
|
View, ViewContext, WeakView,
|
||||||
|
};
|
||||||
|
use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
||||||
|
use ordered_float::OrderedFloat;
|
||||||
|
use picker::{Picker, PickerDelegate};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use ui::{prelude::*, ListItem};
|
||||||
|
use util::paths::PathExt;
|
||||||
|
use workspace::{
|
||||||
|
notifications::simple_message_notification::MessageNotification, Workspace, WorkspaceLocation,
|
||||||
|
WORKSPACE_DB,
|
||||||
|
};
|
||||||
|
|
||||||
|
actions!(OpenRecent);
|
||||||
|
|
||||||
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
cx.observe_new_views(RecentProjects::register).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle(
|
||||||
|
_: &mut Workspace,
|
||||||
|
_: &OpenRecent,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<Task<Result<()>>> {
|
||||||
|
Some(cx.spawn(|workspace, mut cx| async move {
|
||||||
|
let workspace_locations: Vec<_> = cx
|
||||||
|
.background_executor()
|
||||||
|
.spawn(async {
|
||||||
|
WORKSPACE_DB
|
||||||
|
.recent_workspaces_on_disk()
|
||||||
|
.await
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, location)| location)
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
workspace.update(&mut cx, |workspace, cx| {
|
||||||
|
if !workspace_locations.is_empty() {
|
||||||
|
let weak_workspace = cx.view().downgrade();
|
||||||
|
workspace.toggle_modal(cx, |cx| {
|
||||||
|
let delegate =
|
||||||
|
RecentProjectsDelegate::new(weak_workspace, workspace_locations, true);
|
||||||
|
|
||||||
|
RecentProjects::new(delegate, cx)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
workspace.show_notification(0, cx, |cx| {
|
||||||
|
cx.build_view(|_| MessageNotification::new("No recent projects to open."))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecentProjects {
|
||||||
|
picker: View<Picker<RecentProjectsDelegate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecentProjects {
|
||||||
|
fn new(delegate: RecentProjectsDelegate, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
picker: cx.build_view(|cx| Picker::new(delegate, cx)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
|
||||||
|
workspace.register_action(|workspace, _: &OpenRecent, cx| {
|
||||||
|
let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
|
||||||
|
// TODO(Marshall): Is this how we should be handling this?
|
||||||
|
// The previous code was using `cx.add_async_action` to invoke `toggle`.
|
||||||
|
if let Some(handler) = toggle(workspace, &OpenRecent, cx) {
|
||||||
|
handler.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
recent_projects.update(cx, |recent_projects, cx| {
|
||||||
|
recent_projects
|
||||||
|
.picker
|
||||||
|
.update(cx, |picker, cx| picker.cycle_selection(cx))
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventEmitter<DismissEvent> for RecentProjects {}
|
||||||
|
|
||||||
|
impl FocusableView for RecentProjects {
|
||||||
|
fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
|
||||||
|
self.picker.focus_handle(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for RecentProjects {
|
||||||
|
type Element = Div;
|
||||||
|
|
||||||
|
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
|
||||||
|
v_stack().w_96().child(self.picker.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecentProjectsDelegate {
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
|
workspace_locations: Vec<WorkspaceLocation>,
|
||||||
|
selected_match_index: usize,
|
||||||
|
matches: Vec<StringMatch>,
|
||||||
|
render_paths: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RecentProjectsDelegate {
|
||||||
|
fn new(
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
|
workspace_locations: Vec<WorkspaceLocation>,
|
||||||
|
render_paths: bool,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
workspace,
|
||||||
|
workspace_locations,
|
||||||
|
selected_match_index: 0,
|
||||||
|
matches: Default::default(),
|
||||||
|
render_paths,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PickerDelegate for RecentProjectsDelegate {
|
||||||
|
type ListItem = ListItem;
|
||||||
|
|
||||||
|
fn placeholder_text(&self) -> Arc<str> {
|
||||||
|
"Recent Projects...".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_count(&self) -> usize {
|
||||||
|
self.matches.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn selected_index(&self) -> usize {
|
||||||
|
self.selected_match_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
self.selected_match_index = ix;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_matches(
|
||||||
|
&mut self,
|
||||||
|
query: String,
|
||||||
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> gpui::Task<()> {
|
||||||
|
let query = query.trim_start();
|
||||||
|
let smart_case = query.chars().any(|c| c.is_uppercase());
|
||||||
|
let candidates = self
|
||||||
|
.workspace_locations
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(id, location)| {
|
||||||
|
let combined_string = location
|
||||||
|
.paths()
|
||||||
|
.iter()
|
||||||
|
.map(|path| path.compact().to_string_lossy().into_owned())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("");
|
||||||
|
StringMatchCandidate::new(id, combined_string)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.matches = smol::block_on(fuzzy::match_strings(
|
||||||
|
candidates.as_slice(),
|
||||||
|
query,
|
||||||
|
smart_case,
|
||||||
|
100,
|
||||||
|
&Default::default(),
|
||||||
|
cx.background_executor().clone(),
|
||||||
|
));
|
||||||
|
self.matches.sort_unstable_by_key(|m| m.candidate_id);
|
||||||
|
|
||||||
|
self.selected_match_index = self
|
||||||
|
.matches
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.rev()
|
||||||
|
.max_by_key(|(_, m)| OrderedFloat(m.score))
|
||||||
|
.map(|(ix, _)| ix)
|
||||||
|
.unwrap_or(0);
|
||||||
|
Task::ready(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn confirm(&mut self, _: bool, cx: &mut ViewContext<Picker<Self>>) {
|
||||||
|
if let Some((selected_match, workspace)) = self
|
||||||
|
.matches
|
||||||
|
.get(self.selected_index())
|
||||||
|
.zip(self.workspace.upgrade())
|
||||||
|
{
|
||||||
|
let workspace_location = &self.workspace_locations[selected_match.candidate_id];
|
||||||
|
workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
workspace
|
||||||
|
.open_workspace_for_paths(workspace_location.paths().as_ref().clone(), cx)
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
self.dismissed(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
|
||||||
|
|
||||||
|
fn render_match(
|
||||||
|
&self,
|
||||||
|
ix: usize,
|
||||||
|
selected: bool,
|
||||||
|
_cx: &mut ViewContext<Picker<Self>>,
|
||||||
|
) -> Option<Self::ListItem> {
|
||||||
|
let Some(r#match) = self.matches.get(ix) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let highlighted_location = HighlightedWorkspaceLocation::new(
|
||||||
|
&r#match,
|
||||||
|
&self.workspace_locations[r#match.candidate_id],
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(
|
||||||
|
ListItem::new(ix).inset(true).selected(selected).child(
|
||||||
|
v_stack()
|
||||||
|
.child(highlighted_location.names)
|
||||||
|
.when(self.render_paths, |this| {
|
||||||
|
this.children(highlighted_location.paths)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,9 +45,10 @@ use node_runtime::NodeRuntime;
|
||||||
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
|
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
|
||||||
pub use pane::*;
|
pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
|
use persistence::DB;
|
||||||
pub use persistence::{
|
pub use persistence::{
|
||||||
model::{ItemId, SerializedWorkspace, WorkspaceLocation},
|
model::{ItemId, SerializedWorkspace, WorkspaceLocation},
|
||||||
WorkspaceDb, DB,
|
WorkspaceDb, DB as WORKSPACE_DB,
|
||||||
};
|
};
|
||||||
use postage::stream::Stream;
|
use postage::stream::Stream;
|
||||||
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
use project::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
||||||
|
|
|
@ -56,7 +56,7 @@ project = { package = "project2", path = "../project2" }
|
||||||
project_panel = { package = "project_panel2", path = "../project_panel2" }
|
project_panel = { package = "project_panel2", path = "../project_panel2" }
|
||||||
# project_symbols = { path = "../project_symbols" }
|
# project_symbols = { path = "../project_symbols" }
|
||||||
quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
|
quick_action_bar = { package = "quick_action_bar2", path = "../quick_action_bar2" }
|
||||||
# recent_projects = { path = "../recent_projects" }
|
recent_projects = { package = "recent_projects2", path = "../recent_projects2" }
|
||||||
rope = { package = "rope2", path = "../rope2"}
|
rope = { package = "rope2", path = "../rope2"}
|
||||||
rpc = { package = "rpc2", path = "../rpc2" }
|
rpc = { package = "rpc2", path = "../rpc2" }
|
||||||
settings = { package = "settings2", path = "../settings2" }
|
settings = { package = "settings2", path = "../settings2" }
|
||||||
|
|
|
@ -35,7 +35,7 @@ pub fn app_menus() -> Vec<Menu<'static>> {
|
||||||
MenuItem::action("New Window", workspace::NewWindow),
|
MenuItem::action("New Window", workspace::NewWindow),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Open…", workspace::Open),
|
MenuItem::action("Open…", workspace::Open),
|
||||||
// MenuItem::action("Open Recent...", recent_projects::OpenRecent),
|
MenuItem::action("Open Recent...", recent_projects::OpenRecent),
|
||||||
MenuItem::separator(),
|
MenuItem::separator(),
|
||||||
MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject),
|
MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject),
|
||||||
MenuItem::action("Save", workspace::Save { save_intent: None }),
|
MenuItem::action("Save", workspace::Save { save_intent: None }),
|
||||||
|
|
|
@ -200,7 +200,7 @@ fn main() {
|
||||||
auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
|
auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
|
||||||
|
|
||||||
workspace::init(app_state.clone(), cx);
|
workspace::init(app_state.clone(), cx);
|
||||||
// recent_projects::init(cx);
|
recent_projects::init(cx);
|
||||||
|
|
||||||
go_to_line::init(cx);
|
go_to_line::init(cx);
|
||||||
file_finder::init(cx);
|
file_finder::init(cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue