diff --git a/INSTALLING.md b/INSTALLING.md index 7f85bf4..9f67d91 100644 --- a/INSTALLING.md +++ b/INSTALLING.md @@ -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. diff --git a/cmd/cheat/cmd_directories.go b/cmd/cheat/cmd_directories.go index eeadd36..de8b353 100644 --- a/cmd/cheat/cmd_directories.go +++ b/cmd/cheat/cmd_directories.go @@ -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 diff --git a/cmd/cheat/cmd_edit.go b/cmd/cheat/cmd_edit.go index ee17399..6c5d2ff 100644 --- a/cmd/cheat/cmd_edit.go +++ b/cmd/cheat/cmd_edit.go @@ -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) } diff --git a/cmd/cheat/cmd_list.go b/cmd/cheat/cmd_list.go index 9ef5106..28bb442 100644 --- a/cmd/cheat/cmd_list.go +++ b/cmd/cheat/cmd_list.go @@ -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 diff --git a/cmd/cheat/cmd_remove.go b/cmd/cheat/cmd_remove.go index 072e525..ae6053d 100644 --- a/cmd/cheat/cmd_remove.go +++ b/cmd/cheat/cmd_remove.go @@ -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) } } diff --git a/cmd/cheat/cmd_search.go b/cmd/cheat/cmd_search.go index ed11954..88a35e4 100644 --- a/cmd/cheat/cmd_search.go +++ b/cmd/cheat/cmd_search.go @@ -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) } diff --git a/cmd/cheat/cmd_tags.go b/cmd/cheat/cmd_tags.go index 6187d88..8709c3e 100644 --- a/cmd/cheat/cmd_tags.go +++ b/cmd/cheat/cmd_tags.go @@ -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) } diff --git a/cmd/cheat/cmd_view.go b/cmd/cheat/cmd_view.go index 13b3c93..41a47a6 100644 --- a/cmd/cheat/cmd_view.go +++ b/cmd/cheat/cmd_view.go @@ -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), ) diff --git a/cmd/cheat/main.go b/cmd/cheat/main.go index f2790ac..c40e138 100755 --- a/cmd/cheat/main.go +++ b/cmd/cheat/main.go @@ -16,7 +16,7 @@ import ( "github.com/cheat/cheat/internal/installer" ) -const version = "4.2.6" +const version = "4.2.7" func main() { diff --git a/internal/cheatpath/filter_test.go b/internal/cheatpath/filter_test.go index 8587047..92c5025 100644 --- a/internal/cheatpath/filter_test.go +++ b/internal/cheatpath/filter_test.go @@ -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") } diff --git a/internal/cheatpath/writeable.go b/internal/cheatpath/writeable.go index b45482c..1f7c522 100644 --- a/internal/cheatpath/writeable.go +++ b/internal/cheatpath/writeable.go @@ -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 diff --git a/internal/config/config.go b/internal/config/config.go index 4870a54..71002d9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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) } diff --git a/internal/config/init.go b/internal/config/init.go index 91fa6d6..ada5e9d 100644 --- a/internal/config/init.go +++ b/internal/config/init.go @@ -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) } diff --git a/internal/config/init_test.go b/internal/config/init_test.go index 4d17bb2..b40ba2a 100644 --- a/internal/config/init_test.go +++ b/internal/config/init_test.go @@ -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) } diff --git a/internal/config/path_test.go b/internal/config/path_test.go index 2121ff9..51d8f65 100644 --- a/internal/config/path_test.go +++ b/internal/config/path_test.go @@ -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) } diff --git a/internal/display/faint.go b/internal/display/faint.go index aafdd27..45a7343 100644 --- a/internal/display/faint.go +++ b/internal/display/faint.go @@ -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 diff --git a/internal/display/underline.go b/internal/display/underline.go deleted file mode 100644 index 4ac914c..0000000 --- a/internal/display/underline.go +++ /dev/null @@ -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)) -} diff --git a/internal/display/underline_test.go b/internal/display/underline_test.go deleted file mode 100644 index e9743ee..0000000 --- a/internal/display/underline_test.go +++ /dev/null @@ -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) - } -} diff --git a/internal/display/write.go b/internal/display/write.go index c1bd2b4..d4dcfc5 100644 --- a/internal/display/write.go +++ b/internal/display/write.go @@ -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) } } diff --git a/internal/installer/prompt.go b/internal/installer/prompt.go index 7090d1f..3f2d90e 100644 --- a/internal/installer/prompt.go +++ b/internal/installer/prompt.go @@ -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') diff --git a/internal/sheet/copy_test.go b/internal/sheet/copy_test.go index 1cd868b..b7ef29c 100644 --- a/internal/sheet/copy_test.go +++ b/internal/sheet/copy_test.go @@ -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) } diff --git a/internal/sheet/sheet.go b/internal/sheet/sheet.go index 4471401..aaf9d92 100644 --- a/internal/sheet/sheet.go +++ b/internal/sheet/sheet.go @@ -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) } diff --git a/internal/sheets/load.go b/internal/sheets/load.go index 409f505..0d88c24 100644 --- a/internal/sheets/load.go +++ b/internal/sheets/load.go @@ -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