main.rs

  1// Allow binary to be called Zed for a nice application menu when running executable directly
  2#![allow(non_snake_case)]
  3
  4use crate::open_listener::{OpenListener, OpenRequest};
  5use anyhow::{anyhow, Context, Result};
  6use cli::{
  7    ipc::{self, IpcSender},
  8    CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
  9};
 10use fs::RealFs;
 11use futures::{channel::mpsc, SinkExt, StreamExt};
 12use gpui2::{App, AsyncAppContext, Task};
 13use log::LevelFilter;
 14
 15use parking_lot::Mutex;
 16use serde::{Deserialize, Serialize};
 17use settings::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore};
 18use simplelog::ConfigBuilder;
 19use smol::process::Command;
 20use std::{
 21    collections::HashMap,
 22    env,
 23    fs::OpenOptions,
 24    io::IsTerminal,
 25    path::Path,
 26    sync::{
 27        atomic::{AtomicU32, Ordering},
 28        Arc, Weak,
 29    },
 30    thread,
 31};
 32use util::{channel::RELEASE_CHANNEL, http, paths, ResultExt};
 33use zed2::{ensure_only_instance, AppState, Assets, IsOnlyInstance};
 34// use zed2::{
 35//     assets::Assets,
 36//     build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
 37//     only_instance::{ensure_only_instance, IsOnlyInstance},
 38// };
 39
 40mod open_listener;
 41
 42fn main() {
 43    let http = http::client();
 44    init_paths();
 45    init_logger();
 46
 47    if ensure_only_instance() != IsOnlyInstance::Yes {
 48        return;
 49    }
 50
 51    log::info!("========== starting zed ==========");
 52    let mut app = App::production(Arc::new(Assets));
 53
 54    // let installation_id = app.background().block(installation_id()).ok();
 55    // let session_id = Uuid::new_v4().to_string();
 56    // init_panic_hook(&app, installation_id.clone(), session_id.clone());
 57
 58    load_embedded_fonts(&app);
 59
 60    let fs = Arc::new(RealFs);
 61    let user_settings_file_rx =
 62        watch_config_file(app.executor(), fs.clone(), paths::SETTINGS.clone());
 63    let user_keymap_file_rx = watch_config_file(app.executor(), fs.clone(), paths::KEYMAP.clone());
 64
 65    let login_shell_env_loaded = if stdout_is_a_pty() {
 66        Task::ready(())
 67    } else {
 68        app.executor().spawn(async {
 69            load_login_shell_environment().await.log_err();
 70        })
 71    };
 72
 73    let (listener, mut open_rx) = OpenListener::new();
 74    let listener = Arc::new(listener);
 75    let callback_listener = listener.clone();
 76    app.on_open_urls(move |urls, _| callback_listener.open_urls(urls))
 77        .on_reopen(move |cx| {
 78            if cx.has_global::<Weak<AppState>>() {
 79                if let Some(app_state) = cx.global::<Weak<AppState>>().upgrade() {
 80                    // todo!("workspace")
 81                    // workspace::open_new(&app_state, cx, |workspace, cx| {
 82                    //     Editor::new_file(workspace, &Default::default(), cx)
 83                    // })
 84                    // .detach();
 85                }
 86            }
 87        });
 88
 89    app.run(move |cx| {
 90        cx.set_global(*RELEASE_CHANNEL);
 91
 92        let mut store = SettingsStore::default();
 93        store
 94            .set_default_settings(default_settings().as_ref(), cx)
 95            .unwrap();
 96        cx.set_global(store);
 97        handle_settings_file_changes(user_settings_file_rx, cx);
 98        // handle_keymap_file_changes(user_keymap_file_rx, cx);
 99
100        // let client = client::Client::new(http.clone(), cx);
101        // let mut languages = LanguageRegistry::new(login_shell_env_loaded);
102        // let copilot_language_server_id = languages.next_language_server_id();
103        // languages.set_executor(cx.background().clone());
104        // languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
105        // let languages = Arc::new(languages);
106        // let node_runtime = RealNodeRuntime::new(http.clone());
107
108        // languages::init(languages.clone(), node_runtime.clone(), cx);
109        // let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
110        // let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
111
112        // cx.set_global(client.clone());
113
114        // theme::init(Assets, cx);
115        // context_menu::init(cx);
116        // project::Project::init(&client, cx);
117        // client::init(&client, cx);
118        // command_palette::init(cx);
119        // language::init(cx);
120        // editor::init(cx);
121        // go_to_line::init(cx);
122        // file_finder::init(cx);
123        // outline::init(cx);
124        // project_symbols::init(cx);
125        // project_panel::init(Assets, cx);
126        // channel::init(&client, user_store.clone(), cx);
127        // diagnostics::init(cx);
128        // search::init(cx);
129        // semantic_index::init(fs.clone(), http.clone(), languages.clone(), cx);
130        // vim::init(cx);
131        // terminal_view::init(cx);
132        // copilot::init(
133        //     copilot_language_server_id,
134        //     http.clone(),
135        //     node_runtime.clone(),
136        //     cx,
137        // );
138        // assistant::init(cx);
139        // component_test::init(cx);
140
141        // cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach();
142        // cx.spawn(|_| watch_languages(fs.clone(), languages.clone()))
143        //     .detach();
144        // watch_file_types(fs.clone(), cx);
145
146        // languages.set_theme(theme::current(cx).clone());
147        // cx.observe_global::<SettingsStore, _>({
148        //     let languages = languages.clone();
149        //     move |cx| languages.set_theme(theme::current(cx).clone())
150        // })
151        // .detach();
152
153        // client.telemetry().start(installation_id, session_id, cx);
154
155        // todo!("app_state")
156        let app_state = Arc::new(AppState);
157        // let app_state = Arc::new(AppState {
158        //     languages,
159        //     client: client.clone(),
160        //     user_store,
161        //     fs,
162        //     build_window_options,
163        //     initialize_workspace,
164        //     background_actions,
165        //     workspace_store,
166        //     node_runtime,
167        // });
168        // cx.set_global(Arc::downgrade(&app_state));
169
170        // audio::init(Assets, cx);
171        // auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
172
173        // todo!("workspace")
174        // workspace::init(app_state.clone(), cx);
175        // recent_projects::init(cx);
176
177        // journal::init(app_state.clone(), cx);
178        // language_selector::init(cx);
179        // theme_selector::init(cx);
180        // activity_indicator::init(cx);
181        // language_tools::init(cx);
182        // call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
183        // collab_ui::init(&app_state, cx);
184        // feedback::init(cx);
185        // welcome::init(cx);
186        // zed::init(&app_state, cx);
187
188        // cx.set_menus(menus::menus());
189
190        if stdout_is_a_pty() {
191            cx.activate(true);
192            let urls = collect_url_args();
193            if !urls.is_empty() {
194                listener.open_urls(urls)
195            }
196        } else {
197            upload_previous_panics(http.clone(), cx);
198
199            // TODO Development mode that forces the CLI mode usually runs Zed binary as is instead
200            // of an *app, hence gets no specific callbacks run. Emulate them here, if needed.
201            if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some()
202                && !listener.triggered.load(Ordering::Acquire)
203            {
204                listener.open_urls(collect_url_args())
205            }
206        }
207
208        let mut triggered_authentication = false;
209
210        match open_rx.try_next() {
211            Ok(Some(OpenRequest::Paths { paths })) => {
212                // todo!("workspace")
213                // cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
214                //     .detach();
215            }
216            Ok(Some(OpenRequest::CliConnection { connection })) => {
217                cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
218                    .detach();
219            }
220            Ok(Some(OpenRequest::JoinChannel { channel_id })) => {
221                // triggered_authentication = true;
222                // let app_state = app_state.clone();
223                // let client = client.clone();
224                // cx.spawn(|mut cx| async move {
225                //     // ignore errors here, we'll show a generic "not signed in"
226                //     let _ = authenticate(client, &cx).await;
227                //     cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx))
228                //         .await
229                // })
230                // .detach_and_log_err(cx)
231            }
232            Ok(None) | Err(_) => cx
233                .spawn({
234                    let app_state = app_state.clone();
235                    |cx| async move { restore_or_create_workspace(&app_state, cx).await }
236                })
237                .detach(),
238        }
239
240        cx.spawn(|mut cx| {
241            let app_state = app_state.clone();
242            async move {
243                while let Some(request) = open_rx.next().await {
244                    match request {
245                        OpenRequest::Paths { paths } => {
246                            // todo!("workspace")
247                            // cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
248                            //     .detach();
249                        }
250                        OpenRequest::CliConnection { connection } => {
251                            cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
252                                .detach();
253                        }
254                        OpenRequest::JoinChannel { channel_id } => {
255                            // cx
256                            // .update(|cx| {
257                            //     workspace::join_channel(channel_id, app_state.clone(), None, cx)
258                            // })
259                            // .detach()
260                        }
261                    }
262                }
263            }
264        })
265        .detach();
266
267        // if !triggered_authentication {
268        //     cx.spawn(|cx| async move { authenticate(client, &cx).await })
269        //         .detach_and_log_err(cx);
270        // }
271    });
272}
273
274// async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
275//     if stdout_is_a_pty() {
276//         if client::IMPERSONATE_LOGIN.is_some() {
277//             client.authenticate_and_connect(false, &cx).await?;
278//         }
279//     } else if client.has_keychain_credentials(&cx) {
280//         client.authenticate_and_connect(true, &cx).await?;
281//     }
282//     Ok::<_, anyhow::Error>(())
283// }
284
285// async fn installation_id() -> Result<String> {
286//     let legacy_key_name = "device_id";
287
288//     if let Ok(Some(installation_id)) = KEY_VALUE_STORE.read_kvp(legacy_key_name) {
289//         Ok(installation_id)
290//     } else {
291//         let installation_id = Uuid::new_v4().to_string();
292
293//         KEY_VALUE_STORE
294//             .write_kvp(legacy_key_name.to_string(), installation_id.clone())
295//             .await?;
296
297//         Ok(installation_id)
298//     }
299// }
300
301async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncAppContext) {
302    todo!("workspace")
303    // if let Some(location) = workspace::last_opened_workspace_paths().await {
304    //     cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))
305    //         .await
306    //         .log_err();
307    // } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) {
308    //     cx.update(|cx| show_welcome_experience(app_state, cx));
309    // } else {
310    //     cx.update(|cx| {
311    //         workspace::open_new(app_state, cx, |workspace, cx| {
312    //             Editor::new_file(workspace, &Default::default(), cx)
313    //         })
314    //         .detach();
315    //     });
316    // }
317}
318
319fn init_paths() {
320    std::fs::create_dir_all(&*util::paths::CONFIG_DIR).expect("could not create config path");
321    std::fs::create_dir_all(&*util::paths::LANGUAGES_DIR).expect("could not create languages path");
322    std::fs::create_dir_all(&*util::paths::DB_DIR).expect("could not create database path");
323    std::fs::create_dir_all(&*util::paths::LOGS_DIR).expect("could not create logs path");
324}
325
326fn init_logger() {
327    if stdout_is_a_pty() {
328        env_logger::init();
329    } else {
330        let level = LevelFilter::Info;
331
332        // Prevent log file from becoming too large.
333        const KIB: u64 = 1024;
334        const MIB: u64 = 1024 * KIB;
335        const MAX_LOG_BYTES: u64 = MIB;
336        if std::fs::metadata(&*paths::LOG).map_or(false, |metadata| metadata.len() > MAX_LOG_BYTES)
337        {
338            let _ = std::fs::rename(&*paths::LOG, &*paths::OLD_LOG);
339        }
340
341        let log_file = OpenOptions::new()
342            .create(true)
343            .append(true)
344            .open(&*paths::LOG)
345            .expect("could not open logfile");
346
347        let config = ConfigBuilder::new()
348            .set_time_format_str("%Y-%m-%dT%T") //All timestamps are UTC
349            .build();
350
351        simplelog::WriteLogger::init(level, config, log_file).expect("could not initialize logger");
352    }
353}
354
355#[derive(Serialize, Deserialize)]
356struct LocationData {
357    file: String,
358    line: u32,
359}
360
361#[derive(Serialize, Deserialize)]
362struct Panic {
363    thread: String,
364    payload: String,
365    #[serde(skip_serializing_if = "Option::is_none")]
366    location_data: Option<LocationData>,
367    backtrace: Vec<String>,
368    app_version: String,
369    release_channel: String,
370    os_name: String,
371    os_version: Option<String>,
372    architecture: String,
373    panicked_on: u128,
374    #[serde(skip_serializing_if = "Option::is_none")]
375    installation_id: Option<String>,
376    session_id: String,
377}
378
379#[derive(Serialize)]
380struct PanicRequest {
381    panic: Panic,
382    token: String,
383}
384
385static PANIC_COUNT: AtomicU32 = AtomicU32::new(0);
386
387// fn init_panic_hook(app: &App, installation_id: Option<String>, session_id: String) {
388//     let is_pty = stdout_is_a_pty();
389//     let platform = app.platform();
390
391//     panic::set_hook(Box::new(move |info| {
392//         let prior_panic_count = PANIC_COUNT.fetch_add(1, Ordering::SeqCst);
393//         if prior_panic_count > 0 {
394//             // Give the panic-ing thread time to write the panic file
395//             loop {
396//                 std::thread::yield_now();
397//             }
398//         }
399
400//         let thread = thread::current();
401//         let thread_name = thread.name().unwrap_or("<unnamed>");
402
403//         let payload = info
404//             .payload()
405//             .downcast_ref::<&str>()
406//             .map(|s| s.to_string())
407//             .or_else(|| info.payload().downcast_ref::<String>().map(|s| s.clone()))
408//             .unwrap_or_else(|| "Box<Any>".to_string());
409
410//         if *util::channel::RELEASE_CHANNEL == ReleaseChannel::Dev {
411//             let location = info.location().unwrap();
412//             let backtrace = Backtrace::new();
413//             eprintln!(
414//                 "Thread {:?} panicked with {:?} at {}:{}:{}\n{:?}",
415//                 thread_name,
416//                 payload,
417//                 location.file(),
418//                 location.line(),
419//                 location.column(),
420//                 backtrace,
421//             );
422//             std::process::exit(-1);
423//         }
424
425//         let app_version = ZED_APP_VERSION
426//             .or_else(|| platform.app_version().ok())
427//             .map_or("dev".to_string(), |v| v.to_string());
428
429//         let backtrace = Backtrace::new();
430//         let mut backtrace = backtrace
431//             .frames()
432//             .iter()
433//             .filter_map(|frame| Some(format!("{:#}", frame.symbols().first()?.name()?)))
434//             .collect::<Vec<_>>();
435
436//         // Strip out leading stack frames for rust panic-handling.
437//         if let Some(ix) = backtrace
438//             .iter()
439//             .position(|name| name == "rust_begin_unwind")
440//         {
441//             backtrace.drain(0..=ix);
442//         }
443
444//         let panic_data = Panic {
445//             thread: thread_name.into(),
446//             payload: payload.into(),
447//             location_data: info.location().map(|location| LocationData {
448//                 file: location.file().into(),
449//                 line: location.line(),
450//             }),
451//             app_version: app_version.clone(),
452//             release_channel: RELEASE_CHANNEL.display_name().into(),
453//             os_name: platform.os_name().into(),
454//             os_version: platform
455//                 .os_version()
456//                 .ok()
457//                 .map(|os_version| os_version.to_string()),
458//             architecture: env::consts::ARCH.into(),
459//             panicked_on: SystemTime::now()
460//                 .duration_since(UNIX_EPOCH)
461//                 .unwrap()
462//                 .as_millis(),
463//             backtrace,
464//             installation_id: installation_id.clone(),
465//             session_id: session_id.clone(),
466//         };
467
468//         if let Some(panic_data_json) = serde_json::to_string_pretty(&panic_data).log_err() {
469//             log::error!("{}", panic_data_json);
470//         }
471
472//         if !is_pty {
473//             if let Some(panic_data_json) = serde_json::to_string(&panic_data).log_err() {
474//                 let timestamp = chrono::Utc::now().format("%Y_%m_%d %H_%M_%S").to_string();
475//                 let panic_file_path = paths::LOGS_DIR.join(format!("zed-{}.panic", timestamp));
476//                 let panic_file = std::fs::OpenOptions::new()
477//                     .append(true)
478//                     .create(true)
479//                     .open(&panic_file_path)
480//                     .log_err();
481//                 if let Some(mut panic_file) = panic_file {
482//                     writeln!(&mut panic_file, "{}", panic_data_json).log_err();
483//                     panic_file.flush().log_err();
484//                 }
485//             }
486//         }
487
488//         std::process::abort();
489//     }));
490// }
491
492// fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
493//     let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
494
495//     cx.background()
496//         .spawn({
497//             async move {
498//                 let panic_report_url = format!("{}/api/panic", &*client::ZED_SERVER_URL);
499//                 let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?;
500//                 while let Some(child) = children.next().await {
501//                     let child = child?;
502//                     let child_path = child.path();
503
504//                     if child_path.extension() != Some(OsStr::new("panic")) {
505//                         continue;
506//                     }
507//                     let filename = if let Some(filename) = child_path.file_name() {
508//                         filename.to_string_lossy()
509//                     } else {
510//                         continue;
511//                     };
512
513//                     if !filename.starts_with("zed") {
514//                         continue;
515//                     }
516
517//                     if telemetry_settings.diagnostics {
518//                         let panic_file_content = smol::fs::read_to_string(&child_path)
519//                             .await
520//                             .context("error reading panic file")?;
521
522//                         let panic = serde_json::from_str(&panic_file_content)
523//                             .ok()
524//                             .or_else(|| {
525//                                 panic_file_content
526//                                     .lines()
527//                                     .next()
528//                                     .and_then(|line| serde_json::from_str(line).ok())
529//                             })
530//                             .unwrap_or_else(|| {
531//                                 log::error!(
532//                                     "failed to deserialize panic file {:?}",
533//                                     panic_file_content
534//                                 );
535//                                 None
536//                             });
537
538//                         if let Some(panic) = panic {
539//                             let body = serde_json::to_string(&PanicRequest {
540//                                 panic,
541//                                 token: ZED_SECRET_CLIENT_TOKEN.into(),
542//                             })
543//                             .unwrap();
544
545//                             let request = Request::post(&panic_report_url)
546//                                 .redirect_policy(isahc::config::RedirectPolicy::Follow)
547//                                 .header("Content-Type", "application/json")
548//                                 .body(body.into())?;
549//                             let response =
550//                                 http.send(request).await.context("error sending panic")?;
551//                             if !response.status().is_success() {
552//                                 log::error!(
553//                                     "Error uploading panic to server: {}",
554//                                     response.status()
555//                                 );
556//                             }
557//                         }
558//                     }
559
560//                     // We've done what we can, delete the file
561//                     std::fs::remove_file(child_path)
562//                         .context("error removing panic")
563//                         .log_err();
564//                 }
565//                 Ok::<_, anyhow::Error>(())
566//             }
567//             .log_err()
568//         })
569//         .detach();
570// }
571
572async fn load_login_shell_environment() -> Result<()> {
573    let marker = "ZED_LOGIN_SHELL_START";
574    let shell = env::var("SHELL").context(
575        "SHELL environment variable is not assigned so we can't source login environment variables",
576    )?;
577    let output = Command::new(&shell)
578        .args(["-lic", &format!("echo {marker} && /usr/bin/env -0")])
579        .output()
580        .await
581        .context("failed to spawn login shell to source login environment variables")?;
582    if !output.status.success() {
583        Err(anyhow!("login shell exited with error"))?;
584    }
585
586    let stdout = String::from_utf8_lossy(&output.stdout);
587
588    if let Some(env_output_start) = stdout.find(marker) {
589        let env_output = &stdout[env_output_start + marker.len()..];
590        for line in env_output.split_terminator('\0') {
591            if let Some(separator_index) = line.find('=') {
592                let key = &line[..separator_index];
593                let value = &line[separator_index + 1..];
594                env::set_var(key, value);
595            }
596        }
597        log::info!(
598            "set environment variables from shell:{}, path:{}",
599            shell,
600            env::var("PATH").unwrap_or_default(),
601        );
602    }
603
604    Ok(())
605}
606
607fn stdout_is_a_pty() -> bool {
608    std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && std::io::stdout().is_terminal()
609}
610
611fn collect_url_args() -> Vec<String> {
612    env::args()
613        .skip(1)
614        .filter_map(|arg| match std::fs::canonicalize(Path::new(&arg)) {
615            Ok(path) => Some(format!("file://{}", path.to_string_lossy())),
616            Err(error) => {
617                if let Some(_) = parse_zed_link(&arg) {
618                    Some(arg)
619                } else {
620                    log::error!("error parsing path argument: {}", error);
621                    None
622                }
623            }
624        })
625        .collect()
626}
627
628fn load_embedded_fonts(app: &App) {
629    let font_paths = Assets.list("fonts");
630    let embedded_fonts = Mutex::new(Vec::new());
631    smol::block_on(app.background().scoped(|scope| {
632        for font_path in &font_paths {
633            if !font_path.ends_with(".ttf") {
634                continue;
635            }
636
637            scope.spawn(async {
638                let font_path = &*font_path;
639                let font_bytes = Assets.load(font_path).unwrap().to_vec();
640                embedded_fonts.lock().push(Arc::from(font_bytes));
641            });
642        }
643    }));
644    app.platform()
645        .fonts()
646        .add_fonts(&embedded_fonts.into_inner())
647        .unwrap();
648}
649
650// #[cfg(debug_assertions)]
651// async fn watch_themes(fs: Arc<dyn Fs>, mut cx: AsyncAppContext) -> Option<()> {
652//     let mut events = fs
653//         .watch("styles/src".as_ref(), Duration::from_millis(100))
654//         .await;
655//     while (events.next().await).is_some() {
656//         let output = Command::new("npm")
657//             .current_dir("styles")
658//             .args(["run", "build"])
659//             .output()
660//             .await
661//             .log_err()?;
662//         if output.status.success() {
663//             cx.update(|cx| theme_selector::reload(cx))
664//         } else {
665//             eprintln!(
666//                 "build script failed {}",
667//                 String::from_utf8_lossy(&output.stderr)
668//             );
669//         }
670//     }
671//     Some(())
672// }
673
674// #[cfg(debug_assertions)]
675// async fn watch_languages(fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
676//     let mut events = fs
677//         .watch(
678//             "crates/zed/src/languages".as_ref(),
679//             Duration::from_millis(100),
680//         )
681//         .await;
682//     while (events.next().await).is_some() {
683//         languages.reload();
684//     }
685//     Some(())
686// }
687
688// #[cfg(debug_assertions)]
689// fn watch_file_types(fs: Arc<dyn Fs>, cx: &mut AppContext) {
690//     cx.spawn(|mut cx| async move {
691//         let mut events = fs
692//             .watch(
693//                 "assets/icons/file_icons/file_types.json".as_ref(),
694//                 Duration::from_millis(100),
695//             )
696//             .await;
697//         while (events.next().await).is_some() {
698//             cx.update(|cx| {
699//                 cx.update_global(|file_types, _| {
700//                     *file_types = project_panel::file_associations::FileAssociations::new(Assets);
701//                 });
702//             })
703//         }
704//     })
705//     .detach()
706// }
707
708// #[cfg(not(debug_assertions))]
709// async fn watch_themes(_fs: Arc<dyn Fs>, _cx: AsyncAppContext) -> Option<()> {
710//     None
711// }
712
713// #[cfg(not(debug_assertions))]
714// async fn watch_languages(_: Arc<dyn Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
715//     None
716// }
717
718// #[cfg(not(debug_assertions))]
719// fn watch_file_types(_fs: Arc<dyn Fs>, _cx: &mut AppContext) {}
720
721fn connect_to_cli(
722    server_name: &str,
723) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
724    let handshake_tx = cli::ipc::IpcSender::<IpcHandshake>::connect(server_name.to_string())
725        .context("error connecting to cli")?;
726    let (request_tx, request_rx) = ipc::channel::<CliRequest>()?;
727    let (response_tx, response_rx) = ipc::channel::<CliResponse>()?;
728
729    handshake_tx
730        .send(IpcHandshake {
731            requests: request_tx,
732            responses: response_rx,
733        })
734        .context("error sending ipc handshake")?;
735
736    let (mut async_request_tx, async_request_rx) =
737        futures::channel::mpsc::channel::<CliRequest>(16);
738    thread::spawn(move || {
739        while let Ok(cli_request) = request_rx.recv() {
740            if smol::block_on(async_request_tx.send(cli_request)).is_err() {
741                break;
742            }
743        }
744        Ok::<_, anyhow::Error>(())
745    });
746
747    Ok((async_request_rx, response_tx))
748}
749
750async fn handle_cli_connection(
751    (mut requests, responses): (mpsc::Receiver<CliRequest>, IpcSender<CliResponse>),
752    app_state: Arc<AppState>,
753    mut cx: AsyncAppContext,
754) {
755    if let Some(request) = requests.next().await {
756        match request {
757            CliRequest::Open { paths, wait } => {
758                let mut caret_positions = HashMap::new();
759
760                // todo!("workspace")
761                // let paths = if paths.is_empty() {
762                // workspace::last_opened_workspace_paths()
763                //     .await
764                //     .map(|location| location.paths().to_vec())
765                //     .unwrap_or_default()
766                // } else {
767                //     paths
768                //         .into_iter()
769                //         .filter_map(|path_with_position_string| {
770                //             let path_with_position = PathLikeWithPosition::parse_str(
771                //                 &path_with_position_string,
772                //                 |path_str| {
773                //                     Ok::<_, std::convert::Infallible>(
774                //                         Path::new(path_str).to_path_buf(),
775                //                     )
776                //                 },
777                //             )
778                //             .expect("Infallible");
779                //             let path = path_with_position.path_like;
780                //             if let Some(row) = path_with_position.row {
781                //                 if path.is_file() {
782                //                     let row = row.saturating_sub(1);
783                //                     let col =
784                //                         path_with_position.column.unwrap_or(0).saturating_sub(1);
785                //                     caret_positions.insert(path.clone(), Point::new(row, col));
786                //                 }
787                //             }
788                //             Some(path)
789                //         })
790                //         .collect()
791                // };
792
793                // let mut errored = false;
794                // match cx
795                //     .update(|cx| workspace::open_paths(&paths, &app_state, None, cx))
796                //     .await
797                // {
798                //     Ok((workspace, items)) => {
799                //         let mut item_release_futures = Vec::new();
800
801                //         for (item, path) in items.into_iter().zip(&paths) {
802                //             match item {
803                //                 Some(Ok(item)) => {
804                //                     if let Some(point) = caret_positions.remove(path) {
805                //                         if let Some(active_editor) = item.downcast::<Editor>() {
806                //                             active_editor
807                //                                 .downgrade()
808                //                                 .update(&mut cx, |editor, cx| {
809                //                                     let snapshot =
810                //                                         editor.snapshot(cx).display_snapshot;
811                //                                     let point = snapshot
812                //                                         .buffer_snapshot
813                //                                         .clip_point(point, Bias::Left);
814                //                                     editor.change_selections(
815                //                                         Some(Autoscroll::center()),
816                //                                         cx,
817                //                                         |s| s.select_ranges([point..point]),
818                //                                     );
819                //                                 })
820                //                                 .log_err();
821                //                         }
822                //                     }
823
824                //                     let released = oneshot::channel();
825                //                     cx.update(|cx| {
826                //                         item.on_release(
827                //                             cx,
828                //                             Box::new(move |_| {
829                //                                 let _ = released.0.send(());
830                //                             }),
831                //                         )
832                //                         .detach();
833                //                     });
834                //                     item_release_futures.push(released.1);
835                //                 }
836                //                 Some(Err(err)) => {
837                //                     responses
838                //                         .send(CliResponse::Stderr {
839                //                             message: format!("error opening {:?}: {}", path, err),
840                //                         })
841                //                         .log_err();
842                //                     errored = true;
843                //                 }
844                //                 None => {}
845                //             }
846                //         }
847
848                //         if wait {
849                //             let background = cx.background();
850                //             let wait = async move {
851                //                 if paths.is_empty() {
852                //                     let (done_tx, done_rx) = oneshot::channel();
853                //                     if let Some(workspace) = workspace.upgrade(&cx) {
854                //                         let _subscription = cx.update(|cx| {
855                //                             cx.observe_release(&workspace, move |_, _| {
856                //                                 let _ = done_tx.send(());
857                //                             })
858                //                         });
859                //                         drop(workspace);
860                //                         let _ = done_rx.await;
861                //                     }
862                //                 } else {
863                //                     let _ =
864                //                         futures::future::try_join_all(item_release_futures).await;
865                //                 };
866                //             }
867                //             .fuse();
868                //             futures::pin_mut!(wait);
869
870                //             loop {
871                //                 // Repeatedly check if CLI is still open to avoid wasting resources
872                //                 // waiting for files or workspaces to close.
873                //                 let mut timer = background.timer(Duration::from_secs(1)).fuse();
874                //                 futures::select_biased! {
875                //                     _ = wait => break,
876                //                     _ = timer => {
877                //                         if responses.send(CliResponse::Ping).is_err() {
878                //                             break;
879                //                         }
880                //                     }
881                //                 }
882                //             }
883                //         }
884                //     }
885                //     Err(error) => {
886                //         errored = true;
887                //         responses
888                //             .send(CliResponse::Stderr {
889                //                 message: format!("error opening {:?}: {}", paths, error),
890                //             })
891                //             .log_err();
892                //     }
893                // }
894
895                // responses
896                //     .send(CliResponse::Exit {
897                //         status: i32::from(errored),
898                //     })
899                //     .log_err();
900            }
901        }
902    }
903}
904
905// pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] {
906//     &[
907//         ("Go to file", &file_finder::Toggle),
908//         ("Open command palette", &command_palette::Toggle),
909//         ("Open recent projects", &recent_projects::OpenRecent),
910//         ("Change your settings", &zed_actions::OpenSettings),
911//     ]
912// }