From 448f36a6a7eeb89d25a99c13a775b31d82bb937c Mon Sep 17 00:00:00 2001 From: multiplex55 <6619098+multiplex55@users.noreply.github.com> Date: Mon, 1 Sep 2025 17:24:27 -0400 Subject: [PATCH 1/3] Drop note external editor --- settings.json | 1 - src/gui/mod.rs | 6 ----- src/gui/note_panel.rs | 17 ++++++++----- src/plugin_editor.rs | 1 - src/settings.rs | 5 ---- src/settings_editor.rs | 18 -------------- tests/hide_after_run.rs | 34 +++++++++++++++++++++++--- tests/note_panel_shell.rs | 50 +++++++++++++++++++++++++++++++-------- tests/wezterm_command.rs | 4 ++-- 9 files changed, 84 insertions(+), 52 deletions(-) diff --git a/settings.json b/settings.json index 07a5b8b8..372a3c21 100644 --- a/settings.json +++ b/settings.json @@ -20,7 +20,6 @@ "note_save_on_close": false, "note_always_overwrite": false, "note_images_as_links": false, - "note_external_editor": "wezterm", "note_more_limit": 5, "enable_toasts": true, "toast_duration": 3.0, diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 28f8e1b7..c30fa5f8 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -368,7 +368,6 @@ pub struct LauncherApp { pub note_save_on_close: bool, pub note_always_overwrite: bool, pub note_images_as_links: bool, - pub note_external_editor: Option, pub note_external_open: NoteExternalOpen, pub note_font_size: f32, pub note_more_limit: usize, @@ -514,7 +513,6 @@ impl LauncherApp { note_always_overwrite: Option, note_images_as_links: Option, note_more_limit: Option, - note_external_editor: Option, ) { self.plugin_dirs = plugin_dirs; self.index_paths = index_paths; @@ -609,9 +607,6 @@ impl LauncherApp { if let Some(v) = note_more_limit { self.note_more_limit = v; } - if note_external_editor.is_some() { - self.note_external_editor = note_external_editor; - } } pub fn new( @@ -872,7 +867,6 @@ impl LauncherApp { note_save_on_close: settings.note_save_on_close, note_always_overwrite: settings.note_always_overwrite, note_images_as_links: settings.note_images_as_links, - note_external_editor: settings.note_external_editor.clone(), note_external_open, note_font_size: 16.0, note_more_limit: settings.note_more_limit, diff --git a/src/gui/note_panel.rs b/src/gui/note_panel.rs index b4044e6f..4a5a56c8 100644 --- a/src/gui/note_panel.rs +++ b/src/gui/note_panel.rs @@ -16,9 +16,9 @@ use once_cell::sync::Lazy; use regex::Regex; use rfd::FileDialog; use std::collections::HashMap; -use std::process::Command; #[cfg(windows)] use std::os::windows::process::CommandExt; +use std::process::Command; use std::{ env, path::{Path, PathBuf}, @@ -971,9 +971,14 @@ impl NotePanel { cmd.spawn() } NoteExternalOpen::Wezterm => { - let editor = app.note_external_editor.as_deref().unwrap_or("nvim"); - let (mut cmd, _cmd_str) = build_wezterm_command(&path, editor); - cmd.spawn() + let (mut cmd, _cmd_str) = build_wezterm_command(&path); + match cmd.spawn() { + Ok(child) => Ok(child), + Err(_) => { + let (mut cmd, _cmd_str) = build_nvim_command(&path); + cmd.spawn() + } + } } NoteExternalOpen::Notepad => Command::new("notepad.exe").arg(&path).spawn(), NoteExternalOpen::Neither => return, @@ -1086,9 +1091,9 @@ pub fn build_nvim_command(note_path: &Path) -> (Command, String) { (cmd, cmd_str) } -pub fn build_wezterm_command(note_path: &Path, editor: &str) -> (Command, String) { +pub fn build_wezterm_command(note_path: &Path) -> (Command, String) { let mut cmd = Command::new("wezterm"); - cmd.arg("start").arg("--").arg(editor).arg(note_path); + cmd.arg("start").arg("--").arg("nvim").arg(note_path); #[cfg(windows)] { cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW diff --git a/src/plugin_editor.rs b/src/plugin_editor.rs index 5aca76ca..943c1028 100644 --- a/src/plugin_editor.rs +++ b/src/plugin_editor.rs @@ -133,7 +133,6 @@ impl PluginEditor { Some(s.note_always_overwrite), Some(s.note_images_as_links), Some(s.note_more_limit), - s.note_external_editor.clone(), ); let dirs = s.plugin_dirs.clone().unwrap_or_default(); let actions_arc = Arc::clone(&app.actions); diff --git a/src/settings.rs b/src/settings.rs index baf1d335..462df665 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -84,10 +84,6 @@ pub struct Settings { /// textures directly in the preview. #[serde(default)] pub note_images_as_links: bool, - /// External editor command used to open notes. If `None`, a platform - /// specific fallback is used. - #[serde(default)] - pub note_external_editor: Option, /// Number of tags or links shown before an expandable "... (more)" control /// appears in the note panel. #[serde(default = "default_note_more_limit")] @@ -276,7 +272,6 @@ impl Default for Settings { note_save_on_close: default_note_save_on_close(), note_always_overwrite: false, note_images_as_links: false, - note_external_editor: Some("wezterm".to_string()), note_more_limit: default_note_more_limit(), enable_toasts: true, toast_duration: default_toast_duration(), diff --git a/src/settings_editor.rs b/src/settings_editor.rs index d6d75d7b..cb84fee1 100644 --- a/src/settings_editor.rs +++ b/src/settings_editor.rs @@ -5,7 +5,6 @@ use crate::plugins::screenshot::ScreenshotPluginSettings; use crate::settings::Settings; use eframe::egui; use egui_toast::{Toast, ToastKind, ToastOptions}; -use rfd::FileDialog; use std::sync::Arc; #[derive(Default)] @@ -34,7 +33,6 @@ pub struct SettingsEditor { note_always_overwrite: bool, note_images_as_links: bool, note_more_limit: usize, - note_external_editor: String, query_scale: f32, list_scale: f32, history_limit: usize, @@ -124,7 +122,6 @@ impl SettingsEditor { note_always_overwrite: settings.note_always_overwrite, note_images_as_links: settings.note_images_as_links, note_more_limit: settings.note_more_limit, - note_external_editor: settings.note_external_editor.clone().unwrap_or_default(), query_scale: settings.query_scale.unwrap_or(1.0), list_scale: settings.list_scale.unwrap_or(1.0), history_limit: settings.history_limit, @@ -239,11 +236,6 @@ impl SettingsEditor { note_always_overwrite: self.note_always_overwrite, note_images_as_links: self.note_images_as_links, note_more_limit: self.note_more_limit, - note_external_editor: if self.note_external_editor.trim().is_empty() { - None - } else { - Some(self.note_external_editor.clone()) - }, query_scale: Some(self.query_scale), list_scale: Some(self.list_scale), history_limit: self.history_limit, @@ -535,15 +527,6 @@ impl SettingsEditor { .clamp_range(1..=usize::MAX), ); }); - ui.horizontal(|ui| { - ui.label("External editor"); - ui.text_edit_singleline(&mut self.note_external_editor); - if ui.button("Browse").clicked() { - if let Some(file) = FileDialog::new().pick_file() { - self.note_external_editor = file.display().to_string(); - } - } - }); let mut cfg = self .plugin_settings .get("note") @@ -676,7 +659,6 @@ impl SettingsEditor { Some(new_settings.note_always_overwrite), Some(new_settings.note_images_as_links), Some(new_settings.note_more_limit), - new_settings.note_external_editor.clone(), ); ctx.send_viewport_cmd( egui::ViewportCommand::WindowLevel( diff --git a/tests/hide_after_run.rs b/tests/hide_after_run.rs index c03d6a62..8aec2335 100644 --- a/tests/hide_after_run.rs +++ b/tests/hide_after_run.rs @@ -47,9 +47,37 @@ fn run_action(action: &str) -> bool { }]; let (mut app, flag) = new_app_with_settings(&ctx, actions, Settings::default()); app.update_paths( - None, None, None, None, None, None, None, None, None, None, None, None, - None, Some(true), None, None, None, None, None, None, None, None, None, - None, None, None, None, None, None, None, None, None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + Some(true), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, ); flag.store(true, Ordering::SeqCst); let a = app.results[0].clone(); diff --git a/tests/note_panel_shell.rs b/tests/note_panel_shell.rs index 91b116fb..eb3e606e 100644 --- a/tests/note_panel_shell.rs +++ b/tests/note_panel_shell.rs @@ -1,5 +1,5 @@ #![cfg(windows)] -use multi_launcher::gui::build_nvim_command; +use multi_launcher::gui::{build_nvim_command, build_wezterm_command}; use once_cell::sync::Lazy; use std::env; use std::fs; @@ -25,10 +25,7 @@ fn prefers_powershell7_then_powershell_then_cmd() { env::set_var("PATH", ""); } let (cmd, _) = build_nvim_command(note); - assert!(cmd - .get_program() - .to_string_lossy() - .ends_with("pwsh.exe")); + assert!(cmd.get_program().to_string_lossy().ends_with("pwsh.exe")); let args: Vec<_> = cmd .get_args() .map(|a| a.to_string_lossy().into_owned()) @@ -51,12 +48,11 @@ fn prefers_powershell7_then_powershell_then_cmd() { .ends_with("powershell.exe")); // Fallback to cmd.exe - unsafe { env::set_var("PATH", ""); } + unsafe { + env::set_var("PATH", ""); + } let (cmd, _) = build_nvim_command(note); - assert!(cmd - .get_program() - .to_string_lossy() - .ends_with("cmd.exe")); + assert!(cmd.get_program().to_string_lossy().ends_with("cmd.exe")); let args: Vec<_> = cmd .get_args() .map(|a| a.to_string_lossy().into_owned()) @@ -74,3 +70,37 @@ fn prefers_powershell7_then_powershell_then_cmd() { } } } + +#[test] +fn wezterm_fallbacks_to_powershell_when_missing() { + let _lock = TEST_MUTEX.lock().unwrap(); + let note = Path::new("note.txt"); + let orig_ps7 = env::var_os("ML_PWSH7_PATH"); + let orig_path = env::var_os("PATH"); + + let dir = tempdir().unwrap(); + let pwsh = dir.path().join("pwsh.exe"); + fs::write(&pwsh, "").unwrap(); + unsafe { + env::set_var("ML_PWSH7_PATH", &pwsh); + env::set_var("PATH", ""); + } + + let (mut cmd, _) = build_wezterm_command(note); + let res = cmd.spawn(); + assert!(res.is_err()); + + let (cmd, _) = build_nvim_command(note); + assert!(cmd.get_program().to_string_lossy().ends_with("pwsh.exe")); + + unsafe { + match orig_ps7 { + Some(v) => env::set_var("ML_PWSH7_PATH", v), + None => env::remove_var("ML_PWSH7_PATH"), + } + match orig_path { + Some(v) => env::set_var("PATH", v), + None => env::remove_var("PATH"), + } + } +} diff --git a/tests/wezterm_command.rs b/tests/wezterm_command.rs index bd3c2add..f4e97edb 100644 --- a/tests/wezterm_command.rs +++ b/tests/wezterm_command.rs @@ -4,11 +4,11 @@ use std::path::Path; #[test] fn builds_wezterm_command() { let note = Path::new("note.txt"); - let (cmd, _cmd_str) = build_wezterm_command(note, "wezterm"); + let (cmd, _cmd_str) = build_wezterm_command(note); assert_eq!(cmd.get_program().to_string_lossy(), "wezterm"); let args: Vec<_> = cmd .get_args() .map(|a| a.to_string_lossy().into_owned()) .collect(); - assert_eq!(args, ["start", "--", "wezterm", "note.txt"]); + assert_eq!(args, ["start", "--", "nvim", "note.txt"]); } From be04bc60da71ae27fca8c1b0220324187ab38a2f Mon Sep 17 00:00:00 2001 From: multiplex55 <6619098+multiplex55@users.noreply.github.com> Date: Mon, 1 Sep 2025 18:50:08 -0400 Subject: [PATCH 2/3] Open Neovim context menu in WezTerm --- src/gui/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gui/mod.rs b/src/gui/mod.rs index c30fa5f8..5232d8ba 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -2896,8 +2896,14 @@ impl eframe::App for LauncherApp { &slug, crate::plugins::note::load_notes, |path| { - let (mut cmd, _cmd_str) = build_nvim_command(path); - cmd.spawn().map(|_| ()) + let (mut cmd, _cmd_str) = build_wezterm_command(path); + match cmd.spawn() { + Ok(_) => Ok(()), + Err(_) => { + let (mut cmd, _cmd_str) = build_nvim_command(path); + cmd.spawn().map(|_| ()) + } + } }, ) { ui.close_menu(); From 82f69bf3dcd16f5200721b86dbee3db995b7e315 Mon Sep 17 00:00:00 2001 From: multiplex55 <6619098+multiplex55@users.noreply.github.com> Date: Mon, 1 Sep 2025 19:30:25 -0400 Subject: [PATCH 3/3] Use shared external note launcher --- src/gui/mod.rs | 14 +++----------- src/gui/note_panel.rs | 39 +++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 5232d8ba..2545b3dc 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -36,7 +36,8 @@ pub use fav_dialog::FavDialog; pub use image_panel::ImagePanel; pub use macro_dialog::MacroDialog; pub use note_panel::{ - build_nvim_command, build_wezterm_command, extract_links, show_wiki_link, NotePanel, + build_nvim_command, build_wezterm_command, extract_links, show_wiki_link, spawn_external, + NotePanel, }; pub use notes_dialog::NotesDialog; pub use screenshot_editor::ScreenshotEditor; @@ -2895,16 +2896,7 @@ impl eframe::App for LauncherApp { if self.open_note_in_neovim( &slug, crate::plugins::note::load_notes, - |path| { - let (mut cmd, _cmd_str) = build_wezterm_command(path); - match cmd.spawn() { - Ok(_) => Ok(()), - Err(_) => { - let (mut cmd, _cmd_str) = build_nvim_command(path); - cmd.spawn().map(|_| ()) - } - } - }, + |path| spawn_external(path, NoteExternalOpen::Wezterm), ) { ui.close_menu(); } diff --git a/src/gui/note_panel.rs b/src/gui/note_panel.rs index 4a5a56c8..e1f31fb0 100644 --- a/src/gui/note_panel.rs +++ b/src/gui/note_panel.rs @@ -965,27 +965,30 @@ impl NotePanel { fn open_external(&self, app: &mut LauncherApp, choice: NoteExternalOpen) { let path = self.note.path.clone(); - let result = match choice { - NoteExternalOpen::Powershell => { - let (mut cmd, _cmd_str) = build_nvim_command(&path); - cmd.spawn() - } - NoteExternalOpen::Wezterm => { - let (mut cmd, _cmd_str) = build_wezterm_command(&path); - match cmd.spawn() { - Ok(child) => Ok(child), - Err(_) => { - let (mut cmd, _cmd_str) = build_nvim_command(&path); - cmd.spawn() - } + if let Err(e) = spawn_external(&path, choice) { + app.set_error(format!("Failed to open note externally: {e}")); + } + } +} + +pub fn spawn_external(path: &Path, choice: NoteExternalOpen) -> std::io::Result<()> { + match choice { + NoteExternalOpen::Powershell => { + let (mut cmd, _cmd_str) = build_nvim_command(path); + cmd.spawn().map(|_| ()) + } + NoteExternalOpen::Wezterm => { + let (mut cmd, _cmd_str) = build_wezterm_command(path); + match cmd.spawn() { + Ok(_) => Ok(()), + Err(_) => { + let (mut cmd, _cmd_str) = build_nvim_command(path); + cmd.spawn().map(|_| ()) } } - NoteExternalOpen::Notepad => Command::new("notepad.exe").arg(&path).spawn(), - NoteExternalOpen::Neither => return, - }; - if let Err(e) = result { - app.set_error(format!("Failed to open note externally: {e}")); } + NoteExternalOpen::Notepad => Command::new("notepad.exe").arg(path).spawn().map(|_| ()), + NoteExternalOpen::Neither => Ok(()), } }