Skip to content

Commit b6c88c7

Browse files
committed
Detect man pages by contents
Not all man implementations set environment variables when invoking the pager. For those that don't, we now detect man pages by contents. Previously we looked at the existence of MANPATH, but that's *not* set by "man" on macOS, I don't know where I got that from. Maybe it changed?
1 parent 64f9800 commit b6c88c7

File tree

5 files changed

+76
-16
lines changed

5 files changed

+76
-16
lines changed

cmd/moor/moor.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -327,25 +327,23 @@ func russiaNotSupported() {
327327
os.Exit(1)
328328
}
329329

330-
// On man pages, disable line numbers by default.
330+
// For git output and man pages, disable line numbers by default.
331331
//
332332
// Before paging, "man" first checks the terminal width and formats the man page
333-
// to fit that width.
333+
// to fit that width. Git does that as well for git diff --stat.
334334
//
335-
// Then, if moor adds line numbers, the rightmost part of the man page won't be
336-
// visible.
335+
// Then, if moor adds line numbers, the rightmost part of the page will scroll
336+
// out of view.
337337
//
338-
// So we try to detect showing man pages, and in that case disable line numbers
339-
// so that the rightmost part of the page is visible by default.
338+
// So we try to this, and in that case disable line numbers so that the
339+
// rightmost part of the page is visible by default.
340+
//
341+
// See also internal/haveLoadedManPage(), where we try to detect man pages by
342+
// their contents.
340343
func noLineNumbersDefault() bool {
341-
if os.Getenv("MANPATH") != "" {
342-
// Set by "man" on macOS, skip line numbers in this case
343-
return true
344-
}
345-
346344
if os.Getenv("MAN_PN") != "" {
347-
// Set by "man" on Ubuntu 22.04.4 when I tested it inside of Docker,
348-
// skip line numbers in this case
345+
// Set by "man" on Ubuntu 22.04.4 when I tested it inside of Docker.
346+
log.Debug("MAN_PN is set, skipping line numbers for man page")
349347
return true
350348
}
351349

@@ -354,6 +352,7 @@ func noLineNumbersDefault() bool {
354352

355353
// Neither logs nor diffs are helped by line numbers, turn them off by
356354
// default.
355+
log.Debug("GIT_EXEC_PATH is set, skipping line numbers when paging git output")
357356
return true
358357
}
359358

internal/detectManPage.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package internal
2+
3+
import (
4+
"github.com/walles/moor/v2/internal/linemetadata"
5+
)
6+
7+
func (p *Pager) haveLoadedManPage() bool {
8+
reader := p.Reader()
9+
if reader == nil {
10+
return false
11+
}
12+
13+
for _, line := range reader.GetLines(linemetadata.Index{}, 10).Lines {
14+
if line.Line.HasManPageFormatting() {
15+
return true
16+
}
17+
}
18+
return false
19+
}

internal/pager.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,8 +634,17 @@ func (p *Pager) StartPaging(screen twin.Screen, chromaStyle *chroma.Style, chrom
634634
}
635635

636636
case eventMaybeDone:
637-
// Do nothing. We got this just so that we'll do the QuitIfOneScreen
638-
// check (above) as soon as highlighting is done.
637+
// Man pages come pre-formatted for the screen width, and line
638+
// numbers will mess that up. So we disable line numbers if we
639+
// detect a man page by its contents.
640+
//
641+
// See also noLineNumbersDefault() where we use environment
642+
// variables to try to detect man paging.
643+
if p.haveLoadedManPage() && len(p.readers) == 1 {
644+
p.ShowLineNumbers = false
645+
p.showLineNumbers = false
646+
log.Info("man page detected by contents, disabling line numbers")
647+
}
639648

640649
case eventSpinnerUpdate:
641650
spinner = event.spinner

internal/reader/line.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@ func (line *Line) Plain(lineIndex *linemetadata.Index) string {
7474
}
7575
return *line.plain
7676
}
77+
78+
func (line *Line) HasManPageFormatting() bool {
79+
return textstyles.HasManPageFormatting(line.raw)
80+
}

internal/textstyles/ansiTokenizer.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ func tokensFromStyledString(styledString _StyledString) []twin.StyledRune {
368368
return tokens
369369
}
370370

371-
// Special handling for man page formatted lines
371+
// Special handling for man page formatted lines. If this is updated you
372+
// must update HasManPageFormatting() as well.
372373
for index := 0; index < len(runes); index++ {
373374
nextIndex, token := consumeBullet(runes, index)
374375
if nextIndex != index {
@@ -407,6 +408,34 @@ func tokensFromStyledString(styledString _StyledString) []twin.StyledRune {
407408
return tokens
408409
}
409410

411+
// Like tokensFromStyledString(), but only checks without building any formatting
412+
func HasManPageFormatting(s string) bool {
413+
runes := []rune(s)
414+
for index := range runes {
415+
nextIndex, _ := consumeBullet(runes, index)
416+
if nextIndex != index {
417+
return true
418+
}
419+
420+
nextIndex, _ = consumeBoldUnderline(runes, index)
421+
if nextIndex != index {
422+
return true
423+
}
424+
425+
nextIndex, _ = consumeBold(runes, index)
426+
if nextIndex != index {
427+
return true
428+
}
429+
430+
nextIndex, _ = consumeUnderline(runes, index)
431+
if nextIndex != index {
432+
return true
433+
}
434+
}
435+
436+
return false
437+
}
438+
410439
type _StyledString struct {
411440
String string
412441
Style twin.Style

0 commit comments

Comments
 (0)