main.rs

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