2019-10-20 16:02:28 +02:00
|
|
|
package config
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2022-07-04 22:06:37 +02:00
|
|
|
"os/exec"
|
2019-11-06 01:08:40 +01:00
|
|
|
"path/filepath"
|
2022-07-04 22:06:37 +02:00
|
|
|
"runtime"
|
2020-06-26 00:21:51 +02:00
|
|
|
"strings"
|
2019-10-20 16:02:28 +02:00
|
|
|
|
|
|
|
cp "github.com/cheat/cheat/internal/cheatpath"
|
|
|
|
|
|
|
|
"github.com/mitchellh/go-homedir"
|
|
|
|
"gopkg.in/yaml.v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Config encapsulates configuration parameters
|
|
|
|
type Config struct {
|
2020-01-20 18:34:48 +01:00
|
|
|
Colorize bool `yaml:"colorize"`
|
|
|
|
Editor string `yaml:"editor"`
|
|
|
|
Cheatpaths []cp.Cheatpath `yaml:"cheatpaths"`
|
|
|
|
Style string `yaml:"style"`
|
|
|
|
Formatter string `yaml:"formatter"`
|
2020-06-26 00:21:51 +02:00
|
|
|
Pager string `yaml:"pager"`
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new Config struct
|
2019-11-08 01:59:18 +01:00
|
|
|
func New(opts map[string]interface{}, confPath string, resolve bool) (Config, error) {
|
2019-10-20 16:02:28 +02:00
|
|
|
|
|
|
|
// read the config file
|
2022-08-05 02:38:49 +02:00
|
|
|
buf, err := os.ReadFile(confPath)
|
2019-10-20 16:02:28 +02:00
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf("could not read config file: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize a config object
|
|
|
|
conf := Config{}
|
|
|
|
|
|
|
|
// unmarshal the yaml
|
|
|
|
err = yaml.UnmarshalStrict(buf, &conf)
|
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf("could not unmarshal yaml: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-01-20 18:34:48 +01:00
|
|
|
// if a .cheat directory exists locally, append it to the cheatpaths
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf("failed to get cwd: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
local := filepath.Join(cwd, ".cheat")
|
|
|
|
if _, err := os.Stat(local); err == nil {
|
|
|
|
path := cp.Cheatpath{
|
|
|
|
Name: "cwd",
|
|
|
|
Path: local,
|
|
|
|
ReadOnly: false,
|
|
|
|
Tags: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
conf.Cheatpaths = append(conf.Cheatpaths, path)
|
|
|
|
}
|
|
|
|
|
2019-11-06 01:08:40 +01:00
|
|
|
// process cheatpaths
|
2019-10-20 16:02:28 +02:00
|
|
|
for i, cheatpath := range conf.Cheatpaths {
|
|
|
|
|
2019-11-06 01:08:40 +01:00
|
|
|
// expand ~ in config paths
|
2019-10-20 16:02:28 +02:00
|
|
|
expanded, err := homedir.Expand(cheatpath.Path)
|
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf("failed to expand ~: %v", err)
|
|
|
|
}
|
|
|
|
|
2019-11-06 01:08:40 +01:00
|
|
|
// follow symlinks
|
2019-11-08 01:59:18 +01:00
|
|
|
//
|
|
|
|
// NB: `resolve` is an ugly kludge that exists for the sake of unit-tests.
|
|
|
|
// It's necessary because `EvalSymlinks` will error if the symlink points
|
|
|
|
// to a non-existent location on the filesystem. When unit-testing,
|
|
|
|
// however, we don't want to have dependencies on the filesystem. As such,
|
|
|
|
// `resolve` is a switch that allows us to turn off symlink resolution when
|
|
|
|
// running the config tests.
|
|
|
|
if resolve {
|
2020-01-29 20:05:27 +01:00
|
|
|
evaled, err := filepath.EvalSymlinks(expanded)
|
2019-11-08 01:59:18 +01:00
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf(
|
2020-01-29 20:05:27 +01:00
|
|
|
"failed to resolve symlink: %s: %v",
|
2019-11-08 01:59:18 +01:00
|
|
|
expanded,
|
|
|
|
err,
|
|
|
|
)
|
|
|
|
}
|
2020-01-29 20:05:27 +01:00
|
|
|
|
|
|
|
expanded = evaled
|
2019-11-06 01:08:40 +01:00
|
|
|
}
|
|
|
|
|
2019-10-20 16:02:28 +02:00
|
|
|
conf.Cheatpaths[i].Path = expanded
|
|
|
|
}
|
|
|
|
|
|
|
|
// if an editor was not provided in the configs, look to envvars
|
|
|
|
if conf.Editor == "" {
|
|
|
|
if os.Getenv("VISUAL") != "" {
|
|
|
|
conf.Editor = os.Getenv("VISUAL")
|
|
|
|
} else if os.Getenv("EDITOR") != "" {
|
|
|
|
conf.Editor = os.Getenv("EDITOR")
|
2022-07-04 22:06:37 +02:00
|
|
|
} else if runtime.GOOS == "windows" {
|
|
|
|
conf.Editor = "notepad"
|
2019-10-20 16:02:28 +02:00
|
|
|
} else {
|
2022-07-04 22:06:37 +02:00
|
|
|
// try to fall back to `nano`
|
|
|
|
path, err := exec.LookPath("nano")
|
|
|
|
if err != nil {
|
|
|
|
return Config{}, fmt.Errorf("failed to locate nano: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// use `nano` if we found it
|
|
|
|
if path != "" {
|
|
|
|
conf.Editor = "nano"
|
|
|
|
// otherwise, give up
|
|
|
|
} else {
|
|
|
|
return Config{}, fmt.Errorf("no editor set")
|
|
|
|
}
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a chroma style was not provided, set a default
|
|
|
|
if conf.Style == "" {
|
|
|
|
conf.Style = "bw"
|
|
|
|
}
|
|
|
|
|
|
|
|
// if a chroma formatter was not provided, set a default
|
|
|
|
if conf.Formatter == "" {
|
2022-07-04 22:06:37 +02:00
|
|
|
conf.Formatter = "terminal"
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
|
2022-07-04 22:06:37 +02:00
|
|
|
// attempt to fall back to `PAGER` if a pager is not specified in configs
|
|
|
|
conf.Pager = strings.TrimSpace(conf.Pager)
|
2022-08-03 02:27:56 +02:00
|
|
|
if conf.Pager == "" {
|
|
|
|
// look for `pager`, `less`, and `more` on the system PATH
|
|
|
|
pagerPath, _ := exec.LookPath("pager")
|
|
|
|
lessPath, _ := exec.LookPath("less")
|
|
|
|
morePath, _ := exec.LookPath("more")
|
|
|
|
|
|
|
|
// search first for a `PAGER` envvar
|
|
|
|
if os.Getenv("PAGER") != "" {
|
|
|
|
conf.Pager = os.Getenv("PAGER")
|
|
|
|
|
|
|
|
// search for `pager`
|
|
|
|
} else if pagerPath != "" {
|
|
|
|
conf.Pager = pagerPath
|
|
|
|
|
|
|
|
// search for `less`
|
|
|
|
} else if lessPath != "" {
|
|
|
|
conf.Pager = lessPath
|
|
|
|
|
|
|
|
// search for `more`
|
|
|
|
//
|
|
|
|
// XXX: this causes issues on some Linux systems. See:
|
|
|
|
// https://github.com/cheat/cheat/issues/681#issuecomment-1201842334
|
|
|
|
//
|
|
|
|
// By checking for `more` last, we're hoping to at least mitigate
|
|
|
|
// the frequency of this occurrence, because `pager` and `less` are
|
|
|
|
// likely to be available on most systems on which a user is likely
|
|
|
|
// to have installed `cheat`.
|
|
|
|
} else if morePath != "" {
|
|
|
|
conf.Pager = morePath
|
|
|
|
}
|
2020-06-26 00:21:51 +02:00
|
|
|
}
|
|
|
|
|
2019-10-20 16:02:28 +02:00
|
|
|
return conf, nil
|
|
|
|
}
|