zed2.rs
1mod assets;
2pub mod languages;
3mod only_instance;
4mod open_listener;
5
6pub use assets::*;
7use collections::HashMap;
8use gpui2::{AsyncAppContext, Point};
9pub use only_instance::*;
10pub use open_listener::*;
11
12use anyhow::{Context, Result};
13use cli::{
14 ipc::{self, IpcSender},
15 CliRequest, CliResponse, IpcHandshake,
16};
17use futures::{
18 channel::{mpsc, oneshot},
19 FutureExt, SinkExt, StreamExt,
20};
21use std::{path::Path, sync::Arc, thread, time::Duration};
22use util::{paths::PathLikeWithPosition, ResultExt};
23use workspace2::AppState;
24
25pub fn connect_to_cli(
26 server_name: &str,
27) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
28 let handshake_tx = cli::ipc::IpcSender::<IpcHandshake>::connect(server_name.to_string())
29 .context("error connecting to cli")?;
30 let (request_tx, request_rx) = ipc::channel::<CliRequest>()?;
31 let (response_tx, response_rx) = ipc::channel::<CliResponse>()?;
32
33 handshake_tx
34 .send(IpcHandshake {
35 requests: request_tx,
36 responses: response_rx,
37 })
38 .context("error sending ipc handshake")?;
39
40 let (mut async_request_tx, async_request_rx) =
41 futures::channel::mpsc::channel::<CliRequest>(16);
42 thread::spawn(move || {
43 while let Ok(cli_request) = request_rx.recv() {
44 if smol::block_on(async_request_tx.send(cli_request)).is_err() {
45 break;
46 }
47 }
48 Ok::<_, anyhow::Error>(())
49 });
50
51 Ok((async_request_rx, response_tx))
52}
53
54pub async fn handle_cli_connection(
55 (mut requests, responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
56 app_state: Arc<AppState>,
57 mut cx: AsyncAppContext,
58) {
59 if let Some(request) = requests.next().await {
60 match request {
61 CliRequest::Open { paths, wait } => {
62 let mut caret_positions = HashMap::default();
63
64 let paths = if paths.is_empty() {
65 todo!()
66 // workspace::last_opened_workspace_paths()
67 // .await
68 // .map(|location| location.paths().to_vec())
69 // .unwrap_or_default()
70 } else {
71 paths
72 .into_iter()
73 .filter_map(|path_with_position_string| {
74 let path_with_position = PathLikeWithPosition::parse_str(
75 &path_with_position_string,
76 |path_str| {
77 Ok::<_, std::convert::Infallible>(
78 Path::new(path_str).to_path_buf(),
79 )
80 },
81 )
82 .expect("Infallible");
83 let path = path_with_position.path_like;
84 if let Some(row) = path_with_position.row {
85 if path.is_file() {
86 let row = row.saturating_sub(1);
87 let col =
88 path_with_position.column.unwrap_or(0).saturating_sub(1);
89 caret_positions.insert(path.clone(), Point::new(row, col));
90 }
91 }
92 Some(path)
93 })
94 .collect::<Vec<_>>()
95 };
96
97 let mut errored = false;
98
99 if let Some(open_paths_task) = cx
100 .update(|cx| workspace2::open_paths(&paths, &app_state, None, cx))
101 .log_err()
102 {
103 match open_paths_task.await {
104 Ok((workspace, items)) => {
105 let mut item_release_futures = Vec::new();
106
107 for (item, path) in items.into_iter().zip(&paths) {
108 match item {
109 Some(Ok(mut item)) => {
110 if let Some(point) = caret_positions.remove(path) {
111 todo!()
112 // if let Some(active_editor) = item.downcast::<Editor>() {
113 // active_editor
114 // .downgrade()
115 // .update(&mut cx, |editor, cx| {
116 // let snapshot =
117 // editor.snapshot(cx).display_snapshot;
118 // let point = snapshot
119 // .buffer_snapshot
120 // .clip_point(point, Bias::Left);
121 // editor.change_selections(
122 // Some(Autoscroll::center()),
123 // cx,
124 // |s| s.select_ranges([point..point]),
125 // );
126 // })
127 // .log_err();
128 // }
129 }
130
131 let released = oneshot::channel();
132 cx.update(move |cx| {
133 item.on_release(
134 cx,
135 Box::new(move |_| {
136 let _ = released.0.send(());
137 }),
138 )
139 .detach();
140 });
141 item_release_futures.push(released.1);
142 }
143 Some(Err(err)) => {
144 responses
145 .send(CliResponse::Stderr {
146 message: format!(
147 "error opening {:?}: {}",
148 path, err
149 ),
150 })
151 .log_err();
152 errored = true;
153 }
154 None => {}
155 }
156 }
157
158 if wait {
159 let executor = cx.executor().clone();
160 let wait = async move {
161 if paths.is_empty() {
162 let (done_tx, done_rx) = oneshot::channel();
163 let _subscription =
164 cx.update_window_root(&workspace, move |_, cx| {
165 cx.on_release(|_, _| {
166 let _ = done_tx.send(());
167 })
168 });
169 drop(workspace);
170 let _ = done_rx.await;
171 } else {
172 let _ = futures::future::try_join_all(item_release_futures)
173 .await;
174 };
175 }
176 .fuse();
177 futures::pin_mut!(wait);
178
179 loop {
180 // Repeatedly check if CLI is still open to avoid wasting resources
181 // waiting for files or workspaces to close.
182 let mut timer = executor.timer(Duration::from_secs(1)).fuse();
183 futures::select_biased! {
184 _ = wait => break,
185 _ = timer => {
186 if responses.send(CliResponse::Ping).is_err() {
187 break;
188 }
189 }
190 }
191 }
192 }
193 }
194 Err(error) => {
195 errored = true;
196 responses
197 .send(CliResponse::Stderr {
198 message: format!("error opening {:?}: {}", paths, error),
199 })
200 .log_err();
201 }
202 }
203
204 responses
205 .send(CliResponse::Exit {
206 status: i32::from(errored),
207 })
208 .log_err();
209 }
210 }
211 }
212 }
213}