Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ greyed-out comments while you edit and the body stays plain. The cut is located
by the `>8` marker, so the footer is detected and stripped even if its `#`
prefix gets altered.

## Choosing the editor

`scissors` resolves the editor in order: `$SCISSORS_EDITOR`, then `$VISUAL`,
then `$EDITOR`, then `vi`. `SCISSORS_EDITOR` is a dedicated override, like git's
`GIT_EDITOR`: point `scissors` at a specific editor or flags without touching
`$VISUAL`/`$EDITOR`, which the rest of your shell relies on.

A GUI editor needs a blocking flag; a dedicated window also makes the review
easy to find when many windows are open:

```bash
export SCISSORS_EDITOR="code --wait --new-window" # VS Code, dedicated window
```

Without a wait flag the editor returns immediately and `scissors` reports a
silent failure (exit 2).

## File mode

Pass a file to edit it in place, like `$EDITOR <file>`:
Expand Down Expand Up @@ -111,16 +128,6 @@ omitting the argument does the same.

In stdin mode, on abort or error the draft tempfile is preserved and its path is printed to stderr. In file mode, on abort or error the file is left unchanged (the edit happens in a sidecar that is discarded).

## Editor resolution

`scissors` uses `$VISUAL`, then `$EDITOR`, then falls back to `vi`. For GUI
editors, set a blocking flag so the process waits for you to close the file:

```bash
export VISUAL="code --wait"
export VISUAL="subl -w"
```

## Non-interactive / headless use

`scissors` is **fail-closed**: without an editor it errors (exit 2) rather than
Expand Down
23 changes: 17 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub enum Outcome {
/// and report its path so the user can recover their work.
#[derive(Debug, Error)]
pub enum ScissorsError {
#[error("no editor available ($VISUAL, $EDITOR unset and editor not found)")]
#[error("no editor available ($SCISSORS_EDITOR, $VISUAL, $EDITOR unset and editor not found)")]
NoEditor,

#[error("editor exited with code {code}; draft at {draft_path}")]
Expand Down Expand Up @@ -114,7 +114,7 @@ pub enum FileOutcome {
/// failure.
#[derive(Debug, Error)]
pub enum FileError {
#[error("no editor available ($VISUAL, $EDITOR unset and editor not found)")]
#[error("no editor available ($SCISSORS_EDITOR, $VISUAL, $EDITOR unset and editor not found)")]
NoEditor,
#[error("cannot read {}: {source}", path.display())]
Read { path: PathBuf, source: io::Error },
Expand Down Expand Up @@ -197,14 +197,14 @@ fn build_draft(content: &str, context: Option<&str>) -> String {
out
}

/// Resolve the editor command, honouring $VISUAL > $EDITOR > `vi`.
/// Resolve the editor command, honouring $SCISSORS_EDITOR > $VISUAL > $EDITOR > `vi`.
/// Returns the command split into program + args (e.g. `["code", "--wait"]`).
///
/// # Errors
/// [`ScissorsError::Io`] if `$VISUAL`/`$EDITOR` contains unbalanced quotes that
/// [`ScissorsError::Io`] if `$SCISSORS_EDITOR`/`$VISUAL`/`$EDITOR` contains unbalanced quotes that
/// `shell-words` cannot split.
pub fn resolve_editor() -> Result<Vec<String>, ScissorsError> {
for var in ["VISUAL", "EDITOR"] {
for var in ["SCISSORS_EDITOR", "VISUAL", "EDITOR"] {
if let Ok(val) = std::env::var(var) {
let trimmed = val.trim();
if !trimmed.is_empty() {
Expand Down Expand Up @@ -285,7 +285,7 @@ fn keep_draft(dir: TempDir, path: PathBuf) -> PathBuf {
///
/// # Errors
/// All error cases preserve the draft file and report its path.
/// - [`ScissorsError::NoEditor`] -- `$VISUAL`/`$EDITOR` unset/empty and `vi` not found.
/// - [`ScissorsError::NoEditor`] -- `$SCISSORS_EDITOR`/`$VISUAL`/`$EDITOR` unset/empty and `vi` not found.
/// - [`ScissorsError::EditorFailed`] -- the editor exited non-zero.
/// - [`ScissorsError::SilentFailure`] -- the editor returned in under
/// `500 ms` with no change (likely never opened, e.g. a GUI editor without a
Expand Down Expand Up @@ -496,9 +496,20 @@ mod editor_tests {
use super::*;
use serial_test::serial;

#[test]
#[serial]
fn scissors_editor_takes_priority() {
std::env::set_var("SCISSORS_EDITOR", "dedicated --new-window");
std::env::set_var("VISUAL", "myvisual --wait");
std::env::set_var("EDITOR", "myeditor");
assert_eq!(resolve_editor().unwrap(), vec!["dedicated", "--new-window"]);
std::env::remove_var("SCISSORS_EDITOR");
}

#[test]
#[serial]
fn visual_takes_priority() {
std::env::remove_var("SCISSORS_EDITOR");
std::env::set_var("VISUAL", "myvisual --wait");
std::env::set_var("EDITOR", "myeditor");
assert_eq!(resolve_editor().unwrap(), vec!["myvisual", "--wait"]);
Expand Down