Skip to main content

fresh/
types.rs

1//! Shared configuration types used by both schema generation and runtime.
2//!
3//! These types are kept in a separate module so that the schema generator
4//! can import them without pulling in heavy runtime dependencies.
5
6use std::collections::HashMap;
7
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10
11/// Constants for menu context state keys
12/// These are used both in menu item `when` conditions and `checkbox` states
13pub mod context_keys {
14    pub const LINE_NUMBERS: &str = "line_numbers";
15    pub const LINE_WRAP: &str = "line_wrap";
16    pub const COMPOSE_MODE: &str = "compose_mode";
17    pub const FILE_EXPLORER: &str = "file_explorer";
18    pub const MENU_BAR: &str = "menu_bar";
19    pub const FILE_EXPLORER_FOCUSED: &str = "file_explorer_focused";
20    pub const MOUSE_CAPTURE: &str = "mouse_capture";
21    pub const MOUSE_HOVER: &str = "mouse_hover";
22    pub const LSP_AVAILABLE: &str = "lsp_available";
23    pub const FILE_EXPLORER_SHOW_HIDDEN: &str = "file_explorer_show_hidden";
24    pub const FILE_EXPLORER_SHOW_GITIGNORED: &str = "file_explorer_show_gitignored";
25    pub const HAS_SELECTION: &str = "has_selection";
26    pub const FORMATTER_AVAILABLE: &str = "formatter_available";
27    pub const INLAY_HINTS: &str = "inlay_hints";
28    pub const SESSION_MODE: &str = "session_mode";
29    pub const VERTICAL_SCROLLBAR: &str = "vertical_scrollbar";
30    pub const HORIZONTAL_SCROLLBAR: &str = "horizontal_scrollbar";
31    pub const SCROLL_SYNC: &str = "scroll_sync";
32    pub const HAS_SAME_BUFFER_SPLITS: &str = "has_same_buffer_splits";
33}
34
35/// Configuration for process resource limits
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, JsonSchema)]
37pub struct ProcessLimits {
38    /// Maximum memory usage as percentage of total system memory (None = no limit)
39    /// Default is 50% of total system memory
40    #[serde(default)]
41    pub max_memory_percent: Option<u32>,
42
43    /// Maximum CPU usage as percentage of total CPU (None = no limit)
44    /// For multi-core systems, 100% = 1 core, 200% = 2 cores, etc.
45    #[serde(default)]
46    pub max_cpu_percent: Option<u32>,
47
48    /// Enable resource limiting (can be disabled per-platform)
49    #[serde(default = "default_true")]
50    pub enabled: bool,
51}
52
53fn default_true() -> bool {
54    true
55}
56
57impl Default for ProcessLimits {
58    fn default() -> Self {
59        Self {
60            max_memory_percent: Some(50),       // 50% of total memory
61            max_cpu_percent: Some(90),          // 90% of total CPU
62            enabled: cfg!(target_os = "linux"), // Only enabled on Linux by default
63        }
64    }
65}
66
67impl ProcessLimits {
68    /// Create a new ProcessLimits with no restrictions
69    pub fn unlimited() -> Self {
70        Self {
71            max_memory_percent: None,
72            max_cpu_percent: None,
73            enabled: false,
74        }
75    }
76
77    /// Get the default CPU limit (90% of total CPU)
78    pub fn default_cpu_limit_percent() -> u32 {
79        90
80    }
81}
82
83/// LSP server configuration
84#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
85#[schemars(extend("x-display-field" = "/command"))]
86pub struct LspServerConfig {
87    /// Command to spawn the server.
88    /// Required when enabled=true, ignored when enabled=false.
89    #[serde(default)]
90    pub command: String,
91
92    /// Arguments to pass to the server
93    #[serde(default)]
94    pub args: Vec<String>,
95
96    /// Whether the server is enabled
97    #[serde(default = "default_true")]
98    pub enabled: bool,
99
100    /// Whether to auto-start this LSP server when opening matching files
101    /// If false (default), the server must be started manually via command palette
102    #[serde(default)]
103    pub auto_start: bool,
104
105    /// Process resource limits (memory and CPU)
106    #[serde(default)]
107    pub process_limits: ProcessLimits,
108
109    /// Custom initialization options to send to the server
110    /// These are passed in the `initializationOptions` field of the LSP Initialize request
111    #[serde(default)]
112    pub initialization_options: Option<serde_json::Value>,
113
114    /// Environment variables to set for the LSP server process.
115    /// These are added to (or override) the inherited parent environment.
116    #[serde(default)]
117    pub env: HashMap<String, String>,
118
119    /// Override the LSP languageId sent in textDocument/didOpen based on file extension.
120    /// Maps file extension (without dot) to LSP language ID string.
121    /// For example: `{"tsx": "typescriptreact", "jsx": "javascriptreact"}`
122    #[serde(default)]
123    pub language_id_overrides: HashMap<String, String>,
124}
125
126impl LspServerConfig {
127    /// Merge this config with defaults, using default values for empty/unset fields.
128    ///
129    /// This is used when loading configs where fields like `command` may be empty
130    /// (serde's default) because they weren't specified in the user's config file.
131    pub fn merge_with_defaults(self, defaults: &LspServerConfig) -> LspServerConfig {
132        LspServerConfig {
133            command: if self.command.is_empty() {
134                defaults.command.clone()
135            } else {
136                self.command
137            },
138            args: if self.args.is_empty() {
139                defaults.args.clone()
140            } else {
141                self.args
142            },
143            enabled: self.enabled,
144            auto_start: self.auto_start,
145            process_limits: self.process_limits,
146            initialization_options: self
147                .initialization_options
148                .or_else(|| defaults.initialization_options.clone()),
149            env: {
150                let mut merged = defaults.env.clone();
151                merged.extend(self.env);
152                merged
153            },
154            language_id_overrides: {
155                let mut merged = defaults.language_id_overrides.clone();
156                merged.extend(self.language_id_overrides);
157                merged
158            },
159        }
160    }
161}