Compare commits

...

5 Commits
4.2.6 ... 4.2.7

Author SHA1 Message Date
73f80bde48 Merge pull request #688 from chrisallenlane/4.2.7
4.2.7
2022-08-05 07:01:51 -04:00
8130b2f3bd chore: bump version to 4.2.7 2022-08-05 06:41:16 -04:00
f4e6c76e58 fix: escape sequences in search output (#687)
Fix an issue whereby ANSI escape characters could appear in search
output when a pager was not configured.

The root cause of the problem was code that was overzealously applying
an underlying effect to search terms.

This commit simply rips out underlying entirely, both as means of
resolving this problem, and also simply for removing needless visual
noise from search output.
2022-08-05 06:41:16 -04:00
85f5ae8ec7 chore: various lint corrections
Make various lint corrections in order to appease `staticcheck`.
2022-08-04 20:43:50 -04:00
484b447391 perf(Sheets): do not walk hidden directories
Modify `Sheets.Load` to not walk hidden directories like `.git`. This
optimization can potentially prevent thousands of system calls from
being made, because `.git` directories can contain many files.
2022-08-04 20:43:42 -04:00
23 changed files with 55 additions and 90 deletions

View File

@ -9,13 +9,13 @@ On Unix-like systems, you may simply paste the following snippet into your termi
```sh
cd /tmp \
&& wget https://github.com/cheat/cheat/releases/download/4.2.5/cheat-linux-amd64.gz \
&& wget https://github.com/cheat/cheat/releases/download/4.2.7/cheat-linux-amd64.gz \
&& gunzip cheat-linux-amd64.gz \
&& chmod +x cheat-linux-amd64 \
&& sudo mv cheat-linux-amd64 /usr/local/bin/cheat
```
You may need to need to change the version number (`4.2.5`) and the archive
You may need to need to change the version number (`4.2.7`) and the archive
(`cheat-linux-amd64.gz`) depending on your platform.
See the [releases page][releases] for a list of supported platforms.

View File

@ -18,11 +18,7 @@ func cmdDirectories(opts map[string]interface{}, conf config.Config) {
// generate sorted, columnized output
for _, path := range conf.Cheatpaths {
fmt.Fprintln(w, fmt.Sprintf(
"%s:\t%s",
path.Name,
path.Path,
))
fmt.Fprintf(w, "%s:\t%s\n", path.Name, path.Path)
}
// write columnized output to stdout

View File

@ -20,7 +20,7 @@ func cmdEdit(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}

View File

@ -21,7 +21,7 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}
@ -63,10 +63,7 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
// compile the regex
reg, err := regexp.Compile(pattern)
if err != nil {
fmt.Fprintln(
os.Stderr,
fmt.Sprintf("failed to compile regexp: %s, %v", pattern, err),
)
fmt.Fprintf(os.Stderr, "failed to compile regexp: %s, %v\n", pattern, err)
os.Exit(1)
}
@ -95,12 +92,7 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
// generate sorted, columnized output
for _, sheet := range flattened {
fmt.Fprintln(w, fmt.Sprintf(
"%s\t%s\t%s",
sheet.Title,
sheet.Path,
strings.Join(sheet.Tags, ","),
))
fmt.Fprintf(w, "%s\t%s\t%s\n", sheet.Title, sheet.Path, strings.Join(sheet.Tags, ","))
}
// write columnized output to stdout

View File

@ -17,7 +17,7 @@ func cmdRemove(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}
@ -37,19 +37,19 @@ func cmdRemove(opts map[string]interface{}, conf config.Config) {
// fail early if the requested cheatsheet does not exist
sheet, ok := consolidated[cheatsheet]
if !ok {
fmt.Fprintln(os.Stderr, fmt.Sprintf("No cheatsheet found for '%s'.\n", cheatsheet))
fmt.Fprintf(os.Stderr, "No cheatsheet found for '%s'.\n", cheatsheet)
os.Exit(2)
}
// fail early if the sheet is read-only
if sheet.ReadOnly {
fmt.Fprintln(os.Stderr, fmt.Sprintf("cheatsheet '%s' is read-only.", cheatsheet))
fmt.Fprintf(os.Stderr, "cheatsheet '%s' is read-only.\n", cheatsheet)
os.Exit(1)
}
// otherwise, attempt to delete the sheet
if err := os.Remove(sheet.Path); err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to delete sheet: %s, %v", sheet.Title, err))
fmt.Fprintf(os.Stderr, "failed to delete sheet: %s, %v\n", sheet.Title, err)
os.Exit(1)
}
}

View File

