feat: preview opens the configured editor
This commit is contained in:
1 parent
ea986b06ab
commit
a00e39f55c
2 files changed
+86
-14
No files matched your search
+26
-2
@@ -12,6 +12,7 @@ pub struct Config {
|
|||||||
pub capture: Capture,
|
pub capture: Capture,
|
||||||
pub preview: Preview,
|
pub preview: Preview,
|
||||||
pub zoom: Zoom,
|
pub zoom: Zoom,
|
||||||
|
pub editor: Editor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize)]
|
||||||
@@ -51,6 +52,21 @@ impl Default for Zoom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Deserialize, Default)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Editor {
|
||||||
|
pub backend: Backend,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Default)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
pub enum Backend {
|
||||||
|
/// `valw edit`.
|
||||||
|
#[default]
|
||||||
|
Builtin,
|
||||||
|
Satty,
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Preview {
|
impl Default for Preview {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -157,12 +173,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_keys_are_reported_not_rejected() {
|
fn unknown_keys_are_reported_not_rejected() {
|
||||||
let text = "[editor]\nbackend = \"satty\"\n\n[save]\nfolder = \"x\"\n";
|
let text = "[toolbar]\nposition = \"top\"\n\n[save]\nfolder = \"x\"\n";
|
||||||
let (config, unknown) = parse(text).unwrap();
|
let (config, unknown) = parse(text).unwrap();
|
||||||
assert_eq!(config, Config::default());
|
assert_eq!(config, Config::default());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
unknown,
|
unknown,
|
||||||
vec!["editor".to_string(), "save.folder".to_string()]
|
vec!["save.folder".to_string(), "toolbar".to_string()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +236,14 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn editor_backend_default_and_values() {
|
||||||
|
assert_eq!(Config::default().editor.backend, Backend::Builtin);
|
||||||
|
let (config, _) = parse("[editor]\nbackend = \"satty\"\n").unwrap();
|
||||||
|
assert_eq!(config.editor.backend, Backend::Satty);
|
||||||
|
assert!(parse("[editor]\nbackend = \"gimp\"\n").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_file_gives_defaults() {
|
fn missing_file_gives_defaults() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
+60
-12
@@ -24,6 +24,7 @@ use wayland_client::{Connection, QueueHandle, protocol::wl_surface::WlSurface};
|
|||||||
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
|
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
|
||||||
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
use wayland_protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||||
|
|
||||||
|
use crate::config::{self, Backend};
|
||||||
use crate::dnd::{self, Outcome};
|
use crate::dnd::{self, Outcome};
|
||||||
use crate::ipc::{self, Reply, Request};
|
use crate::ipc::{self, Reply, Request};
|
||||||
use crate::lock::Lock;
|
use crate::lock::Lock;
|
||||||
@@ -500,26 +501,50 @@ fn add(state: &mut State, path: &Path, output_name: &str) -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens the screenshot in Satty; saving there overwrites the file.
|
/// The command that opens `path` in the configured editor; both save over
|
||||||
|
/// the file.
|
||||||
|
fn editor_command(backend: Backend, path: &Path, exe: &Path) -> std::process::Command {
|
||||||
|
let mut command = match backend {
|
||||||
|
Backend::Builtin => {
|
||||||
|
let mut c = std::process::Command::new(exe);
|
||||||
|
c.arg("edit").arg(path);
|
||||||
|
c
|
||||||
|
}
|
||||||
|
Backend::Satty => {
|
||||||
|
let mut c = std::process::Command::new("satty");
|
||||||
|
c.arg("--filename")
|
||||||
|
.arg(path)
|
||||||
|
.arg("--output-filename")
|
||||||
|
.arg(path);
|
||||||
|
c
|
||||||
|
}
|
||||||
|
};
|
||||||
|
command
|
||||||
|
.stdin(std::process::Stdio::null())
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null());
|
||||||
|
command
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens the screenshot in the configured editor.
|
||||||
fn open_editor(path: &Path) {
|
fn open_editor(path: &Path) {
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
let mut command = Command::new("satty");
|
let backend = match config::load(&config::default_path()) {
|
||||||
command
|
Ok(c) => c.editor.backend,
|
||||||
.arg("--filename")
|
Err(e) => {
|
||||||
.arg(path)
|
tracing::warn!("using the built-in editor: {e:#}");
|
||||||
.arg("--output-filename")
|
Backend::Builtin
|
||||||
.arg(path)
|
}
|
||||||
.stdin(Stdio::null())
|
};
|
||||||
.stdout(Stdio::null())
|
let exe = std::env::current_exe().unwrap_or_else(|_| "valw".into());
|
||||||
.stderr(Stdio::null());
|
let mut command = editor_command(backend, path, &exe);
|
||||||
// SAFETY: setsid is async-signal-safe.
|
// SAFETY: setsid is async-signal-safe.
|
||||||
unsafe {
|
unsafe {
|
||||||
command.pre_exec(|| rustix::process::setsid().map(drop).map_err(Into::into));
|
command.pre_exec(|| rustix::process::setsid().map(drop).map_err(Into::into));
|
||||||
}
|
}
|
||||||
if let Err(e) = command.spawn() {
|
if let Err(e) = command.spawn() {
|
||||||
tracing::warn!("could not start satty: {e}");
|
tracing::warn!("could not start the editor ({backend:?}): {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,6 +552,29 @@ fn open_editor(path: &Path) {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn editor_command_per_backend() {
|
||||||
|
let path = Path::new("/p/Shot 1.png");
|
||||||
|
let exe = Path::new("/nix/store/x/bin/valw");
|
||||||
|
let builtin = editor_command(Backend::Builtin, path, exe);
|
||||||
|
assert_eq!(builtin.get_program(), exe);
|
||||||
|
assert_eq!(
|
||||||
|
builtin.get_args().collect::<Vec<_>>(),
|
||||||
|
["edit", "/p/Shot 1.png"]
|
||||||
|
);
|
||||||
|
let satty = editor_command(Backend::Satty, path, exe);
|
||||||
|
assert_eq!(satty.get_program(), "satty");
|
||||||
|
assert_eq!(
|
||||||
|
satty.get_args().collect::<Vec<_>>(),
|
||||||
|
[
|
||||||
|
"--filename",
|
||||||
|
"/p/Shot 1.png",
|
||||||
|
"--output-filename",
|
||||||
|
"/p/Shot 1.png"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hidden_until_every_hider_leaves() {
|
fn hidden_until_every_hider_leaves() {
|
||||||
let mut core = Core::new(Instant::now());
|
let mut core = Core::new(Instant::now());
|
||||||
|
|||||||
Reference in new issue
Block a user