Add support for editing message in external editor
Closes #313 Fixes #311
This commit is contained in:
		@@ -146,6 +146,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
 | 
			
		||||
			"redact":     cmdRedact,
 | 
			
		||||
			"react":      cmdReact,
 | 
			
		||||
			"edit":       cmdEdit,
 | 
			
		||||
			"external":   cmdExternalEditor,
 | 
			
		||||
			"download":   cmdDownload,
 | 
			
		||||
			"upload":     cmdUpload,
 | 
			
		||||
			"open":       cmdOpen,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,12 +17,14 @@
 | 
			
		||||
package ui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"math"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"runtime"
 | 
			
		||||
@@ -177,6 +179,75 @@ func cmdEdit(cmd *Command) {
 | 
			
		||||
	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) {
 | 
			
		||||
	cmd.Room.StartSelecting(SelectRedact, strings.Join(cmd.Args, " "))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								ui/ui.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								ui/ui.go
									
									
									
									
									
								
							@@ -18,6 +18,7 @@ package ui
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
 | 
			
		||||
	"github.com/zyedidia/clipboard"
 | 
			
		||||
 | 
			
		||||
@@ -119,3 +120,16 @@ func (ui *GomuksUI) SetView(name View) {
 | 
			
		||||
func (ui *GomuksUI) MainView() ifc.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
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user