Detailed changes
@@ -581,13 +581,11 @@ impl Item for AgentDiffPane {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
- Self::new(self.thread.clone(), self.workspace.clone(), window, cx)
- })))
+ Some(cx.new(|cx| Self::new(self.thread.clone(), self.workspace.clone(), window, cx)))
}
fn is_dirty(&self, cx: &App) -> bool {
@@ -776,30 +776,26 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
.unwrap();
// Clients A and B follow each other in split panes
- workspace_a
- .update_in(cx_a, |workspace, window, cx| {
- workspace.split_and_clone(
- workspace.active_pane().clone(),
- SplitDirection::Right,
- window,
- cx,
- )
- })
- .await;
+ workspace_a.update_in(cx_a, |workspace, window, cx| {
+ workspace.split_and_clone(
+ workspace.active_pane().clone(),
+ SplitDirection::Right,
+ window,
+ cx,
+ );
+ });
workspace_a.update_in(cx_a, |workspace, window, cx| {
workspace.follow(client_b.peer_id().unwrap(), window, cx)
});
executor.run_until_parked();
- workspace_b
- .update_in(cx_b, |workspace, window, cx| {
- workspace.split_and_clone(
- workspace.active_pane().clone(),
- SplitDirection::Right,
- window,
- cx,
- )
- })
- .await;
+ workspace_b.update_in(cx_b, |workspace, window, cx| {
+ workspace.split_and_clone(
+ workspace.active_pane().clone(),
+ SplitDirection::Right,
+ window,
+ cx,
+ );
+ });
workspace_b.update_in(cx_b, |workspace, window, cx| {
workspace.follow(client_a.peer_id().unwrap(), window, cx)
});
@@ -1373,11 +1369,9 @@ async fn test_auto_unfollowing(cx_a: &mut TestAppContext, cx_b: &mut TestAppCont
);
// When client B activates a different pane, it continues following client A in the original pane.
- workspace_b
- .update_in(cx_b, |workspace, window, cx| {
- workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, window, cx)
- })
- .await;
+ workspace_b.update_in(cx_b, |workspace, window, cx| {
+ workspace.split_and_clone(pane_b.clone(), SplitDirection::Right, window, cx)
+ });
assert_eq!(
workspace_b.update(cx_b, |workspace, _| workspace.leader_for_pane(&pane_b)),
Some(leader_id.into())
@@ -6748,7 +6748,7 @@ async fn test_preview_tabs(cx: &mut TestAppContext) {
pane.update(cx, |pane, cx| {
pane.split(workspace::SplitDirection::Right, cx);
});
- cx.run_until_parked();
+
let right_pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
pane.update(cx, |pane, cx| {
@@ -498,8 +498,8 @@ impl Item for ChannelView {
_: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>> {
- Task::ready(Some(cx.new(|cx| {
+ ) -> Option<Entity<Self>> {
+ Some(cx.new(|cx| {
Self::new(
self.project.clone(),
self.workspace.clone(),
@@ -508,7 +508,7 @@ impl Item for ChannelView {
window,
cx,
)
- })))
+ }))
}
fn navigate(
@@ -693,11 +693,11 @@ impl Item for BufferDiagnosticsEditor {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
+ Some(cx.new(|cx| {
BufferDiagnosticsEditor::new(
self.project_path.clone(),
self.project.clone(),
@@ -706,7 +706,7 @@ impl Item for BufferDiagnosticsEditor {
window,
cx,
)
- })))
+ }))
}
fn deactivated(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -732,11 +732,11 @@ impl Item for ProjectDiagnosticsEditor {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
+ Some(cx.new(|cx| {
ProjectDiagnosticsEditor::new(
self.include_warnings,
self.project.clone(),
@@ -744,7 +744,7 @@ impl Item for ProjectDiagnosticsEditor {
window,
cx,
)
- })))
+ }))
}
fn is_dirty(&self, cx: &App) -> bool {
@@ -762,11 +762,11 @@ impl Item for Editor {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Editor>>>
+ ) -> Option<Entity<Editor>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| self.clone(window, cx))))
+ Some(cx.new(|cx| self.clone(window, cx)))
}
fn set_nav_history(
@@ -4,8 +4,8 @@ use editor::{Editor, EditorEvent, MultiBuffer, SelectionEffects, multibuffer_con
use git::repository::{CommitDetails, CommitDiff, RepoPath};
use gpui::{
Action, AnyElement, AnyView, App, AppContext as _, AsyncApp, AsyncWindowContext, Context,
- Entity, EventEmitter, FocusHandle, Focusable, IntoElement, PromptLevel, Render, Task,
- WeakEntity, Window, actions,
+ Entity, EventEmitter, FocusHandle, Focusable, IntoElement, PromptLevel, Render, WeakEntity,
+ Window, actions,
};
use language::{
Anchor, Buffer, Capability, DiskState, File, LanguageRegistry, LineEnding, OffsetRangeExt as _,
@@ -561,11 +561,11 @@ impl Item for CommitView {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
+ Some(cx.new(|cx| {
let editor = cx.new(|cx| {
self.editor
.update(cx, |editor, cx| editor.clone(window, cx))
@@ -577,7 +577,7 @@ impl Item for CommitView {
commit: self.commit.clone(),
stash: self.stash,
}
- })))
+ }))
}
}
@@ -640,16 +640,12 @@ impl Item for ProjectDiff {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- let Some(workspace) = self.workspace.upgrade() else {
- return Task::ready(None);
- };
- Task::ready(Some(cx.new(|cx| {
- ProjectDiff::new(self.project.clone(), workspace, window, cx)
- })))
+ let workspace = self.workspace.upgrade()?;
+ Some(cx.new(|cx| ProjectDiff::new(self.project.clone(), workspace, window, cx)))
}
fn is_dirty(&self, cx: &App) -> bool {
@@ -179,15 +179,15 @@ impl Item for ImageView {
_workspace_id: Option<WorkspaceId>,
_: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| Self {
+ Some(cx.new(|cx| Self {
image_item: self.image_item.clone(),
project: self.project.clone(),
focus_handle: cx.focus_handle(),
- })))
+ }))
}
fn has_deleted_file(&self, cx: &App) -> bool {
@@ -1,7 +1,6 @@
use gpui::{
Action, App, AppContext as _, Entity, EventEmitter, FocusHandle, Focusable,
- KeyBindingContextPredicate, KeyContext, Keystroke, MouseButton, Render, Subscription, Task,
- actions,
+ KeyBindingContextPredicate, KeyContext, Keystroke, MouseButton, Render, Subscription, actions,
};
use itertools::Itertools;
use serde_json::json;
@@ -158,11 +157,11 @@ impl Item for KeyContextView {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| KeyContextView::new(window, cx))))
+ Some(cx.new(|cx| KeyContextView::new(window, cx)))
}
}
@@ -3,7 +3,7 @@ use copilot::Copilot;
use editor::{Editor, EditorEvent, actions::MoveToEnd, scroll::Autoscroll};
use gpui::{
AnyView, App, Context, Corner, Entity, EventEmitter, FocusHandle, Focusable, IntoElement,
- ParentElement, Render, Styled, Subscription, Task, WeakEntity, Window, actions, div,
+ ParentElement, Render, Styled, Subscription, WeakEntity, Window, actions, div,
};
use itertools::Itertools;
use language::{LanguageServerId, language_settings::SoftWrap};
@@ -763,11 +763,11 @@ impl Item for LspLogView {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
+ Some(cx.new(|cx| {
let mut new_view = Self::new(self.project.clone(), self.log_store.clone(), window, cx);
if let Some(server_id) = self.current_server_id {
match self.active_entry_kind {
@@ -778,7 +778,7 @@ impl Item for LspLogView {
}
}
new_view
- })))
+ }))
}
}
@@ -3,7 +3,7 @@ use editor::{Anchor, Editor, ExcerptId, SelectionEffects, scroll::Autoscroll};
use gpui::{
App, AppContext as _, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
Hsla, InteractiveElement, IntoElement, MouseButton, MouseDownEvent, MouseMoveEvent,
- ParentElement, Render, ScrollStrategy, SharedString, Styled, Task, UniformListScrollHandle,
+ ParentElement, Render, ScrollStrategy, SharedString, Styled, UniformListScrollHandle,
WeakEntity, Window, actions, div, rems, uniform_list,
};
use language::{Buffer, OwnedSyntaxLayer};
@@ -573,17 +573,17 @@ impl Item for SyntaxTreeView {
_: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
+ Some(cx.new(|cx| {
let mut clone = Self::new(self.workspace_handle.clone(), None, window, cx);
if let Some(editor) = &self.editor {
clone.set_editor(editor.editor.clone(), window, cx)
}
clone
- })))
+ }))
}
}
@@ -383,14 +383,14 @@ impl Item for Onboarding {
_workspace_id: Option<WorkspaceId>,
_: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>> {
- Task::ready(Some(cx.new(|cx| Onboarding {
+ ) -> Option<Entity<Self>> {
+ Some(cx.new(|cx| Onboarding {
workspace: self.workspace.clone(),
user_store: self.user_store.clone(),
scroll_handle: ScrollHandle::new(),
focus_handle: cx.focus_handle(),
_settings_subscription: cx.observe_global::<SettingsStore>(move |_, cx| cx.notify()),
- })))
+ }))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {
@@ -139,146 +139,141 @@ impl Project {
.await
.unwrap_or_default();
- let builder = project
- .update(cx, move |_, cx| {
- let format_to_run = || {
- if let Some(command) = &spawn_task.command {
- let mut command: Option<Cow<str>> = shell_kind.try_quote(command);
- if let Some(command) = &mut command
- && command.starts_with('"')
- && let Some(prefix) = shell_kind.command_prefix()
- {
- *command = Cow::Owned(format!("{prefix}{command}"));
- }
-
- let args = spawn_task
- .args
- .iter()
- .filter_map(|arg| shell_kind.try_quote(&arg));
-
- command.into_iter().chain(args).join(" ")
- } else {
- // todo: this breaks for remotes to windows
- format!("exec {shell} -l")
+ project.update(cx, move |this, cx| {
+ let format_to_run = || {
+ if let Some(command) = &spawn_task.command {
+ let mut command: Option<Cow<str>> = shell_kind.try_quote(command);
+ if let Some(command) = &mut command
+ && command.starts_with('"')
+ && let Some(prefix) = shell_kind.command_prefix()
+ {
+ *command = Cow::Owned(format!("{prefix}{command}"));
}
- };
- let (shell, env) = {
- env.extend(spawn_task.env);
- match remote_client {
- Some(remote_client) => match activation_script.clone() {
- activation_script if !activation_script.is_empty() => {
- let activation_script = activation_script.join("; ");
- let to_run = format_to_run();
- let args = vec![
- "-c".to_owned(),
- format!("{activation_script}; {to_run}"),
- ];
- create_remote_shell(
- Some((
- &remote_client
- .read(cx)
- .shell()
- .unwrap_or_else(get_default_system_shell),
- &args,
- )),
- env,
- path,
- remote_client,
- cx,
- )?
- }
- _ => create_remote_shell(
- spawn_task
- .command
- .as_ref()
- .map(|command| (command, &spawn_task.args)),
+ let args = spawn_task
+ .args
+ .iter()
+ .filter_map(|arg| shell_kind.try_quote(&arg));
+
+ command.into_iter().chain(args).join(" ")
+ } else {
+ // todo: this breaks for remotes to windows
+ format!("exec {shell} -l")
+ }
+ };
+
+ let (shell, env) = {
+ env.extend(spawn_task.env);
+ match remote_client {
+ Some(remote_client) => match activation_script.clone() {
+ activation_script if !activation_script.is_empty() => {
+ let activation_script = activation_script.join("; ");
+ let to_run = format_to_run();
+ let args =
+ vec!["-c".to_owned(), format!("{activation_script}; {to_run}")];
+ create_remote_shell(
+ Some((
+ &remote_client
+ .read(cx)
+ .shell()
+ .unwrap_or_else(get_default_system_shell),
+ &args,
+ )),
env,
path,
remote_client,
cx,
- )?,
- },
- None => match activation_script.clone() {
- activation_script if !activation_script.is_empty() => {
- let separator = shell_kind.sequential_commands_separator();
- let activation_script =
- activation_script.join(&format!("{separator} "));
- let to_run = format_to_run();
-
- let mut arg =
- format!("{activation_script}{separator} {to_run}");
- if shell_kind == ShellKind::Cmd {
- // We need to put the entire command in quotes since otherwise CMD tries to execute them
- // as separate commands rather than chaining one after another.
- arg = format!("\"{arg}\"");
- }
+ )?
+ }
+ _ => create_remote_shell(
+ spawn_task
+ .command
+ .as_ref()
+ .map(|command| (command, &spawn_task.args)),
+ env,
+ path,
+ remote_client,
+ cx,
+ )?,
+ },
+ None => match activation_script.clone() {
+ activation_script if !activation_script.is_empty() => {
+ let separator = shell_kind.sequential_commands_separator();
+ let activation_script =
+ activation_script.join(&format!("{separator} "));
+ let to_run = format_to_run();
+
+ let mut arg = format!("{activation_script}{separator} {to_run}");
+ if shell_kind == ShellKind::Cmd {
+ // We need to put the entire command in quotes since otherwise CMD tries to execute them
+ // as separate commands rather than chaining one after another.
+ arg = format!("\"{arg}\"");
+ }
- let args = shell_kind.args_for_shell(false, arg);
+ let args = shell_kind.args_for_shell(false, arg);
- (
- Shell::WithArguments {
- program: shell,
- args,
- title_override: None,
- },
- env,
- )
- }
- _ => (
- if let Some(program) = spawn_task.command {
- Shell::WithArguments {
- program,
- args: spawn_task.args,
- title_override: None,
- }
- } else {
- Shell::System
+ (
+ Shell::WithArguments {
+ program: shell,
+ args,
+ title_override: None,
},
env,
- ),
- },
+ )
+ }
+ _ => (
+ if let Some(program) = spawn_task.command {
+ Shell::WithArguments {
+ program,
+ args: spawn_task.args,
+ title_override: None,
+ }
+ } else {
+ Shell::System
+ },
+ env,
+ ),
+ },
+ }
+ };
+ TerminalBuilder::new(
+ local_path.map(|path| path.to_path_buf()),
+ task_state,
+ shell,
+ env,
+ settings.cursor_shape,
+ settings.alternate_scroll,
+ settings.max_scroll_history_lines,
+ is_via_remote,
+ cx.entity_id().as_u64(),
+ Some(completion_tx),
+ cx,
+ activation_script,
+ )
+ .map(|builder| {
+ let terminal_handle = cx.new(|cx| builder.subscribe(cx));
+
+ this.terminals
+ .local_handles
+ .push(terminal_handle.downgrade());
+
+ let id = terminal_handle.entity_id();
+ cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
+ let handles = &mut project.terminals.local_handles;
+
+ if let Some(index) = handles
+ .iter()
+ .position(|terminal| terminal.entity_id() == id)
+ {
+ handles.remove(index);
+ cx.notify();
}
- };
- anyhow::Ok(TerminalBuilder::new(
- local_path.map(|path| path.to_path_buf()),
- task_state,
- shell,
- env,
- settings.cursor_shape,
- settings.alternate_scroll,
- settings.max_scroll_history_lines,
- is_via_remote,
- cx.entity_id().as_u64(),
- Some(completion_tx),
- cx,
- activation_script,
- ))
- })??
- .await?;
- project.update(cx, move |this, cx| {
- let terminal_handle = cx.new(|cx| builder.subscribe(cx));
+ })
+ .detach();
- this.terminals
- .local_handles
- .push(terminal_handle.downgrade());
-
- let id = terminal_handle.entity_id();
- cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
- let handles = &mut project.terminals.local_handles;
-
- if let Some(index) = handles
- .iter()
- .position(|terminal| terminal.entity_id() == id)
- {
- handles.remove(index);
- cx.notify();
- }
+ terminal_handle
})
- .detach();
-
- terminal_handle
- })
+ })?
})
}
@@ -359,55 +354,53 @@ impl Project {
})
.await
.unwrap_or_default();
- let builder = project
- .update(cx, move |_, cx| {
- let (shell, env) = {
- match remote_client {
- Some(remote_client) => {
- create_remote_shell(None, env, path, remote_client, cx)?
- }
- None => (settings.shell, env),
- }
- };
- anyhow::Ok(TerminalBuilder::new(
- local_path.map(|path| path.to_path_buf()),
- None,
- shell,
- env,
- settings.cursor_shape,
- settings.alternate_scroll,
- settings.max_scroll_history_lines,
- is_via_remote,
- cx.entity_id().as_u64(),
- None,
- cx,
- activation_script,
- ))
- })??
- .await?;
project.update(cx, move |this, cx| {
- let terminal_handle = cx.new(|cx| builder.subscribe(cx));
-
- this.terminals
- .local_handles
- .push(terminal_handle.downgrade());
-
- let id = terminal_handle.entity_id();
- cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
- let handles = &mut project.terminals.local_handles;
-
- if let Some(index) = handles
- .iter()
- .position(|terminal| terminal.entity_id() == id)
- {
- handles.remove(index);
- cx.notify();
+ let (shell, env) = {
+ match remote_client {
+ Some(remote_client) => {
+ create_remote_shell(None, env, path, remote_client, cx)?
+ }
+ None => (settings.shell, env),
}
- })
- .detach();
+ };
+ TerminalBuilder::new(
+ local_path.map(|path| path.to_path_buf()),
+ None,
+ shell,
+ env,
+ settings.cursor_shape,
+ settings.alternate_scroll,
+ settings.max_scroll_history_lines,
+ is_via_remote,
+ cx.entity_id().as_u64(),
+ None,
+ cx,
+ activation_script,
+ )
+ .map(|builder| {
+ let terminal_handle = cx.new(|cx| builder.subscribe(cx));
+
+ this.terminals
+ .local_handles
+ .push(terminal_handle.downgrade());
+
+ let id = terminal_handle.entity_id();
+ cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
+ let handles = &mut project.terminals.local_handles;
+
+ if let Some(index) = handles
+ .iter()
+ .position(|terminal| terminal.entity_id() == id)
+ {
+ handles.remove(index);
+ cx.notify();
+ }
+ })
+ .detach();
- terminal_handle
- })
+ terminal_handle
+ })
+ })?
})
}
@@ -416,27 +409,20 @@ impl Project {
terminal: &Entity<Terminal>,
cx: &mut Context<'_, Project>,
cwd: Option<PathBuf>,
- ) -> Task<Result<Entity<Terminal>>> {
- // We cannot clone the task's terminal, as it will effectively re-spawn the task, which might not be desirable.
- // For now, create a new shell instead.
- if terminal.read(cx).task().is_some() {
- return self.create_terminal_shell(cwd, cx);
- }
-
+ ) -> Result<Entity<Terminal>> {
let local_path = if self.is_via_remote_server() {
None
} else {
cwd
};
- let builder = terminal.read(cx).clone_builder(cx, local_path);
- cx.spawn(async |project, cx| {
- let terminal = builder.await?;
- project.update(cx, |project, cx| {
- let terminal_handle = cx.new(|cx| terminal.subscribe(cx));
+ terminal
+ .read(cx)
+ .clone_builder(cx, local_path)
+ .map(|builder| {
+ let terminal_handle = cx.new(|cx| builder.subscribe(cx));
- project
- .terminals
+ self.terminals
.local_handles
.push(terminal_handle.downgrade());
@@ -456,7 +442,6 @@ impl Project {
terminal_handle
})
- })
}
pub fn terminal_settings<'a>(
@@ -699,13 +699,11 @@ impl Item for NotebookEditor {
_workspace_id: Option<workspace::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| {
- Self::new(self.project.clone(), self.notebook_item.clone(), window, cx)
- })))
+ Some(cx.new(|cx| Self::new(self.project.clone(), self.notebook_item.clone(), window, cx)))
}
fn buffer_kind(&self, _: &App) -> workspace::item::ItemBufferKind {
@@ -572,14 +572,12 @@ impl Item for ProjectSearchView {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
let model = self.entity.update(cx, |model, cx| model.clone(cx));
- Task::ready(Some(cx.new(|cx| {
- Self::new(self.workspace.clone(), model, window, cx, None)
- })))
+ Some(cx.new(|cx| Self::new(self.workspace.clone(), model, window, cx, None)))
}
fn added_to_workspace(
@@ -3679,7 +3677,6 @@ pub mod tests {
)
})
.unwrap()
- .await
.unwrap();
assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 1);
@@ -3875,7 +3872,6 @@ pub mod tests {
)
})
.unwrap()
- .await
.unwrap();
assert_eq!(cx.update(|cx| second_pane.read(cx).items_len()), 1);
assert!(
@@ -423,233 +423,232 @@ impl TerminalBuilder {
completion_tx: Option<Sender<Option<ExitStatus>>>,
cx: &App,
activation_script: Vec<String>,
- ) -> Task<Result<TerminalBuilder>> {
- let version = release_channel::AppVersion::global(cx);
- cx.background_spawn(async move {
- // If the parent environment doesn't have a locale set
- // (As is the case when launched from a .app on MacOS),
- // and the Project doesn't have a locale set, then
- // set a fallback for our child environment to use.
- if std::env::var("LANG").is_err() {
- env.entry("LANG".to_string())
- .or_insert_with(|| "en_US.UTF-8".to_string());
- }
-
- env.insert("ZED_TERM".to_string(), "true".to_string());
- env.insert("TERM_PROGRAM".to_string(), "zed".to_string());
- env.insert("TERM".to_string(), "xterm-256color".to_string());
- env.insert("COLORTERM".to_string(), "truecolor".to_string());
- env.insert("TERM_PROGRAM_VERSION".to_string(), version.to_string());
-
- #[derive(Default)]
- struct ShellParams {
+ ) -> Result<TerminalBuilder> {
+ // If the parent environment doesn't have a locale set
+ // (As is the case when launched from a .app on MacOS),
+ // and the Project doesn't have a locale set, then
+ // set a fallback for our child environment to use.
+ if std::env::var("LANG").is_err() {
+ env.entry("LANG".to_string())
+ .or_insert_with(|| "en_US.UTF-8".to_string());
+ }
+
+ env.insert("ZED_TERM".to_string(), "true".to_string());
+ env.insert("TERM_PROGRAM".to_string(), "zed".to_string());
+ env.insert("TERM".to_string(), "xterm-256color".to_string());
+ env.insert("COLORTERM".to_string(), "truecolor".to_string());
+ env.insert(
+ "TERM_PROGRAM_VERSION".to_string(),
+ release_channel::AppVersion::global(cx).to_string(),
+ );
+
+ #[derive(Default)]
+ struct ShellParams {
+ program: String,
+ args: Option<Vec<String>>,
+ title_override: Option<SharedString>,
+ }
+
+ impl ShellParams {
+ fn new(
program: String,
args: Option<Vec<String>>,
title_override: Option<SharedString>,
+ ) -> Self {
+ log::info!("Using {program} as shell");
+ Self {
+ program,
+ args,
+ title_override,
+ }
}
+ }
- impl ShellParams {
- fn new(
- program: String,
- args: Option<Vec<String>>,
- title_override: Option<SharedString>,
- ) -> Self {
- log::debug!("Using {program} as shell");
- Self {
- program,
- args,
- title_override,
- }
+ let shell_params = match shell.clone() {
+ Shell::System => {
+ if cfg!(windows) {
+ Some(ShellParams::new(
+ util::shell::get_windows_system_shell(),
+ None,
+ None,
+ ))
+ } else {
+ None
}
}
+ Shell::Program(program) => Some(ShellParams::new(program, None, None)),
+ Shell::WithArguments {
+ program,
+ args,
+ title_override,
+ } => Some(ShellParams::new(program, Some(args), title_override)),
+ };
+ let terminal_title_override = shell_params.as_ref().and_then(|e| e.title_override.clone());
- let shell_params = match shell.clone() {
- Shell::System => {
- if cfg!(windows) {
- Some(ShellParams::new(
- util::shell::get_windows_system_shell(),
- None,
- None,
- ))
- } else {
- None
- }
- }
- Shell::Program(program) => Some(ShellParams::new(program, None, None)),
- Shell::WithArguments {
- program,
- args,
- title_override,
- } => Some(ShellParams::new(program, Some(args), title_override)),
- };
- let terminal_title_override =
- shell_params.as_ref().and_then(|e| e.title_override.clone());
+ #[cfg(windows)]
+ let shell_program = shell_params.as_ref().map(|params| {
+ use util::ResultExt;
- #[cfg(windows)]
- let shell_program = shell_params.as_ref().map(|params| {
- use util::ResultExt;
+ Self::resolve_path(¶ms.program)
+ .log_err()
+ .unwrap_or(params.program.clone())
+ });
- Self::resolve_path(¶ms.program)
- .log_err()
- .unwrap_or(params.program.clone())
+ // Note: when remoting, this shell_kind will scrutinize `ssh` or
+ // `wsl.exe` as a shell and fall back to posix or powershell based on
+ // the compilation target. This is fine right now due to the restricted
+ // way we use the return value, but would become incorrect if we
+ // supported remoting into windows.
+ let shell_kind = shell.shell_kind(cfg!(windows));
+
+ let pty_options = {
+ let alac_shell = shell_params.as_ref().map(|params| {
+ alacritty_terminal::tty::Shell::new(
+ params.program.clone(),
+ params.args.clone().unwrap_or_default(),
+ )
});
- // Note: when remoting, this shell_kind will scrutinize `ssh` or
- // `wsl.exe` as a shell and fall back to posix or powershell based on
- // the compilation target. This is fine right now due to the restricted
- // way we use the return value, but would become incorrect if we
- // supported remoting into windows.
- let shell_kind = shell.shell_kind(cfg!(windows));
-
- let pty_options = {
- let alac_shell = shell_params.as_ref().map(|params| {
- alacritty_terminal::tty::Shell::new(
- params.program.clone(),
- params.args.clone().unwrap_or_default(),
- )
- });
-
- alacritty_terminal::tty::Options {
- shell: alac_shell,
- working_directory: working_directory.clone(),
- drain_on_exit: true,
- env: env.clone().into_iter().collect(),
- // We do not want to escape arguments if we are using CMD as our shell.
- // If we do we end up with too many quotes/escaped quotes for CMD to handle.
- #[cfg(windows)]
- escape_args: shell_kind != util::shell::ShellKind::Cmd,
- }
- };
+ alacritty_terminal::tty::Options {
+ shell: alac_shell,
+ working_directory: working_directory.clone(),
+ drain_on_exit: true,
+ env: env.clone().into_iter().collect(),
+ // We do not want to escape arguments if we are using CMD as our shell.
+ // If we do we end up with too many quotes/escaped quotes for CMD to handle.
+ #[cfg(windows)]
+ escape_args: shell_kind != util::shell::ShellKind::Cmd,
+ }
+ };
- let default_cursor_style = AlacCursorStyle::from(cursor_shape);
- let scrolling_history = if task.is_some() {
- // Tasks like `cargo build --all` may produce a lot of output, ergo allow maximum scrolling.
- // After the task finishes, we do not allow appending to that terminal, so small tasks output should not
- // cause excessive memory usage over time.
- MAX_SCROLL_HISTORY_LINES
- } else {
- max_scroll_history_lines
- .unwrap_or(DEFAULT_SCROLL_HISTORY_LINES)
- .min(MAX_SCROLL_HISTORY_LINES)
- };
- let config = Config {
- scrolling_history,
- default_cursor_style,
- ..Config::default()
- };
+ let default_cursor_style = AlacCursorStyle::from(cursor_shape);
+ let scrolling_history = if task.is_some() {
+ // Tasks like `cargo build --all` may produce a lot of output, ergo allow maximum scrolling.
+ // After the task finishes, we do not allow appending to that terminal, so small tasks output should not
+ // cause excessive memory usage over time.
+ MAX_SCROLL_HISTORY_LINES
+ } else {
+ max_scroll_history_lines
+ .unwrap_or(DEFAULT_SCROLL_HISTORY_LINES)
+ .min(MAX_SCROLL_HISTORY_LINES)
+ };
+ let config = Config {
+ scrolling_history,
+ default_cursor_style,
+ ..Config::default()
+ };
- //Spawn a task so the Alacritty EventLoop can communicate with us
- //TODO: Remove with a bounded sender which can be dispatched on &self
- let (events_tx, events_rx) = unbounded();
- //Set up the terminal...
- let mut term = Term::new(
- config.clone(),
- &TerminalBounds::default(),
- ZedListener(events_tx.clone()),
- );
+ //Spawn a task so the Alacritty EventLoop can communicate with us
+ //TODO: Remove with a bounded sender which can be dispatched on &self
+ let (events_tx, events_rx) = unbounded();
+ //Set up the terminal...
+ let mut term = Term::new(
+ config.clone(),
+ &TerminalBounds::default(),
+ ZedListener(events_tx.clone()),
+ );
- //Alacritty defaults to alternate scrolling being on, so we just need to turn it off.
- if let AlternateScroll::Off = alternate_scroll {
- term.unset_private_mode(PrivateMode::Named(NamedPrivateMode::AlternateScroll));
- }
+ //Alacritty defaults to alternate scrolling being on, so we just need to turn it off.
+ if let AlternateScroll::Off = alternate_scroll {
+ term.unset_private_mode(PrivateMode::Named(NamedPrivateMode::AlternateScroll));
+ }
- let term = Arc::new(FairMutex::new(term));
+ let term = Arc::new(FairMutex::new(term));
- //Setup the pty...
- let pty = match tty::new(&pty_options, TerminalBounds::default().into(), window_id) {
- Ok(pty) => pty,
- Err(error) => {
- bail!(TerminalError {
- directory: working_directory,
- program: shell_params.as_ref().map(|params| params.program.clone()),
- args: shell_params.as_ref().and_then(|params| params.args.clone()),
- title_override: terminal_title_override,
- source: error,
- });
- }
- };
+ //Setup the pty...
+ let pty = match tty::new(&pty_options, TerminalBounds::default().into(), window_id) {
+ Ok(pty) => pty,
+ Err(error) => {
+ bail!(TerminalError {
+ directory: working_directory,
+ program: shell_params.as_ref().map(|params| params.program.clone()),
+ args: shell_params.as_ref().and_then(|params| params.args.clone()),
+ title_override: terminal_title_override,
+ source: error,
+ });
+ }
+ };
- let pty_info = PtyProcessInfo::new(&pty);
+ let pty_info = PtyProcessInfo::new(&pty);
- //And connect them together
- let event_loop = EventLoop::new(
- term.clone(),
- ZedListener(events_tx),
- pty,
- pty_options.drain_on_exit,
- false,
- )
- .context("failed to create event loop")?;
+ //And connect them together
+ let event_loop = EventLoop::new(
+ term.clone(),
+ ZedListener(events_tx),
+ pty,
+ pty_options.drain_on_exit,
+ false,
+ )
+ .context("failed to create event loop")?;
- //Kick things off
- let pty_tx = event_loop.channel();
- let _io_thread = event_loop.spawn(); // DANGER
+ //Kick things off
+ let pty_tx = event_loop.channel();
+ let _io_thread = event_loop.spawn(); // DANGER
- let no_task = task.is_none();
+ let no_task = task.is_none();
- let terminal = Terminal {
- task,
- terminal_type: TerminalType::Pty {
- pty_tx: Notifier(pty_tx),
- info: pty_info,
- },
- completion_tx,
- term,
- term_config: config,
- title_override: terminal_title_override,
- events: VecDeque::with_capacity(10), //Should never get this high.
- last_content: Default::default(),
- last_mouse: None,
- matches: Vec::new(),
- selection_head: None,
- breadcrumb_text: String::new(),
- scroll_px: px(0.),
- next_link_id: 0,
- selection_phase: SelectionPhase::Ended,
- hyperlink_regex_searches: RegexSearches::new(),
- vi_mode_enabled: false,
- is_ssh_terminal,
- last_mouse_move_time: Instant::now(),
- last_hyperlink_search_position: None,
- #[cfg(windows)]
- shell_program,
- activation_script: activation_script.clone(),
- template: CopyTemplate {
- shell,
- env,
- cursor_shape,
- alternate_scroll,
- max_scroll_history_lines,
- window_id,
- },
- child_exited: None,
- };
+ let terminal = Terminal {
+ task,
+ terminal_type: TerminalType::Pty {
+ pty_tx: Notifier(pty_tx),
+ info: pty_info,
+ },
+ completion_tx,
+ term,
+ term_config: config,
+ title_override: terminal_title_override,
+ events: VecDeque::with_capacity(10), //Should never get this high.
+ last_content: Default::default(),
+ last_mouse: None,
+ matches: Vec::new(),
+ selection_head: None,
+ breadcrumb_text: String::new(),
+ scroll_px: px(0.),
+ next_link_id: 0,
+ selection_phase: SelectionPhase::Ended,
+ hyperlink_regex_searches: RegexSearches::new(),
+ vi_mode_enabled: false,
+ is_ssh_terminal,
+ last_mouse_move_time: Instant::now(),
+ last_hyperlink_search_position: None,
+ #[cfg(windows)]
+ shell_program,
+ activation_script: activation_script.clone(),
+ template: CopyTemplate {
+ shell,
+ env,
+ cursor_shape,
+ alternate_scroll,
+ max_scroll_history_lines,
+ window_id,
+ },
+ child_exited: None,
+ };
- if !activation_script.is_empty() && no_task {
- for activation_script in activation_script {
- terminal.write_to_pty(activation_script.into_bytes());
- // Simulate enter key press
- // NOTE(PowerShell): using `\r\n` will put PowerShell in a continuation mode (infamous >> character)
- // and generally mess up the rendering.
- terminal.write_to_pty(b"\x0d");
- }
- // In order to clear the screen at this point, we have two options:
- // 1. We can send a shell-specific command such as "clear" or "cls"
- // 2. We can "echo" a marker message that we will then catch when handling a Wakeup event
- // and clear the screen using `terminal.clear()` method
- // We cannot issue a `terminal.clear()` command at this point as alacritty is evented
- // and while we have sent the activation script to the pty, it will be executed asynchronously.
- // Therefore, we somehow need to wait for the activation script to finish executing before we
- // can proceed with clearing the screen.
- terminal.write_to_pty(shell_kind.clear_screen_command().as_bytes());
+ if !activation_script.is_empty() && no_task {
+ for activation_script in activation_script {
+ terminal.write_to_pty(activation_script.into_bytes());
// Simulate enter key press
+ // NOTE(PowerShell): using `\r\n` will put PowerShell in a continuation mode (infamous >> character)
+ // and generally mess up the rendering.
terminal.write_to_pty(b"\x0d");
}
+ // In order to clear the screen at this point, we have two options:
+ // 1. We can send a shell-specific command such as "clear" or "cls"
+ // 2. We can "echo" a marker message that we will then catch when handling a Wakeup event
+ // and clear the screen using `terminal.clear()` method
+ // We cannot issue a `terminal.clear()` command at this point as alacritty is evented
+ // and while we have sent the activation script to the pty, it will be executed asynchronously.
+ // Therefore, we somehow need to wait for the activation script to finish executing before we
+ // can proceed with clearing the screen.
+ terminal.write_to_pty(shell_kind.clear_screen_command().as_bytes());
+ // Simulate enter key press
+ terminal.write_to_pty(b"\x0d");
+ }
- Ok(TerminalBuilder {
- terminal,
- events_rx,
- })
+ Ok(TerminalBuilder {
+ terminal,
+ events_rx,
})
}
@@ -2154,7 +2153,7 @@ impl Terminal {
self.vi_mode_enabled
}
- pub fn clone_builder(&self, cx: &App, cwd: Option<PathBuf>) -> Task<Result<TerminalBuilder>> {
+ pub fn clone_builder(&self, cx: &App, cwd: Option<PathBuf>) -> Result<TerminalBuilder> {
let working_directory = self.working_directory().or_else(|| cwd);
TerminalBuilder::new(
working_directory,
@@ -2390,30 +2389,28 @@ mod tests {
let (completion_tx, completion_rx) = smol::channel::unbounded();
let (program, args) = ShellBuilder::new(&Shell::System, false)
.build(Some("echo".to_owned()), &["hello".to_owned()]);
- let builder = cx
- .update(|cx| {
- TerminalBuilder::new(
- None,
- None,
- task::Shell::WithArguments {
- program,
- args,
- title_override: None,
- },
- HashMap::default(),
- CursorShape::default(),
- AlternateScroll::On,
- None,
- false,
- 0,
- Some(completion_tx),
- cx,
- vec![],
- )
- })
- .await
- .unwrap();
- let terminal = cx.new(|cx| builder.subscribe(cx));
+ let terminal = cx.new(|cx| {
+ TerminalBuilder::new(
+ None,
+ None,
+ task::Shell::WithArguments {
+ program,
+ args,
+ title_override: None,
+ },
+ HashMap::default(),
+ CursorShape::default(),
+ AlternateScroll::On,
+ None,
+ false,
+ 0,
+ Some(completion_tx),
+ cx,
+ vec![],
+ )
+ .unwrap()
+ .subscribe(cx)
+ });
assert_eq!(
completion_rx.recv().await.unwrap(),
Some(ExitStatus::default())
@@ -2442,27 +2439,25 @@ mod tests {
cx.executor().allow_parking();
let (completion_tx, completion_rx) = smol::channel::unbounded();
- let builder = cx
- .update(|cx| {
- TerminalBuilder::new(
- None,
- None,
- task::Shell::System,
- HashMap::default(),
- CursorShape::default(),
- AlternateScroll::On,
- None,
- false,
- 0,
- Some(completion_tx),
- cx,
- Vec::new(),
- )
- })
- .await
- .unwrap();
// Build an empty command, which will result in a tty shell spawned.
- let terminal = cx.new(|cx| builder.subscribe(cx));
+ let terminal = cx.new(|cx| {
+ TerminalBuilder::new(
+ None,
+ None,
+ task::Shell::System,
+ HashMap::default(),
+ CursorShape::default(),
+ AlternateScroll::On,
+ None,
+ false,
+ 0,
+ Some(completion_tx),
+ cx,
+ Vec::new(),
+ )
+ .unwrap()
+ .subscribe(cx)
+ });
let (event_tx, event_rx) = smol::channel::unbounded::<Event>();
cx.update(|cx| {
@@ -2513,30 +2508,28 @@ mod tests {
let (completion_tx, completion_rx) = smol::channel::unbounded();
let (program, args) = ShellBuilder::new(&Shell::System, false)
.build(Some("asdasdasdasd".to_owned()), &["@@@@@".to_owned()]);
- let builder = cx
- .update(|cx| {
- TerminalBuilder::new(
- None,
- None,
- task::Shell::WithArguments {
- program,
- args,
- title_override: None,
- },
- HashMap::default(),
- CursorShape::default(),
- AlternateScroll::On,
- None,
- false,
- 0,
- Some(completion_tx),
- cx,
- Vec::new(),
- )
- })
- .await
- .unwrap();
- let terminal = cx.new(|cx| builder.subscribe(cx));
+ let terminal = cx.new(|cx| {
+ TerminalBuilder::new(
+ None,
+ None,
+ task::Shell::WithArguments {
+ program,
+ args,
+ title_override: None,
+ },
+ HashMap::default(),
+ CursorShape::default(),
+ AlternateScroll::On,
+ None,
+ false,
+ 0,
+ Some(completion_tx),
+ cx,
+ Vec::new(),
+ )
+ .unwrap()
+ .subscribe(cx)
+ });
let (event_tx, event_rx) = smol::channel::unbounded::<Event>();
cx.update(|cx| {
@@ -214,6 +214,14 @@ async fn deserialize_pane_group(
}
SerializedPaneGroup::Pane(serialized_pane) => {
let active = serialized_pane.active;
+ let new_items = deserialize_terminal_views(
+ workspace_id,
+ project.clone(),
+ workspace.clone(),
+ serialized_pane.children.as_slice(),
+ cx,
+ )
+ .await;
let pane = panel
.update_in(cx, |terminal_panel, window, cx| {
@@ -228,71 +236,56 @@ async fn deserialize_pane_group(
.log_err()?;
let active_item = serialized_pane.active_item;
let pinned_count = serialized_pane.pinned_count;
- let new_items = deserialize_terminal_views(
- workspace_id,
- project.clone(),
- workspace.clone(),
- serialized_pane.children.as_slice(),
- cx,
- );
- cx.spawn({
- let pane = pane.downgrade();
- async move |cx| {
- let new_items = new_items.await;
-
- let items = pane.update_in(cx, |pane, window, cx| {
- populate_pane_items(pane, new_items, active_item, window, cx);
- pane.set_pinned_count(pinned_count);
- pane.items_len()
- });
+ let terminal = pane
+ .update_in(cx, |pane, window, cx| {
+ populate_pane_items(pane, new_items, active_item, window, cx);
+ pane.set_pinned_count(pinned_count);
// Avoid blank panes in splits
- if items.is_ok_and(|items| items == 0) {
+ if pane.items_len() == 0 {
let working_directory = workspace
.update(cx, |workspace, cx| default_working_directory(workspace, cx))
.ok()
.flatten();
- let Some(terminal) = project
- .update(cx, |project, cx| {
- project.create_terminal_shell(working_directory, cx)
- })
- .log_err()
- else {
- return;
- };
-
- let terminal = terminal.await.log_err();
- pane.update_in(cx, |pane, window, cx| {
- if let Some(terminal) = terminal {
- let terminal_view = Box::new(cx.new(|cx| {
- TerminalView::new(
- terminal,
- workspace.clone(),
- Some(workspace_id),
- project.downgrade(),
- window,
- cx,
- )
- }));
- pane.add_item(terminal_view, true, false, None, window, cx);
- }
- })
- .ok();
+ let terminal = project.update(cx, |project, cx| {
+ project.create_terminal_shell(working_directory, cx)
+ });
+ Some(Some(terminal))
+ } else {
+ Some(None)
}
- }
- })
- .detach();
+ })
+ .ok()
+ .flatten()?;
+ if let Some(terminal) = terminal {
+ let terminal = terminal.await.ok()?;
+ pane.update_in(cx, |pane, window, cx| {
+ let terminal_view = Box::new(cx.new(|cx| {
+ TerminalView::new(
+ terminal,
+ workspace.clone(),
+ Some(workspace_id),
+ project.downgrade(),
+ window,
+ cx,
+ )
+ }));
+ pane.add_item(terminal_view, true, false, None, window, cx);
+ })
+ .ok()?;
+ }
Some((Member::Pane(pane.clone()), active.then_some(pane)))
}
}
}
-fn deserialize_terminal_views(
+async fn deserialize_terminal_views(
workspace_id: WorkspaceId,
project: Entity<Project>,
workspace: WeakEntity<Workspace>,
item_ids: &[u64],
cx: &mut AsyncWindowContext,
-) -> impl Future<Output = Vec<Entity<TerminalView>>> + use<> {
+) -> Vec<Entity<TerminalView>> {
+ let mut items = Vec::with_capacity(item_ids.len());
let mut deserialized_items = item_ids
.iter()
.map(|item_id| {
@@ -309,15 +302,12 @@ fn deserialize_terminal_views(
.unwrap_or_else(|e| Task::ready(Err(e.context("no window present"))))
})
.collect::<FuturesUnordered<_>>();
- async move {
- let mut items = Vec::with_capacity(deserialized_items.len());
- while let Some(item) = deserialized_items.next().await {
- if let Some(item) = item.log_err() {
- items.push(item);
- }
+ while let Some(item) = deserialized_items.next().await {
+ if let Some(item) = item.log_err() {
+ items.push(item);
}
- items
}
+ items
}
#[derive(Debug, Serialize, Deserialize)]
@@ -461,11 +461,11 @@ impl TerminalPanel {
cx.spawn_in(window, async move |panel, cx| {
let terminal = project
.update(cx, |project, cx| match terminal_view {
- Some(view) => project.clone_terminal(
+ Some(view) => Task::ready(project.clone_terminal(
&view.read(cx).terminal.clone(),
cx,
working_directory,
- ),
+ )),
None => project.create_terminal_shell(working_directory, cx),
})
.ok()?
@@ -1218,31 +1218,28 @@ impl Item for TerminalView {
workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>> {
- let Ok(terminal) = self.project.update(cx, |project, cx| {
- let cwd = project
- .active_project_directory(cx)
- .map(|it| it.to_path_buf());
- project.clone_terminal(self.terminal(), cx, cwd)
- }) else {
- return Task::ready(None);
- };
- cx.spawn_in(window, async move |this, cx| {
- let terminal = terminal.await.log_err()?;
- this.update_in(cx, |this, window, cx| {
- cx.new(|cx| {
- TerminalView::new(
- terminal,
- this.workspace.clone(),
- workspace_id,
- this.project.clone(),
- window,
- cx,
- )
- })
+ ) -> Option<Entity<Self>> {
+ let terminal = self
+ .project
+ .update(cx, |project, cx| {
+ let cwd = project
+ .active_project_directory(cx)
+ .map(|it| it.to_path_buf());
+ project.clone_terminal(self.terminal(), cx, cwd)
})
- .ok()
- })
+ .ok()?
+ .log_err()?;
+
+ Some(cx.new(|cx| {
+ TerminalView::new(
+ terminal,
+ self.workspace.clone(),
+ workspace_id,
+ self.project.clone(),
+ window,
+ cx,
+ )
+ }))
}
fn is_dirty(&self, cx: &gpui::App) -> bool {
@@ -11,9 +11,8 @@ use anyhow::Result;
use client::{Client, proto};
use futures::{StreamExt, channel::mpsc};
use gpui::{
- Action, AnyElement, AnyView, App, AppContext, Context, Entity, EntityId, EventEmitter,
- FocusHandle, Focusable, Font, HighlightStyle, Pixels, Point, Render, SharedString, Task,
- WeakEntity, Window,
+ Action, AnyElement, AnyView, App, Context, Entity, EntityId, EventEmitter, FocusHandle,
+ Focusable, Font, HighlightStyle, Pixels, Point, Render, SharedString, Task, WeakEntity, Window,
};
use project::{Project, ProjectEntryId, ProjectPath};
pub use settings::{
@@ -218,11 +217,11 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
_workspace_id: Option<WorkspaceId>,
_window: &mut Window,
_: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(None)
+ None
}
fn is_dirty(&self, _: &App) -> bool {
false
@@ -423,7 +422,7 @@ pub trait ItemHandle: 'static + Send {
workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut App,
- ) -> Task<Option<Box<dyn ItemHandle>>>;
+ ) -> Option<Box<dyn ItemHandle>>;
fn added_to_pane(
&self,
workspace: &mut Workspace,
@@ -636,12 +635,9 @@ impl<T: Item> ItemHandle for Entity<T> {
workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut App,
- ) -> Task<Option<Box<dyn ItemHandle>>> {
- let task = self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx));
- cx.background_spawn(async move {
- task.await
- .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
- })
+ ) -> Option<Box<dyn ItemHandle>> {
+ self.update(cx, |item, cx| item.clone_on_split(workspace_id, window, cx))
+ .map(|handle| Box::new(handle) as Box<dyn ItemHandle>)
}
fn added_to_pane(
@@ -1508,11 +1504,11 @@ pub mod test {
_workspace_id: Option<WorkspaceId>,
_: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| Self {
+ Some(cx.new(|cx| Self {
state: self.state.clone(),
label: self.label.clone(),
save_count: self.save_count,
@@ -1529,7 +1525,7 @@ pub mod test {
workspace_id: self.workspace_id,
focus_handle: cx.focus_handle(),
serialize: None,
- })))
+ }))
}
fn is_dirty(&self, _: &App) -> bool {
@@ -3292,18 +3292,11 @@ impl Pane {
else {
return;
};
- let task = item.clone_on_split(database_id, window, cx);
- let to_pane = to_pane.downgrade();
- cx.spawn_in(window, async move |_, cx| {
- if let Some(item) = task.await {
- to_pane
- .update_in(cx, |pane, window, cx| {
- pane.add_item(item, true, true, None, window, cx)
- })
- .ok();
- }
- })
- .detach();
+ if let Some(item) = item.clone_on_split(database_id, window, cx) {
+ to_pane.update(cx, |pane, cx| {
+ pane.add_item(item, true, true, None, window, cx);
+ })
+ }
} else {
move_item(&from_pane, &to_pane, item_id, ix, true, window, cx);
}
@@ -6,7 +6,7 @@ use call::{RemoteVideoTrack, RemoteVideoTrackView, Room};
use client::{User, proto::PeerId};
use gpui::{
AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement,
- ParentElement, Render, SharedString, Styled, Task, div,
+ ParentElement, Render, SharedString, Styled, div,
};
use std::sync::Arc;
use ui::{Icon, IconName, prelude::*};
@@ -114,14 +114,14 @@ impl Item for SharedScreen {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>> {
- Task::ready(Some(cx.new(|cx| Self {
+ ) -> Option<Entity<Self>> {
+ Some(cx.new(|cx| Self {
view: self.view.update(cx, |view, cx| view.clone(window, cx)),
peer_id: self.peer_id,
user: self.user.clone(),
nav_history: Default::default(),
focus: cx.focus_handle(),
- })))
+ }))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
@@ -1,7 +1,5 @@
#![allow(unused, dead_code)]
-use gpui::{
- AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, Hsla, Task, actions, hsla,
-};
+use gpui::{AnyElement, App, Entity, EventEmitter, FocusHandle, Focusable, Hsla, actions, hsla};
use strum::IntoEnumIterator;
use theme::all_theme_colors;
use ui::{
@@ -102,11 +100,11 @@ impl Item for ThemePreview {
_workspace_id: Option<crate::WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Self>>>
+ ) -> Option<Entity<Self>>
where
Self: Sized,
{
- Task::ready(Some(cx.new(|cx| Self::new(window, cx))))
+ Some(cx.new(|cx| Self::new(window, cx)))
}
}
@@ -3627,8 +3627,7 @@ impl Workspace {
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
window.focus(&pane.focus_handle(cx));
} else {
- self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, window, cx)
- .detach();
+ self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, window, cx);
}
}
@@ -3995,8 +3994,7 @@ impl Workspace {
clone_active_item,
} => {
if *clone_active_item {
- self.split_and_clone(pane.clone(), *direction, window, cx)
- .detach();
+ self.split_and_clone(pane.clone(), *direction, window, cx);
} else {
self.split_and_move(pane.clone(), *direction, window, cx);
}
@@ -4137,27 +4135,21 @@ impl Workspace {
direction: SplitDirection,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<Entity<Pane>>> {
- let Some(item) = pane.read(cx).active_item() else {
- return Task::ready(None);
- };
- let task = item.clone_on_split(self.database_id(), window, cx);
- cx.spawn_in(window, async move |this, cx| {
- if let Some(clone) = task.await {
- this.update_in(cx, |this, window, cx| {
- let new_pane = this.add_pane(window, cx);
- new_pane.update(cx, |pane, cx| {
- pane.add_item(clone, true, true, None, window, cx)
- });
- this.center.split(&pane, &new_pane, direction).unwrap();
- cx.notify();
- new_pane
- })
- .ok()
+ ) -> Option<Entity<Pane>> {
+ let item = pane.read(cx).active_item()?;
+ let maybe_pane_handle =
+ if let Some(clone) = item.clone_on_split(self.database_id(), window, cx) {
+ let new_pane = self.add_pane(window, cx);
+ new_pane.update(cx, |pane, cx| {
+ pane.add_item(clone, true, true, None, window, cx)
+ });
+ self.center.split(&pane, &new_pane, direction).unwrap();
+ cx.notify();
+ Some(new_pane)
} else {
None
- }
- })
+ };
+ maybe_pane_handle
}
pub fn join_all_panes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -8191,27 +8183,19 @@ pub fn clone_active_item(
let Some(active_item) = source.read(cx).active_item() else {
return;
};
- let destination = destination.downgrade();
- let task = active_item.clone_on_split(workspace_id, window, cx);
- window
- .spawn(cx, async move |cx| {
- let Some(clone) = task.await else {
- return;
- };
- destination
- .update_in(cx, |target_pane, window, cx| {
- target_pane.add_item(
- clone,
- focus_destination,
- focus_destination,
- Some(target_pane.items_len()),
- window,
- cx,
- );
- })
- .log_err();
- })
- .detach();
+ destination.update(cx, |target_pane, cx| {
+ let Some(clone) = active_item.clone_on_split(workspace_id, window, cx) else {
+ return;
+ };
+ target_pane.add_item(
+ clone,
+ focus_destination,
+ focus_destination,
+ Some(target_pane.items_len()),
+ window,
+ cx,
+ );
+ });
}
#[derive(Debug)]
@@ -8718,24 +8702,25 @@ mod tests {
cx,
);
- let right_pane =
- workspace.split_and_clone(left_pane.clone(), SplitDirection::Right, window, cx);
+ let right_pane = workspace
+ .split_and_clone(left_pane.clone(), SplitDirection::Right, window, cx)
+ .unwrap();
- let boxed_clone = single_entry_items[1].boxed_clone();
- let right_pane = window.spawn(cx, async move |cx| {
- right_pane.await.inspect(|right_pane| {
- right_pane
- .update_in(cx, |pane, window, cx| {
- pane.add_item(boxed_clone, true, true, None, window, cx);
- pane.add_item(Box::new(item_3_4.clone()), true, true, None, window, cx);
- })
- .unwrap();
- })
+ right_pane.update(cx, |pane, cx| {
+ pane.add_item(
+ single_entry_items[1].boxed_clone(),
+ true,
+ true,
+ None,
+ window,
+ cx,
+ );
+ pane.add_item(Box::new(item_3_4.clone()), true, true, None, window, cx);
});
(left_pane, right_pane)
});
- let right_pane = right_pane.await.unwrap();
+
cx.focus(&right_pane);
let mut close = right_pane.update_in(cx, |pane, window, cx| {
@@ -10552,10 +10537,7 @@ mod tests {
window,
cx,
);
- });
- cx.run_until_parked();
- workspace.update(cx, |workspace, cx| {
assert_eq!(workspace.panes.len(), 3, "Two new panes were created");
for pane in workspace.panes() {
assert_eq!(
@@ -2839,16 +2839,14 @@ mod tests {
});
// Split the pane with the first entry, then open the second entry again.
- let (task1, task2) = window
+ window
.update(cx, |w, window, cx| {
- (
- w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx),
- w.open_path(file2.clone(), None, true, window, cx),
- )
+ w.split_and_clone(w.active_pane().clone(), SplitDirection::Right, window, cx);
+ w.open_path(file2.clone(), None, true, window, cx)
})
+ .unwrap()
+ .await
.unwrap();
- task1.await.unwrap();
- task2.await.unwrap();
window
.read_with(cx, |w, cx| {
@@ -3471,13 +3469,7 @@ mod tests {
SplitDirection::Right,
window,
cx,
- )
- })
- .unwrap()
- .await
- .unwrap();
- window
- .update(cx, |workspace, window, cx| {
+ );
workspace.open_path(
(worktree.read(cx).id(), rel_path("the-new-name.rs")),
None,
@@ -720,7 +720,7 @@ impl Item for ComponentPreview {
_workspace_id: Option<WorkspaceId>,
window: &mut Window,
cx: &mut Context<Self>,
- ) -> Task<Option<gpui::Entity<Self>>>
+ ) -> Option<gpui::Entity<Self>>
where
Self: Sized,
{
@@ -742,13 +742,13 @@ impl Item for ComponentPreview {
cx,
);
- Task::ready(match self_result {
+ match self_result {
Ok(preview) => Some(cx.new(|_cx| preview)),
Err(e) => {
log::error!("Failed to clone component preview: {}", e);
None
}
- })
+ }
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(workspace::item::ItemEvent)) {