Skip to main content

gpui_cn/terminal/
options.rs

1//! Options for the engine and for a local terminal.
2
3use std::collections::BTreeMap;
4use std::path::PathBuf;
5use std::time::Duration;
6
7/// Engine limits, with Ghostty's defaults: 10,000,000 bytes of scrollback
8/// (`scrollback-limit` in `ghostty +show-config --default`), so a busy
9/// terminal holds as much history as Ghostty's and no more.
10#[derive(Clone, Debug, PartialEq, Eq)]
11#[non_exhaustive]
12pub struct EngineOptions {
13    /// Maximum scrollback in bytes. Zero disables scrollback.
14    pub(crate) scrollback_bytes: usize,
15    /// Maximum scrollback in lines; `None` is unlimited.
16    pub(crate) scrollback_lines: Option<usize>,
17    /// The terminfo name reported to XTGETTCAP.
18    pub(crate) terminfo_name: String,
19    /// Maximum decoded bytes per Kitty clipboard write.
20    pub(crate) clipboard_write_max_bytes: usize,
21    pub(crate) park: crate::terminal::park::ParkOptions,
22}
23
24impl Default for EngineOptions {
25    fn default() -> Self {
26        Self {
27            scrollback_bytes: 10_000_000,
28            scrollback_lines: None,
29            terminfo_name: ghostty_vt::terminfo::TERM.to_owned(),
30            clipboard_write_max_bytes: 64 << 20,
31            park: crate::terminal::park::ParkOptions::default(),
32        }
33    }
34}
35
36impl EngineOptions {
37    /// Set the scrollback byte limit.
38    #[must_use]
39    pub fn with_scrollback_bytes(mut self, bytes: usize) -> Self {
40        self.scrollback_bytes = bytes;
41        self
42    }
43
44    /// Set the scrollback line limit.
45    #[must_use]
46    pub fn with_scrollback_lines(mut self, lines: Option<usize>) -> Self {
47        self.scrollback_lines = lines;
48        self
49    }
50
51    /// Set the terminfo name.
52    #[must_use]
53    pub fn with_terminfo_name(mut self, name: impl Into<String>) -> Self {
54        self.terminfo_name = name.into();
55        self
56    }
57
58    /// Set the largest decoded Kitty clipboard write.
59    #[must_use]
60    pub fn with_clipboard_write_max_bytes(mut self, bytes: usize) -> Self {
61        self.clipboard_write_max_bytes = bytes;
62        self
63    }
64
65    /// Set when an idle terminal parks and where its snapshot goes. On by
66    /// default, after 60 seconds idle, in
67    /// memory.
68    #[must_use]
69    pub fn with_park(mut self, park: crate::terminal::park::ParkOptions) -> Self {
70        self.park = park;
71        self
72    }
73
74    /// When an idle terminal parks and where its snapshot goes.
75    pub fn park(&self) -> &crate::terminal::park::ParkOptions {
76        &self.park
77    }
78
79    /// The scrollback byte limit. Zero disables scrollback.
80    pub fn scrollback_bytes(&self) -> usize {
81        self.scrollback_bytes
82    }
83
84    /// The scrollback line limit; `None` is unlimited.
85    pub fn scrollback_lines(&self) -> Option<usize> {
86        self.scrollback_lines
87    }
88
89    /// The terminfo name reported to XTGETTCAP.
90    pub fn terminfo_name(&self) -> &str {
91        &self.terminfo_name
92    }
93
94    /// The largest decoded Kitty clipboard write.
95    pub fn clipboard_write_max_bytes(&self) -> usize {
96        self.clipboard_write_max_bytes
97    }
98}
99
100/// Where a local terminal starts.
101#[derive(Clone, Debug, Default, PartialEq, Eq)]
102pub enum WorkingDirectory {
103    /// The host process's working directory.
104    #[default]
105    Inherit,
106    /// The user's home directory.
107    Home,
108    /// A path; a missing path falls back to home, then the root.
109    Path(PathBuf),
110}
111
112/// Which shell integration script to inject.
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
114pub enum ShellIntegration {
115    /// Pick by the program's file name.
116    #[default]
117    Detect,
118    /// Inject nothing.
119    None,
120    /// Bash through `--posix` and `ENV`.
121    Bash,
122    /// Zsh through `ZDOTDIR`.
123    Zsh,
124    /// Fish through `XDG_DATA_DIRS`.
125    Fish,
126    /// Elvish through `XDG_DATA_DIRS`.
127    Elvish,
128    /// Nushell through `XDG_DATA_DIRS` and `--execute`.
129    Nushell,
130}
131
132/// Options for a local terminal with its own pty.
133#[derive(Clone, Debug, PartialEq, Eq)]
134#[non_exhaustive]
135pub struct LocalTerminalOptions {
136    /// Engine limits.
137    pub(crate) engine: EngineOptions,
138    /// The program; `None` runs the user's shell.
139    pub(crate) program: Option<PathBuf>,
140    /// Arguments for the program.
141    pub(crate) args: Vec<String>,
142    /// Working directory.
143    pub(crate) cwd: WorkingDirectory,
144    /// Extra environment variables.
145    pub(crate) env: BTreeMap<String, String>,
146    /// Environment variables to remove.
147    pub(crate) env_remove: Vec<String>,
148    /// Bytes written to the program right after it starts.
149    pub(crate) input: Vec<u8>,
150    /// Shell integration.
151    pub(crate) shell_integration: ShellIntegration,
152    /// Shell integration features, as `GHOSTTY_SHELL_FEATURES`.
153    pub(crate) shell_integration_features: Vec<String>,
154    /// Start the shell as a login shell.
155    pub(crate) login: bool,
156    /// An exit faster than this is reported as abnormal.
157    pub(crate) abnormal_exit_runtime: Duration,
158}
159
160impl Default for LocalTerminalOptions {
161    fn default() -> Self {
162        Self {
163            engine: EngineOptions::default(),
164            program: None,
165            args: Vec::new(),
166            cwd: WorkingDirectory::Inherit,
167            env: BTreeMap::new(),
168            env_remove: Vec::new(),
169            input: Vec::new(),
170            shell_integration: ShellIntegration::Detect,
171            shell_integration_features: vec![
172                "cursor".to_owned(),
173                "sudo".to_owned(),
174                "title".to_owned(),
175            ],
176            login: true,
177            abnormal_exit_runtime: Duration::from_millis(250),
178        }
179    }
180}
181
182impl LocalTerminalOptions {
183    /// Set the program and its arguments.
184    #[must_use]
185    pub fn with_program(
186        mut self,
187        program: impl Into<PathBuf>,
188        args: impl IntoIterator<Item = impl Into<String>>,
189    ) -> Self {
190        self.program = Some(program.into());
191        self.args = args.into_iter().map(Into::into).collect();
192        self
193    }
194
195    /// Set the working directory.
196    #[must_use]
197    pub fn with_cwd(mut self, cwd: WorkingDirectory) -> Self {
198        self.cwd = cwd;
199        self
200    }
201
202    /// Add an environment variable.
203    #[must_use]
204    pub fn with_env(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
205        self.env.insert(name.into(), value.into());
206        self
207    }
208
209    /// Set the shell integration.
210    #[must_use]
211    pub fn with_shell_integration(mut self, integration: ShellIntegration) -> Self {
212        self.shell_integration = integration;
213        self
214    }
215
216    /// Set the engine options.
217    #[must_use]
218    pub fn with_engine(mut self, engine: EngineOptions) -> Self {
219        self.engine = engine;
220        self
221    }
222
223    /// Set whether the shell starts as a login shell.
224    #[must_use]
225    pub fn with_login(mut self, login: bool) -> Self {
226        self.login = login;
227        self
228    }
229
230    /// Set the bytes written right after the program starts.
231    #[must_use]
232    pub fn with_input(mut self, input: impl Into<Vec<u8>>) -> Self {
233        self.input = input.into();
234        self
235    }
236
237    /// Remove an environment variable from the program's environment.
238    #[must_use]
239    pub fn without_env(mut self, name: impl Into<String>) -> Self {
240        self.env_remove.push(name.into());
241        self
242    }
243
244    /// Set the shell integration features, as `GHOSTTY_SHELL_FEATURES`.
245    #[must_use]
246    pub fn with_shell_integration_features(
247        mut self,
248        features: impl IntoIterator<Item = impl Into<String>>,
249    ) -> Self {
250        self.shell_integration_features = features.into_iter().map(Into::into).collect();
251        self
252    }
253
254    /// Set the run time under which an exit is reported as abnormal.
255    #[must_use]
256    pub fn with_abnormal_exit_runtime(mut self, runtime: Duration) -> Self {
257        self.abnormal_exit_runtime = runtime;
258        self
259    }
260
261    /// The engine options.
262    pub fn engine(&self) -> &EngineOptions {
263        &self.engine
264    }
265
266    /// The program; `None` runs the user's shell.
267    pub fn program(&self) -> Option<&std::path::Path> {
268        self.program.as_deref()
269    }
270
271    /// The program's arguments.
272    pub fn args(&self) -> &[String] {
273        &self.args
274    }
275
276    /// The working directory.
277    pub fn cwd(&self) -> &WorkingDirectory {
278        &self.cwd
279    }
280
281    /// Extra environment variables.
282    pub fn env(&self) -> &BTreeMap<String, String> {
283        &self.env
284    }
285
286    /// Environment variables removed from the program's environment.
287    pub fn env_remove(&self) -> &[String] {
288        &self.env_remove
289    }
290
291    /// The bytes written right after the program starts.
292    pub fn input(&self) -> &[u8] {
293        &self.input
294    }
295
296    /// The shell integration.
297    pub fn shell_integration(&self) -> ShellIntegration {
298        self.shell_integration
299    }
300
301    /// The shell integration features.
302    pub fn shell_integration_features(&self) -> &[String] {
303        &self.shell_integration_features
304    }
305
306    /// Whether the shell starts as a login shell.
307    pub fn login(&self) -> bool {
308        self.login
309    }
310
311    /// The run time under which an exit is reported as abnormal.
312    pub fn abnormal_exit_runtime(&self) -> Duration {
313        self.abnormal_exit_runtime
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    #[test]
320    fn the_scrollback_limit_is_ghosttys_default() {
321        assert_eq!(
322            super::EngineOptions::default().scrollback_bytes(),
323            10_000_000
324        );
325    }
326}