1// Allow binary to be called Zed for a nice application menu when running executable direcly
2#![allow(non_snake_case)]
3
4use fs::OpenOptions;
5use gpui::AssetSource;
6use log::LevelFilter;
7use parking_lot::Mutex;
8use simplelog::SimpleLogger;
9use std::{fs, path::PathBuf, sync::Arc};
10use zed::{
11 self,
12 assets::Assets,
13 channel::ChannelList,
14 chat_panel, editor, file_finder,
15 fs::RealFs,
16 http, language, menus, project_panel, rpc, settings, theme_selector,
17 user::UserStore,
18 workspace::{self, OpenNew, OpenParams, OpenPaths},
19 AppState,
20};
21
22fn main() {
23 init_logger();
24
25 let app = gpui::App::new(Assets).unwrap();
26 let embedded_fonts = Assets
27 .list("fonts")
28 .into_iter()
29 .map(|f| Arc::new(Assets.load(&f).unwrap().to_vec()))
30 .collect::<Vec<_>>();
31 app.platform().fonts().add_fonts(&embedded_fonts).unwrap();
32
33 let themes = settings::ThemeRegistry::new(Assets, app.font_cache());
34 let (settings_tx, settings) = settings::channel(&app.font_cache(), &themes).unwrap();
35 let languages = Arc::new(language::build_language_registry());
36 languages.set_theme(&settings.borrow().theme.syntax);
37
38 app.run(move |cx| {
39 let rpc = rpc::Client::new();
40 let http = http::client();
41 let user_store = cx.add_model(|cx| UserStore::new(rpc.clone(), http.clone(), cx));
42 let app_state = Arc::new(AppState {
43 languages: languages.clone(),
44 settings_tx: Arc::new(Mutex::new(settings_tx)),
45 settings,
46 themes,
47 channel_list: cx.add_model(|cx| ChannelList::new(user_store.clone(), rpc.clone(), cx)),
48 rpc,
49 user_store,
50 fs: Arc::new(RealFs),
51 });
52
53 zed::init(&app_state, cx);
54 workspace::init(cx);
55 editor::init(cx);
56 file_finder::init(cx);
57 chat_panel::init(cx);
58 project_panel::init(cx);
59 theme_selector::init(&app_state, cx);
60
61 cx.set_menus(menus::menus(&app_state.clone()));
62
63 if stdout_is_a_pty() {
64 cx.platform().activate(true);
65 }
66
67 let paths = collect_path_args();
68 if paths.is_empty() {
69 cx.dispatch_global_action(OpenNew(app_state));
70 } else {
71 cx.dispatch_global_action(OpenPaths(OpenParams { paths, app_state }));
72 }
73 });
74}
75
76fn init_logger() {
77 let level = LevelFilter::Info;
78
79 if stdout_is_a_pty() {
80 SimpleLogger::init(level, Default::default()).expect("could not initialize logger");
81 } else {
82 let log_dir_path = dirs::home_dir()
83 .expect("could not locate home directory for logging")
84 .join("Library/Logs/");
85 let log_file_path = log_dir_path.join("Zed.log");
86 fs::create_dir_all(&log_dir_path).expect("could not create log directory");
87 let log_file = OpenOptions::new()
88 .create(true)
89 .append(true)
90 .open(log_file_path)
91 .expect("could not open logfile");
92 simplelog::WriteLogger::init(level, simplelog::Config::default(), log_file)
93 .expect("could not initialize logger");
94 log_panics::init();
95 }
96}
97
98fn stdout_is_a_pty() -> bool {
99 unsafe { libc::isatty(libc::STDOUT_FILENO as i32) != 0 }
100}
101
102fn collect_path_args() -> Vec<PathBuf> {
103 std::env::args()
104 .skip(1)
105 .filter_map(|arg| match fs::canonicalize(arg) {
106 Ok(path) => Some(path),
107 Err(error) => {
108 log::error!("error parsing path argument: {}", error);
109 None
110 }
111 })
112 .collect::<Vec<_>>()
113}