Compare commits

...

11 Commits

Author SHA1 Message Date
Kirill Bulatov
de71d7c0a0 zed 0.125.2 2024-03-04 20:08:08 +02:00
Jason Lee
da671d9771 Return "open in new window" as default in recent projects (#8798)
https://github.com/zed-industries/zed/assets/5518/8bbd13a7-9144-48b0-9bc8-6651725476f8

Closes https://github.com/zed-industries/zed/issues/8651

Reworks `recent_projects::OpenRecent` action with collab projects in mind:
* keep the "open in new window" behavior for corresponding menu and command entries
* use new, "reuse current window" behavior in the recent projects picker up in the toolbar

This way, old Zed behavior is not customizable, kept as original in all main use cases — so that projects shared via remote entities: a channel and a call, are never accidentally closed, breaking the sharing. 

Release Notes:

- Return "open in new window" as default in recent projects
2024-03-04 11:43:11 +02:00
Kirill Bulatov
993c001fc4 When clicking the checkbox, toggle open the LSP trace logs (#8689)
Before this change, enabling LSP trace checkbox closed the panel and
toggled the server logs on.
Now, the newly enabled trace logs are shown instead.

Release Notes:

- Improved LSP logs checkbox behavior
2024-03-02 02:04:03 +02:00
Kirill Bulatov
7e501f6c1b Add a way to change what menu::Confirm does in the recent projects modal (#8688)
Follow-up of
https://github.com/zed-industries/zed/issues/8651#issuecomment-1973411072

Zed current default is still to reuse the current window, but now it's
possible to do
```json
"alt-cmd-o": [
  "projects::OpenRecent",
  {
    "create_new_window": true
  }
]
```
and change this.

menu::Secondary confirm does the action with opposite window creation
strategy.

Release Notes:

- Improved open recent projects flexibility: settings can change whether
`menu::Confirm` opens a new window or reuses the old one
2024-03-02 00:32:59 +02:00
Kirill Bulatov
3a0f842f3d Prompt to save files on recent project selection (#8673) 2024-03-01 22:01:02 +02:00
Conrad Irwin
38564c5ab2 zed 0.125.1 2024-02-29 10:35:20 -07:00
gcp-cherry-pick-bot[bot]
023498ef53 Ensure panel and pane sizes are integral (cherry-pick #8619) (#8620)
Cherry-picked Ensure panel and pane sizes are integral (#8619)

Fixes: #8050

For some reason that we didn't investigate, if you have view caching
enabled,
and you have non-integer sized bounds, and you are right aligning
things, the
co-ordinates can differ by +/- 1px when using the cached view.

The easiest fix for now is to just not do that.

Co-Authored-By: Antonio <as-cii@zed.dev>

Release Notes:

- Fixed the pane icons flickering
([#8050](https://github.com/zed-industries/zed/issues/8050)).

Co-authored-by: Antonio <as-cii@zed.dev>

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Antonio <as-cii@zed.dev>
2024-02-29 10:03:11 -07:00
gcp-cherry-pick-bot[bot]
1f61804ae0 Revert "Introduce a new ToggleGraphicsProfiler command (#7607)" (cherry-pick #8567) (#8569)
Cherry-picked Revert "Introduce a new `ToggleGraphicsProfiler` command
(#7607)" (#8567)

This reverts commit 0cebf68306.

Although this thing is very cool, it is a top source of crashes.

Example crash:
```
Segmentation fault: 11 on thread 26
  objc_retain +16
  invocation function for block in Overlay::onCommandBufferCommit(id<MTLCommandBuffer>) +60
  MTLDispatchListApply +52
```

Release Notes:

- Removed "Toggle Graphics Profiler" as it crashes too much.

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
2024-02-28 19:06:09 -07:00
gcp-cherry-pick-bot[bot]
d229b39621 Avoid an unwrap when loading languages (cherry-pick #8562) (#8566)
Cherry-picked Avoid an unwrap when loading languages (#8562)

We couldn't reproduce the panic, but I believe it was possible when
uninstalling an extension while one if its grammars was still loading.

Release Notes:
- Fixed a crash that could happen when uninstalling a language extension
while its grammar was loading.

---------

Co-authored-by: Conrad <conrad@zed.dev>

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Conrad <conrad@zed.dev>
2024-02-28 15:51:52 -07:00
Joseph T. Lyons
ae2326c781 Fix view release notes locally (#8553)
🤦‍♂️

Release Notes:

- N/A
2024-02-28 15:50:41 -05:00
Joseph T. Lyons
c4d1855824 v0.125.x preview 2024-02-28 13:13:28 -05:00
31 changed files with 312 additions and 113 deletions

7
Cargo.lock generated
View File

@@ -7143,11 +7143,16 @@ dependencies = [
name = "recent_projects"
version = "0.1.0"
dependencies = [
"editor",
"fuzzy",
"gpui",
"language",
"menu",
"ordered-float 2.10.0",
"picker",
"project",
"serde",
"serde_json",
"smol",
"ui",
"util",
@@ -11603,7 +11608,7 @@ dependencies = [
[[package]]
name = "zed"
version = "0.125.0"
version = "0.125.2"
dependencies = [
"activity_indicator",
"anyhow",

View File

@@ -341,6 +341,13 @@
{
"context": "Workspace",
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": [
// "projects::OpenRecent",
// {
// "create_new_window": true
// }
// ]
"ctrl-alt-o": "projects::OpenRecent",
"ctrl-alt-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",

View File

@@ -383,6 +383,13 @@
{
"context": "Workspace",
"bindings": {
// Change the default action on `menu::Confirm` by setting the parameter
// "alt-cmd-o": [
// "projects::OpenRecent",
// {
// "create_new_window": true
// }
// ]
"alt-cmd-o": "projects::OpenRecent",
"alt-cmd-b": "branches::OpenRecent",
"ctrl-~": "workspace::NewTerminal",

View File

@@ -20,7 +20,7 @@ use smol::io::AsyncReadExt;
use settings::{Settings, SettingsStore};
use smol::{fs::File, process::Command};
use release_channel::{AppCommitSha, ReleaseChannel};
use release_channel::{AppCommitSha, AppVersion, ReleaseChannel};
use std::{
env::consts::{ARCH, OS},
ffi::OsString,
@@ -190,7 +190,7 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<(
fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
let release_channel = ReleaseChannel::global(cx);
let version = env!("CARGO_PKG_VERSION");
let version = AppVersion::global(cx).to_string();
let client = client::Client::global(cx).http_client();
let url = client.build_url(&format!(

View File

@@ -204,7 +204,7 @@ impl ChatPanel {
let panel = Self::new(workspace, cx);
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
panel.width = serialized_panel.width;
panel.width = serialized_panel.width.map(|r| r.round());
cx.notify();
});
}

View File

@@ -327,7 +327,7 @@ impl CollabPanel {
let panel = CollabPanel::new(workspace, cx);
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
panel.width = serialized_panel.width;
panel.width = serialized_panel.width.map(|w| w.round());
panel.collapsed_channels = serialized_panel
.collapsed_channels
.unwrap_or_else(|| Vec::new())

View File

@@ -183,7 +183,7 @@ impl NotificationPanel {
let panel = Self::new(workspace, cx);
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
panel.width = serialized_panel.width;
panel.width = serialized_panel.width.map(|w| w.round());
cx.notify();
});
}

View File

@@ -195,8 +195,8 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
fn draw(&self, scene: &Scene);
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas>;
fn set_graphics_profiler_enabled(&self, enabled: bool);
#[cfg(any(test, feature = "test-support"))]
fn as_test(&mut self) -> Option<&mut TestWindow> {

View File

@@ -396,10 +396,6 @@ impl PlatformWindow for WaylandWindow {
let inner = self.0.inner.lock();
inner.renderer.sprite_atlas().clone()
}
fn set_graphics_profiler_enabled(&self, enabled: bool) {
//todo!(linux)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]

View File

@@ -513,8 +513,4 @@ impl PlatformWindow for X11Window {
let inner = self.0.inner.lock();
inner.renderer.sprite_atlas().clone()
}
fn set_graphics_profiler_enabled(&self, enabled: bool) {
unimplemented!("linux")
}
}

View File

@@ -1060,27 +1060,6 @@ impl PlatformWindow for MacWindow {
fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
self.0.lock().renderer.sprite_atlas().clone()
}
/// Enables or disables the Metal HUD for debugging purposes. Note that this only works
/// when the app is bundled and it has the `MetalHudEnabled` key set to true in Info.plist.
fn set_graphics_profiler_enabled(&self, enabled: bool) {
let this_lock = self.0.lock();
let layer = this_lock.renderer.layer();
unsafe {
if enabled {
let hud_properties = NSDictionary::dictionaryWithObject_forKey_(
nil,
ns_string("default"),
ns_string("mode"),
);
let _: () = msg_send![layer, setDeveloperHUDProperties: hud_properties];
} else {
let _: () =
msg_send![layer, setDeveloperHUDProperties: NSDictionary::dictionary(nil)];
}
}
}
}
impl HasWindowHandle for MacWindow {

View File

@@ -251,8 +251,6 @@ impl PlatformWindow for TestWindow {
self.0.lock().sprite_atlas.clone()
}
fn set_graphics_profiler_enabled(&self, _enabled: bool) {}
fn as_test(&mut self) -> Option<&mut TestWindow> {
Some(self)
}

View File

@@ -280,7 +280,6 @@ pub struct Window {
pub(crate) focus: Option<FocusId>,
focus_enabled: bool,
pending_input: Option<PendingInput>,
graphics_profiler_enabled: bool,
}
#[derive(Default, Debug)]
@@ -474,7 +473,6 @@ impl Window {
focus: None,
focus_enabled: true,
pending_input: None,
graphics_profiler_enabled: false,
}
}
fn new_focus_listener(
@@ -1519,14 +1517,6 @@ impl<'a> WindowContext<'a> {
}
}
/// Toggle the graphics profiler to debug your application's rendering performance.
pub fn toggle_graphics_profiler(&mut self) {
self.window.graphics_profiler_enabled = !self.window.graphics_profiler_enabled;
self.window
.platform_window
.set_graphics_profiler_enabled(self.window.graphics_profiler_enabled);
}
/// Register the given handler to be invoked whenever the global of the given type
/// is updated.
pub fn observe_global<G: Global>(

View File

@@ -97,14 +97,14 @@ fn test_select_language() {
// matching file extension
assert_eq!(
registry
.language_for_file("zed/lib.rs", None)
.language_for_file("zed/lib.rs".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
Some("Rust".into())
);
assert_eq!(
registry
.language_for_file("zed/lib.mk", None)
.language_for_file("zed/lib.mk".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
Some("Make".into())
@@ -113,7 +113,7 @@ fn test_select_language() {
// matching filename
assert_eq!(
registry
.language_for_file("zed/Makefile", None)
.language_for_file("zed/Makefile".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
Some("Make".into())
@@ -122,21 +122,21 @@ fn test_select_language() {
// matching suffix that is not the full file extension or filename
assert_eq!(
registry
.language_for_file("zed/cars", None)
.language_for_file("zed/cars".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
None
);
assert_eq!(
registry
.language_for_file("zed/a.cars", None)
.language_for_file("zed/a.cars".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
None
);
assert_eq!(
registry
.language_for_file("zed/sumk", None)
.language_for_file("zed/sumk".as_ref(), None)
.now_or_never()
.and_then(|l| Some(l.ok()?.name())),
None

View File

@@ -1522,16 +1522,16 @@ mod tests {
});
languages
.language_for_file("the/script", None)
.language_for_file("the/script".as_ref(), None)
.await
.unwrap_err();
languages
.language_for_file("the/script", Some(&"nothing".into()))
.language_for_file("the/script".as_ref(), Some(&"nothing".into()))
.await
.unwrap_err();
assert_eq!(
languages
.language_for_file("the/script", Some(&"#!/bin/env node".into()))
.language_for_file("the/script".as_ref(), Some(&"#!/bin/env node".into()))
.await
.unwrap()
.name()

View File

@@ -7,7 +7,7 @@ use collections::{hash_map, HashMap};
use futures::{
channel::{mpsc, oneshot},
future::Shared,
FutureExt as _, TryFutureExt as _,
Future, FutureExt as _, TryFutureExt as _,
};
use gpui::{AppContext, AsyncAppContext, BackgroundExecutor, Task};
use lsp::{LanguageServerBinary, LanguageServerId};
@@ -24,7 +24,7 @@ use sum_tree::Bias;
use text::{Point, Rope};
use theme::Theme;
use unicase::UniCase;
use util::{paths::PathExt, post_inc, ResultExt, TryFutureExt as _, UnwrapFuture};
use util::{paths::PathExt, post_inc, ResultExt};
pub struct LanguageRegistry {
state: RwLock<LanguageRegistryState>,
@@ -291,35 +291,36 @@ impl LanguageRegistry {
pub fn language_for_name(
self: &Arc<Self>,
name: &str,
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
) -> impl Future<Output = Result<Arc<Language>>> {
let name = UniCase::new(name);
self.get_or_load_language(|language_name, _| UniCase::new(language_name) == name)
let rx = self.get_or_load_language(|language_name, _| UniCase::new(language_name) == name);
async move { rx.await? }
}
pub fn language_for_name_or_extension(
self: &Arc<Self>,
string: &str,
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
) -> impl Future<Output = Result<Arc<Language>>> {
let string = UniCase::new(string);
self.get_or_load_language(|name, config| {
let rx = self.get_or_load_language(|name, config| {
UniCase::new(name) == string
|| config
.path_suffixes
.iter()
.any(|suffix| UniCase::new(suffix) == string)
})
});
async move { rx.await? }
}
pub fn language_for_file(
self: &Arc<Self>,
path: impl AsRef<Path>,
path: &Path,
content: Option<&Rope>,
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
let path = path.as_ref();
) -> impl Future<Output = Result<Arc<Language>>> {
let filename = path.file_name().and_then(|name| name.to_str());
let extension = path.extension_or_hidden_file_name();
let path_suffixes = [extension, filename];
self.get_or_load_language(|_, config| {
let rx = self.get_or_load_language(move |_, config| {
let path_matches = config
.path_suffixes
.iter()
@@ -334,13 +335,14 @@ impl LanguageRegistry {
},
);
path_matches || content_matches
})
});
async move { rx.await? }
}
fn get_or_load_language(
self: &Arc<Self>,
callback: impl Fn(&str, &LanguageMatcher) -> bool,
) -> UnwrapFuture<oneshot::Receiver<Result<Arc<Language>>>> {
) -> oneshot::Receiver<Result<Arc<Language>>> {
let (tx, rx) = oneshot::channel();
let mut state = self.state.write();
@@ -421,13 +423,13 @@ impl LanguageRegistry {
let _ = tx.send(Err(anyhow!("executor does not exist")));
}
rx.unwrap()
rx
}
fn get_or_load_grammar(
self: &Arc<Self>,
name: Arc<str>,
) -> UnwrapFuture<oneshot::Receiver<Result<tree_sitter::Language>>> {
) -> impl Future<Output = Result<tree_sitter::Language>> {
let (tx, rx) = oneshot::channel();
let mut state = self.state.write();
@@ -483,7 +485,7 @@ impl LanguageRegistry {
tx.send(Err(anyhow!("no such grammar {}", name))).ok();
}
rx.unwrap()
async move { rx.await? }
}
pub fn to_vec(&self) -> Vec<Arc<Language>> {

View File

@@ -823,7 +823,7 @@ impl Render for LspLogToolbarItemView {
selection,
Selection::Selected
);
view.toggle_logging_for_server(
view.toggle_rpc_logging_for_server(
row.server_id,
enabled,
cx,
@@ -887,7 +887,7 @@ impl LspLogToolbarItemView {
}
}
fn toggle_logging_for_server(
fn toggle_rpc_logging_for_server(
&mut self,
id: LanguageServerId,
enabled: bool,
@@ -899,6 +899,9 @@ impl LspLogToolbarItemView {
if !enabled && Some(id) == log_view.current_server_id {
log_view.show_logs_for_server(id, cx);
cx.notify();
} else if enabled {
log_view.show_rpc_trace_for_server(id, cx);
cx.notify();
}
cx.focus(&log_view.focus_handle);
});

View File

@@ -338,7 +338,7 @@ impl ProjectPanel {
let panel = ProjectPanel::new(workspace, cx);
if let Some(serialized_panel) = serialized_panel {
panel.update(cx, |panel, cx| {
panel.width = serialized_panel.width;
panel.width = serialized_panel.width.map(|px| px.round());
cx.notify();
});
}

View File

@@ -15,7 +15,15 @@ gpui.workspace = true
menu.workspace = true
ordered-float.workspace = true
picker.workspace = true
serde.workspace = true
smol.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
[dev-dependencies]
editor = { workspace = true, features = ["test-support"] }
language = { workspace = true, features = ["test-support"] }
project = { workspace = true, features = ["test-support"] }
serde_json.workspace = true
workspace = { workspace = true, features = ["test-support"] }

View File

@@ -8,12 +8,23 @@ use gpui::{
use highlighted_workspace_location::HighlightedWorkspaceLocation;
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
use serde::Deserialize;
use std::sync::Arc;
use ui::{prelude::*, tooltip_container, HighlightedLabel, ListItem, ListItemSpacing, Tooltip};
use util::paths::PathExt;
use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB};
gpui::actions!(projects, [OpenRecent]);
#[derive(PartialEq, Clone, Deserialize, Default)]
pub struct OpenRecent {
#[serde(default = "default_create_new_window")]
pub create_new_window: bool,
}
fn default_create_new_window() -> bool {
true
}
gpui::impl_actions!(projects, [OpenRecent]);
pub fn init(cx: &mut AppContext) {
cx.observe_new_views(RecentProjects::register).detach();
@@ -63,9 +74,9 @@ impl RecentProjects {
}
fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
workspace.register_action(|workspace, _: &OpenRecent, cx| {
workspace.register_action(|workspace, open_recent: &OpenRecent, cx| {
let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
if let Some(handler) = Self::open(workspace, cx) {
if let Some(handler) = Self::open(workspace, open_recent.create_new_window, cx) {
handler.detach_and_log_err(cx);
}
return;
@@ -79,12 +90,17 @@ impl RecentProjects {
});
}
fn open(_: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Option<Task<Result<()>>> {
fn open(
_: &mut Workspace,
create_new_window: bool,
cx: &mut ViewContext<Workspace>,
) -> Option<Task<Result<()>>> {
Some(cx.spawn(|workspace, mut cx| async move {
workspace.update(&mut cx, |workspace, cx| {
let weak_workspace = cx.view().downgrade();
workspace.toggle_modal(cx, |cx| {
let delegate = RecentProjectsDelegate::new(weak_workspace, true);
let delegate =
RecentProjectsDelegate::new(weak_workspace, create_new_window, true);
let modal = Self::new(delegate, 34., cx);
modal
@@ -95,7 +111,13 @@ impl RecentProjects {
}
pub fn open_popover(workspace: WeakView<Workspace>, cx: &mut WindowContext<'_>) -> View<Self> {
cx.new_view(|cx| Self::new(RecentProjectsDelegate::new(workspace, false), 20., cx))
cx.new_view(|cx| {
Self::new(
RecentProjectsDelegate::new(workspace, false, false),
20.,
cx,
)
})
}
}
@@ -126,17 +148,19 @@ pub struct RecentProjectsDelegate {
selected_match_index: usize,
matches: Vec<StringMatch>,
render_paths: bool,
create_new_window: bool,
// Flag to reset index when there is a new query vs not reset index when user delete an item
reset_selected_match_index: bool,
}
impl RecentProjectsDelegate {
fn new(workspace: WeakView<Workspace>, render_paths: bool) -> Self {
fn new(workspace: WeakView<Workspace>, create_new_window: bool, render_paths: bool) -> Self {
Self {
workspace,
workspaces: vec![],
selected_match_index: 0,
matches: Default::default(),
create_new_window,
render_paths,
reset_selected_match_index: true,
}
@@ -147,10 +171,19 @@ impl PickerDelegate for RecentProjectsDelegate {
type ListItem = ListItem;
fn placeholder_text(&self, cx: &mut WindowContext) -> Arc<str> {
let (create_window, reuse_window) = if self.create_new_window {
(
cx.keystroke_text_for(&menu::Confirm),
cx.keystroke_text_for(&menu::SecondaryConfirm),
)
} else {
(
cx.keystroke_text_for(&menu::SecondaryConfirm),
cx.keystroke_text_for(&menu::Confirm),
)
};
Arc::from(format!(
"{} reuses the window, {} opens a new one",
cx.keystroke_text_for(&menu::Confirm),
cx.keystroke_text_for(&menu::SecondaryConfirm),
"{reuse_window} reuses the window, {create_window} opens a new one",
))
}
@@ -219,15 +252,39 @@ impl PickerDelegate for RecentProjectsDelegate {
{
let (candidate_workspace_id, candidate_workspace_location) =
&self.workspaces[selected_match.candidate_id];
let replace_current_window = !secondary;
let replace_current_window = if self.create_new_window {
secondary
} else {
!secondary
};
workspace
.update(cx, |workspace, cx| {
if workspace.database_id() != *candidate_workspace_id {
workspace.open_workspace_for_paths(
replace_current_window,
candidate_workspace_location.paths().as_ref().clone(),
cx,
)
let candidate_paths = candidate_workspace_location.paths().as_ref().clone();
if replace_current_window {
cx.spawn(move |workspace, mut cx| async move {
let continue_replacing = workspace
.update(&mut cx, |workspace, cx| {
workspace.prepare_to_close(true, cx)
})?
.await?;
if continue_replacing {
workspace
.update(&mut cx, |workspace, cx| {
workspace.open_workspace_for_paths(
true,
candidate_paths,
cx,
)
})?
.await
} else {
Ok(())
}
})
} else {
workspace.open_workspace_for_paths(false, candidate_paths, cx)
}
} else {
Task::ready(Ok(()))
}
@@ -360,3 +417,141 @@ impl Render for MatchTooltip {
})
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use editor::Editor;
use gpui::{TestAppContext, WindowHandle};
use project::Project;
use serde_json::json;
use workspace::{open_paths, AppState};
use super::*;
#[gpui::test]
async fn test_prompts_on_dirty_before_submit(cx: &mut TestAppContext) {
let app_state = init_test(cx);
app_state
.fs
.as_fake()
.insert_tree(
"/dir",
json!({
"main.ts": "a"
}),
)
.await;
cx.update(|cx| open_paths(&[PathBuf::from("/dir/main.ts")], &app_state, None, cx))
.await
.unwrap();
assert_eq!(cx.update(|cx| cx.windows().len()), 1);
let workspace = cx.update(|cx| cx.windows()[0].downcast::<Workspace>().unwrap());
workspace
.update(cx, |workspace, _| assert!(!workspace.is_edited()))
.unwrap();
let editor = workspace
.read_with(cx, |workspace, cx| {
workspace
.active_item(cx)
.unwrap()
.downcast::<Editor>()
.unwrap()
})
.unwrap();
workspace
.update(cx, |_, cx| {
editor.update(cx, |editor, cx| editor.insert("EDIT", cx));
})
.unwrap();
workspace
.update(cx, |workspace, _| assert!(workspace.is_edited(), "After inserting more text into the editor without saving, we should have a dirty project"))
.unwrap();
let recent_projects_picker = open_recent_projects(&workspace, cx);
workspace
.update(cx, |_, cx| {
recent_projects_picker.update(cx, |picker, cx| {
assert_eq!(picker.query(cx), "");
let delegate = &mut picker.delegate;
delegate.matches = vec![StringMatch {
candidate_id: 0,
score: 1.0,
positions: Vec::new(),
string: "fake candidate".to_string(),
}];
delegate.workspaces = vec![(0, WorkspaceLocation::new(vec!["/test/path/"]))];
});
})
.unwrap();
assert!(
!cx.has_pending_prompt(),
"Should have no pending prompt on dirty project before opening the new recent project"
);
cx.dispatch_action((*workspace).into(), menu::Confirm);
workspace
.update(cx, |workspace, cx| {
assert!(
workspace.active_modal::<RecentProjects>(cx).is_none(),
"Should remove the modal after selecting new recent project"
)
})
.unwrap();
assert!(
cx.has_pending_prompt(),
"Dirty workspace should prompt before opening the new recent project"
);
// Cancel
cx.simulate_prompt_answer(0);
assert!(
!cx.has_pending_prompt(),
"Should have no pending prompt after cancelling"
);
workspace
.update(cx, |workspace, _| {
assert!(
workspace.is_edited(),
"Should be in the same dirty project after cancelling"
)
})
.unwrap();
}
fn open_recent_projects(
workspace: &WindowHandle<Workspace>,
cx: &mut TestAppContext,
) -> View<Picker<RecentProjectsDelegate>> {
cx.dispatch_action(
(*workspace).into(),
OpenRecent {
create_new_window: false,
},
);
workspace
.update(cx, |workspace, cx| {
workspace
.active_modal::<RecentProjects>(cx)
.unwrap()
.read(cx)
.picker
.clone()
})
.unwrap()
}
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
cx.update(|cx| {
let state = AppState::test(cx);
language::init(cx);
crate::init(cx);
editor::init(cx);
workspace::init_settings(cx);
Project::init_settings(cx);
state
})
}
}

View File

@@ -315,10 +315,13 @@ impl Peer {
"incoming response: requester resumed"
);
} else {
let message_type = proto::build_typed_envelope(connection_id, incoming)
.map(|p| p.payload_type_name());
tracing::warn!(
%connection_id,
message_id,
responding_to,
message_type,
"incoming response: unknown request"
);
}

View File

@@ -192,8 +192,8 @@ impl TerminalPanel {
let items = if let Some(serialized_panel) = serialized_panel.as_ref() {
panel.update(cx, |panel, cx| {
cx.notify();
panel.height = serialized_panel.height;
panel.width = serialized_panel.width;
panel.height = serialized_panel.height.map(|h| h.round());
panel.width = serialized_panel.width.map(|w| w.round());
panel.pane.update(cx, |_, cx| {
serialized_panel
.items

View File

@@ -522,7 +522,7 @@ impl Dock {
pub fn resize_active_panel(&mut self, size: Option<Pixels>, cx: &mut ViewContext<Self>) {
if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) {
let size = size.map(|size| size.max(RESIZE_HANDLE_SIZE));
let size = size.map(|size| size.max(RESIZE_HANDLE_SIZE).round());
entry.panel.set_size(size, cx);
cx.notify();
}

View File

@@ -883,7 +883,8 @@ mod element {
let child_size = bounds
.size
.apply_along(self.axis, |_| space_per_flex * child_flex);
.apply_along(self.axis, |_| space_per_flex * child_flex)
.map(|d| d.round());
let child_bounds = Bounds {
origin,

View File

@@ -22,6 +22,16 @@ impl WorkspaceLocation {
pub fn paths(&self) -> Arc<Vec<PathBuf>> {
self.0.clone()
}
#[cfg(any(test, feature = "test-support"))]
pub fn new<P: AsRef<Path>>(paths: Vec<P>) -> Self {
Self(Arc::new(
paths
.into_iter()
.map(|p| p.as_ref().to_path_buf())
.collect(),
))
}
}
impl<P: AsRef<Path>, T: IntoIterator<Item = P>> From<T> for WorkspaceLocation {

View File

@@ -121,7 +121,6 @@ actions!(
ToggleRightDock,
ToggleBottomDock,
CloseAllDocks,
ToggleGraphicsProfiler,
]
);
@@ -3572,7 +3571,6 @@ impl Workspace {
workspace.reopen_closed_item(cx).detach();
}),
)
.on_action(|_: &ToggleGraphicsProfiler, cx| cx.toggle_graphics_profiler())
}
#[cfg(any(test, feature = "test-support"))]

View File

@@ -2,7 +2,7 @@
description = "The fast, collaborative code editor."
edition = "2021"
name = "zed"
version = "0.125.0"
version = "0.125.2"
publish = false
license = "GPL-3.0-or-later"

View File

@@ -1 +1 @@
dev
preview

View File

@@ -22,5 +22,3 @@
<string>An application in Zed wants to use speech recognition.</string>
<key>NSRemindersUsageDescription</key>
<string>An application in Zed wants to use your reminders.</string>
<key>MetalHudEnabled</key>
<true />

View File

@@ -37,7 +37,12 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("New Window", workspace::NewWindow),
MenuItem::separator(),
MenuItem::action("Open…", workspace::Open),
MenuItem::action("Open Recent...", recent_projects::OpenRecent),
MenuItem::action(
"Open Recent...",
recent_projects::OpenRecent {
create_new_window: true,
},
),
MenuItem::separator(),
MenuItem::action("Add Folder to Project…", workspace::AddFolderToProject),
MenuItem::action("Save", workspace::Save { save_intent: None }),
@@ -156,10 +161,6 @@ pub fn app_menus() -> Vec<Menu<'static>> {
MenuItem::action("View Telemetry", crate::OpenTelemetryLog),
MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
MenuItem::action("Show Welcome", workspace::Welcome),
MenuItem::action(
"Toggle Graphics Profiler",
workspace::ToggleGraphicsProfiler,
),
MenuItem::separator(),
MenuItem::action(
"Documentation",

View File

@@ -2751,18 +2751,20 @@ mod tests {
}
#[gpui::test]
fn test_bundled_languages(cx: &mut AppContext) {
let settings = SettingsStore::test(cx);
async fn test_bundled_languages(cx: &mut TestAppContext) {
let settings = cx.update(|cx| SettingsStore::test(cx));
cx.set_global(settings);
let mut languages = LanguageRegistry::test();
languages.set_executor(cx.background_executor().clone());
languages.set_executor(cx.executor().clone());
let languages = Arc::new(languages);
let node_runtime = node_runtime::FakeNodeRuntime::new();
languages::init(languages.clone(), node_runtime, cx);
cx.update(|cx| {
languages::init(languages.clone(), node_runtime, cx);
});
for name in languages.language_names() {
languages.language_for_name(&name);
languages.language_for_name(&name).await.unwrap();
}
cx.background_executor().run_until_parked();
cx.run_until_parked();
}
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {