From 517464894017a698e54c351dbf677e16814de056 Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 13:34:39 -0700 Subject: [PATCH 1/6] Add launch environment process helper --- src/lib.rs | 1 + src/process.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/process.rs diff --git a/src/lib.rs b/src/lib.rs index 42ba3ca..3ff3adc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod matching; pub mod notification; pub mod opencode; pub mod projects; +pub mod process; pub mod realm; pub mod settings; pub mod task_palette; diff --git a/src/process.rs b/src/process.rs new file mode 100644 index 0000000..fe85ca8 --- /dev/null +++ b/src/process.rs @@ -0,0 +1,85 @@ +use std::env; +use std::ffi::{OsStr, OsString}; +use std::process::Command; +use std::sync::LazyLock; + +static LAUNCH_ENV: LazyLock> = + LazyLock::new(|| env::vars_os().collect()); + +pub fn launch_env() -> Vec<(OsString, OsString)> { + LAUNCH_ENV.clone() +} + +pub fn command(program: impl AsRef) -> Command { + let mut command = Command::new(program); + command.envs(launch_env()); + command +} + +pub fn tmux_env_args() -> Vec { + tmux_env_args_from(launch_env()) +} + +pub(crate) fn tmux_env_args_from(env: I) -> Vec +where + I: IntoIterator, + K: AsRef, + V: AsRef, +{ + let mut entries: Vec<(String, String)> = env + .into_iter() + .filter_map(|(key, value)| { + let key = key.as_ref().to_str()?; + let value = value.as_ref().to_str()?; + should_forward_tmux_env(key).then(|| (key.to_string(), value.to_string())) + }) + .collect(); + entries.sort_by(|left, right| left.0.cmp(&right.0)); + + let mut args = Vec::with_capacity(entries.len() * 2); + for (key, value) in entries { + args.push("-e".to_string()); + args.push(format!("{key}={value}")); + } + args +} + +fn should_forward_tmux_env(key: &str) -> bool { + !key.is_empty() + && !key.contains('=') + && !matches!(key, "TERM" | "TERMCAP" | "TMUX" | "TMUX_PANE") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_tmux_env_args_forward_user_env() { + let args = tmux_env_args_from([ + ("GIT_LFS_SKIP_SMUDGE", "1"), + ("GIT_SSH_COMMAND", "ssh -i /tmp/key"), + ]); + + assert_eq!( + args, + vec![ + "-e", + "GIT_LFS_SKIP_SMUDGE=1", + "-e", + "GIT_SSH_COMMAND=ssh -i /tmp/key" + ] + ); + } + + #[test] + fn test_tmux_env_args_skip_tmux_internal_env() { + let args = tmux_env_args_from([ + ("TERM", "xterm-256color"), + ("TMUX", "/tmp/tmux"), + ("TMUX_PANE", "%1"), + ]); + + assert!(args.is_empty()); + } +} From 94aa6f40f27a7d9b036c056e243dfeb7a580ae53 Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 13:34:39 -0700 Subject: [PATCH 2/6] Use launch environment for child commands --- src/app/runtime.rs | 7 ++++--- src/db/mod.rs | 4 ++-- src/git/mod.rs | 9 ++++++--- src/opencode/mod.rs | 10 +++++----- src/opencode/server.rs | 6 ++++-- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/app/runtime.rs b/src/app/runtime.rs index a4245de..9f5b577 100644 --- a/src/app/runtime.rs +++ b/src/app/runtime.rs @@ -8,6 +8,7 @@ use crate::git::{ git_check_branch_up_to_date, git_create_worktree, git_detect_default_branch, git_fetch, git_is_valid_repo, git_remove_worktree, }; +use crate::process::command; use crate::tmux::{ PopupThemeStyle, sanitize_session_name_for_project, tmux_create_session, tmux_kill_session, tmux_open_session_in_new_terminal, tmux_session_exists, tmux_show_popup, tmux_switch_client, @@ -120,7 +121,7 @@ impl CreateTaskRuntime for RealCreateTaskRuntime { } fn git_resolve_repo_root(&self, path: &Path) -> Result { - let output = std::process::Command::new("git") + let output = command("git") .args(["rev-parse", "--show-toplevel"]) .current_dir(path) .output() @@ -144,7 +145,7 @@ impl CreateTaskRuntime for RealCreateTaskRuntime { } fn git_current_branch(&self, path: &Path) -> Result { - let output = std::process::Command::new("git") + let output = command("git") .args(["branch", "--show-current"]) .current_dir(path) .output() @@ -171,7 +172,7 @@ impl CreateTaskRuntime for RealCreateTaskRuntime { } fn git_validate_branch(&self, repo_path: &Path, branch_name: &str) -> Result<()> { - let output = std::process::Command::new("git") + let output = command("git") .args(["check-ref-format", "--branch", branch_name]) .current_dir(repo_path) .output() diff --git a/src/db/mod.rs b/src/db/mod.rs index 7a0d38c..fd3aca0 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -5,7 +5,6 @@ use std::{ fs, future::Future, path::{Path, PathBuf}, - process::Command, str::FromStr, sync::OnceLock, }; @@ -17,6 +16,7 @@ use sqlx::{Row, SqlitePool}; use tokio::runtime::{Builder as RuntimeBuilder, Handle, RuntimeFlavor}; use uuid::Uuid; +use crate::process::command; use crate::types::{Category, CommandFrequency, Repo, Task}; const DEFAULT_TMUX_STATUS: &str = "unknown"; @@ -1238,7 +1238,7 @@ fn detect_remote_url(path: &Path) -> Option { } fn run_git(path: &Path, args: [&str; N]) -> Option { - let output = Command::new("git") + let output = command("git") .arg("-C") .arg(path) .args(args) diff --git a/src/git/mod.rs b/src/git/mod.rs index ac0490d..83968cb 100644 --- a/src/git/mod.rs +++ b/src/git/mod.rs @@ -1,8 +1,10 @@ use std::path::{Path, PathBuf}; -use std::process::{Command, Output}; +use std::process::Output; use anyhow::{Context, Result, bail}; +use crate::process::command; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Branch { pub name: String, @@ -198,7 +200,7 @@ pub fn git_delete_branch(repo_path: &Path, branch_name: &str) -> Result<()> { } pub fn git_is_valid_repo(path: &Path) -> bool { - Command::new("git") + command("git") .args(["rev-parse", "--git-dir"]) .current_dir(path) .output() @@ -420,7 +422,7 @@ where .into_iter() .map(|arg| arg.as_ref().to_string()) .collect(); - let output = Command::new("git") + let output = command("git") .args(args_vec.iter().map(String::as_str)) .current_dir(repo_path) .output() @@ -451,6 +453,7 @@ where mod tests { use super::*; use std::fs; + use std::process::Command; use tempfile::TempDir; diff --git a/src/opencode/mod.rs b/src/opencode/mod.rs index 80712b5..9f4b066 100644 --- a/src/opencode/mod.rs +++ b/src/opencode/mod.rs @@ -1,6 +1,5 @@ use std::env; use std::path::Path; -use std::process::Command; use std::sync::LazyLock; use anyhow::{Context, Result, anyhow, bail}; @@ -10,6 +9,7 @@ use reqwest::blocking::Client; use serde::Deserialize; use urlencoding::encode; +use crate::process::command; use crate::tmux::tmux_get_pane_pid; use crate::types::SessionStatus; @@ -78,7 +78,7 @@ pub fn opencode_launch(working_dir: &Path, session_id: Option) -> Result let binary = opencode_binary(); ensure_opencode_available(&binary)?; - let mut cmd = Command::new(&binary); + let mut cmd = command(&binary); cmd.current_dir(working_dir); if let Some(existing) = session_id { @@ -137,7 +137,7 @@ pub fn opencode_resume_session(session_id: &str, working_dir: &Path) -> Result<( let binary = opencode_binary(); ensure_opencode_available(&binary)?; - let output = Command::new(&binary) + let output = command(&binary) .args(["-s", session_id]) .current_dir(working_dir) .output() @@ -167,7 +167,7 @@ pub fn opencode_is_running_in_session(tmux_session_name: &str) -> bool { return false; }; - let process_output = Command::new("ps") + let process_output = command("ps") .args(["-p", &pane_pid.to_string(), "-o", "command="]) .output(); @@ -184,7 +184,7 @@ fn opencode_binary() -> String { } fn ensure_opencode_available(binary: &str) -> Result<()> { - let output = Command::new(binary).arg("--version").output(); + let output = command(binary).arg("--version").output(); match output { Ok(output) if output.status.success() => Ok(()), Ok(_) => bail!( diff --git a/src/opencode/server.rs b/src/opencode/server.rs index 837483e..1c3fea0 100644 --- a/src/opencode/server.rs +++ b/src/opencode/server.rs @@ -1,6 +1,8 @@ use reqwest::StatusCode; use reqwest::blocking::Client; -use std::process::{Command, Stdio}; + +use crate::process::command; +use std::process::Stdio; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -204,7 +206,7 @@ fn is_healthy_response(response: &str) -> bool { fn spawn_opencode_server(binary: &str, config: &ServerConfig) -> Result<(), String> { let port = config.port.to_string(); - let mut cmd = Command::new(binary); + let mut cmd = command(binary); if let Some(home_dir) = dirs::home_dir() { cmd.current_dir(home_dir); } From 2056a361128cca8de700669d7ff716d482a7390b Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 13:34:39 -0700 Subject: [PATCH 3/6] Forward launch environment to tmux sessions --- src/main.rs | 26 ++++++----- src/tmux/mod.rs | 114 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 26 deletions(-) diff --git a/src/main.rs b/src/main.rs index 585b968..b5d401c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ use std::{ io::{self, Write}, panic, - process::Command, str::FromStr, sync::{ Arc, Mutex, @@ -27,6 +26,7 @@ use opencode_kanban::{ app::App, cli::{self, RootCommand}, logging::{init_logging, print_log_location}, + process::{command, launch_env, tmux_env_args}, realm::{RootId, apply_message, init_application, should_quit}, theme::ThemePreset, tmux::{ensure_tmux_installed, tmux_session_exists}, @@ -100,6 +100,7 @@ async fn main() -> Result<()> { } fn run_app() -> Result { + let _ = launch_env(); let cli = Cli::parse(); if let Some(command) = cli.command { @@ -177,7 +178,7 @@ fn validate_runtime_environment() -> Result<()> { let exe_path = current_exe.to_string_lossy(); if tmux_session_exists(session_name) { - let status = Command::new("tmux") + let status = command("tmux") .args(["attach-session", "-t", session_name]) .status() .context("failed to attach to tmux session")?; @@ -185,16 +186,17 @@ fn validate_runtime_environment() -> Result<()> { std::process::exit(0); } - let status = Command::new("tmux") - .args([ - "new-session", - "-A", - "-s", - session_name, - "-c", - ".", - exe_path.as_ref(), - ]) + let mut args = vec!["new-session".to_string(), "-A".to_string()]; + args.extend(tmux_env_args()); + args.extend([ + "-s".to_string(), + session_name.to_string(), + "-c".to_string(), + ".".to_string(), + exe_path.to_string(), + ]); + let status = command("tmux") + .args(args) .status() .context("failed to create tmux session")?; ensure_command_succeeded("tmux new-session", status)?; diff --git a/src/tmux/mod.rs b/src/tmux/mod.rs index 9f88e13..91c40d1 100644 --- a/src/tmux/mod.rs +++ b/src/tmux/mod.rs @@ -7,6 +7,8 @@ use std::process::Command; use anyhow::{Context, Result, anyhow, bail}; use termlauncher::{Application, CustomTerminal, Error as TermlauncherError, Terminal}; +use crate::process::{command, tmux_env_args}; + const TMUX_SOCKET: &str = ""; #[derive(Debug, Clone, Eq, PartialEq)] @@ -32,7 +34,7 @@ pub struct TmuxSession { } pub fn ensure_tmux_installed() -> Result<()> { - let output = Command::new("tmux") + let output = command("tmux") .arg("-V") .output() .context("failed to execute tmux")?; @@ -90,7 +92,7 @@ pub fn tmux_switch_client( tmux_ensure_return_binding()?; tmux_ensure_overlay_binding(reopen_lines, style)?; - let output = Command::new("tmux") + let output = command("tmux") .args(switch_client_args(session_name)) .output() .context("failed to run tmux switch-client")?; @@ -139,7 +141,7 @@ pub fn tmux_show_popup(lines: &[String], style: &PopupThemeStyle) -> Result<()> } fn tmux_ensure_return_binding() -> Result<()> { - let output = Command::new("tmux") + let output = command("tmux") .args(return_binding_args()) .output() .context("failed to configure return key binding")?; @@ -147,7 +149,7 @@ fn tmux_ensure_return_binding() -> Result<()> { } fn tmux_ensure_overlay_binding(lines: &[String], style: &PopupThemeStyle) -> Result<()> { - let output = Command::new("tmux") + let output = command("tmux") .args(overlay_binding_args(lines, style)) .output() .context("failed to configure attach overlay key binding")?; @@ -333,7 +335,7 @@ fn sanitize_fragment(input: &str) -> String { } fn tmux_command() -> Command { - let mut cmd = Command::new("tmux"); + let mut cmd = command("tmux"); let socket = tmux_socket(); if !socket.is_empty() { cmd.args(socket_args(&socket)); @@ -354,14 +356,26 @@ fn has_session_args(session_name: &str) -> Vec { } fn new_session_args(session_name: &str, working_dir: &Path) -> Vec { - vec![ + new_session_args_with_env(session_name, working_dir, tmux_env_args()) +} + +fn new_session_args_with_env( + session_name: &str, + working_dir: &Path, + env_args: Vec, +) -> Vec { + let mut args = vec![ "new-session".to_string(), "-d".to_string(), + ]; + args.extend(env_args); + args.extend([ "-s".to_string(), session_name.to_string(), "-c".to_string(), working_dir.to_string_lossy().to_string(), - ] + ]); + args } fn kill_session_args(session_name: &str) -> Vec { @@ -619,12 +633,7 @@ mod tests { use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; fn sleep_for(duration: Duration) { - if let Ok(runtime) = tokio::runtime::Builder::new_current_thread() - .enable_time() - .build() - { - runtime.block_on(tokio::time::sleep(duration)); - } + std::thread::sleep(duration); } struct SessionCleanup { @@ -667,6 +676,19 @@ mod tests { None } + fn wait_for_file_contents(path: &Path, expected: &str, timeout: Duration) -> bool { + let start = Instant::now(); + while start.elapsed() < timeout { + if let Ok(contents) = std::fs::read_to_string(path) + && contents == expected + { + return true; + } + sleep_for(Duration::from_millis(50)); + } + false + } + #[test] fn test_sanitize_session_name() { assert_eq!( @@ -728,13 +750,43 @@ mod tests { #[test] fn test_new_session_args_builder() { - let args = new_session_args("ok-test", Path::new("/tmp/worktree")); + let args = new_session_args_with_env("ok-test", Path::new("/tmp/worktree"), Vec::new()); assert_eq!( args, vec!["new-session", "-d", "-s", "ok-test", "-c", "/tmp/worktree"] ); } + #[test] + fn test_new_session_args_builder_forwards_env() { + let args = new_session_args_with_env( + "ok-test", + Path::new("/tmp/worktree"), + vec![ + "-e".to_string(), + "GIT_LFS_SKIP_SMUDGE=1".to_string(), + "-e".to_string(), + "GIT_SSH_COMMAND=ssh -i /tmp/key".to_string(), + ], + ); + + assert_eq!( + args, + vec![ + "new-session", + "-d", + "-e", + "GIT_LFS_SKIP_SMUDGE=1", + "-e", + "GIT_SSH_COMMAND=ssh -i /tmp/key", + "-s", + "ok-test", + "-c", + "/tmp/worktree" + ] + ); + } + #[test] fn test_kill_session_args_builder() { assert_eq!( @@ -923,6 +975,40 @@ mod tests { assert!(!tmux_session_exists(&session_name)); } + #[test] + fn test_tmux_create_session_forwards_launch_env() { + if !tmux_available() { + return; + } + let Ok(expected) = std::env::var("GIT_LFS_SKIP_SMUDGE") else { + return; + }; + let session_name = unique_session_name("env"); + let _cleanup = SessionCleanup::new(session_name.clone()); + let output_path = std::env::temp_dir().join(format!( + "opencode-kanban-env-{}", + SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time should be valid") + .as_nanos() + )); + let output_path_string = output_path.to_string_lossy().to_string(); + let pane_command = format!( + "printf '%s' \"$GIT_LFS_SKIP_SMUDGE\" > {}; sleep 2", + shell_single_quote(&output_path_string) + ); + + tmux_create_session(&session_name, Path::new("."), Some(&pane_command)) + .expect("create session should succeed"); + + assert!(wait_for_file_contents( + &output_path, + &expected, + Duration::from_secs(2) + )); + let _ = std::fs::remove_file(&output_path); + } + fn tmux_available() -> bool { Command::new("tmux") .arg("-V") From 6d01ae1598c6fdf911ea3b2bb8e2b0cee3cabffa Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 14:28:14 -0700 Subject: [PATCH 4/6] Format env propagation changes --- src/lib.rs | 2 +- src/process.rs | 3 +-- src/tmux/mod.rs | 5 +---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3ff3adc..1933bff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,8 @@ pub mod logging; pub mod matching; pub mod notification; pub mod opencode; -pub mod projects; pub mod process; +pub mod projects; pub mod realm; pub mod settings; pub mod task_palette; diff --git a/src/process.rs b/src/process.rs index fe85ca8..9f1bd32 100644 --- a/src/process.rs +++ b/src/process.rs @@ -3,8 +3,7 @@ use std::ffi::{OsStr, OsString}; use std::process::Command; use std::sync::LazyLock; -static LAUNCH_ENV: LazyLock> = - LazyLock::new(|| env::vars_os().collect()); +static LAUNCH_ENV: LazyLock> = LazyLock::new(|| env::vars_os().collect()); pub fn launch_env() -> Vec<(OsString, OsString)> { LAUNCH_ENV.clone() diff --git a/src/tmux/mod.rs b/src/tmux/mod.rs index 91c40d1..ca60cd1 100644 --- a/src/tmux/mod.rs +++ b/src/tmux/mod.rs @@ -364,10 +364,7 @@ fn new_session_args_with_env( working_dir: &Path, env_args: Vec, ) -> Vec { - let mut args = vec![ - "new-session".to_string(), - "-d".to_string(), - ]; + let mut args = vec!["new-session".to_string(), "-d".to_string()]; args.extend(env_args); args.extend([ "-s".to_string(), From 4184e909f363e63ae83add30ff8278b9cf240b13 Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 14:28:14 -0700 Subject: [PATCH 5/6] Skip Codecov upload when token is unavailable --- .github/workflows/ci.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f0fa59a..9e71466 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,6 +30,8 @@ jobs: test: name: Test runs-on: ubuntu-latest + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -68,13 +70,14 @@ jobs: run: cargo llvm-cov --lib --codecov --output-path codecov.json - name: Upload coverage to Codecov + if: ${{ env.CODECOV_TOKEN != '' }} uses: codecov/codecov-action@v4 with: files: ./codecov.json flags: unittests name: code-coverage fail_ci_if_error: true - token: ${{ secrets.CODECOV_TOKEN }} + token: ${{ env.CODECOV_TOKEN }} lint: name: Lint From 6f68f029447af8119c78a111b80c7ce34bbe713b Mon Sep 17 00:00:00 2001 From: cc Date: Mon, 1 Jun 2026 14:28:14 -0700 Subject: [PATCH 6/6] Fix clippy match guard warnings --- src/app/dialogs.rs | 80 ++++++++++++++++---------------------------- src/app/input/key.rs | 26 +++++--------- 2 files changed, 37 insertions(+), 69 deletions(-) diff --git a/src/app/dialogs.rs b/src/app/dialogs.rs index ebcf8f8..8e600f2 100644 --- a/src/app/dialogs.rs +++ b/src/app/dialogs.rs @@ -243,20 +243,16 @@ fn handle_new_task_dialog_key( KeyCode::Right if state.focused_field == NewTaskField::UseExistingDirectory => { state.use_existing_directory = true; } - KeyCode::Left if state.focused_field == NewTaskField::Repo => { - if !repos.is_empty() { - state.repo_idx = state.repo_idx.saturating_sub(1); - if let Some(repo) = repos.get(state.repo_idx) { - state.base_input = repo_default_base(repo); - } + KeyCode::Left if state.focused_field == NewTaskField::Repo && !repos.is_empty() => { + state.repo_idx = state.repo_idx.saturating_sub(1); + if let Some(repo) = repos.get(state.repo_idx) { + state.base_input = repo_default_base(repo); } } - KeyCode::Right if state.focused_field == NewTaskField::Repo => { - if !repos.is_empty() { - state.repo_idx = (state.repo_idx + 1).min(repos.len() - 1); - if let Some(repo) = repos.get(state.repo_idx) { - state.base_input = repo_default_base(repo); - } + KeyCode::Right if state.focused_field == NewTaskField::Repo && !repos.is_empty() => { + state.repo_idx = (state.repo_idx + 1).min(repos.len() - 1); + if let Some(repo) = repos.get(state.repo_idx) { + state.base_input = repo_default_base(repo); } } KeyCode::Left if state.focused_field == NewTaskField::Create => { @@ -700,10 +696,8 @@ fn handle_new_project_dialog_key( KeyCode::Right if state.focused_field == NewProjectField::Cancel => { state.focused_field = NewProjectField::Create; } - KeyCode::Backspace => { - if state.focused_field == NewProjectField::Name { - state.name_input.pop(); - } + KeyCode::Backspace if state.focused_field == NewProjectField::Name => { + state.name_input.pop(); } KeyCode::Enter => { *follow_up = Some(match state.focused_field { @@ -711,10 +705,8 @@ fn handle_new_project_dialog_key( _ => Message::CreateProject, }); } - KeyCode::Char(ch) => { - if state.focused_field == NewProjectField::Name { - state.name_input.push(ch); - } + KeyCode::Char(ch) if state.focused_field == NewProjectField::Name => { + state.name_input.push(ch); } _ => {} } @@ -760,10 +752,8 @@ fn handle_category_input_dialog_key( KeyCode::Right if state.focused_field == CategoryInputField::Cancel => { state.focused_field = CategoryInputField::Confirm; } - KeyCode::Backspace => { - if state.focused_field == CategoryInputField::Name { - state.name_input.pop(); - } + KeyCode::Backspace if state.focused_field == CategoryInputField::Name => { + state.name_input.pop(); } KeyCode::Enter => { *follow_up = Some(match state.focused_field { @@ -771,10 +761,8 @@ fn handle_category_input_dialog_key( _ => Message::SubmitCategoryInput, }); } - KeyCode::Char(ch) => { - if state.focused_field == CategoryInputField::Name { - state.name_input.push(ch); - } + KeyCode::Char(ch) if state.focused_field == CategoryInputField::Name => { + state.name_input.push(ch); } _ => {} } @@ -967,10 +955,8 @@ fn handle_edit_task_dialog_key( KeyCode::Right if state.focused_field == EditTaskField::Cancel => { state.focused_field = EditTaskField::Save; } - KeyCode::Backspace => { - if state.focused_field == EditTaskField::Title { - state.title_input.pop(); - } + KeyCode::Backspace if state.focused_field == EditTaskField::Title => { + state.title_input.pop(); } KeyCode::Enter => { *follow_up = Some(match state.focused_field { @@ -978,10 +964,8 @@ fn handle_edit_task_dialog_key( _ => Message::ConfirmEditTask, }); } - KeyCode::Char(ch) => { - if state.focused_field == EditTaskField::Title { - state.title_input.push(ch); - } + KeyCode::Char(ch) if state.focused_field == EditTaskField::Title => { + state.title_input.push(ch); } _ => {} } @@ -1127,10 +1111,8 @@ fn handle_rename_project_dialog_key( KeyCode::Right if state.focused_field == RenameProjectField::Cancel => { state.focused_field = RenameProjectField::Confirm; } - KeyCode::Backspace => { - if state.focused_field == RenameProjectField::Name { - state.name_input.pop(); - } + KeyCode::Backspace if state.focused_field == RenameProjectField::Name => { + state.name_input.pop(); } KeyCode::Enter => { *follow_up = Some(match state.focused_field { @@ -1138,10 +1120,8 @@ fn handle_rename_project_dialog_key( _ => Message::ConfirmRenameProject, }); } - KeyCode::Char(ch) => { - if state.focused_field == RenameProjectField::Name { - state.name_input.push(ch); - } + KeyCode::Char(ch) if state.focused_field == RenameProjectField::Name => { + state.name_input.push(ch); } _ => {} } @@ -1187,10 +1167,8 @@ fn handle_rename_repo_dialog_key( KeyCode::Right if state.focused_field == RenameRepoField::Cancel => { state.focused_field = RenameRepoField::Confirm; } - KeyCode::Backspace => { - if state.focused_field == RenameRepoField::Name { - state.name_input.pop(); - } + KeyCode::Backspace if state.focused_field == RenameRepoField::Name => { + state.name_input.pop(); } KeyCode::Enter => { *follow_up = Some(match state.focused_field { @@ -1198,10 +1176,8 @@ fn handle_rename_repo_dialog_key( _ => Message::ConfirmRenameRepo, }); } - KeyCode::Char(ch) => { - if state.focused_field == RenameRepoField::Name { - state.name_input.push(ch); - } + KeyCode::Char(ch) if state.focused_field == RenameRepoField::Name => { + state.name_input.push(ch); } _ => {} } diff --git a/src/app/input/key.rs b/src/app/input/key.rs index 736a9fe..e7265ca 100644 --- a/src/app/input/key.rs +++ b/src/app/input/key.rs @@ -86,15 +86,11 @@ impl App { KeyAction::OpenArchiveView => { self.update(Message::OpenArchiveView)?; } - KeyAction::ProjectNext => { - if self.current_view == View::Board { - self.update(Message::SwitchToNextProject)?; - } + KeyAction::ProjectNext if self.current_view == View::Board => { + self.update(Message::SwitchToNextProject)?; } - KeyAction::ProjectPrev => { - if self.current_view == View::Board { - self.update(Message::SwitchToPrevProject)?; - } + KeyAction::ProjectPrev if self.current_view == View::Board => { + self.update(Message::SwitchToPrevProject)?; } _ => {} } @@ -116,11 +112,9 @@ impl App { self.cycle_detail_focus(); return Ok(()); } - KeyCode::Enter | KeyCode::Char('e') => { - if self.detail_focus == DetailFocus::Log { - self.toggle_selected_log_entry(false); - return Ok(()); - } + KeyCode::Enter | KeyCode::Char('e') if self.detail_focus == DetailFocus::Log => { + self.toggle_selected_log_entry(false); + return Ok(()); } KeyCode::Char('f') => { if self.detail_focus == DetailFocus::Log { @@ -351,10 +345,8 @@ impl App { self.update(Message::DismissDialog)?; } } - KeyAction::ToggleCategoryEditMode => { - if self.active_dialog == ActiveDialog::None { - self.category_edit_mode = !self.category_edit_mode; - } + KeyAction::ToggleCategoryEditMode if self.active_dialog == ActiveDialog::None => { + self.category_edit_mode = !self.category_edit_mode; } _ => {} }