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