From a6c25d4b9c00d4f6761b567984af9c44b261a7ee Mon Sep 17 00:00:00 2001 From: Chris Lane Date: Sat, 15 Feb 2020 14:40:33 -0500 Subject: [PATCH] feat: --search improvements - Deprecates the `Match` struct - Applies syntax highlighting to search results output in a manner consistent with the 'View' output - Refactors search to move colorization functionality outside of its concern --- cmd/cheat/cmd_search.go | 49 ++++++++++--- internal/sheet/match.go | 7 -- internal/sheet/search.go | 16 ++--- internal/sheet/search_test.go | 128 +++++----------------------------- 4 files changed, 62 insertions(+), 138 deletions(-) delete mode 100644 internal/sheet/match.go diff --git a/cmd/cheat/cmd_search.go b/cmd/cheat/cmd_search.go index 91779d9..455772d 100644 --- a/cmd/cheat/cmd_search.go +++ b/cmd/cheat/cmd_search.go @@ -1,11 +1,13 @@ package main import ( + "bytes" "fmt" "os" "regexp" "strings" + "github.com/alecthomas/chroma/quick" "github.com/cheat/cheat/internal/config" "github.com/cheat/cheat/internal/sheet" "github.com/cheat/cheat/internal/sheets" @@ -71,17 +73,44 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) { os.Exit(1) } - // search the sheet - matches := sheet.Search(reg) + // `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) - // display the results - if len(matches) > 0 { - fmt.Printf("%s:\n", sheet.Title) - for _, m := range matches { - fmt.Printf(" %s\n", m.Text) - } - fmt.Print("\n") + // if the sheet did not match the search, ignore it and move on + if sheet.Text == "" { + continue } - } + // if colorization was requested, apply it here + if conf.Color(opts) { + + // if the syntax was not specified, default to bash + lex := sheet.Syntax + if lex == "" { + lex = "bash" + } + + var buf bytes.Buffer + err = quick.Highlight( + &buf, + sheet.Text, + lex, + conf.Formatter, + conf.Style, + ) + + sheet.Text = buf.String() + } + + // output the cheatsheet title + fmt.Printf("%s:\n", sheet.Title) + + // indent each line of content with two spaces + for _, line := range strings.Split(sheet.Text, "\n") { + fmt.Printf(" %s\n", line) + } + fmt.Println("") + } } diff --git a/internal/sheet/match.go b/internal/sheet/match.go deleted file mode 100644 index 7022aee..0000000 --- a/internal/sheet/match.go +++ /dev/null @@ -1,7 +0,0 @@ -package sheet - -// Match encapsulates search matches within cheatsheets -type Match struct { - Line int - Text string -} diff --git a/internal/sheet/search.go b/internal/sheet/search.go index 2155fde..ed9f9fd 100644 --- a/internal/sheet/search.go +++ b/internal/sheet/search.go @@ -5,12 +5,11 @@ import ( "strings" ) -// Search searches for regexp matches in a cheatsheet's text, and optionally -// colorizes matching strings. -func (s *Sheet) Search(reg *regexp.Regexp) []Match { +// Search returns lines within a sheet's Text that match the search regex +func (s *Sheet) Search(reg *regexp.Regexp) string { // record matches - matches := []Match{} + matches := []string{} // search through the cheatsheet's text line by line // TODO: searching line-by-line is surely the "naive" approach. Revisit this @@ -22,14 +21,9 @@ func (s *Sheet) Search(reg *regexp.Regexp) []Match { continue } - // init the match - m := Match{ - Text: strings.TrimSpace(line), - } - // record the match - matches = append(matches, m) + matches = append(matches, line) } - return matches + return strings.Join(matches, "\n") } diff --git a/internal/sheet/search_test.go b/internal/sheet/search_test.go index 5335812..41dc41c 100644 --- a/internal/sheet/search_test.go +++ b/internal/sheet/search_test.go @@ -4,8 +4,6 @@ import ( "reflect" "regexp" "testing" - - "github.com/davecgh/go-spew/spew" ) // TestSearchNoMatch ensures that the expected output is returned when no @@ -24,17 +22,17 @@ func TestSearchNoMatch(t *testing.T) { } // search the sheet - matches := sheet.Search(reg, false) + matches := sheet.Search(reg) // assert that no matches were found - if len(matches) != 0 { - t.Errorf("failure: expected no matches: got: %s", spew.Sdump(matches)) + if matches != "" { + t.Errorf("failure: expected no matches: got: %s", matches) } } -// TestSearchSingleMatchNoColor asserts that the expected output is returned -// when a single match is returned, and no colorization is applied. -func TestSearchSingleMatchNoColor(t *testing.T) { +// TestSearchSingleMatch asserts that the expected output is returned +// when a single match is returned +func TestSearchSingleMatch(t *testing.T) { // mock a cheatsheet sheet := Sheet{ @@ -48,65 +46,24 @@ func TestSearchSingleMatchNoColor(t *testing.T) { } // search the sheet - matches := sheet.Search(reg, false) + matches := sheet.Search(reg) // specify the expected results - want := []Match{ - Match{ - Line: 1, - Text: "The quick brown fox", - }, - } + want := "The quick brown fox" // assert that the correct matches were returned - if !reflect.DeepEqual(matches, want) { + if matches != want { t.Errorf( "failed to return expected matches: want:\n%s, got:\n%s", - spew.Sdump(want), - spew.Sdump(matches), + want, + matches, ) } } -// TestSearchSingleMatchColorized asserts that the expected output is returned -// when a single match is returned, and colorization is applied -func TestSearchSingleMatchColorized(t *testing.T) { - - // mock a cheatsheet - sheet := Sheet{ - Text: "The quick brown fox\njumped over\nthe lazy dog.", - } - - // compile the search regex - reg, err := regexp.Compile("(?i)fox") - if err != nil { - t.Errorf("failed to compile regex: %v", err) - } - - // search the sheet - matches := sheet.Search(reg, true) - - // specify the expected results - want := []Match{ - Match{ - Line: 1, - Text: "The quick brown \x1b[1;31mfox\x1b[0m", - }, - } - - // assert that the correct matches were returned - if !reflect.DeepEqual(matches, want) { - t.Errorf( - "failed to return expected matches: want:\n%s, got:\n%s", - spew.Sdump(want), - spew.Sdump(matches), - ) - } -} - -// TestSearchMultiMatchNoColor asserts that the expected output is returned -// when a multiple matches are returned, and no colorization is applied -func TestSearchMultiMatchNoColor(t *testing.T) { +// TestSearchMultiMatch asserts that the expected output is returned +// when a multiple matches are returned +func TestSearchMultiMatch(t *testing.T) { // mock a cheatsheet sheet := Sheet{ @@ -120,66 +77,17 @@ func TestSearchMultiMatchNoColor(t *testing.T) { } // search the sheet - matches := sheet.Search(reg, false) + matches := sheet.Search(reg) // specify the expected results - want := []Match{ - Match{ - Line: 1, - Text: "The quick brown fox", - }, - Match{ - Line: 3, - Text: "the lazy dog.", - }, - } + want := "The quick brown fox\nthe lazy dog." // assert that the correct matches were returned if !reflect.DeepEqual(matches, want) { t.Errorf( "failed to return expected matches: want:\n%s, got:\n%s", - spew.Sdump(want), - spew.Sdump(matches), - ) - } -} - -// TestSearchMultiMatchColorized asserts that the expected output is returned -// when a multiple matches are returned, and colorization is applied -func TestSearchMultiMatchColorized(t *testing.T) { - - // mock a cheatsheet - sheet := Sheet{ - Text: "The quick brown fox\njumped over\nthe lazy dog.", - } - - // compile the search regex - reg, err := regexp.Compile("(?i)the") - if err != nil { - t.Errorf("failed to compile regex: %v", err) - } - - // search the sheet - matches := sheet.Search(reg, true) - - // specify the expected results - want := []Match{ - Match{ - Line: 1, - Text: "\x1b[1;31mThe\x1b[0m quick brown fox", - }, - Match{ - Line: 3, - Text: "\x1b[1;31mthe\x1b[0m lazy dog.", - }, - } - - // assert that the correct matches were returned - if !reflect.DeepEqual(matches, want) { - t.Errorf( - "failed to return expected matches: want:\n%s, got:\n%s", - spew.Sdump(want), - spew.Sdump(matches), + want, + matches, ) } }