mirror of
https://github.com/cheat/cheat.git
synced 2025-09-01 17:48:30 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
219db679e1 | |||
53177cb09d | |||
ef7a41f9a9 | |||
008316d030 | |||
a59c019642 | |||
57225442be | |||
2c7ce48859 | |||
a3fe4f40bb | |||
506fb8be15 | |||
408e944eea | |||
8a313b92ca | |||
6912771c39 |
@ -3,6 +3,10 @@ language: go
|
|||||||
go:
|
go:
|
||||||
- 1.13.x
|
- 1.13.x
|
||||||
|
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
7
Makefile
7
Makefile
@ -21,6 +21,7 @@ ZIP := zip -m
|
|||||||
# build flags
|
# build flags
|
||||||
BUILD_FLAGS := -ldflags="-s -w" -mod vendor -trimpath
|
BUILD_FLAGS := -ldflags="-s -w" -mod vendor -trimpath
|
||||||
GOBIN :=
|
GOBIN :=
|
||||||
|
TMPDIR := /tmp
|
||||||
|
|
||||||
# release binaries
|
# release binaries
|
||||||
releases := \
|
releases := \
|
||||||
@ -137,6 +138,12 @@ vet:
|
|||||||
test:
|
test:
|
||||||
$(GO) test ./...
|
$(GO) test ./...
|
||||||
|
|
||||||
|
## coverage: generates a test coverage report
|
||||||
|
.PHONY: coverage
|
||||||
|
coverage:
|
||||||
|
$(GO) test ./... -coverprofile=$(TMPDIR)/cheat-coverage.out && \
|
||||||
|
$(GO) tool cover -html=$(TMPDIR)/cheat-coverage.out
|
||||||
|
|
||||||
## check: formats, lints, vets, vendors, and run unit-tests
|
## check: formats, lints, vets, vendors, and run unit-tests
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check: | vendor fmt lint vet test
|
check: | vendor fmt lint vet test
|
||||||
|
12
README.md
12
README.md
@ -193,9 +193,17 @@ cheat -p personal -t networking --regex -s '(?:[0-9]{1,3}\.){3}[0-9]{1,3}'
|
|||||||
|
|
||||||
Advanced Usage
|
Advanced Usage
|
||||||
--------------
|
--------------
|
||||||
`cheat` may be integrated with [fzf][]. See [fzf.bash][bash] for instructions.
|
Shell autocompletion is currently available for the `bash` and `fish` shells.
|
||||||
(Support for other shells will be added in future releases.)
|
Copy the relevant [completion script][completion-scripts] into the appropriate
|
||||||
|
directory on your filesystem to enable autocompletion. (This directory will
|
||||||
|
vary depending on operating system and shell specifics.)
|
||||||
|
|
||||||
|
Additionally, `cheat` supports enhanced autocompletion via integration with
|
||||||
|
[fzf][]. (This feature is currently available on bash only.) To enable `fzf`
|
||||||
|
integration:
|
||||||
|
|
||||||
|
1. Ensure that `fzf` is available on your `$PATH`
|
||||||
|
2. Set an envvar: `export CHEAT_USE_FZF=true`
|
||||||
|
|
||||||
[Releases]: https://github.com/cheat/cheat/releases
|
[Releases]: https://github.com/cheat/cheat/releases
|
||||||
[bash]: https://github.com/cheat/cheat/blob/master/scripts/fzf.bash
|
[bash]: https://github.com/cheat/cheat/blob/master/scripts/fzf.bash
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# TODO: this script has been made obsolete by the Makefile, yet downstream
|
|
||||||
# package managers plausibly rely on it for compiling locally. Remove this file
|
|
||||||
# after downstream maintainers have had time to modify their packages to simply
|
|
||||||
# invoke `make` in the project root.
|
|
||||||
|
|
||||||
# locate the cheat project root
|
|
||||||
BINDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
APPDIR=$(readlink -f "$BINDIR/..")
|
|
||||||
|
|
||||||
# compile the executable
|
|
||||||
cd $APPDIR
|
|
||||||
|
|
||||||
make
|
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/docopt/docopt-go"
|
"github.com/docopt/docopt-go"
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ import (
|
|||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "3.4.0"
|
const version = "3.5.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
@ -31,8 +32,15 @@ func main() {
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read the envvars into a map of strings
|
||||||
|
envvars := map[string]string{}
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
pair := strings.SplitN(e, "=", 2)
|
||||||
|
envvars[pair[0]] = pair[1]
|
||||||
|
}
|
||||||
|
|
||||||
// load the os-specifc paths at which the config file may be located
|
// load the os-specifc paths at which the config file may be located
|
||||||
confpaths, err := config.Paths(runtime.GOOS)
|
confpaths, err := config.Paths(runtime.GOOS, envvars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
|
fmt.Fprintf(os.Stderr, "failed to load config: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -2,7 +2,6 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
@ -10,13 +9,13 @@ import (
|
|||||||
|
|
||||||
// Paths returns config file paths that are appropriate for the operating
|
// Paths returns config file paths that are appropriate for the operating
|
||||||
// system
|
// system
|
||||||
func Paths(sys string) ([]string, error) {
|
func Paths(sys string, envvars map[string]string) ([]string, error) {
|
||||||
|
|
||||||
// if CHEAT_CONFIG_PATH is set, return it
|
// if `CHEAT_CONFIG_PATH` is set, expand ~ and return it
|
||||||
if os.Getenv("CHEAT_CONFIG_PATH") != "" {
|
if confpath, ok := envvars["CHEAT_CONFIG_PATH"]; ok {
|
||||||
|
|
||||||
// expand ~
|
// expand ~
|
||||||
expanded, err := homedir.Expand(os.Getenv("CHEAT_CONFIG_PATH"))
|
expanded, err := homedir.Expand(confpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []string{}, fmt.Errorf("failed to expand ~: %v", err)
|
return []string{}, fmt.Errorf("failed to expand ~: %v", err)
|
||||||
}
|
}
|
||||||
@ -26,15 +25,24 @@ func Paths(sys string) ([]string, error) {
|
|||||||
|
|
||||||
switch sys {
|
switch sys {
|
||||||
case "darwin", "linux", "freebsd":
|
case "darwin", "linux", "freebsd":
|
||||||
return []string{
|
paths := []string{}
|
||||||
path.Join(os.Getenv("XDG_CONFIG_HOME"), "/cheat/conf.yml"),
|
|
||||||
path.Join(os.Getenv("HOME"), ".config/cheat/conf.yml"),
|
// don't include the `XDG_CONFIG_HOME` path if that envvar is not set
|
||||||
path.Join(os.Getenv("HOME"), ".cheat/conf.yml"),
|
if xdgpath, ok := envvars["XDG_CONFIG_HOME"]; ok {
|
||||||
}, nil
|
paths = append(paths, path.Join(xdgpath, "/cheat/conf.yml"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// `HOME` will always be set on a POSIX-compliant system, though
|
||||||
|
paths = append(paths, []string{
|
||||||
|
path.Join(envvars["HOME"], ".config/cheat/conf.yml"),
|
||||||
|
path.Join(envvars["HOME"], ".cheat/conf.yml"),
|
||||||
|
}...)
|
||||||
|
|
||||||
|
return paths, nil
|
||||||
case "windows":
|
case "windows":
|
||||||
return []string{
|
return []string{
|
||||||
fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("APPDATA")),
|
path.Join(envvars["APPDATA"], "/cheat/conf.yml"),
|
||||||
fmt.Sprintf("%s/cheat/conf.yml", os.Getenv("PROGRAMDATA")),
|
path.Join(envvars["PROGRAMDATA"], "/cheat/conf.yml"),
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return []string{}, fmt.Errorf("unsupported os: %s", sys)
|
return []string{}, fmt.Errorf("unsupported os: %s", sys)
|
||||||
|
165
internal/config/paths_test.go
Normal file
165
internal/config/paths_test.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestValidatePathsNix asserts that the proper config paths are returned on
|
||||||
|
// *nix platforms
|
||||||
|
func TestValidatePathsNix(t *testing.T) {
|
||||||
|
|
||||||
|
// mock some envvars
|
||||||
|
envvars := map[string]string{
|
||||||
|
"HOME": "/home/foo",
|
||||||
|
"XDG_CONFIG_HOME": "/home/bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the platforms to test
|
||||||
|
oses := []string{
|
||||||
|
"darwin",
|
||||||
|
"freebsd",
|
||||||
|
"linux",
|
||||||
|
}
|
||||||
|
|
||||||
|
// test each *nix os
|
||||||
|
for _, os := range oses {
|
||||||
|
// get the paths for the platform
|
||||||
|
paths, err := Paths(os, envvars)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("paths returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the expected output
|
||||||
|
want := []string{
|
||||||
|
"/home/bar/cheat/conf.yml",
|
||||||
|
"/home/foo/.config/cheat/conf.yml",
|
||||||
|
"/home/foo/.cheat/conf.yml",
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that output matches expectations
|
||||||
|
if !reflect.DeepEqual(paths, want) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed to return expected paths: want:\n%s, got:\n%s",
|
||||||
|
spew.Sdump(want),
|
||||||
|
spew.Sdump(paths),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatePathsNixNoXDG asserts that the proper config paths are returned
|
||||||
|
// on *nix platforms when `XDG_CONFIG_HOME is not set
|
||||||
|
func TestValidatePathsNixNoXDG(t *testing.T) {
|
||||||
|
|
||||||
|
// mock some envvars
|
||||||
|
envvars := map[string]string{
|
||||||
|
"HOME": "/home/foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the platforms to test
|
||||||
|
oses := []string{
|
||||||
|
"darwin",
|
||||||
|
"freebsd",
|
||||||
|
"linux",
|
||||||
|
}
|
||||||
|
|
||||||
|
// test each *nix os
|
||||||
|
for _, os := range oses {
|
||||||
|
// get the paths for the platform
|
||||||
|
paths, err := Paths(os, envvars)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("paths returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the expected output
|
||||||
|
want := []string{
|
||||||
|
"/home/foo/.config/cheat/conf.yml",
|
||||||
|
"/home/foo/.cheat/conf.yml",
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that output matches expectations
|
||||||
|
if !reflect.DeepEqual(paths, want) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed to return expected paths: want:\n%s, got:\n%s",
|
||||||
|
spew.Sdump(want),
|
||||||
|
spew.Sdump(paths),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatePathsWindows asserts that the proper config paths are returned
|
||||||
|
// on Windows platforms
|
||||||
|
func TestValidatePathsWindows(t *testing.T) {
|
||||||
|
|
||||||
|
// mock some envvars
|
||||||
|
envvars := map[string]string{
|
||||||
|
"APPDATA": "/apps",
|
||||||
|
"PROGRAMDATA": "/programs",
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the paths for the platform
|
||||||
|
paths, err := Paths("windows", envvars)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("paths returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the expected output
|
||||||
|
want := []string{
|
||||||
|
"/apps/cheat/conf.yml",
|
||||||
|
"/programs/cheat/conf.yml",
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that output matches expectations
|
||||||
|
if !reflect.DeepEqual(paths, want) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed to return expected paths: want:\n%s, got:\n%s",
|
||||||
|
spew.Sdump(want),
|
||||||
|
spew.Sdump(paths),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatePathsUnsupported asserts that an error is returned on
|
||||||
|
// unsupported platforms
|
||||||
|
func TestValidatePathsUnsupported(t *testing.T) {
|
||||||
|
_, err := Paths("unsupported", map[string]string{})
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("failed to return error on unsupported platform")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidatePathsCheatConfigPath asserts that the proper config path is
|
||||||
|
// returned when `CHEAT_CONFIG_PATH` is explicitly specified.
|
||||||
|
func TestValidatePathsCheatConfigPath(t *testing.T) {
|
||||||
|
|
||||||
|
// mock some envvars
|
||||||
|
envvars := map[string]string{
|
||||||
|
"HOME": "/home/foo",
|
||||||
|
"XDG_CONFIG_HOME": "/home/bar",
|
||||||
|
"CHEAT_CONFIG_PATH": "/home/baz/conf.yml",
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the paths for the platform
|
||||||
|
paths, err := Paths("linux", envvars)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("paths returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// specify the expected output
|
||||||
|
want := []string{
|
||||||
|
"/home/baz/conf.yml",
|
||||||
|
}
|
||||||
|
|
||||||
|
// assert that output matches expectations
|
||||||
|
if !reflect.DeepEqual(paths, want) {
|
||||||
|
t.Errorf(
|
||||||
|
"failed to return expected paths: want:\n%s, got:\n%s",
|
||||||
|
spew.Sdump(want),
|
||||||
|
spew.Sdump(paths),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
function _cheat_autocomplete {
|
|
||||||
sheets=$(cheat -l | sed -n '2,$p'|cut -d' ' -f1)
|
|
||||||
COMPREPLY=()
|
|
||||||
if [ $COMP_CWORD = 1 ]; then
|
|
||||||
COMPREPLY=(`compgen -W "$sheets" -- $2`)
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
complete -F _cheat_autocomplete cheat
|
|
74
scripts/cheat.bash
Executable file
74
scripts/cheat.bash
Executable file
@ -0,0 +1,74 @@
|
|||||||
|
# cheat(1) completion -*- shell-script -*-
|
||||||
|
|
||||||
|
# generate cheatsheet completions, optionally using `fzf`
|
||||||
|
_cheat_complete_cheatsheets()
|
||||||
|
{
|
||||||
|
if [[ "$CHEAT_USE_FZF" = true ]]; then
|
||||||
|
FZF_COMPLETION_TRIGGER='' _fzf_complete "--no-multi" "$@" < <(
|
||||||
|
cheat -l | tail -n +2 | cut -d' ' -f1
|
||||||
|
)
|
||||||
|
else
|
||||||
|
COMPREPLY=( $(compgen -W "$(cheat -l | tail -n +2 | cut -d' ' -f1)" -- "$cur") )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate tag completions, optionally using `fzf`
|
||||||
|
_cheat_complete_tags()
|
||||||
|
{
|
||||||
|
if [ "$CHEAT_USE_FZF" = true ]; then
|
||||||
|
FZF_COMPLETION_TRIGGER='' _fzf_complete "--no-multi" "$@" < <(cheat -T)
|
||||||
|
else
|
||||||
|
COMPREPLY=( $(compgen -W "$(cheat -T)" -- "$cur") )
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# implement the `cheat` autocompletions
|
||||||
|
_cheat()
|
||||||
|
{
|
||||||
|
local cur prev words cword split
|
||||||
|
_init_completion -s || return
|
||||||
|
|
||||||
|
# complete options that are currently being typed: `--col` => `--colorize`
|
||||||
|
if [[ $cur == -* ]]; then
|
||||||
|
COMPREPLY=( $(compgen -W '$(_parse_help "$1" | sed "s/=//g")' -- "$cur") )
|
||||||
|
[[ $COMPREPLY == *= ]] && compopt -o nospace
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# implement completions
|
||||||
|
case $prev in
|
||||||
|
--colorize|-c|\
|
||||||
|
--directories|-d|\
|
||||||
|
--init|\
|
||||||
|
--regex|-r|\
|
||||||
|
--search|-s|\
|
||||||
|
--tags|-T|\
|
||||||
|
--version|-v)
|
||||||
|
# noop the above, which should implement no completions
|
||||||
|
;;
|
||||||
|
--edit|-e)
|
||||||
|
_cheat_complete_cheatsheets
|
||||||
|
;;
|
||||||
|
--list|-l)
|
||||||
|
_cheat_complete_cheatsheets
|
||||||
|
;;
|
||||||
|
--path|-p)
|
||||||
|
COMPREPLY=( $(compgen -W "$(cheat -d | cut -d':' -f1)" -- "$cur") )
|
||||||
|
;;
|
||||||
|
--rm)
|
||||||
|
_cheat_complete_cheatsheets
|
||||||
|
;;
|
||||||
|
--tag|-t)
|
||||||
|
_cheat_complete_tags
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_cheat_complete_cheatsheets
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
$split && return
|
||||||
|
|
||||||
|
} &&
|
||||||
|
complete -F _cheat cheat
|
||||||
|
|
||||||
|
# ex: filetype=sh
|
@ -1,11 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This function enables you to choose a cheatsheet to view by selecting output
|
|
||||||
# from `cheat -l`. `source` it in your shell to enable it. (Consider renaming
|
|
||||||
# or aliasing it to something convenient.)
|
|
||||||
|
|
||||||
# Arguments passed to this function (like --color) will be passed to the second
|
|
||||||
# invokation of `cheat`.
|
|
||||||
function cheat-fzf {
|
|
||||||
eval `cheat -l | tail -n +2 | fzf | awk -v vars="$*" '{ print "cheat " $1 " -t " $3, vars }'`
|
|
||||||
}
|
|
Reference in New Issue
Block a user