mirror of
https://github.com/cheat/cheat.git
synced 2026-03-07 03:03:32 +01:00
fix: respect $VISUAL and $EDITOR env vars at runtime
Previously, env vars were only consulted during config generation and baked into conf.yml. At runtime, the config file value was always used, making it impossible to override the editor via environment variables. Now the precedence is: $VISUAL > $EDITOR > conf.yml > auto-detect. Closes #589 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ package main
|
|||||||
// configs returns the default configuration template
|
// configs returns the default configuration template
|
||||||
func configs() string {
|
func configs() string {
|
||||||
return `---
|
return `---
|
||||||
# The editor to use with 'cheat -e <sheet>'. Defaults to $EDITOR or $VISUAL.
|
# The editor to use with 'cheat -e <sheet>'. Overridden by $VISUAL or $EDITOR.
|
||||||
editor: EDITOR_PATH
|
editor: EDITOR_PATH
|
||||||
|
|
||||||
# Should 'cheat' always colorize output?
|
# Should 'cheat' always colorize output?
|
||||||
|
|||||||
@@ -107,10 +107,17 @@ func New(_ map[string]interface{}, confPath string, resolve bool) (Config, error
|
|||||||
}
|
}
|
||||||
conf.Cheatpaths = validPaths
|
conf.Cheatpaths = validPaths
|
||||||
|
|
||||||
// trim editor whitespace
|
// determine the editor: env vars override the config file value,
|
||||||
|
// following standard Unix convention (see #589)
|
||||||
|
if v := os.Getenv("VISUAL"); v != "" {
|
||||||
|
conf.Editor = v
|
||||||
|
} else if v := os.Getenv("EDITOR"); v != "" {
|
||||||
|
conf.Editor = v
|
||||||
|
} else {
|
||||||
conf.Editor = strings.TrimSpace(conf.Editor)
|
conf.Editor = strings.TrimSpace(conf.Editor)
|
||||||
|
}
|
||||||
|
|
||||||
// if an editor was not provided in the configs, attempt to choose one
|
// if an editor was still not determined, attempt to choose one
|
||||||
// that's appropriate for the environment
|
// that's appropriate for the environment
|
||||||
if conf.Editor == "" {
|
if conf.Editor == "" {
|
||||||
if conf.Editor, err = Editor(); err != nil {
|
if conf.Editor, err = Editor(); err != nil {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
@@ -17,6 +16,16 @@ import (
|
|||||||
// TestConfig asserts that the configs are loaded correctly
|
// TestConfig asserts that the configs are loaded correctly
|
||||||
func TestConfigSuccessful(t *testing.T) {
|
func TestConfigSuccessful(t *testing.T) {
|
||||||
|
|
||||||
|
// clear env vars so they don't override the config file value
|
||||||
|
oldVisual := os.Getenv("VISUAL")
|
||||||
|
oldEditor := os.Getenv("EDITOR")
|
||||||
|
os.Unsetenv("VISUAL")
|
||||||
|
os.Unsetenv("EDITOR")
|
||||||
|
defer func() {
|
||||||
|
os.Setenv("VISUAL", oldVisual)
|
||||||
|
os.Setenv("EDITOR", oldEditor)
|
||||||
|
}()
|
||||||
|
|
||||||
// initialize a config
|
// initialize a config
|
||||||
conf, err := New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
conf, err := New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,40 +85,78 @@ func TestConfigFailure(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestEmptyEditor asserts that envvars are respected if an editor is not
|
// TestEditorEnvOverride asserts that $VISUAL and $EDITOR override the
|
||||||
// specified in the configs
|
// config file value at runtime (regression test for #589)
|
||||||
func TestEmptyEditor(t *testing.T) {
|
func TestEditorEnvOverride(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
// save and clear the environment variables
|
||||||
t.Skip("Editor() returns notepad on Windows before checking env vars")
|
oldVisual := os.Getenv("VISUAL")
|
||||||
|
oldEditor := os.Getenv("EDITOR")
|
||||||
|
defer func() {
|
||||||
|
os.Setenv("VISUAL", oldVisual)
|
||||||
|
os.Setenv("EDITOR", oldEditor)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// with no env vars, the config file value should be used
|
||||||
|
os.Unsetenv("VISUAL")
|
||||||
|
os.Unsetenv("EDITOR")
|
||||||
|
conf, err := New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to init configs: %v", err)
|
||||||
|
}
|
||||||
|
if conf.Editor != "vim" {
|
||||||
|
t.Errorf("expected config file editor: want: vim, got: %s", conf.Editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear the environment variables
|
// $EDITOR should override the config file value
|
||||||
os.Setenv("VISUAL", "")
|
os.Setenv("EDITOR", "nano")
|
||||||
os.Setenv("EDITOR", "")
|
conf, err = New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to init configs: %v", err)
|
||||||
|
}
|
||||||
|
if conf.Editor != "nano" {
|
||||||
|
t.Errorf("$EDITOR should override config: want: nano, got: %s", conf.Editor)
|
||||||
|
}
|
||||||
|
|
||||||
// initialize a config
|
// $VISUAL should override both $EDITOR and the config file value
|
||||||
|
os.Setenv("VISUAL", "emacs")
|
||||||
|
conf, err = New(map[string]interface{}{}, mock.Path("conf/conf.yml"), false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to init configs: %v", err)
|
||||||
|
}
|
||||||
|
if conf.Editor != "emacs" {
|
||||||
|
t.Errorf("$VISUAL should override all: want: emacs, got: %s", conf.Editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEditorEnvFallback asserts that env vars are used as fallback when
|
||||||
|
// no editor is specified in the config file
|
||||||
|
func TestEditorEnvFallback(t *testing.T) {
|
||||||
|
// save and clear the environment variables
|
||||||
|
oldVisual := os.Getenv("VISUAL")
|
||||||
|
oldEditor := os.Getenv("EDITOR")
|
||||||
|
defer func() {
|
||||||
|
os.Setenv("VISUAL", oldVisual)
|
||||||
|
os.Setenv("EDITOR", oldEditor)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// set $EDITOR and assert it's used when config has no editor
|
||||||
|
os.Unsetenv("VISUAL")
|
||||||
|
os.Setenv("EDITOR", "foo")
|
||||||
conf, err := New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
conf, err := New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize test: %v", err)
|
t.Fatalf("failed to init configs: %v", err)
|
||||||
}
|
|
||||||
|
|
||||||
// set editor, and assert that it is respected
|
|
||||||
os.Setenv("EDITOR", "foo")
|
|
||||||
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to init configs: %v", err)
|
|
||||||
}
|
}
|
||||||
if conf.Editor != "foo" {
|
if conf.Editor != "foo" {
|
||||||
t.Errorf("failed to respect editor: want: foo, got: %s", conf.Editor)
|
t.Errorf("failed to respect $EDITOR: want: foo, got: %s", conf.Editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set visual, and assert that it overrides editor
|
// set $VISUAL and assert it takes precedence over $EDITOR
|
||||||
os.Setenv("VISUAL", "bar")
|
os.Setenv("VISUAL", "bar")
|
||||||
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
conf, err = New(map[string]interface{}{}, mock.Path("conf/empty.yml"), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to init configs: %v", err)
|
t.Fatalf("failed to init configs: %v", err)
|
||||||
}
|
}
|
||||||
if conf.Editor != "bar" {
|
if conf.Editor != "bar" {
|
||||||
t.Errorf("failed to respect editor: want: bar, got: %s", conf.Editor)
|
t.Errorf("failed to respect $VISUAL: want: bar, got: %s", conf.Editor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewTrimsWhitespace(t *testing.T) {
|
func TestNewTrimsWhitespace(t *testing.T) {
|
||||||
|
// clear env vars so they don't override the config file value
|
||||||
|
oldVisual := os.Getenv("VISUAL")
|
||||||
|
oldEditor := os.Getenv("EDITOR")
|
||||||
|
os.Unsetenv("VISUAL")
|
||||||
|
os.Unsetenv("EDITOR")
|
||||||
|
defer func() {
|
||||||
|
os.Setenv("VISUAL", oldVisual)
|
||||||
|
os.Setenv("EDITOR", oldEditor)
|
||||||
|
}()
|
||||||
|
|
||||||
// Create a temporary config file with whitespace in editor and pager
|
// Create a temporary config file with whitespace in editor and pager
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
configPath := filepath.Join(tmpDir, "config.yml")
|
configPath := filepath.Join(tmpDir, "config.yml")
|
||||||
|
|||||||
Reference in New Issue
Block a user