Add support for editing message in external editor

Closes #313
Fixes #311
This commit is contained in:
Tulir Asokan 2022-03-06 22:13:55 +02:00
parent 6525f9ec66
commit b2ca5ed37e
3 changed files with 86 additions and 0 deletions

View File

@ -146,6 +146,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
"redact": cmdRedact, "redact": cmdRedact,
"react": cmdReact, "react": cmdReact,
"edit": cmdEdit, "edit": cmdEdit,
"external": cmdExternalEditor,
"download": cmdDownload, "download": cmdDownload,
"upload": cmdUpload, "upload": cmdUpload,
"open": cmdOpen, "open": cmdOpen,

View File

@ -17,12 +17,14 @@
package ui package ui
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"math" "math"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime" "runtime"
@ -177,6 +179,75 @@ func cmdEdit(cmd *Command) {
cmd.Room.StartSelecting(SelectEdit, "") cmd.Room.StartSelecting(SelectEdit, "")
} }
func findEditorExecutable() (string, string, error) {
if editor := os.Getenv("VISUAL"); len(editor) > 0 {
if path, err := exec.LookPath(editor); err != nil {
return "", "", fmt.Errorf("$VISUAL ('%s') not found in $PATH", editor)
} else {
return editor, path, nil
}
} else if editor = os.Getenv("EDITOR"); len(editor) > 0 {
if path, err := exec.LookPath(editor); err != nil {
return "", "", fmt.Errorf("$EDITOR ('%s') not found in $PATH", editor)
} else {
return editor, path, nil
}
} else if path, _ := exec.LookPath("nano"); len(path) > 0 {
return "nano", path, nil
} else if path, _ = exec.LookPath("vi"); len(path) > 0 {
return "vi", path, nil
} else {
return "", "", fmt.Errorf("$VISUAL and $EDITOR not set, nano and vi not found in $PATH")
}
}
func cmdExternalEditor(cmd *Command) {
var file *os.File
defer func() {
if file != nil {
_ = file.Close()
_ = os.Remove(file.Name())
}
}()
fileExtension := "md"
if cmd.Config.Preferences.DisableMarkdown {
if cmd.Config.Preferences.DisableHTML {
fileExtension = "txt"
} else {
fileExtension = "html"
}
}
if editorName, executablePath, err := findEditorExecutable(); err != nil {
cmd.Reply("Couldn't find editor to use: %v", err)
return
} else if file, err = os.CreateTemp("", fmt.Sprintf("gomuks-draft-*.%s", fileExtension)); err != nil {
cmd.Reply("Failed to create temp file: %v", err)
return
} else if _, err = file.WriteString(cmd.RawArgs); err != nil {
cmd.Reply("Failed to write to temp file: %v", err)
} else if err = file.Close(); err != nil {
cmd.Reply("Failed to close temp file: %v", err)
} else if err = cmd.UI.RunExternal(executablePath, file.Name()); err != nil {
var exitErr *exec.ExitError
if isExit := errors.As(err, &exitErr); isExit {
cmd.Reply("%s exited with non-zero status %d", editorName, exitErr.ExitCode())
} else {
cmd.Reply("Failed to run %s: %v", editorName, err)
}
} else if data, err := os.ReadFile(file.Name()); err != nil {
cmd.Reply("Failed to read temp file: %v", err)
} else if len(bytes.TrimSpace(data)) > 0 {
cmd.Room.InputSubmit(string(data))
} else {
cmd.Reply("Temp file was blank, sending cancelled")
if cmd.Room.editing != nil {
cmd.Room.SetEditing(nil)
}
}
}
func cmdRedact(cmd *Command) { func cmdRedact(cmd *Command) {
cmd.Room.StartSelecting(SelectRedact, strings.Join(cmd.Args, " ")) cmd.Room.StartSelecting(SelectRedact, strings.Join(cmd.Args, " "))
} }

View File

@ -18,6 +18,7 @@ package ui
import ( import (
"os" "os"
"os/exec"
"github.com/zyedidia/clipboard" "github.com/zyedidia/clipboard"
@ -119,3 +120,16 @@ func (ui *GomuksUI) SetView(name View) {
func (ui *GomuksUI) MainView() ifc.MainView { func (ui *GomuksUI) MainView() ifc.MainView {
return ui.mainView return ui.mainView
} }
func (ui *GomuksUI) RunExternal(executablePath string, args ...string) error {
var err error
ui.app.Suspend(func() {
cmd := exec.Command(executablePath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = os.Environ()
err = cmd.Run()
})
return err
}