1
1
mirror of https://github.com/walles/moar.git synced 2024-09-11 20:17:13 +03:00

Make a lot fewer functions public

This commit is contained in:
Johan Walles 2020-12-29 22:57:44 +01:00
parent f069409725
commit 2b18347022
10 changed files with 78 additions and 60 deletions

View File

@ -75,24 +75,33 @@ you can send questions to <johan.walles@gmail.com>.
Embedding
---------
Here's how to embed `moar` in your app:
Here's one way to embed `moar` in your app:
```go
package main
import (
"bytes"
"fmt"
"github.com/walles/moar/m"
)
func main() {
err := m.PageString("Months", "March, April\nMay")
if err != nil {
// Handle pager problems
panic(err)
buf := bytes.Buffer{}
fmt.Fprintln(&buf, "March")
fmt.Fprintln(&buf, "April")
name := "Months"
e := m.Page(m.NewReaderFromStream(&name, &buf))
if e != nil {
panic(e)
}
}
```
The `m.Page()` parameter can also be initialized using `NewReaderFromText()` or
`NewReaderFromFilename()`.
Developing
----------

View File

@ -47,13 +47,13 @@ func resetManPageFormatForTesting() {
func termcapToStyle(termcap string) tcell.Style {
// Add a character to be sure we have one to take the format from
tokens, _ := TokensFromString(termcap + "x")
tokens, _ := tokensFromString(termcap + "x")
return tokens[len(tokens)-1].Style
}
// TokensFromString turns a (formatted) string into a series of tokens,
// tokensFromString turns a (formatted) string into a series of tokens,
// and an unformatted string
func TokensFromString(s string) ([]Token, *string) {
func tokensFromString(s string) ([]Token, *string) {
var tokens []Token
styleBrokenUtf8 := tcell.StyleDefault.Background(tcell.ColorSilver).Foreground(tcell.ColorMaroon)

View File

@ -34,7 +34,7 @@ func TestTokenize(t *testing.T) {
var loglines strings.Builder
log.SetOutput(&loglines)
tokens, plainString := TokensFromString(line)
tokens, plainString := tokensFromString(line)
if len(tokens) != utf8.RuneCountInString(*plainString) {
t.Errorf("%s:%d: len(tokens)=%d, len(plainString)=%d for: <%s>",
fileName, lineNumber,
@ -51,7 +51,7 @@ func TestTokenize(t *testing.T) {
}
func TestUnderline(t *testing.T) {
tokens, _ := TokensFromString("a\x1b[4mb\x1b[24mc")
tokens, _ := tokensFromString("a\x1b[4mb\x1b[24mc")
assert.Equal(t, len(tokens), 3)
assert.Equal(t, tokens[0], Token{Rune: 'a', Style: tcell.StyleDefault})
assert.Equal(t, tokens[1], Token{Rune: 'b', Style: tcell.StyleDefault.Underline(true)})
@ -60,14 +60,14 @@ func TestUnderline(t *testing.T) {
func TestManPages(t *testing.T) {
// Bold
tokens, _ := TokensFromString("ab\bbc")
tokens, _ := tokensFromString("ab\bbc")
assert.Equal(t, len(tokens), 3)
assert.Equal(t, tokens[0], Token{Rune: 'a', Style: tcell.StyleDefault})
assert.Equal(t, tokens[1], Token{Rune: 'b', Style: tcell.StyleDefault.Bold(true)})
assert.Equal(t, tokens[2], Token{Rune: 'c', Style: tcell.StyleDefault})
// Underline
tokens, _ = TokensFromString("a_\bbc")
tokens, _ = tokensFromString("a_\bbc")
assert.Equal(t, len(tokens), 3)
assert.Equal(t, tokens[0], Token{Rune: 'a', Style: tcell.StyleDefault})
assert.Equal(t, tokens[1], Token{Rune: 'b', Style: tcell.StyleDefault.Underline(true)})
@ -75,7 +75,7 @@ func TestManPages(t *testing.T) {
// Bullet point 1, taken from doing this on my macOS system:
// env PAGER="hexdump -C" man printf | moar
tokens, _ = TokensFromString("a+\b+\bo\bob")
tokens, _ = tokensFromString("a+\b+\bo\bob")
assert.Equal(t, len(tokens), 3)
assert.Equal(t, tokens[0], Token{Rune: 'a', Style: tcell.StyleDefault})
assert.Equal(t, tokens[1], Token{Rune: '•', Style: tcell.StyleDefault})
@ -83,7 +83,7 @@ func TestManPages(t *testing.T) {
// Bullet point 2, taken from doing this using the "fish" shell on my macOS system:
// man printf | hexdump -C | moar
tokens, _ = TokensFromString("a+\bob")
tokens, _ = tokensFromString("a+\bob")
assert.Equal(t, len(tokens), 3)
assert.Equal(t, tokens[0], Token{Rune: 'a', Style: tcell.StyleDefault})
assert.Equal(t, tokens[1], Token{Rune: '•', Style: tcell.StyleDefault})

View File

@ -7,8 +7,8 @@ type MatchRanges struct {
Matches [][2]int
}
// GetMatchRanges locates one or more regexp matches in a string
func GetMatchRanges(String *string, Pattern *regexp.Regexp) *MatchRanges {
// getMatchRanges locates one or more regexp matches in a string
func getMatchRanges(String *string, Pattern *regexp.Regexp) *MatchRanges {
if Pattern == nil {
return nil
}

View File

@ -11,7 +11,7 @@ import (
var _TestString = "mamma"
func TestGetMatchRanges(t *testing.T) {
matchRanges := GetMatchRanges(&_TestString, regexp.MustCompile("m+"))
matchRanges := getMatchRanges(&_TestString, regexp.MustCompile("m+"))
assert.Equal(t, len(matchRanges.Matches), 2) // Two matches
assert.DeepEqual(t, matchRanges.Matches[0][0], 0) // First match starts at 0
@ -22,14 +22,14 @@ func TestGetMatchRanges(t *testing.T) {
}
func TestGetMatchRangesNilPattern(t *testing.T) {
matchRanges := GetMatchRanges(&_TestString, nil)
matchRanges := getMatchRanges(&_TestString, nil)
assert.Assert(t, matchRanges == nil)
assert.Assert(t, !matchRanges.InRange(0))
}
func TestInRange(t *testing.T) {
// Should match the one in TestGetMatchRanges()
matchRanges := GetMatchRanges(&_TestString, regexp.MustCompile("m+"))
matchRanges := getMatchRanges(&_TestString, regexp.MustCompile("m+"))
assert.Assert(t, !matchRanges.InRange(-1)) // Before start
assert.Assert(t, matchRanges.InRange(0)) // m
@ -43,7 +43,7 @@ func TestInRange(t *testing.T) {
func TestUtf8(t *testing.T) {
// This test verifies that the match ranges are by rune rather than by byte
unicodes := "-ä-ä-"
matchRanges := GetMatchRanges(&unicodes, regexp.MustCompile("ä"))
matchRanges := getMatchRanges(&unicodes, regexp.MustCompile("ä"))
assert.Assert(t, !matchRanges.InRange(0)) // -
assert.Assert(t, matchRanges.InRange(1)) // ä
@ -55,7 +55,7 @@ func TestUtf8(t *testing.T) {
func TestNoMatch(t *testing.T) {
// This test verifies that the match ranges are by rune rather than by byte
unicodes := "gris"
matchRanges := GetMatchRanges(&unicodes, regexp.MustCompile("apa"))
matchRanges := getMatchRanges(&unicodes, regexp.MustCompile("apa"))
assert.Assert(t, !matchRanges.InRange(0))
assert.Assert(t, !matchRanges.InRange(1))
@ -67,7 +67,7 @@ func TestNoMatch(t *testing.T) {
func TestEndMatch(t *testing.T) {
// This test verifies that the match ranges are by rune rather than by byte
unicodes := "-ä"
matchRanges := GetMatchRanges(&unicodes, regexp.MustCompile("ä"))
matchRanges := getMatchRanges(&unicodes, regexp.MustCompile("ä"))
assert.Assert(t, !matchRanges.InRange(0)) // -
assert.Assert(t, matchRanges.InRange(1)) // ä

View File

@ -152,13 +152,13 @@ func createScreenLine(
searchHitDelta = -1
}
tokens, plainString := TokensFromString(line)
tokens, plainString := tokensFromString(line)
if stringIndexAtColumnZero >= len(tokens) {
// Nothing (more) to display, never mind
return returnMe
}
matchRanges := GetMatchRanges(plainString, search)
matchRanges := getMatchRanges(plainString, search)
for _, token := range tokens[stringIndexAtColumnZero:] {
if len(returnMe) >= screenColumnsCount {
// We are trying to add a character to the right of the screen.
@ -329,7 +329,7 @@ func (p *Pager) _FindFirstHitLineOneBased(firstLineOneBased int, backwards bool)
return nil
}
_, lineText := TokensFromString(*line)
_, lineText := tokensFromString(*line)
if p.searchPattern.MatchString(*lineText) {
return &lineNumber
}
@ -413,21 +413,21 @@ func (p *Pager) _ScrollToPreviousSearchHit() {
}
func (p *Pager) _UpdateSearchPattern() {
p.searchPattern = ToPattern(p.searchString)
p.searchPattern = toPattern(p.searchString)
p._ScrollToSearchHits()
// FIXME: If the user is typing, indicate to user if we didn't find anything
}
// ToPattern compiles a search string into a pattern.
// toPattern compiles a search string into a pattern.
//
// If the string contains only lower-case letter the pattern will be case insensitive.
//
// If the string is empty the pattern will be nil.
//
// If the string does not compile into a regexp the pattern will match the string verbatim
func ToPattern(compileMe string) *regexp.Regexp {
func toPattern(compileMe string) *regexp.Regexp {
if len(compileMe) == 0 {
return nil
}

View File

@ -12,7 +12,7 @@ import (
)
func TestUnicodeRendering(t *testing.T) {
reader := NewReaderFromStream(strings.NewReader("åäö"))
reader := NewReaderFromStream(nil, strings.NewReader("åäö"))
if err := reader._Wait(); err != nil {
panic(err)
}
@ -47,7 +47,7 @@ func createExpectedCell(Rune rune, Style tcell.Style) Token {
}
func TestFgColorRendering(t *testing.T) {
reader := NewReaderFromStream(strings.NewReader(
reader := NewReaderFromStream(nil, strings.NewReader(
"\x1b[30ma\x1b[31mb\x1b[32mc\x1b[33md\x1b[34me\x1b[35mf\x1b[36mg\x1b[37mh\x1b[0mi"))
if err := reader._Wait(); err != nil {
panic(err)
@ -73,7 +73,7 @@ func TestFgColorRendering(t *testing.T) {
func TestBrokenUtf8(t *testing.T) {
// The broken UTF8 character in the middle is based on "©" = 0xc2a9
reader := NewReaderFromStream(strings.NewReader("abc\xc2def"))
reader := NewReaderFromStream(nil, strings.NewReader("abc\xc2def"))
if err := reader._Wait(); err != nil {
panic(err)
}
@ -113,7 +113,7 @@ func startPaging(t *testing.T, reader *Reader) []tcell.SimCell {
// assertIndexOfFirstX verifies the (zero-based) index of the first 'x'
func assertIndexOfFirstX(t *testing.T, s string, expectedIndex int) {
reader := NewReaderFromStream(strings.NewReader(s))
reader := NewReaderFromStream(nil, strings.NewReader(s))
if err := reader._Wait(); err != nil {
panic(err)
}
@ -192,7 +192,7 @@ func TestCodeHighlighting(t *testing.T) {
}
func testManPageFormatting(t *testing.T, input string, expected Token) {
reader := NewReaderFromStream(strings.NewReader(input))
reader := NewReaderFromStream(nil, strings.NewReader(input))
if err := reader._Wait(); err != nil {
panic(err)
}
@ -221,23 +221,23 @@ func TestManPageFormatting(t *testing.T) {
}
func TestToPattern(t *testing.T) {
assert.Assert(t, ToPattern("") == nil)
assert.Assert(t, toPattern("") == nil)
// Test regexp matching
assert.Assert(t, ToPattern("G.*S").MatchString("GRIIIS"))
assert.Assert(t, !ToPattern("G.*S").MatchString("gRIIIS"))
assert.Assert(t, toPattern("G.*S").MatchString("GRIIIS"))
assert.Assert(t, !toPattern("G.*S").MatchString("gRIIIS"))
// Test case insensitive regexp matching
assert.Assert(t, ToPattern("g.*s").MatchString("GRIIIS"))
assert.Assert(t, ToPattern("g.*s").MatchString("gRIIIS"))
assert.Assert(t, toPattern("g.*s").MatchString("GRIIIS"))
assert.Assert(t, toPattern("g.*s").MatchString("gRIIIS"))
// Test non-regexp matching
assert.Assert(t, ToPattern(")G").MatchString(")G"))
assert.Assert(t, !ToPattern(")G").MatchString(")g"))
assert.Assert(t, toPattern(")G").MatchString(")G"))
assert.Assert(t, !toPattern(")G").MatchString(")g"))
// Test case insensitive non-regexp matching
assert.Assert(t, ToPattern(")g").MatchString(")G"))
assert.Assert(t, ToPattern(")g").MatchString(")g"))
assert.Assert(t, toPattern(")g").MatchString(")G"))
assert.Assert(t, toPattern(")g").MatchString(")g"))
}
func assertTokenRangesEqual(t *testing.T, actual []Token, expected []Token) {
@ -342,13 +342,13 @@ func TestCreateScreenLineScrolled2Utf8SearchHit(t *testing.T) {
}
func TestFindFirstLineOneBasedSimple(t *testing.T) {
reader := NewReaderFromStream(strings.NewReader("AB"))
reader := NewReaderFromStream(nil, strings.NewReader("AB"))
pager := NewPager(reader)
// Wait for reader to finish reading
<-reader.done
pager.searchPattern = ToPattern("AB")
pager.searchPattern = toPattern("AB")
hitLine := pager._FindFirstHitLineOneBased(1, false)
assert.Check(t, hitLine != nil)
@ -356,13 +356,13 @@ func TestFindFirstLineOneBasedSimple(t *testing.T) {
}
func TestFindFirstLineOneBasedAnsi(t *testing.T) {
reader := NewReaderFromStream(strings.NewReader("A\x1b[30mB"))
reader := NewReaderFromStream(nil, strings.NewReader("A\x1b[30mB"))
pager := NewPager(reader)
// Wait for reader to finish reading
<-reader.done
pager.searchPattern = ToPattern("AB")
pager.searchPattern = toPattern("AB")
hitLine := pager._FindFirstHitLineOneBased(1, false)
assert.Check(t, hitLine != nil)

View File

@ -146,8 +146,13 @@ func readStream(stream io.Reader, reader *Reader, fromFilter *exec.Cmd) {
}
// NewReaderFromStream creates a new stream reader
func NewReaderFromStream(reader io.Reader) *Reader {
return newReaderFromStream(reader, nil)
func NewReaderFromStream(name *string, reader io.Reader) *Reader {
mReader := newReaderFromStream(reader, nil)
mReader.lock.Lock()
mReader.name = name
mReader.lock.Unlock()
return mReader
}
// newReaderFromStream creates a new stream reader
@ -178,7 +183,10 @@ func newReaderFromStream(reader io.Reader, fromFilter *exec.Cmd) *Reader {
return &returnMe
}
// NewReaderFromText creates a Reader from a block of text
// NewReaderFromText creates a Reader from a block of text.
//
// First parameter is the name of this Reader. This name will be displayed by
// Moar in the bottom left corner of the screen.
func NewReaderFromText(name string, text string) *Reader {
noExternalNewlines := strings.Trim(text, "\n")
done := make(chan bool, 1)
@ -299,7 +307,11 @@ func tryOpen(filename string) error {
return err
}
// NewReaderFromFilename creates a new file reader
// NewReaderFromFilename creates a new file reader.
//
// The Reader will try to uncompress various compressed file format, and also
// apply highlighting to the file using highlight:
// http://www.andre-simon.de/doku/highlight/en/highlight.php
func NewReaderFromFilename(filename string) (*Reader, error) {
fileError := tryOpen(filename)
if fileError != nil {
@ -330,10 +342,7 @@ func NewReaderFromFilename(filename string) (*Reader, error) {
return nil, err
}
reader := NewReaderFromStream(stream)
reader.lock.Lock()
reader.name = &filename
reader.lock.Unlock()
reader := NewReaderFromStream(&filename, stream)
return reader, nil
}

View File

@ -172,7 +172,7 @@ func TestGetLongLine(t *testing.T) {
}
func getReaderWithLineCount(totalLines int) *Reader {
reader := NewReaderFromStream(strings.NewReader(strings.Repeat("x\n", totalLines)))
reader := NewReaderFromStream(nil, strings.NewReader(strings.Repeat("x\n", totalLines)))
if err := reader._Wait(); err != nil {
panic(err)
}

10
moar.go
View File

@ -59,8 +59,8 @@ func printUsage(output io.Writer) {
}
}
// PrintProblemsHeader prints bug reporting information to stderr
func PrintProblemsHeader() {
// printProblemsHeader prints bug reporting information to stderr
func printProblemsHeader() {
fmt.Fprintln(os.Stderr, "Please post the following report at <https://github.com/walles/moar/issues>,")
fmt.Fprintln(os.Stderr, "or e-mail it to johan.walles@gmail.com.")
fmt.Fprintln(os.Stderr)
@ -85,7 +85,7 @@ func main() {
return
}
PrintProblemsHeader()
printProblemsHeader()
panic(err)
}()
@ -117,7 +117,7 @@ func main() {
if stdinIsRedirected && !stdoutIsRedirected {
// Display input pipe contents
reader := m.NewReaderFromStream(os.Stdin)
reader := m.NewReaderFromStream(nil, os.Stdin)
startPaging(reader)
return
}
@ -171,7 +171,7 @@ func startPaging(reader *m.Reader) {
}
if len(loglines.String()) > 0 {
PrintProblemsHeader()
printProblemsHeader()
// FIXME: Don't print duplicate log messages more than once,
// maybe invent our own logger for this?