use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct EditorConfig {
pub code_block_wrap: bool,
pub line_wrap: bool,
pub preserve_blank_lines: bool,
pub visual_line_nav: bool,
pub show_welcome: bool,
pub check_for_updates: bool,
pub daily_tips: bool,
pub show_line_numbers: bool,
pub mouse_scroll_lines: usize,
pub transient_ms: u64,
pub max_width_enabled: bool,
pub max_width_cols: usize,
pub big_h1: bool,
pub syntax_highlighting: bool,
pub reflow: bool,
pub autosave_enabled: bool,
pub autosave_idle_ms: u64,
pub diff_on_change: bool,
pub show_diff_intro: bool,
pub cursor_blink: bool,
pub cursor_blink_ms: u64,
pub remember_cursor: bool,
}
pub const MAX_WIDTH_COLS_MIN: usize = 20;
pub const AUTOSAVE_IDLE_MS_MIN_EXCLUSIVE: u64 = 1000;
pub const AUTOSAVE_IDLE_MS_MAX_EXCLUSIVE: u64 = 600_000;
pub const AUTOSAVE_IDLE_MS_DEFAULT: u64 = 5000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AppearanceMode {
#[default]
Dark,
Light,
}
impl AppearanceMode {
pub fn opposite(self) -> Self {
match self {
AppearanceMode::Dark => AppearanceMode::Light,
AppearanceMode::Light => AppearanceMode::Dark,
}
}
}
impl Default for EditorConfig {
fn default() -> Self {
Self {
code_block_wrap: false,
line_wrap: true,
preserve_blank_lines: true,
visual_line_nav: true,
show_welcome: true,
check_for_updates: true,
daily_tips: true,
show_line_numbers: false,
mouse_scroll_lines: 1,
transient_ms: 1500,
max_width_enabled: true,
max_width_cols: 100,
big_h1: false,
syntax_highlighting: true,
reflow: true,
autosave_enabled: false,
autosave_idle_ms: AUTOSAVE_IDLE_MS_DEFAULT,
diff_on_change: true,
show_diff_intro: true,
cursor_blink: true,
cursor_blink_ms: 530,
remember_cursor: true,
}
}
}
pub const VIM_HANDLER: &str = "vim";
pub const DEFAULT_HANDLER: &str = "default";
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ModalConfig {
pub handler: String,
}
impl Default for ModalConfig {
fn default() -> Self {
Self {
handler: DEFAULT_HANDLER.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct TableConfig {
pub show_buttons: bool,
pub row_striping: bool,
pub warn_on_width_injection: bool,
}
impl Default for TableConfig {
fn default() -> Self {
Self {
show_buttons: true,
row_striping: true,
warn_on_width_injection: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum RemoteImagePolicy {
#[default]
Ask,
Always,
Never,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ImagesEnabled {
#[default]
Ask,
Always,
Never,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ImagesConfig {
pub enabled: ImagesEnabled,
pub max_width: usize,
pub max_height: usize,
pub remote_policy: RemoteImagePolicy,
pub sharp_scrolling: bool,
}
impl Default for ImagesConfig {
fn default() -> Self {
Self {
enabled: ImagesEnabled::Ask,
max_width: 100,
max_height: 24,
remote_policy: RemoteImagePolicy::Ask,
sharp_scrolling: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FiguresEnabled {
#[default]
Ask,
Always,
Never,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct FiguresConfig {
pub enabled: FiguresEnabled,
pub math_preview: bool,
}
impl Default for FiguresConfig {
fn default() -> Self {
Self {
enabled: FiguresEnabled::Ask,
math_preview: true,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ExportConfig {
pub html: HtmlExportConfig,
pub custom: Vec<CustomExportEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct HtmlExportConfig {
pub stylesheet: String,
pub inline_images: bool,
#[serde(alias = "diagrams")]
pub figures: bool,
}
impl Default for HtmlExportConfig {
fn default() -> Self {
Self {
stylesheet: "builtin".into(),
inline_images: false,
figures: true,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct CustomExportEntry {
pub name: String,
pub command: Vec<String>,
pub extension: String,
}
impl CustomExportEntry {
pub fn output_extension(&self) -> &str {
let trimmed = self.extension.trim();
trimmed.strip_prefix('.').unwrap_or(trimmed)
}
pub fn config_problem(&self) -> Option<&'static str> {
if self.name.trim().is_empty() {
Some("`name` is empty; it is what labels the command-palette entry")
} else if self.command.is_empty() {
Some("`command` is empty; it needs at least the program to run")
} else if self.output_extension().is_empty() {
Some("`extension` is empty; the export would replace the document's own name")
} else if self.output_extension().contains(['/', '\\']) {
Some("`extension` contains a path separator; it must name a suffix, not a location")
} else {
None
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct DevConfig {
pub logging: bool,
}