#![cfg_attr(windows, windows_subsystem = "windows")]
mod app;
mod convert;
mod defaults;
mod export;
mod highlight;
mod insert;
mod io;
mod keymap;
mod rendered;
mod settings;
mod theme;
use std::io::Write as _;
use std::path::PathBuf;
use futures::StreamExt as _;
use gpui_kit::assets::Assets;
use gpui_kit::component::{Root, TitleBar};
use gpui_kit::*;
use io::Document;
use settings::Settings;
const USAGE: &str = "\
Usage: smep [FILE]
A Simple Markdown Editor & Previewer. Opens FILE (Markdown or HTML), or an
empty document; a FILE that does not exist yet is a new document that saves
there. A PDF, Word (.docx) or text FILE is converted to a new, unsaved
Markdown document.
Options:
-h, --help Print this help and exit
-V, --version Print the version and exit";
#[cfg(windows)]
fn attach_parent_console() {
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows_sys::Win32::System::Console::{
ATTACH_PARENT_PROCESS, AttachConsole, GetStdHandle, STD_ERROR_HANDLE, STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE, SetStdHandle,
};
let kinds = [STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE];
unsafe {
let inherited = kinds.map(|kind| GetStdHandle(kind));
if AttachConsole(ATTACH_PARENT_PROCESS) == 0 {
return;
}
for (kind, handle) in kinds.into_iter().zip(inherited) {
if !handle.is_null() && handle != INVALID_HANDLE_VALUE {
SetStdHandle(kind, handle);
}
}
}
}
#[cfg(not(windows))]
fn attach_parent_console() {}
fn main() {
attach_parent_console();
let argument = std::env::args_os().nth(1);
match argument.as_deref().and_then(|arg| arg.to_str()) {
Some("-h" | "--help") => {
let _ = writeln!(std::io::stdout(), "{USAGE}");
return;
}
Some("-V" | "--version") => {
let _ = writeln!(std::io::stdout(), "smep {}", env!("CARGO_PKG_VERSION"));
return;
}
_ => {}
}
let settings = Settings::load();
let document = match argument.map(PathBuf::from) {
Some(path) => {
let read = match convert::Format::for_import(&path) {
Some(format) => Document::import(path.clone(), format),
None => Document::open(path.clone()).map_err(anyhow::Error::from),
};
match read {
Ok(document) => document,
Err(err) => {
eprintln!("smep: cannot read {}: {err}", path.display());
std::process::exit(1);
}
}
}
None => Document::empty(),
};
let (opens, mut requested) = futures::channel::mpsc::unbounded::<PathBuf>();
let app = gpui_kit::application().with_assets(Assets);
app.on_open_urls(move |urls| {
for path in urls.iter().filter_map(|url| io::path_from_file_url(url)) {
let _ = opens.unbounded_send(path);
}
});
app.run(move |cx| {
gpui_kit::init(cx);
theme::install(cx);
keymap::init(cx);
cx.set_menus(keymap::native_menus());
cx.set_quit_mode(QuitMode::LastWindowClosed);
let options = WindowOptions {
window_bounds: Some(WindowBounds::centered(size(px(1200.), px(800.)), cx)),
#[cfg(target_os = "linux")]
window_decorations: Some(WindowDecorations::Client),
..TitleBar::window_options()
};
cx.spawn(async move |cx| {
let mut smep = None;
let window = cx
.open_window(options, |window, cx| {
let view = cx.new(|cx| app::Smep::new(document, settings, window, cx));
smep = Some(view.clone());
cx.new(|cx| Root::new(view, window, cx))
})
.expect("failed to open the smep window");
let smep = smep.expect("the window builder ran");
while let Some(path) = requested.next().await {
let opened = window.update(cx, |_, window, cx| {
smep.update(cx, |this, cx| this.open_document_at(path, window, cx));
});
if opened.is_err() {
break; }
}
})
.detach();
});
}