@ -19,7 +19,7 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}
@ -55,13 +55,13 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
// compile the regex
reg, err := regexp.Compile(pattern)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to compile regexp: %s, %v", pattern, err))
fmt.Fprintf(os.Stderr, "failed to compile regexp: %s, %v\n", pattern, err)
os.Exit(1)
}
// `Search` will return text entries that match the search terms. We're
// using it here to overwrite the prior cheatsheet Text, filtering it to
// only what is relevant
// `Search` will return text entries that match the search terms.
// We're using it here to overwrite the prior cheatsheet Text,
// filtering it to only what is relevant.
sheet.Text = sheet.Search(reg)
// if the sheet did not match the search, ignore it and move on
@ -74,14 +74,16 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
sheet.Colorize(conf)
}
// display the cheatsheet title and path
out += fmt.Sprintf("%s %s\n",
display.Underline(sheet.Title),
// display the cheatsheet body
out += fmt.Sprintf(
"%s %s\n%s\n",
// append the cheatsheet title
sheet.Title,
// append the cheatsheet path
display.Faint(fmt.Sprintf("(%s)", sheet.CheatPath), conf),
// indent each line of content
display.Indent(sheet.Text),
)
// indent each line of content
out += display.Indent(sheet.Text) + "\n"
}
}
@ -89,7 +91,7 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
out = strings.TrimSpace(out)
// display the output
// NB: resist the temptation to call `display.Display` multiple times in
// the loop above. That will not play nicely with the paginator.
// NB: resist the temptation to call `display.Write` multiple times in the
// loop above. That will not play nicely with the paginator.
display.Write(out, conf)
}

View File

@ -15,7 +15,7 @@ func cmdTags(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}

View File

