mirror of
				https://github.com/cheat/cheat.git
				synced 2025-11-04 07:45:28 +01:00 
			
		
		
		
	feat(pagination): implement paginated output
Implement a `pager` config option. If configured, `cheat` will automatically pipe output through the configured pager (where appropriate).
This commit is contained in:
		@@ -1,18 +1,20 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
	"github.com/cheat/cheat/internal/display"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// cmdDirectories lists the configured cheatpaths.
 | 
			
		||||
func cmdDirectories(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
 | 
			
		||||
	// initialize a tabwriter to produce cleanly columnized output
 | 
			
		||||
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
 | 
			
		||||
	var out bytes.Buffer
 | 
			
		||||
	w := tabwriter.NewWriter(&out, 0, 0, 1, ' ', 0)
 | 
			
		||||
 | 
			
		||||
	// generate sorted, columnized output
 | 
			
		||||
	for _, path := range conf.Cheatpaths {
 | 
			
		||||
@@ -25,4 +27,5 @@ func cmdDirectories(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
 | 
			
		||||
	// write columnized output to stdout
 | 
			
		||||
	w.Flush()
 | 
			
		||||
	display.Display(out.String(), conf)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
@@ -9,6 +10,7 @@ import (
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
	"github.com/cheat/cheat/internal/display"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheet"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheets"
 | 
			
		||||
)
 | 
			
		||||
@@ -85,10 +87,13 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// initialize a tabwriter to produce cleanly columnized output
 | 
			
		||||
	w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
 | 
			
		||||
	var out bytes.Buffer
 | 
			
		||||
	w := tabwriter.NewWriter(&out, 0, 0, 1, ' ', 0)
 | 
			
		||||
 | 
			
		||||
	// write a header row
 | 
			
		||||
	fmt.Fprintln(w, "title:\tfile:\ttags:")
 | 
			
		||||
 | 
			
		||||
	// generate sorted, columnized output
 | 
			
		||||
	fmt.Fprintln(w, "title:\tfile:\ttags:")
 | 
			
		||||
	for _, sheet := range flattened {
 | 
			
		||||
		fmt.Fprintln(w, fmt.Sprintf(
 | 
			
		||||
			"%s\t%s\t%s",
 | 
			
		||||
@@ -100,4 +105,5 @@ func cmdList(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
 | 
			
		||||
	// write columnized output to stdout
 | 
			
		||||
	w.Flush()
 | 
			
		||||
	display.Display(out.String(), conf)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
	"github.com/cheat/cheat/internal/display"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheet"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheets"
 | 
			
		||||
)
 | 
			
		||||
@@ -87,12 +88,14 @@ func cmdSearch(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// output the cheatsheet title
 | 
			
		||||
		fmt.Printf("%s:\n", sheet.Title)
 | 
			
		||||
		out := fmt.Sprintf("%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)
 | 
			
		||||
			out += fmt.Sprintf("  %s\n", line)
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Println("")
 | 
			
		||||
 | 
			
		||||
		// display the output
 | 
			
		||||
		display.Display(out, conf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
	"github.com/cheat/cheat/internal/display"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -18,8 +19,12 @@ func cmdTags(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// write sheet tags to stdout
 | 
			
		||||
	// assemble the output
 | 
			
		||||
	out := ""
 | 
			
		||||
	for _, tag := range sheets.Tags(cheatsheets) {
 | 
			
		||||
		fmt.Println(tag)
 | 
			
		||||
		out += fmt.Sprintln(tag)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// display the output
 | 
			
		||||
	display.Display(out, conf)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
	"github.com/cheat/cheat/internal/display"
 | 
			
		||||
	"github.com/cheat/cheat/internal/sheets"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -47,5 +48,5 @@ func cmdView(opts map[string]interface{}, conf config.Config) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// display the cheatsheet
 | 
			
		||||
	fmt.Print(sheet.Text)
 | 
			
		||||
	display.Display(sheet.Text, conf)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,9 @@ style: monokai
 | 
			
		||||
# One of: "terminal", "terminal256", "terminal16m"
 | 
			
		||||
formatter: terminal16m
 | 
			
		||||
 | 
			
		||||
# Through which pager should output be piped? (Unset this key for no pager.)
 | 
			
		||||
pager: less -FRX
 | 
			
		||||
 | 
			
		||||
# The paths at which cheatsheets are available. Tags associated with a cheatpath
 | 
			
		||||
# are automatically attached to all cheatsheets residing on that path.
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,9 @@ style: monokai
 | 
			
		||||
# One of: "terminal", "terminal256", "terminal16m"
 | 
			
		||||
formatter: terminal16m
 | 
			
		||||
 | 
			
		||||
# Through which pager should output be piped? (Unset this key for no pager.)
 | 
			
		||||
pager: less -FRX
 | 
			
		||||
 | 
			
		||||
# The paths at which cheatsheets are available. Tags associated with a cheatpath
 | 
			
		||||
# are automatically attached to all cheatsheets residing on that path.
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import (
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	cp "github.com/cheat/cheat/internal/cheatpath"
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +20,7 @@ type Config struct {
 | 
			
		||||
	Cheatpaths []cp.Cheatpath `yaml:"cheatpaths"`
 | 
			
		||||
	Style      string         `yaml:"style"`
 | 
			
		||||
	Formatter  string         `yaml:"formatter"`
 | 
			
		||||
	Pager      string         `yaml:"pager"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns a new Config struct
 | 
			
		||||
@@ -111,5 +113,10 @@ func New(opts map[string]interface{}, confPath string, resolve bool) (Config, er
 | 
			
		||||
		conf.Formatter = "terminal16m"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if a pager was not provided, set a default
 | 
			
		||||
	if strings.TrimSpace(conf.Pager) == "" {
 | 
			
		||||
		conf.Pager = ""
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return conf, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								internal/display/display.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								internal/display/display.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
package display
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cheat/cheat/internal/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Display writes output either directly to stdout, or through a pager,
 | 
			
		||||
// depending upon configuration.
 | 
			
		||||
func Display(out string, conf config.Config) {
 | 
			
		||||
	// if no pager was configured, print the output to stdout and exit
 | 
			
		||||
	if conf.Pager == "" {
 | 
			
		||||
		fmt.Print(out)
 | 
			
		||||
		os.Exit(0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// otherwise, pipe output through the pager
 | 
			
		||||
	parts := strings.Split(conf.Pager, " ")
 | 
			
		||||
	pager := parts[0]
 | 
			
		||||
	args := parts[1:]
 | 
			
		||||
 | 
			
		||||
	// run 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))
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user