Skip to content

Commit a519336

Browse files
committed
feat: use dynamic Linux terminal ids
1 parent d01e637 commit a519336

12 files changed

Lines changed: 145 additions & 152 deletions

File tree

‎src-tauri/Cargo.lock‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎src-tauri/config.example.toml‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ updateEndpoint = "https://github.com/cst8t/gitmun/releases/latest/download/lates
8282
# "Native" applies no overrides; uses full hardware acceleration.
8383
linuxGraphicsMode = "Auto"
8484

85+
# Terminal emulator for Linux repository opens.
86+
# Use "auto", "custom", or a known terminal ID such as "ghostty" or "gnome-terminal".
87+
linuxTerminalEmulator = "auto"
88+
89+
# Custom Linux terminal command. Used only when linuxTerminalEmulator is "custom".
90+
# Use {path} where the repository path should be inserted.
91+
linuxTerminalCustomCommand = ""
92+
8593
# Behaviour when opening a repository.
8694
# "Ask" prompts for which window to use.
8795
# "ExistingWindow" opens the repository in the current window.

‎src-tauri/src/commands/repo.rs‎

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
#[cfg(target_os = "linux")]
2-
use crate::git::types::LinuxTerminalEmulator;
31
use crate::git::types::{
42
CloneRequest, CommitDetails, CommitDetailsRequest, CommitFileItem, CommitFilesRequest,
53
CommitMarkers, CommitMessageRecovery, CommitRequest, DiffRequest, ExportCommitPatchRequest,
6-
ExportPatchRequest,
7-
ExternalDiffRequest, FetchRequest, FileDiff, FileRequest, GitIdentity, HunkStageRequest,
8-
IdentityRequest, ImportPatchRequest, NumstatRequest, NumstatResult, OperationResult,
9-
PullAnalysis, PullStrategyRequest, PushRequest, PushResult, RepoRequest, RepoStatus,
10-
SetIdentityRequest, SshAllowedSignerStatus, StageFilesRequest, StashEntry, StashPushRequest,
11-
StashRequest, SubmoduleActionRequest,
4+
ExportPatchRequest, ExternalDiffRequest, FetchRequest, FileDiff, FileRequest, GitIdentity,
5+
HunkStageRequest, IdentityRequest, ImportPatchRequest, NumstatRequest, NumstatResult,
6+
OperationResult, PullAnalysis, PullStrategyRequest, PushRequest, PushResult, RepoRequest,
7+
RepoStatus, SetIdentityRequest, SshAllowedSignerStatus, StageFilesRequest, StashEntry,
8+
StashPushRequest, StashRequest, SubmoduleActionRequest,
129
};
10+
#[cfg(target_os = "linux")]
11+
use crate::git::types::{LINUX_TERMINAL_AUTO_ID, LINUX_TERMINAL_CUSTOM_ID};
1312
use crate::{AppState, CloneCancelFlag, configure_command};
1413
use serde::{Deserialize, Serialize};
1514
use std::io::Read;
@@ -268,7 +267,7 @@ fn default_file_manager_label() -> &'static str {
268267
fn default_terminal_label(settings: &crate::git::types::Settings) -> String {
269268
#[cfg(target_os = "linux")]
270269
{
271-
return linux_terminal_label(settings).to_string();
270+
return linux_terminal_label(settings);
272271
}
273272

274273
#[cfg(not(target_os = "linux"))]
@@ -370,9 +369,10 @@ fn open_terminal_at_linux(
370369
}
371370

372371
#[cfg(target_os = "linux")]
373-
fn linux_terminal_label(settings: &crate::git::types::Settings) -> &'static str {
374-
linux_terminal_launch::terminal_label(linux_terminal_preference(
375-
settings.linux_terminal_emulator,
372+
fn linux_terminal_label(settings: &crate::git::types::Settings) -> String {
373+
let registry = linux_terminal_launch::TerminalRegistry::with_known_terminals();
374+
registry.label_for_preference(&linux_terminal_preference(
375+
settings.linux_terminal_emulator.as_str(),
376376
))
377377
}
378378

@@ -383,40 +383,20 @@ fn linux_terminal_launcher(
383383
) -> linux_terminal_launch::TerminalLauncher {
384384
linux_terminal_launch::TerminalLauncher::new()
385385
.working_dir(path)
386-
.preference(linux_terminal_preference(settings.linux_terminal_emulator))
386+
.registry(linux_terminal_launch::TerminalRegistry::with_known_terminals())
387+
.preference(linux_terminal_preference(
388+
settings.linux_terminal_emulator.as_str(),
389+
))
387390
.custom_command(settings.linux_terminal_custom_command.clone())
388391
.detach_from_parent(true)
389392
}
390393

391394
#[cfg(target_os = "linux")]
392-
fn linux_terminal_preference(
393-
emulator: LinuxTerminalEmulator,
394-
) -> linux_terminal_launch::TerminalPreference {
395-
use linux_terminal_launch::{KnownTerminal, TerminalPreference};
396-
397-
match emulator {
398-
LinuxTerminalEmulator::Auto => TerminalPreference::Auto,
399-
LinuxTerminalEmulator::Konsole => TerminalPreference::Known(KnownTerminal::Konsole),
400-
LinuxTerminalEmulator::GnomeTerminal => {
401-
TerminalPreference::Known(KnownTerminal::GnomeTerminal)
402-
}
403-
LinuxTerminalEmulator::GnomeConsole => {
404-
TerminalPreference::Known(KnownTerminal::GnomeConsole)
405-
}
406-
LinuxTerminalEmulator::Xfce4Terminal => {
407-
TerminalPreference::Known(KnownTerminal::Xfce4Terminal)
408-
}
409-
LinuxTerminalEmulator::MateTerminal => {
410-
TerminalPreference::Known(KnownTerminal::MateTerminal)
411-
}
412-
LinuxTerminalEmulator::Lxterminal => TerminalPreference::Known(KnownTerminal::Lxterminal),
413-
LinuxTerminalEmulator::Alacritty => TerminalPreference::Known(KnownTerminal::Alacritty),
414-
LinuxTerminalEmulator::Ghostty => TerminalPreference::Known(KnownTerminal::Ghostty),
415-
LinuxTerminalEmulator::Kitty => TerminalPreference::Known(KnownTerminal::Kitty),
416-
LinuxTerminalEmulator::WezTerm => TerminalPreference::Known(KnownTerminal::WezTerm),
417-
LinuxTerminalEmulator::Foot => TerminalPreference::Known(KnownTerminal::Foot),
418-
LinuxTerminalEmulator::Xterm => TerminalPreference::Known(KnownTerminal::Xterm),
419-
LinuxTerminalEmulator::Custom => TerminalPreference::Custom,
395+
fn linux_terminal_preference(id: &str) -> linux_terminal_launch::TerminalPreference {
396+
match id {
397+
LINUX_TERMINAL_AUTO_ID => linux_terminal_launch::TerminalPreference::Auto,
398+
LINUX_TERMINAL_CUSTOM_ID => linux_terminal_launch::TerminalPreference::Custom,
399+
other => linux_terminal_launch::TerminalPreference::registered(other),
420400
}
421401
}
422402

‎src-tauri/src/commands/settings.rs‎

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::git::types::{
22
AvatarProviderMode, BackendMode, CommitDateMode, CommitPrimaryAction, ExternalDiffTool,
3-
LinuxGraphicsMode, LinuxTerminalEmulator, OperationResult, RepoOpenBehaviour, RowStriping,
4-
Settings, ThemeMode,
3+
LINUX_TERMINAL_AUTO_ID, LINUX_TERMINAL_CUSTOM_ID, LinuxGraphicsMode, LinuxTerminalId,
4+
OperationResult, RepoOpenBehaviour, RowStriping, Settings, ThemeMode,
5+
normalise_linux_terminal_id,
56
};
67
use crate::{AppState, configure_command, git_command};
78
use reqwest::header::{ACCEPT, HeaderValue, RANGE};
@@ -1130,8 +1131,8 @@ pub fn set_linux_graphics_mode(
11301131
#[derive(Debug, Clone, Serialize)]
11311132
#[serde(rename_all = "camelCase")]
11321133
pub struct LinuxTerminalOption {
1133-
pub emulator: LinuxTerminalEmulator,
1134-
pub label: &'static str,
1134+
pub id: LinuxTerminalId,
1135+
pub label: String,
11351136
}
11361137

11371138
#[tauri::command]
@@ -1141,74 +1142,52 @@ pub fn get_linux_terminal_options() -> Vec<LinuxTerminalOption> {
11411142

11421143
#[cfg(target_os = "linux")]
11431144
fn linux_terminal_options() -> Vec<LinuxTerminalOption> {
1145+
let registry = linux_terminal_launch::TerminalRegistry::with_known_terminals();
11441146
let mut options = vec![LinuxTerminalOption {
1145-
emulator: LinuxTerminalEmulator::Auto,
1146-
label: linux_terminal_launch::terminal_label(
1147-
linux_terminal_launch::TerminalPreference::Auto,
1148-
),
1147+
id: LINUX_TERMINAL_AUTO_ID.to_string(),
1148+
label: registry.label_for_preference(&linux_terminal_launch::TerminalPreference::Auto),
11491149
}];
11501150

11511151
options.extend(
1152-
linux_terminal_launch::known_terminals()
1152+
registry
1153+
.definitions()
11531154
.iter()
11541155
.map(|terminal| LinuxTerminalOption {
1155-
emulator: linux_terminal_emulator(terminal.terminal),
1156-
label: terminal.label,
1156+
id: terminal.id().to_string(),
1157+
label: terminal.label().to_string(),
11571158
}),
11581159
);
11591160

11601161
options.push(LinuxTerminalOption {
1161-
emulator: LinuxTerminalEmulator::Custom,
1162-
label: linux_terminal_launch::terminal_label(
1163-
linux_terminal_launch::TerminalPreference::Custom,
1164-
),
1162+
id: LINUX_TERMINAL_CUSTOM_ID.to_string(),
1163+
label: registry.label_for_preference(&linux_terminal_launch::TerminalPreference::Custom),
11651164
});
11661165

11671166
options
11681167
}
11691168

1170-
#[cfg(target_os = "linux")]
1171-
fn linux_terminal_emulator(
1172-
terminal: linux_terminal_launch::KnownTerminal,
1173-
) -> LinuxTerminalEmulator {
1174-
match terminal {
1175-
linux_terminal_launch::KnownTerminal::Konsole => LinuxTerminalEmulator::Konsole,
1176-
linux_terminal_launch::KnownTerminal::GnomeTerminal => LinuxTerminalEmulator::GnomeTerminal,
1177-
linux_terminal_launch::KnownTerminal::GnomeConsole => LinuxTerminalEmulator::GnomeConsole,
1178-
linux_terminal_launch::KnownTerminal::Xfce4Terminal => LinuxTerminalEmulator::Xfce4Terminal,
1179-
linux_terminal_launch::KnownTerminal::MateTerminal => LinuxTerminalEmulator::MateTerminal,
1180-
linux_terminal_launch::KnownTerminal::Lxterminal => LinuxTerminalEmulator::Lxterminal,
1181-
linux_terminal_launch::KnownTerminal::Alacritty => LinuxTerminalEmulator::Alacritty,
1182-
linux_terminal_launch::KnownTerminal::Ghostty => LinuxTerminalEmulator::Ghostty,
1183-
linux_terminal_launch::KnownTerminal::Kitty => LinuxTerminalEmulator::Kitty,
1184-
linux_terminal_launch::KnownTerminal::WezTerm => LinuxTerminalEmulator::WezTerm,
1185-
linux_terminal_launch::KnownTerminal::Foot => LinuxTerminalEmulator::Foot,
1186-
linux_terminal_launch::KnownTerminal::Xterm => LinuxTerminalEmulator::Xterm,
1187-
}
1188-
}
1189-
11901169
#[cfg(not(target_os = "linux"))]
11911170
fn linux_terminal_options() -> Vec<LinuxTerminalOption> {
11921171
vec![
11931172
LinuxTerminalOption {
1194-
emulator: LinuxTerminalEmulator::Auto,
1195-
label: "Terminal",
1173+
id: LINUX_TERMINAL_AUTO_ID.to_string(),
1174+
label: "Terminal".to_string(),
11961175
},
11971176
LinuxTerminalOption {
1198-
emulator: LinuxTerminalEmulator::Custom,
1199-
label: "Terminal",
1177+
id: LINUX_TERMINAL_CUSTOM_ID.to_string(),
1178+
label: "Terminal".to_string(),
12001179
},
12011180
]
12021181
}
12031182

12041183
#[tauri::command]
12051184
pub fn set_linux_terminal_emulator(
1206-
linux_terminal_emulator: LinuxTerminalEmulator,
1185+
linux_terminal_emulator: LinuxTerminalId,
12071186
state: tauri::State<'_, AppState>,
12081187
) -> Settings {
12091188
state
12101189
.git_service
1211-
.set_linux_terminal_emulator(linux_terminal_emulator)
1190+
.set_linux_terminal_emulator(normalise_linux_terminal_id(&linux_terminal_emulator))
12121191
}
12131192

12141193
#[tauri::command]

‎src-tauri/src/config_file.rs‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,33 @@ mod tests {
212212
assert_eq!(settings.ui_text_scale, 1.2);
213213
}
214214

215+
#[test]
216+
fn load_toml_normalises_old_linux_terminal_values() {
217+
let dir = TempDir::new().unwrap();
218+
let toml_path = dir.path().join("config.toml");
219+
let json_path = dir.path().join("config.json");
220+
221+
write_file(&toml_path, "linuxTerminalEmulator = \"GnomeTerminal\"\n");
222+
223+
let (settings, should_persist) = load_or_migrate(&toml_path, &json_path);
224+
assert!(!should_persist);
225+
assert_eq!(settings.linux_terminal_emulator, "gnome-terminal");
226+
}
227+
228+
#[test]
229+
fn load_toml_defaults_empty_linux_terminal_value() {
230+
let settings: Settings = toml::from_str("linuxTerminalEmulator = \"\"\n").unwrap();
231+
232+
assert_eq!(settings.linux_terminal_emulator, "auto");
233+
}
234+
235+
#[test]
236+
fn load_toml_preserves_unknown_linux_terminal_id() {
237+
let settings: Settings = toml::from_str("linuxTerminalEmulator = \"coolterm\"\n").unwrap();
238+
239+
assert_eq!(settings.linux_terminal_emulator, "coolterm");
240+
}
241+
215242
#[test]
216243
fn load_toml_ignores_old_ms_store_update_flow_config() {
217244
let dir = TempDir::new().unwrap();

‎src-tauri/src/git/handler.rs‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -352,12 +352,9 @@ impl GitService {
352352
})
353353
}
354354

355-
pub fn set_linux_terminal_emulator(
356-
&self,
357-
emulator: super::types::LinuxTerminalEmulator,
358-
) -> Settings {
355+
pub fn set_linux_terminal_emulator(&self, id: super::types::LinuxTerminalId) -> Settings {
359356
self.update_settings(|settings| {
360-
settings.linux_terminal_emulator = emulator;
357+
settings.linux_terminal_emulator = id;
361358
})
362359
}
363360

‎src-tauri/src/git/types.rs‎

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -112,29 +112,10 @@ impl Default for LinuxGraphicsMode {
112112
}
113113
}
114114

115-
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
116-
pub enum LinuxTerminalEmulator {
117-
Auto,
118-
Konsole,
119-
GnomeTerminal,
120-
GnomeConsole,
121-
Xfce4Terminal,
122-
MateTerminal,
123-
Lxterminal,
124-
Alacritty,
125-
Ghostty,
126-
Kitty,
127-
WezTerm,
128-
Foot,
129-
Xterm,
130-
Custom,
131-
}
132-
133-
impl Default for LinuxTerminalEmulator {
134-
fn default() -> Self {
135-
Self::Auto
136-
}
137-
}
115+
pub type LinuxTerminalId = String;
116+
117+
pub const LINUX_TERMINAL_AUTO_ID: &str = "auto";
118+
pub const LINUX_TERMINAL_CUSTOM_ID: &str = "custom";
138119

139120
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
140121
pub enum RepoOpenBehaviour {
@@ -227,6 +208,38 @@ where
227208
Ok(normalise_ui_text_scale(scale))
228209
}
229210

211+
fn default_linux_terminal_id() -> LinuxTerminalId {
212+
LINUX_TERMINAL_AUTO_ID.to_string()
213+
}
214+
215+
pub fn normalise_linux_terminal_id(value: &str) -> LinuxTerminalId {
216+
match value.trim() {
217+
"" | "Auto" | "auto" => LINUX_TERMINAL_AUTO_ID.to_string(),
218+
"Custom" | "custom" => LINUX_TERMINAL_CUSTOM_ID.to_string(),
219+
"Konsole" | "konsole" => "konsole".to_string(),
220+
"GnomeTerminal" | "gnome-terminal" => "gnome-terminal".to_string(),
221+
"GnomeConsole" | "gnome-console" => "gnome-console".to_string(),
222+
"Xfce4Terminal" | "xfce4-terminal" => "xfce4-terminal".to_string(),
223+
"MateTerminal" | "mate-terminal" => "mate-terminal".to_string(),
224+
"Lxterminal" | "lxterminal" => "lxterminal".to_string(),
225+
"Alacritty" | "alacritty" => "alacritty".to_string(),
226+
"Ghostty" | "ghostty" => "ghostty".to_string(),
227+
"Kitty" | "kitty" => "kitty".to_string(),
228+
"WezTerm" | "wezterm" => "wezterm".to_string(),
229+
"Foot" | "foot" => "foot".to_string(),
230+
"Xterm" | "xterm" => "xterm".to_string(),
231+
other => other.to_string(),
232+
}
233+
}
234+
235+
fn deserialise_linux_terminal_id<'de, D>(deserialiser: D) -> Result<LinuxTerminalId, D::Error>
236+
where
237+
D: Deserializer<'de>,
238+
{
239+
let value = String::deserialize(deserialiser)?;
240+
Ok(normalise_linux_terminal_id(&value))
241+
}
242+
230243
#[derive(Debug, Clone, Serialize, Deserialize)]
231244
#[serde(default, rename_all = "camelCase")]
232245
pub struct Settings {
@@ -279,8 +292,11 @@ pub struct Settings {
279292
pub update_endpoint: String,
280293
#[serde(default)]
281294
pub linux_graphics_mode: LinuxGraphicsMode,
282-
#[serde(default)]
283-
pub linux_terminal_emulator: LinuxTerminalEmulator,
295+
#[serde(
296+
default = "default_linux_terminal_id",
297+
deserialize_with = "deserialise_linux_terminal_id"
298+
)]
299+
pub linux_terminal_emulator: LinuxTerminalId,
284300
#[serde(default)]
285301
pub linux_terminal_custom_command: String,
286302
#[serde(default)]
@@ -352,7 +368,7 @@ impl Default for Settings {
352368
auto_install_updates: false,
353369
update_endpoint: Self::default_update_endpoint(),
354370
linux_graphics_mode: LinuxGraphicsMode::Auto,
355-
linux_terminal_emulator: LinuxTerminalEmulator::Auto,
371+
linux_terminal_emulator: default_linux_terminal_id(),
356372
linux_terminal_custom_command: String::new(),
357373
repo_open_behaviour: RepoOpenBehaviour::Ask,
358374
git_executable_path: String::new(),

‎src/api/commands.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ import type {
5454
TagInfo,
5555
BackendMode,
5656
LinuxTerminalOption,
57-
LinuxTerminalEmulator,
57+
LinuxTerminalId,
5858
ThemeMode,
5959
ThemeBundle,
6060
UiTextScale,
@@ -502,7 +502,7 @@ export function getLinuxTerminalOptions(): Promise<LinuxTerminalOption[]> {
502502
return invoke<LinuxTerminalOption[]>("get_linux_terminal_options");
503503
}
504504

505-
export function setLinuxTerminalEmulator(linuxTerminalEmulator: LinuxTerminalEmulator): Promise<Settings> {
505+
export function setLinuxTerminalEmulator(linuxTerminalEmulator: LinuxTerminalId): Promise<Settings> {
506506
return invoke<Settings>("set_linux_terminal_emulator", {linuxTerminalEmulator});
507507
}
508508

0 commit comments

Comments
 (0)