@ -18,7 +18,7 @@ func cmdView(opts map[string]interface{}, conf config.Config) {
// load the cheatsheets
cheatsheets, err := sheets.Load(conf.Cheatpaths)
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to list cheatsheets: %v", err))
fmt.Fprintf(os.Stderr, "failed to list cheatsheets: %v\n", err)
os.Exit(1)
}
@ -41,7 +41,7 @@ func cmdView(opts map[string]interface{}, conf config.Config) {
// identify the matching cheatsheet
out += fmt.Sprintf("%s %s\n",
display.Underline(sheet.Title),
sheet.Title,
display.Faint(fmt.Sprintf("(%s)", sheet.CheatPath), conf),
)

View File

@ -16,7 +16,7 @@ import (
"github.com/cheat/cheat/internal/installer"
)
const version = "4.2.6"
const version = "4.2.7"
func main() {

View File

@ -46,7 +46,7 @@ func TestFilterFailure(t *testing.T) {
}
// filter the paths
paths, err := Filter(paths, "qux")
_, err := Filter(paths, "qux")
if err == nil {
t.Errorf("failed to return an error on non-existent cheatpath")
}

View File

@ -11,12 +11,10 @@ func Writeable(cheatpaths []Cheatpath) (Cheatpath, error) {
// NB: we're going backwards because we assume that the most "local"
// cheatpath will be specified last in the configs
for i := len(cheatpaths) - 1; i >= 0; i-- {
// if the cheatpath is not read-only, it is writeable, and thus returned
if cheatpaths[i].ReadOnly == false {
if !cheatpaths[i].ReadOnly {
return cheatpaths[i], nil
}
}
// otherwise, return an error

View File

@ -2,7 +2,6 @@ package config
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
@ -29,7 +28,7 @@ type Config struct {
func New(opts map[string]interface{}, confPath string, resolve bool) (Config, error) {
// read the config file
buf, err := ioutil.ReadFile(confPath)
buf, err := os.ReadFile(confPath)
if err != nil {
return Config{}, fmt.Errorf("could not read config file: %v", err)
}

View File

@ -2,7 +2,6 @@ package config
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
@ -16,7 +15,7 @@ func Init(confpath string, configs string) error {
}
// write the config file
if err := ioutil.WriteFile(confpath, []byte(configs), 0644); err != nil {
if err := os.WriteFile(confpath, []byte(configs), 0644); err != nil {
return fmt.Errorf("failed to create file: %v", err)
}

View File

@ -1,7 +1,6 @@
package config
import (
"io/ioutil"
"os"
"testing"
)
@ -10,7 +9,7 @@ import (
func TestInit(t *testing.T) {
// initialize a temporary config file
confFile, err := ioutil.TempFile("", "cheat-test")
confFile, err := os.CreateTemp("", "cheat-test")
if err != nil {
t.Errorf("failed to create temp file: %v", err)
}
@ -25,7 +24,7 @@ func TestInit(t *testing.T) {
}
// read back the config file contents
bytes, err := ioutil.ReadFile(confFile.Name())
bytes, err := os.ReadFile(confFile.Name())
if err != nil {
t.Errorf("failed to read config file: %v", err)
}

View File

@ -1,7 +1,6 @@
package config
import (
"io/ioutil"
"os"
"testing"
)
@ -24,7 +23,7 @@ func TestPathConfigNotExists(t *testing.T) {
func TestPathConfigExists(t *testing.T) {
// initialize a temporary config file
confFile, err := ioutil.TempFile("", "cheat-test")
confFile, err := os.CreateTemp("", "cheat-test")
if err != nil {
t.Errorf("failed to create temp file: %v", err)
}

View File

@ -10,7 +10,7 @@ import (
func Faint(str string, conf config.Config) string {
// make `str` faint only if colorization has been requested
if conf.Colorize {
return fmt.Sprintf(fmt.Sprintf("\033[2m%s\033[0m", str))
return fmt.Sprintf("\033[2m%s\033[0m", str)
}
// otherwise, return the string unmodified

View File

@ -1,8 +0,0 @@
package display
import "fmt"
// Underline returns an underlined string
func Underline(str string) string {
return fmt.Sprintf(fmt.Sprintf("\033[4m%s\033[0m", str))
}

View File

@ -1,14 +0,0 @@
package display
import (
"testing"
)
// TestUnderline asserts that Underline applies underline formatting
func TestUnderline(t *testing.T) {
want := "\033[4mfoo\033[0m"
got := Underline("foo")
if want != got {
t.Errorf("failed to underline: want: %s, got: %s", want, got)
}
}

View File

@ -23,15 +23,14 @@ func Write(out string, conf config.Config) {
pager := parts[0]
args := parts[1:]
// run the pager
// configure the pager
cmd := exec.Command(pager, args...)
cmd.Stdin = strings.NewReader(out)
cmd.Stdout = os.Stdout
// handle errors
err := cmd.Run()
if err != nil {
fmt.Fprintln(os.Stderr, fmt.Sprintf("failed to write to pager: %v", err))
// run the pager and handle errors
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "failed to write to pager: %v\n", err)
os.Exit(1)
}
}

View File

@ -14,7 +14,7 @@ func Prompt(prompt string, def bool) (bool, error) {
reader := bufio.NewReader(os.Stdin)
// display the prompt
fmt.Print(fmt.Sprintf("%s: ", prompt))
fmt.Printf("%s: ", prompt)
// read the answer
ans, err := reader.ReadString('\n')

View File

@ -1,7 +1,6 @@
package sheet
import (
"io/ioutil"
"os"
"path"
"testing"
@ -13,7 +12,7 @@ func TestCopyFlat(t *testing.T) {
// mock a cheatsheet file
text := "this is the cheatsheet text"
src, err := ioutil.TempFile("", "foo-src")
src, err := os.CreateTemp("", "foo-src")
if err != nil {
t.Errorf("failed to mock cheatsheet: %v", err)
}
@ -41,7 +40,7 @@ func TestCopyFlat(t *testing.T) {
}
// assert that the destination file contains the correct text
got, err := ioutil.ReadFile(outpath)
got, err := os.ReadFile(outpath)
if err != nil {
t.Errorf("failed to read destination file: %v", err)
}
@ -60,7 +59,7 @@ func TestCopyDeep(t *testing.T) {
// mock a cheatsheet file
text := "this is the cheatsheet text"
src, err := ioutil.TempFile("", "foo-src")
src, err := os.CreateTemp("", "foo-src")
if err != nil {
t.Errorf("failed to mock cheatsheet: %v", err)
}
@ -94,7 +93,7 @@ func TestCopyDeep(t *testing.T) {
}
// assert that the destination file contains the correct text
got, err := ioutil.ReadFile(outpath)
got, err := os.ReadFile(outpath)
if err != nil {
t.Errorf("failed to read destination file: %v", err)
}

View File

@ -2,7 +2,7 @@ package sheet
import (
"fmt"
"io/ioutil"
"os"
"sort"
"github.com/cheat/cheat/internal/frontmatter"
@ -29,7 +29,7 @@ func New(
) (Sheet, error) {
// read the cheatsheet file
markdown, err := ioutil.ReadFile(path)
markdown, err := os.ReadFile(path)
if err != nil {
return Sheet{}, fmt.Errorf("failed to read file: %s, %v", path, err)
}

View File

@ -2,6 +2,7 @@ package sheets
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
@ -55,7 +56,11 @@ func Load(cheatpaths []cp.Cheatpath) ([]map[string]sheet.Sheet, error) {
// contained within hidden directories in the middle of a path, though
// that should not realistically occur.
if strings.HasPrefix(title, ".") || strings.HasPrefix(info.Name(), ".") {
return nil
// Do not walk hidden directories. This is important,
// because it's common for cheatsheets to be stored in
// version-control, and a `.git` directory can easily
// contain thousands of files.
return fs.SkipDir
}
// parse the cheatsheet file into a `sheet` struct