mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-24 17:28:40 +00:00
Allow pasting ZED urls in the command palette in development
This commit is contained in:
parent
2feb091961
commit
6ffbc3a0f5
10 changed files with 245 additions and 208 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1623,6 +1623,7 @@ dependencies = [
|
||||||
"theme",
|
"theme",
|
||||||
"util",
|
"util",
|
||||||
"workspace",
|
"workspace",
|
||||||
|
"zed-actions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -10213,6 +10214,7 @@ name = "zed-actions"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gpui",
|
"gpui",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -979,7 +979,7 @@ impl Database {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the channel ancestors, include itself, deepest first
|
/// Returns the channel ancestors in arbitrary order
|
||||||
pub async fn get_channel_ancestors(
|
pub async fn get_channel_ancestors(
|
||||||
&self,
|
&self,
|
||||||
channel_id: ChannelId,
|
channel_id: ChannelId,
|
||||||
|
|
|
@ -19,6 +19,7 @@ settings = { path = "../settings" }
|
||||||
util = { path = "../util" }
|
util = { path = "../util" }
|
||||||
theme = { path = "../theme" }
|
theme = { path = "../theme" }
|
||||||
workspace = { path = "../workspace" }
|
workspace = { path = "../workspace" }
|
||||||
|
zed-actions = { path = "../zed-actions" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gpui = { path = "../gpui", features = ["test-support"] }
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
|
|
|
@ -6,8 +6,12 @@ use gpui::{
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use std::cmp::{self, Reverse};
|
use std::cmp::{self, Reverse};
|
||||||
use util::ResultExt;
|
use util::{
|
||||||
|
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL, RELEASE_CHANNEL_NAME},
|
||||||
|
ResultExt,
|
||||||
|
};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
use zed_actions::OpenZedURL;
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
cx.add_action(toggle_command_palette);
|
cx.add_action(toggle_command_palette);
|
||||||
|
@ -167,13 +171,22 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
};
|
};
|
||||||
let intercept_result = cx.read(|cx| {
|
let mut intercept_result = cx.read(|cx| {
|
||||||
if cx.has_global::<CommandPaletteInterceptor>() {
|
if cx.has_global::<CommandPaletteInterceptor>() {
|
||||||
cx.global::<CommandPaletteInterceptor>()(&query, cx)
|
cx.global::<CommandPaletteInterceptor>()(&query, cx)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if *RELEASE_CHANNEL == ReleaseChannel::Dev {
|
||||||
|
if parse_zed_link(&query).is_some() {
|
||||||
|
intercept_result = Some(CommandInterceptResult {
|
||||||
|
action: OpenZedURL { url: query.clone() }.boxed_clone(),
|
||||||
|
string: query.clone(),
|
||||||
|
positions: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(CommandInterceptResult {
|
if let Some(CommandInterceptResult {
|
||||||
action,
|
action,
|
||||||
string,
|
string,
|
||||||
|
|
|
@ -288,6 +288,7 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
cx.add_global_action(restart);
|
cx.add_global_action(restart);
|
||||||
cx.add_async_action(Workspace::save_all);
|
cx.add_async_action(Workspace::save_all);
|
||||||
cx.add_action(Workspace::add_folder_to_project);
|
cx.add_action(Workspace::add_folder_to_project);
|
||||||
|
|
||||||
cx.add_action(
|
cx.add_action(
|
||||||
|workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
|
|workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext<Workspace>| {
|
||||||
let pane = workspace.active_pane().clone();
|
let pane = workspace.active_pane().clone();
|
||||||
|
|
|
@ -8,3 +8,4 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpui = { path = "../gpui" }
|
gpui = { path = "../gpui" }
|
||||||
|
serde.workspace = true
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
use gpui::actions;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui::{actions, impl_actions};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
zed,
|
zed,
|
||||||
|
@ -26,3 +29,13 @@ actions!(
|
||||||
ResetDatabase,
|
ResetDatabase,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct OpenBrowser {
|
||||||
|
pub url: Arc<str>,
|
||||||
|
}
|
||||||
|
#[derive(Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct OpenZedURL {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
impl_actions!(zed, [OpenBrowser, OpenZedURL]);
|
||||||
|
|
|
@ -3,22 +3,16 @@
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use cli::{
|
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||||
ipc::{self, IpcSender},
|
|
||||||
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
|
||||||
};
|
|
||||||
use client::{
|
use client::{
|
||||||
self, Client, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
self, Client, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN,
|
||||||
};
|
};
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
use editor::Editor;
|
||||||
use futures::{
|
use futures::StreamExt;
|
||||||
channel::{mpsc, oneshot},
|
|
||||||
FutureExt, SinkExt, StreamExt,
|
|
||||||
};
|
|
||||||
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task};
|
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task};
|
||||||
use isahc::{config::Configurable, Request};
|
use isahc::{config::Configurable, Request};
|
||||||
use language::{LanguageRegistry, Point};
|
use language::LanguageRegistry;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use node_runtime::RealNodeRuntime;
|
use node_runtime::RealNodeRuntime;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -28,7 +22,6 @@ use settings::{default_settings, handle_settings_file_changes, watch_config_file
|
||||||
use simplelog::ConfigBuilder;
|
use simplelog::ConfigBuilder;
|
||||||
use smol::process::Command;
|
use smol::process::Command;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
env,
|
env,
|
||||||
ffi::OsStr,
|
ffi::OsStr,
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
|
@ -42,11 +35,9 @@ use std::{
|
||||||
thread,
|
thread,
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
use sum_tree::Bias;
|
|
||||||
use util::{
|
use util::{
|
||||||
channel::{parse_zed_link, ReleaseChannel},
|
channel::{parse_zed_link, ReleaseChannel},
|
||||||
http::{self, HttpClient},
|
http::{self, HttpClient},
|
||||||
paths::PathLikeWithPosition,
|
|
||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use welcome::{show_welcome_experience, FIRST_OPEN};
|
use welcome::{show_welcome_experience, FIRST_OPEN};
|
||||||
|
@ -58,12 +49,9 @@ use zed::{
|
||||||
assets::Assets,
|
assets::Assets,
|
||||||
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
||||||
only_instance::{ensure_only_instance, IsOnlyInstance},
|
only_instance::{ensure_only_instance, IsOnlyInstance},
|
||||||
|
open_listener::{handle_cli_connection, OpenListener, OpenRequest},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::open_listener::{OpenListener, OpenRequest};
|
|
||||||
|
|
||||||
mod open_listener;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let http = http::client();
|
let http = http::client();
|
||||||
init_paths();
|
init_paths();
|
||||||
|
@ -113,6 +101,7 @@ fn main() {
|
||||||
|
|
||||||
app.run(move |cx| {
|
app.run(move |cx| {
|
||||||
cx.set_global(*RELEASE_CHANNEL);
|
cx.set_global(*RELEASE_CHANNEL);
|
||||||
|
cx.set_global(listener.clone());
|
||||||
|
|
||||||
let mut store = SettingsStore::default();
|
let mut store = SettingsStore::default();
|
||||||
store
|
store
|
||||||
|
@ -729,189 +718,6 @@ async fn watch_languages(_: Arc<dyn Fs>, _: Arc<LanguageRegistry>) -> Option<()>
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
fn watch_file_types(_fs: Arc<dyn Fs>, _cx: &mut AppContext) {}
|
fn watch_file_types(_fs: Arc<dyn Fs>, _cx: &mut AppContext) {}
|
||||||
|
|
||||||
fn connect_to_cli(
|
|
||||||
server_name: &str,
|
|
||||||
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
|
|
||||||
let handshake_tx = cli::ipc::IpcSender::<IpcHandshake>::connect(server_name.to_string())
|
|
||||||
.context("error connecting to cli")?;
|
|
||||||
let (request_tx, request_rx) = ipc::channel::<CliRequest>()?;
|
|
||||||
let (response_tx, response_rx) = ipc::channel::<CliResponse>()?;
|
|
||||||
|
|
||||||
handshake_tx
|
|
||||||
.send(IpcHandshake {
|
|
||||||
requests: request_tx,
|
|
||||||
responses: response_rx,
|
|
||||||
})
|
|
||||||
.context("error sending ipc handshake")?;
|
|
||||||
|
|
||||||
let (mut async_request_tx, async_request_rx) =
|
|
||||||
futures::channel::mpsc::channel::<CliRequest>(16);
|
|
||||||
thread::spawn(move || {
|
|
||||||
while let Ok(cli_request) = request_rx.recv() {
|
|
||||||
if smol::block_on(async_request_tx.send(cli_request)).is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok::<_, anyhow::Error>(())
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok((async_request_rx, response_tx))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_cli_connection(
|
|
||||||
(mut requests, responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
|
|
||||||
app_state: Arc<AppState>,
|
|
||||||
mut cx: AsyncAppContext,
|
|
||||||
) {
|
|
||||||
if let Some(request) = requests.next().await {
|
|
||||||
match request {
|
|
||||||
CliRequest::Open { paths, wait } => {
|
|
||||||
let mut caret_positions = HashMap::new();
|
|
||||||
|
|
||||||
let paths = if paths.is_empty() {
|
|
||||||
workspace::last_opened_workspace_paths()
|
|
||||||
.await
|
|
||||||
.map(|location| location.paths().to_vec())
|
|
||||||
.unwrap_or_default()
|
|
||||||
} else {
|
|
||||||
paths
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|path_with_position_string| {
|
|
||||||
let path_with_position = PathLikeWithPosition::parse_str(
|
|
||||||
&path_with_position_string,
|
|
||||||
|path_str| {
|
|
||||||
Ok::<_, std::convert::Infallible>(
|
|
||||||
Path::new(path_str).to_path_buf(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Infallible");
|
|
||||||
let path = path_with_position.path_like;
|
|
||||||
if let Some(row) = path_with_position.row {
|
|
||||||
if path.is_file() {
|
|
||||||
let row = row.saturating_sub(1);
|
|
||||||
let col =
|
|
||||||
path_with_position.column.unwrap_or(0).saturating_sub(1);
|
|
||||||
caret_positions.insert(path.clone(), Point::new(row, col));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(path)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut errored = false;
|
|
||||||
match cx
|
|
||||||
.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok((workspace, items)) => {
|
|
||||||
let mut item_release_futures = Vec::new();
|
|
||||||
|
|
||||||
for (item, path) in items.into_iter().zip(&paths) {
|
|
||||||
match item {
|
|
||||||
Some(Ok(item)) => {
|
|
||||||
if let Some(point) = caret_positions.remove(path) {
|
|
||||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
|
||||||
active_editor
|
|
||||||
.downgrade()
|
|
||||||
.update(&mut cx, |editor, cx| {
|
|
||||||
let snapshot =
|
|
||||||
editor.snapshot(cx).display_snapshot;
|
|
||||||
let point = snapshot
|
|
||||||
.buffer_snapshot
|
|
||||||
.clip_point(point, Bias::Left);
|
|
||||||
editor.change_selections(
|
|
||||||
Some(Autoscroll::center()),
|
|
||||||
cx,
|
|
||||||
|s| s.select_ranges([point..point]),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let released = oneshot::channel();
|
|
||||||
cx.update(|cx| {
|
|
||||||
item.on_release(
|
|
||||||
cx,
|
|
||||||
Box::new(move |_| {
|
|
||||||
let _ = released.0.send(());
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.detach();
|
|
||||||
});
|
|
||||||
item_release_futures.push(released.1);
|
|
||||||
}
|
|
||||||
Some(Err(err)) => {
|
|
||||||
responses
|
|
||||||
.send(CliResponse::Stderr {
|
|
||||||
message: format!("error opening {:?}: {}", path, err),
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
errored = true;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if wait {
|
|
||||||
let background = cx.background();
|
|
||||||
let wait = async move {
|
|
||||||
if paths.is_empty() {
|
|
||||||
let (done_tx, done_rx) = oneshot::channel();
|
|
||||||
if let Some(workspace) = workspace.upgrade(&cx) {
|
|
||||||
let _subscription = cx.update(|cx| {
|
|
||||||
cx.observe_release(&workspace, move |_, _| {
|
|
||||||
let _ = done_tx.send(());
|
|
||||||
})
|
|
||||||
});
|
|
||||||
drop(workspace);
|
|
||||||
let _ = done_rx.await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let _ =
|
|
||||||
futures::future::try_join_all(item_release_futures).await;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
.fuse();
|
|
||||||
futures::pin_mut!(wait);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Repeatedly check if CLI is still open to avoid wasting resources
|
|
||||||
// waiting for files or workspaces to close.
|
|
||||||
let mut timer = background.timer(Duration::from_secs(1)).fuse();
|
|
||||||
futures::select_biased! {
|
|
||||||
_ = wait => break,
|
|
||||||
_ = timer => {
|
|
||||||
if responses.send(CliResponse::Ping).is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
errored = true;
|
|
||||||
responses
|
|
||||||
.send(CliResponse::Stderr {
|
|
||||||
message: format!("error opening {:?}: {}", paths, error),
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
responses
|
|
||||||
.send(CliResponse::Exit {
|
|
||||||
status: i32::from(errored),
|
|
||||||
})
|
|
||||||
.log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
|
pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
|
||||||
&[
|
&[
|
||||||
("Go to file", &file_finder::Toggle),
|
("Go to file", &file_finder::Toggle),
|
||||||
|
|
|
@ -1,15 +1,26 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use cli::{ipc, IpcHandshake};
|
||||||
use cli::{ipc::IpcSender, CliRequest, CliResponse};
|
use cli::{ipc::IpcSender, CliRequest, CliResponse};
|
||||||
use futures::channel::mpsc;
|
use editor::scroll::autoscroll::Autoscroll;
|
||||||
|
use editor::Editor;
|
||||||
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
|
use futures::channel::{mpsc, oneshot};
|
||||||
|
use futures::{FutureExt, SinkExt, StreamExt};
|
||||||
|
use gpui::AsyncAppContext;
|
||||||
|
use language::{Bias, Point};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::os::unix::prelude::OsStrExt;
|
use std::os::unix::prelude::OsStrExt;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
use std::{path::PathBuf, sync::atomic::AtomicBool};
|
use std::{path::PathBuf, sync::atomic::AtomicBool};
|
||||||
use util::channel::parse_zed_link;
|
use util::channel::parse_zed_link;
|
||||||
|
use util::paths::PathLikeWithPosition;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
use workspace::AppState;
|
||||||
use crate::connect_to_cli;
|
|
||||||
|
|
||||||
pub enum OpenRequest {
|
pub enum OpenRequest {
|
||||||
Paths {
|
Paths {
|
||||||
|
@ -96,3 +107,186 @@ impl OpenListener {
|
||||||
Some(OpenRequest::Paths { paths })
|
Some(OpenRequest::Paths { paths })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn connect_to_cli(
|
||||||
|
server_name: &str,
|
||||||
|
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
|
||||||
|
let handshake_tx = cli::ipc::IpcSender::<IpcHandshake>::connect(server_name.to_string())
|
||||||
|
.context("error connecting to cli")?;
|
||||||
|
let (request_tx, request_rx) = ipc::channel::<CliRequest>()?;
|
||||||
|
let (response_tx, response_rx) = ipc::channel::<CliResponse>()?;
|
||||||
|
|
||||||
|
handshake_tx
|
||||||
|
.send(IpcHandshake {
|
||||||
|
requests: request_tx,
|
||||||
|
responses: response_rx,
|
||||||
|
})
|
||||||
|
.context("error sending ipc handshake")?;
|
||||||
|
|
||||||
|
let (mut async_request_tx, async_request_rx) =
|
||||||
|
futures::channel::mpsc::channel::<CliRequest>(16);
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(cli_request) = request_rx.recv() {
|
||||||
|
if smol::block_on(async_request_tx.send(cli_request)).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok((async_request_rx, response_tx))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_cli_connection(
|
||||||
|
(mut requests, responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
|
||||||
|
app_state: Arc<AppState>,
|
||||||
|
mut cx: AsyncAppContext,
|
||||||
|
) {
|
||||||
|
if let Some(request) = requests.next().await {
|
||||||
|
match request {
|
||||||
|
CliRequest::Open { paths, wait } => {
|
||||||
|
let mut caret_positions = HashMap::new();
|
||||||
|
|
||||||
|
let paths = if paths.is_empty() {
|
||||||
|
workspace::last_opened_workspace_paths()
|
||||||
|
.await
|
||||||
|
.map(|location| location.paths().to_vec())
|
||||||
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
paths
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|path_with_position_string| {
|
||||||
|
let path_with_position = PathLikeWithPosition::parse_str(
|
||||||
|
&path_with_position_string,
|
||||||
|
|path_str| {
|
||||||
|
Ok::<_, std::convert::Infallible>(
|
||||||
|
Path::new(path_str).to_path_buf(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Infallible");
|
||||||
|
let path = path_with_position.path_like;
|
||||||
|
if let Some(row) = path_with_position.row {
|
||||||
|
if path.is_file() {
|
||||||
|
let row = row.saturating_sub(1);
|
||||||
|
let col =
|
||||||
|
path_with_position.column.unwrap_or(0).saturating_sub(1);
|
||||||
|
caret_positions.insert(path.clone(), Point::new(row, col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(path)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut errored = false;
|
||||||
|
match cx
|
||||||
|
.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok((workspace, items)) => {
|
||||||
|
let mut item_release_futures = Vec::new();
|
||||||
|
|
||||||
|
for (item, path) in items.into_iter().zip(&paths) {
|
||||||
|
match item {
|
||||||
|
Some(Ok(item)) => {
|
||||||
|
if let Some(point) = caret_positions.remove(path) {
|
||||||
|
if let Some(active_editor) = item.downcast::<Editor>() {
|
||||||
|
active_editor
|
||||||
|
.downgrade()
|
||||||
|
.update(&mut cx, |editor, cx| {
|
||||||
|
let snapshot =
|
||||||
|
editor.snapshot(cx).display_snapshot;
|
||||||
|
let point = snapshot
|
||||||
|
.buffer_snapshot
|
||||||
|
.clip_point(point, Bias::Left);
|
||||||
|
editor.change_selections(
|
||||||
|
Some(Autoscroll::center()),
|
||||||
|
cx,
|
||||||
|
|s| s.select_ranges([point..point]),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let released = oneshot::channel();
|
||||||
|
cx.update(|cx| {
|
||||||
|
item.on_release(
|
||||||
|
cx,
|
||||||
|
Box::new(move |_| {
|
||||||
|
let _ = released.0.send(());
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.detach();
|
||||||
|
});
|
||||||
|
item_release_futures.push(released.1);
|
||||||
|
}
|
||||||
|
Some(Err(err)) => {
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Stderr {
|
||||||
|
message: format!("error opening {:?}: {}", path, err),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
errored = true;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wait {
|
||||||
|
let background = cx.background();
|
||||||
|
let wait = async move {
|
||||||
|
if paths.is_empty() {
|
||||||
|
let (done_tx, done_rx) = oneshot::channel();
|
||||||
|
if let Some(workspace) = workspace.upgrade(&cx) {
|
||||||
|
let _subscription = cx.update(|cx| {
|
||||||
|
cx.observe_release(&workspace, move |_, _| {
|
||||||
|
let _ = done_tx.send(());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
drop(workspace);
|
||||||
|
let _ = done_rx.await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let _ =
|
||||||
|
futures::future::try_join_all(item_release_futures).await;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
.fuse();
|
||||||
|
futures::pin_mut!(wait);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Repeatedly check if CLI is still open to avoid wasting resources
|
||||||
|
// waiting for files or workspaces to close.
|
||||||
|
let mut timer = background.timer(Duration::from_secs(1)).fuse();
|
||||||
|
futures::select_biased! {
|
||||||
|
_ = wait => break,
|
||||||
|
_ = timer => {
|
||||||
|
if responses.send(CliResponse::Ping).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
errored = true;
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Stderr {
|
||||||
|
message: format!("error opening {:?}: {}", paths, error),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Exit {
|
||||||
|
status: i32::from(errored),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ pub mod assets;
|
||||||
pub mod languages;
|
pub mod languages;
|
||||||
pub mod menus;
|
pub mod menus;
|
||||||
pub mod only_instance;
|
pub mod only_instance;
|
||||||
|
pub mod open_listener;
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub mod test;
|
pub mod test;
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ use gpui::{
|
||||||
AppContext, AsyncAppContext, Task, ViewContext, WeakViewHandle,
|
AppContext, AsyncAppContext, Task, ViewContext, WeakViewHandle,
|
||||||
};
|
};
|
||||||
pub use lsp;
|
pub use lsp;
|
||||||
|
use open_listener::OpenListener;
|
||||||
pub use project;
|
pub use project;
|
||||||
use project_panel::ProjectPanel;
|
use project_panel::ProjectPanel;
|
||||||
use quick_action_bar::QuickActionBar;
|
use quick_action_bar::QuickActionBar;
|
||||||
|
@ -87,6 +89,10 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
cx.add_global_action(quit);
|
cx.add_global_action(quit);
|
||||||
|
cx.add_global_action(move |action: &OpenZedURL, cx| {
|
||||||
|
cx.global::<Arc<OpenListener>>()
|
||||||
|
.open_urls(vec![action.url.clone()])
|
||||||
|
});
|
||||||
cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url));
|
cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url));
|
||||||
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
||||||
theme::adjust_font_size(cx, |size| *size += 1.0)
|
theme::adjust_font_size(cx, |size| *size += 1.0)
|
||||||
|
|
Loading…
Reference in a new issue