diff --git a/config/config.go b/config/config.go index 26183b4..b88b729 100644 --- a/config/config.go +++ b/config/config.go @@ -54,6 +54,7 @@ type UserConfig struct { PreserveTitleAndBody bool `default:"false" yaml:"preserveTitleAndBody"` NoRebase bool `default:"false" yaml:"noRebase"` DeleteMergedBranches bool `default:"false" yaml:"deleteMergedBranches"` + ShortPRLink bool `default:"false" yaml:"shortPRLink"` } type InternalState struct { diff --git a/github/pullrequest.go b/github/pullrequest.go index 899811f..422e0a6 100644 --- a/github/pullrequest.go +++ b/github/pullrequest.go @@ -176,9 +176,13 @@ func (pr *PullRequest) String(config *config.Config) string { } prInfo := fmt.Sprintf("%3d", pr.Number) - if config.User.ShowPRLink { - prInfo = fmt.Sprintf("https://%s/%s/%s/pull/%d", - config.Repo.GitHubHost, config.Repo.GitHubRepoOwner, config.Repo.GitHubRepoName, pr.Number) + prURL := fmt.Sprintf("https://%s/%s/%s/pull/%d", + config.Repo.GitHubHost, config.Repo.GitHubRepoOwner, config.Repo.GitHubRepoName, pr.Number) + if config.User.ShortPRLink { + // OSC 8 terminal hyperlink: \033]8;;URL\033\\TEXT\033]8;;\033\\ + prInfo = fmt.Sprintf("\033]8;;%s\033\\PR-%d\033]8;;\033\\", prURL, pr.Number) + } else if config.User.ShowPRLink { + prInfo = prURL } var mq string @@ -202,6 +206,11 @@ func (pr *PullRequest) String(config *config.Config) string { terminalWidth = 1000 } lineLength := utf8.RuneCountInString(line) + if config.User.ShortPRLink { + // OSC 8 escape sequences are invisible; subtract their length + // The escape overhead is: \033]8;; + URL + \033\\ + \033]8;;\033\\ = 12 + len(URL) + lineLength -= 12 + utf8.RuneCountInString(prURL) + } if config.User.StatusBitsEmojis { // each emoji consumes 2 chars in the terminal lineLength += 4 diff --git a/github/pullrequest_test.go b/github/pullrequest_test.go index 9a8a282..51fa172 100644 --- a/github/pullrequest_test.go +++ b/github/pullrequest_test.go @@ -170,10 +170,42 @@ func TestString(t *testing.T) { } } + cfgWithShowPRLink := &config.Config{ + Repo: &config.RepoConfig{ + RequireChecks: true, + RequireApproval: true, + GitHubHost: "github.com", + GitHubRepoOwner: "testowner", + GitHubRepoName: "testrepo", + }, + User: &config.UserConfig{ + StatusBitsEmojis: false, + ShowPRLink: true, + }, + } + + cfgWithShortPRLink := &config.Config{ + Repo: &config.RepoConfig{ + RequireChecks: true, + RequireApproval: true, + GitHubHost: "github.com", + GitHubRepoOwner: "testowner", + GitHubRepoName: "testrepo", + }, + User: &config.UserConfig{ + StatusBitsEmojis: false, + ShortPRLink: true, + }, + } + tests := []testcase{ {expect: "[?xxx] . 0 : Title", pr: pr(true, 1), cfg: cfg}, {expect: "[?xxx] . 0 : Title", pr: pr(true, 2), cfg: cfg}, {expect: "[?xxx] ! 0 : Title", pr: pr(false, 2), cfg: cfg}, + // ShowPRLink: full URL is displayed + {expect: "[?xxx] . https://github.com/testowner/testrepo/pull/0 : Title", pr: pr(true, 1), cfg: cfgWithShowPRLink}, + // ShortPRLink: clickable short link via OSC 8 + {expect: "[?xxx] . \033]8;;https://github.com/testowner/testrepo/pull/0\033\\PR-0\033]8;;\033\\ : Title", pr: pr(true, 1), cfg: cfgWithShortPRLink}, } for i, test := range tests { assert.Equal(t, test.expect, test.pr.String(test.cfg), fmt.Sprintf("case %d failed", i)) diff --git a/readme.md b/readme.md index 65cff14..d6dcbe1 100644 --- a/readme.md +++ b/readme.md @@ -197,6 +197,7 @@ User specific configuration is saved to .spr.yml in the user home directory. | preserveTitleAndBody | bool | false | updating pull requests will not overwrite the pr title and body | | noRebase | bool | false | when true spr update will not rebase on top of origin | | deleteMergedBranches | bool | false | delete branches after prs are merged | +| shortPRLink | bool | false | show pull request links as clickable PR- instead of full URL | Happy Coding! -------------