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}