mirror of https://github.com/cheat/cheat.git
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
This commit is contained in:
parent
e24ac2b385
commit
a6c25d4b9c
|
@ -1,11 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alecthomas/chroma/quick"
|
||||||
"github.com/cheat/cheat/internal/config"
|
"github.com/cheat/cheat/internal/config"
|
||||||
"github.com/cheat/cheat/internal/sheet"
|
"github.com/cheat/cheat/internal/sheet"
|
||||||
"github.com/cheat/cheat/internal/sheets"
|
"github.com/cheat/cheat/internal/sheets"
|
||||||
|
@ -71,17 +73,44 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// `Search` will return text entries that match the search terms. We're
|
||||||
matches := sheet.Search(reg)
|
// 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 the sheet did not match the search, ignore it and move on
|
||||||
if len(matches) > 0 {
|
if sheet.Text == "" {
|
||||||
fmt.Printf("%s:\n", sheet.Title)
|
continue
|
||||||
for _, m := range matches {
|
|
||||||
fmt.Printf(" %s\n", m.Text)
|
|
||||||
}
|
|
||||||
fmt.Print("\n")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package sheet
|
|
||||||
|
|
||||||
// Match encapsulates search matches within cheatsheets
|
|
||||||
type Match struct {
|
|
||||||
Line int
|
|
||||||
Text string
|
|
||||||
}
|
|
|
@ -5,12 +5,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Search searches for regexp matches in a cheatsheet's text, and optionally
|
// Search returns lines within a sheet's Text that match the search regex
|
||||||
// colorizes matching strings.
|
func (s *Sheet) Search(reg *regexp.Regexp) string {
|
||||||
func (s *Sheet) Search(reg *regexp.Regexp) []Match {
|
|
||||||
|
|
||||||
// record matches
|
// record matches
|
||||||
matches := []Match{}
|
matches := []string{}
|
||||||
|
|
||||||
// search through the cheatsheet's text line by line
|
// search through the cheatsheet's text line by line
|
||||||
// TODO: searching line-by-line is surely the "naive" approach. Revisit this
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// init the match
|
|
||||||
m := Match{
|
|
||||||
Text: strings.TrimSpace(line),
|
|
||||||
}
|
|
||||||
|
|
||||||
// record the match
|
// record the match
|
||||||
matches = append(matches, m)
|
matches = append(matches, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches
|
return strings.Join(matches, "\n")
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSearchNoMatch ensures that the expected output is returned when no
|
// TestSearchNoMatch ensures that the expected output is returned when no
|
||||||
|
@ -24,17 +22,17 @@ func TestSearchNoMatch(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// assert that no matches were found
|
// assert that no matches were found
|
||||||
if len(matches) != 0 {
|
if matches != "" {
|
||||||
t.Errorf("failure: expected no matches: got: %s", spew.Sdump(matches))
|
t.Errorf("failure: expected no matches: got: %s", matches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSearchSingleMatchNoColor asserts that the expected output is returned
|
// TestSearchSingleMatch asserts that the expected output is returned
|
||||||
// when a single match is returned, and no colorization is applied.
|
// when a single match is returned
|
||||||
func TestSearchSingleMatchNoColor(t *testing.T) {
|
func TestSearchSingleMatch(t *testing.T) {
|
||||||
|
|
||||||
// mock a cheatsheet
|
// mock a cheatsheet
|
||||||
sheet := Sheet{
|
sheet := Sheet{
|
||||||
|
@ -48,65 +46,24 @@ func TestSearchSingleMatchNoColor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// specify the expected results
|
// specify the expected results
|
||||||
want := []Match{
|
want := "The quick brown fox"
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "The quick brown fox",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
// assert that the correct matches were returned
|
||||||
if !reflect.DeepEqual(matches, want) {
|
if matches != want {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
"failed to return expected matches: want:\n%s, got:\n%s",
|
||||||
spew.Sdump(want),
|
want,
|
||||||
spew.Sdump(matches),
|
matches,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSearchSingleMatchColorized asserts that the expected output is returned
|
// TestSearchMultiMatch asserts that the expected output is returned
|
||||||
// when a single match is returned, and colorization is applied
|
// when a multiple matches are returned
|
||||||
func TestSearchSingleMatchColorized(t *testing.T) {
|
func TestSearchMultiMatch(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) {
|
|
||||||
|
|
||||||
// mock a cheatsheet
|
// mock a cheatsheet
|
||||||
sheet := Sheet{
|
sheet := Sheet{
|
||||||
|
@ -120,66 +77,17 @@ func TestSearchMultiMatchNoColor(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// search the sheet
|
// search the sheet
|
||||||
matches := sheet.Search(reg, false)
|
matches := sheet.Search(reg)
|
||||||
|
|
||||||
// specify the expected results
|
// specify the expected results
|
||||||
want := []Match{
|
want := "The quick brown fox\nthe lazy dog."
|
||||||
Match{
|
|
||||||
Line: 1,
|
|
||||||
Text: "The quick brown fox",
|
|
||||||
},
|
|
||||||
Match{
|
|
||||||
Line: 3,
|
|
||||||
Text: "the lazy dog.",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert that the correct matches were returned
|
// assert that the correct matches were returned
|
||||||
if !reflect.DeepEqual(matches, want) {
|
if !reflect.DeepEqual(matches, want) {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed to return expected matches: want:\n%s, got:\n%s",
|
"failed to return expected matches: want:\n%s, got:\n%s",
|
||||||
spew.Sdump(want),
|
want,
|
||||||
spew.Sdump(matches),
|
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),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue