X Tutup
Skip to content

Commit 3049db6

Browse files
authored
Merge pull request cli#1630 from cli/pager-for-everyone
Extend PAGER support to all commands producing significant output
2 parents 5a2c7e7 + fa2513d commit 3049db6

File tree

19 files changed

+197
-115
lines changed

19 files changed

+197
-115
lines changed

cmd/gh/main.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ func main() {
5151
os.Exit(2)
5252
}
5353

54-
prompt, _ := cfg.Get("", "prompt")
55-
56-
if prompt == config.PromptsDisabled {
54+
if prompt, _ := cfg.Get("", "prompt"); prompt == config.PromptsDisabled {
5755
cmdFactory.IOStreams.SetNeverPrompt(true)
5856
}
5957

58+
if pager, _ := cfg.Get("", "pager"); pager != "" {
59+
cmdFactory.IOStreams.SetPager(pager)
60+
}
61+
6062
expandedArgs := []string{}
6163
if len(os.Args) > 0 {
6264
expandedArgs = os.Args[1:]

internal/config/config_file_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ github.com:
110110
_, err := ParseConfig("config.yml")
111111
assert.Nil(t, err)
112112

113-
expectedMain := "# What protocol to use when performing git operations. Supported values: ssh, https\ngit_protocol: https\n# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment.\neditor:\n# When to interactively prompt. This is a global config that cannot be overriden by hostname. Supported values: enabled, disabled\nprompt: enabled\n# Aliases allow you to create nicknames for gh commands\naliases:\n co: pr checkout\n"
114113
expectedHosts := `github.com:
115114
user: keiyuri
116115
oauth_token: "123456"
117116
`
118117

119-
assert.Equal(t, expectedMain, mainBuf.String())
120118
assert.Equal(t, expectedHosts, hostsBuf.String())
119+
assert.NotContains(t, mainBuf.String(), "github.com")
120+
assert.NotContains(t, mainBuf.String(), "oauth_token")
121121
}
122122

123123
func Test_parseConfigFile(t *testing.T) {

internal/config/config_type.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ func NewBlankRoot() *yaml.Node {
180180
Kind: yaml.ScalarNode,
181181
Value: PromptsEnabled,
182182
},
183+
{
184+
HeadComment: "A pager program to send command output to. Example value: less",
185+
Kind: yaml.ScalarNode,
186+
Value: "pager",
187+
},
188+
{
189+
Kind: yaml.ScalarNode,
190+
Value: "",
191+
},
183192
{
184193
HeadComment: "Aliases allow you to create nicknames for gh commands",
185194
Kind: yaml.ScalarNode,

internal/config/config_type_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"testing"
66

7+
"github.com/MakeNowJust/heredoc"
78
"github.com/stretchr/testify/assert"
89
)
910

@@ -19,8 +20,8 @@ func Test_fileConfig_Set(t *testing.T) {
1920
assert.NoError(t, c.Set("github.com", "user", "hubot"))
2021
assert.NoError(t, c.Write())
2122

22-
expected := "# What protocol to use when performing git operations. Supported values: ssh, https\ngit_protocol: https\n# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment.\neditor: nano\n# When to interactively prompt. This is a global config that cannot be overriden by hostname. Supported values: enabled, disabled\nprompt: enabled\n# Aliases allow you to create nicknames for gh commands\naliases:\n co: pr checkout\n"
23-
assert.Equal(t, expected, mainBuf.String())
23+
assert.Contains(t, mainBuf.String(), "editor: nano")
24+
assert.Contains(t, mainBuf.String(), "git_protocol: https")
2425
assert.Equal(t, `github.com:
2526
git_protocol: ssh
2627
user: hubot
@@ -37,7 +38,19 @@ func Test_defaultConfig(t *testing.T) {
3738
cfg := NewBlankConfig()
3839
assert.NoError(t, cfg.Write())
3940

40-
expected := "# What protocol to use when performing git operations. Supported values: ssh, https\ngit_protocol: https\n# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment.\neditor:\n# When to interactively prompt. This is a global config that cannot be overriden by hostname. Supported values: enabled, disabled\nprompt: enabled\n# Aliases allow you to create nicknames for gh commands\naliases:\n co: pr checkout\n"
41+
expected := heredoc.Doc(`
42+
# What protocol to use when performing git operations. Supported values: ssh, https
43+
git_protocol: https
44+
# What editor gh should run when creating issues, pull requests, etc. If blank, will refer to environment.
45+
editor:
46+
# When to interactively prompt. This is a global config that cannot be overriden by hostname. Supported values: enabled, disabled
47+
prompt: enabled
48+
# A pager program to send command output to. Example value: less
49+
pager:
50+
# Aliases allow you to create nicknames for gh commands
51+
aliases:
52+
co: pr checkout
53+
`)
4154
assert.Equal(t, expected, mainBuf.String())
4255
assert.Equal(t, "", hostsBuf.String())
4356

pkg/cmd/api/api.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sort"
1414
"strconv"
1515
"strings"
16+
"syscall"
1617

1718
"github.com/MakeNowJust/heredoc"
1819
"github.com/cli/cli/internal/ghinstance"
@@ -196,6 +197,12 @@ func apiRun(opts *ApiOptions) error {
196197
headersOutputStream := opts.IO.Out
197198
if opts.Silent {
198199
opts.IO.Out = ioutil.Discard
200+
} else {
201+
err := opts.IO.StartPager()
202+
if err != nil {
203+
return err
204+
}
205+
defer opts.IO.StopPager()
199206
}
200207

201208
host := ghinstance.OverridableDefault()
@@ -265,12 +272,13 @@ func processResponse(resp *http.Response, opts *ApiOptions, headersOutputStream
265272

266273
if isJSON && opts.IO.ColorEnabled() {
267274
err = jsoncolor.Write(opts.IO.Out, responseBody, " ")
268-
if err != nil {
269-
return
270-
}
271275
} else {
272276
_, err = io.Copy(opts.IO.Out, responseBody)
273-
if err != nil {
277+
}
278+
if err != nil {
279+
if errors.Is(err, syscall.EPIPE) {
280+
err = nil
281+
} else {
274282
return
275283
}
276284
}

pkg/cmd/issue/list/fixtures/issueList.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"number": 1,
1010
"title": "number won",
1111
"url": "https://wow.com",
12+
"updatedAt": "2011-01-26T19:01:12Z",
1213
"labels": {
1314
"nodes": [
1415
{
@@ -22,6 +23,7 @@
2223
"number": 2,
2324
"title": "number too",
2425
"url": "https://wow.com",
26+
"updatedAt": "2011-01-26T19:01:12Z",
2527
"labels": {
2628
"nodes": [
2729
{
@@ -35,6 +37,7 @@
3537
"number": 4,
3638
"title": "number fore",
3739
"url": "https://wow.com",
40+
"updatedAt": "2011-01-26T19:01:12Z",
3841
"labels": {
3942
"nodes": [
4043
{

pkg/cmd/issue/list/list.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,16 @@ func listRun(opts *ListOptions) error {
116116
return err
117117
}
118118

119+
err = opts.IO.StartPager()
120+
if err != nil {
121+
return err
122+
}
123+
defer opts.IO.StopPager()
124+
119125
if isTerminal {
120126
hasFilters := opts.State != "open" || len(opts.Labels) > 0 || opts.Assignee != "" || opts.Author != "" || opts.Mention != "" || opts.Milestone != ""
121127
title := prShared.ListHeader(ghrepo.FullName(baseRepo), "issue", len(listResult.Issues), listResult.TotalCount, hasFilters)
122-
fmt.Fprintf(opts.IO.ErrOut, "\n%s\n\n", title)
128+
fmt.Fprintf(opts.IO.Out, "\n%s\n\n", title)
123129
}
124130

125131
issueShared.PrintIssues(opts.IO, "", len(listResult.Issues), listResult.Issues)

pkg/cmd/issue/list/list_test.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
"net/http"
88
"os/exec"
99
"reflect"
10+
"regexp"
1011
"testing"
1112

13+
"github.com/MakeNowJust/heredoc"
1214
"github.com/cli/cli/internal/config"
1315
"github.com/cli/cli/internal/ghrepo"
1416
"github.com/cli/cli/internal/run"
@@ -97,15 +99,19 @@ func TestIssueList_tty(t *testing.T) {
9799
t.Errorf("error running command `issue list`: %v", err)
98100
}
99101

100-
eq(t, output.Stderr(), `
101-
Showing 3 of 3 open issues in OWNER/REPO
102+
out := output.String()
103+
timeRE := regexp.MustCompile(`\d+ years`)
104+
out = timeRE.ReplaceAllString(out, "X years")
102105

103-
`)
106+
assert.Equal(t, heredoc.Doc(`
104107
105-
test.ExpectLines(t, output.String(),
106-
"number won",
107-
"number too",
108-
"number fore")
108+
Showing 3 of 3 open issues in OWNER/REPO
109+
110+
#1 number won (label) about X years ago
111+
#2 number too (label) about X years ago
112+
#4 number fore (label) about X years ago
113+
`), out)
114+
assert.Equal(t, ``, output.Stderr())
109115
}
110116

111117
func TestIssueList_tty_withFlags(t *testing.T) {
@@ -141,8 +147,8 @@ func TestIssueList_tty_withFlags(t *testing.T) {
141147
t.Errorf("error running command `issue list`: %v", err)
142148
}
143149

144-
eq(t, output.String(), "")
145-
eq(t, output.Stderr(), `
150+
eq(t, output.Stderr(), "")
151+
eq(t, output.String(), `
146152
No issues match your search in OWNER/REPO
147153
148154
`)

pkg/cmd/issue/status/status.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ func statusRun(opts *StatusOptions) error {
6868
return err
6969
}
7070

71+
err = opts.IO.StartPager()
72+
if err != nil {
73+
return err
74+
}
75+
defer opts.IO.StopPager()
76+
7177
out := opts.IO.Out
7278

7379
fmt.Fprintln(out, "")

pkg/cmd/issue/view/view.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,16 @@ func viewRun(opts *ViewOptions) error {
8888
}
8989
return utils.OpenInBrowser(openURL)
9090
}
91+
92+
err = opts.IO.StartPager()
93+
if err != nil {
94+
return err
95+
}
96+
defer opts.IO.StopPager()
97+
9198
if opts.IO.IsStdoutTTY() {
9299
return printHumanIssuePreview(opts.IO.Out, issue)
93100
}
94-
95101
return printRawIssuePreview(opts.IO.Out, issue)
96102
}
97103

0 commit comments

Comments
 (0)
X Tutup