1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-11-24 08:52:21 +02:00

Switch to github.com/gookit/color for terminal colors

This commit is contained in:
mjarkk 2021-07-27 15:00:37 +02:00
parent a3b820fb5f
commit 79848087bc
100 changed files with 12406 additions and 1704 deletions

View File

@ -265,7 +265,7 @@ The available attributes are:
- magenta
- cyan
- white
- '#ff00ff' # can't be used on text
- '#ff00ff'
**Modifiers**

5
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/cli/safeexec v1.0.0
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
github.com/creack/pty v1.1.11
github.com/fatih/color v1.9.0
github.com/fatih/color v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/gdamore/tcell/v2 v2.3.11 // indirect
github.com/go-errors/errors v1.4.0
@ -17,6 +17,7 @@ require (
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
github.com/golang/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/gookit/color v1.4.2
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
@ -35,7 +36,7 @@ require (
github.com/sahilm/fuzzy v0.1.0
github.com/sirupsen/logrus v1.4.2
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect

10
go.sum
View File

@ -57,6 +57,8 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
@ -138,11 +140,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -169,6 +174,7 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -193,3 +199,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -10,9 +10,9 @@ import (
"strconv"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/sirupsen/logrus"
)
@ -165,8 +165,7 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
if rebaseMode != "" {
currentCommit := commits[len(rebasingCommits)]
blue := color.New(color.FgYellow)
youAreHere := blue.Sprintf("<-- %s ---", c.Tr.YouAreHere)
youAreHere := style.FgYellow.Sprintf("<-- %s ---", c.Tr.YouAreHere)
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
}

View File

@ -4,7 +4,7 @@ import (
"regexp"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
@ -95,45 +95,38 @@ func (l *PatchLine) render(selected bool, included bool) string {
if l.Kind == HUNK_HEADER {
re := regexp.MustCompile("(@@.*?@@)(.*)")
match := re.FindStringSubmatch(content)
return coloredString(color.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
return coloredString(style.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
}
var colorAttr color.Attribute
colorAttr := theme.DefaultTextColor
switch l.Kind {
case PATCH_HEADER:
colorAttr = color.Bold
colorAttr = colorAttr.SetBold(true)
case ADDITION:
colorAttr = color.FgGreen
colorAttr = colorAttr.SetColor(style.FgGreen)
case DELETION:
colorAttr = color.FgRed
colorAttr = colorAttr.SetColor(style.FgRed)
case COMMIT_SHA:
colorAttr = color.FgYellow
default:
colorAttr = theme.DefaultTextColor
colorAttr = colorAttr.SetColor(style.FgYellow)
}
return coloredString(colorAttr, content, selected, included)
}
func coloredString(colorAttr color.Attribute, str string, selected bool, included bool) string {
var cl *color.Color
attributes := []color.Attribute{colorAttr}
func coloredString(colorAttr style.TextStyle, str string, selected bool, included bool) string {
if selected {
attributes = append(attributes, theme.SelectedRangeBgColor)
}
cl = color.New(attributes...)
var clIncluded *color.Color
if included {
clIncluded = color.New(append(attributes, color.BgGreen)...)
} else {
clIncluded = color.New(attributes...)
colorAttr = colorAttr.SetColor(theme.SelectedRangeBgColor)
}
if len(str) < 2 {
return utils.ColoredStringDirect(str, clIncluded)
return colorAttr.Sprint(str)
}
return utils.ColoredStringDirect(str[:1], clIncluded) + utils.ColoredStringDirect(str[1:], cl)
res := colorAttr.Sprint(str[:1])
if included {
return res + colorAttr.SetColor(style.BgGreen).Sprint(str[1:])
}
return res + colorAttr.Sprint(str[1:])
}
func parsePatch(patch string) ([]int, []int, []*PatchLine) {

View File

@ -554,7 +554,7 @@ func (gui *Gui) findBranchNameSuggestions(input string) []*types.Suggestion {
for i, branchName := range matchingBranchNames {
suggestions[i] = &types.Suggestion{
Value: branchName,
Label: utils.ColoredString(branchName, presentation.GetBranchColor(branchName)),
Label: presentation.GetBranchColor(branchName).Sprint(branchName),
}
}

View File

@ -6,11 +6,10 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
@ -25,17 +24,17 @@ func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
gui.Views.Extras.Autoscroll = true
if entry.GetSpan() != currentSpan {
fmt.Fprint(gui.Views.Extras, "\n"+utils.ColoredString(entry.GetSpan(), color.FgYellow))
fmt.Fprint(gui.Views.Extras, "\n"+style.FgYellow.Sprint(entry.GetSpan()))
currentSpan = entry.GetSpan()
}
clrAttr := theme.DefaultTextColor
if !entry.GetCommandLine() {
clrAttr = color.FgMagenta
clrAttr = clrAttr.SetColor(style.FgMagenta)
}
gui.CmdLog = append(gui.CmdLog, entry.GetCmdStr())
indentedCmdStr := " " + strings.Replace(entry.GetCmdStr(), "\n", "\n ", -1)
fmt.Fprint(gui.Views.Extras, "\n"+utils.ColoredString(indentedCmdStr, clrAttr))
fmt.Fprint(gui.Views.Extras, "\n"+clrAttr.Sprint(indentedCmdStr))
}
}
@ -44,14 +43,14 @@ func (gui *Gui) printCommandLogHeader() {
gui.Tr.CommandLogHeader,
gui.getKeyDisplay(gui.Config.GetUserConfig().Keybinding.Universal.ExtrasMenu),
)
fmt.Fprintln(gui.Views.Extras, utils.ColoredString(introStr, color.FgCyan))
fmt.Fprintln(gui.Views.Extras, style.FgCyan.Sprint(introStr))
if gui.Config.GetUserConfig().Gui.ShowRandomTip {
fmt.Fprintf(
gui.Views.Extras,
"%s: %s",
utils.ColoredString(gui.Tr.RandomTip, color.FgYellow),
utils.ColoredString(gui.getRandomTip(), color.FgGreen),
style.FgYellow.Sprint(gui.Tr.RandomTip),
style.FgGreen.Sprint(gui.getRandomTip()),
)
}
}

View File

@ -9,8 +9,8 @@ package gui
import (
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
@ -316,8 +316,7 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
}
func (gui *Gui) createErrorPanel(message string) error {
colorFunction := color.New(color.FgRed).SprintFunc()
coloredMessage := colorFunction(strings.TrimSpace(message))
coloredMessage := style.FgRed.Sprint(strings.TrimSpace(message))
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
return err
}

View File

@ -9,10 +9,10 @@ import (
"strings"
"text/template"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -102,7 +102,7 @@ func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []
}
menuItems[i] = &menuItem{
displayStrings: []string{name, utils.ColoredString(description, color.FgYellow)},
displayStrings: []string{name, style.FgYellow.Sprint(description)},
onPress: func() error {
promptResponses[responseIdx] = value
return wrappedF()

View File

@ -12,7 +12,6 @@ import (
"strings"
"time"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@ -24,6 +23,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
"github.com/jesseduffield/lazygit/pkg/gui/modes/filtering"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/tasks"
@ -611,7 +611,7 @@ func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
subprocess.Stderr = os.Stdout
subprocess.Stdin = os.Stdin
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(subprocess.Args, " "), color.FgBlue))
fmt.Fprintf(os.Stdout, "\n%s\n\n", style.FgBlue.Sprint("+ "+strings.Join(subprocess.Args, " ")))
if err := subprocess.Run(); err != nil {
// not handling the error explicitly because usually we're going to see it
@ -623,7 +623,7 @@ func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
subprocess.Stderr = ioutil.Discard
subprocess.Stdin = nil
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint(gui.Tr.PressEnterToReturn))
fmt.Scanln() // wait for enter press
return nil

View File

@ -3,8 +3,8 @@ package gui
import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) informationStr() string {
@ -15,8 +15,8 @@ func (gui *Gui) informationStr() string {
}
if gui.g.Mouse {
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.Donate)
askQuestion := color.New(color.FgYellow, color.Underline).Sprint(gui.Tr.AskQuestion)
donate := style.FgMagenta.SetUnderline(true).Sprint(gui.Tr.Donate)
askQuestion := style.FgYellow.SetUnderline(true).Sprint(gui.Tr.AskQuestion)
return fmt.Sprintf("%s %s %s", donate, askQuestion, gui.Config.GetVersion())
} else {
return gui.Config.GetVersion()

View File

@ -1,10 +1,9 @@
package gui
import (
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) menuListContext() *ListContext {
@ -278,7 +277,7 @@ func (gui *Gui) commitFilesListContext() *ListContext {
ResetMainViewOriginOnFocus: true,
GetDisplayStrings: func() [][]string {
if gui.State.CommitFileManager.GetItemsLength() == 0 {
return [][]string{{utils.ColoredString("(none)", color.FgRed)}}
return [][]string{{style.FgRed.Sprint("(none)")}}
}
lines := gui.State.CommitFileManager.Render(gui.State.Modes.Diffing.Ref, gui.GitCommand.PatchManager)

View File

@ -3,7 +3,7 @@ package mergeconflicts
import (
"bytes"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -15,19 +15,18 @@ func ColoredConflictFile(content string, state *State, hasFocus bool) string {
conflict, remainingConflicts := shiftConflict(state.conflicts)
var outputBuffer bytes.Buffer
for i, line := range utils.SplitLines(content) {
colourAttr := theme.DefaultTextColor
colour := theme.DefaultTextColor
if i == conflict.start || i == conflict.middle || i == conflict.end {
colourAttr = color.FgRed
colour.SetColor(style.FgRed)
}
colour := color.New(colourAttr)
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
colour.Add(color.Bold)
colour.Add(theme.SelectedRangeBgColor)
colour = theme.SelectedRangeBgColor.SetBold(true)
}
if i == conflict.end && len(remainingConflicts) > 0 {
conflict, remainingConflicts = shiftConflict(remainingConflicts)
}
outputBuffer.WriteString(utils.ColoredStringDirect(line, colour) + "\n")
outputBuffer.WriteString(colour.Sprint(line) + "\n")
}
return outputBuffer.String()
}

View File

@ -1,10 +1,7 @@
package gui
import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
type modeStatus struct {
@ -18,9 +15,11 @@ func (gui *Gui) modeStatuses() []modeStatus {
{
isActive: gui.State.Modes.Diffing.Active,
description: func() string {
return utils.ColoredString(
fmt.Sprintf("%s %s %s", gui.Tr.LcShowingGitDiff, "git diff "+gui.diffStr(), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
color.FgMagenta,
return style.FgMagenta.Sprintf(
"%s %s %s",
gui.Tr.LcShowingGitDiff,
"git diff "+gui.diffStr(),
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
)
},
reset: gui.exitDiffMode,
@ -28,10 +27,10 @@ func (gui *Gui) modeStatuses() []modeStatus {
{
isActive: gui.GitCommand.PatchManager.Active,
description: func() string {
return utils.ColoredString(
fmt.Sprintf("%s %s", gui.Tr.LcBuildingPatch, utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
color.FgYellow,
color.Bold,
return style.FgYellow.SetBold(true).Sprintf(
"%s %s",
gui.Tr.LcBuildingPatch,
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
)
},
reset: gui.handleResetPatch,
@ -39,10 +38,11 @@ func (gui *Gui) modeStatuses() []modeStatus {
{
isActive: gui.State.Modes.Filtering.Active,
description: func() string {
return utils.ColoredString(
fmt.Sprintf("%s '%s' %s", gui.Tr.LcFilteringBy, gui.State.Modes.Filtering.GetPath(), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
color.FgRed,
color.Bold,
return style.FgRed.SetBold(true).Sprintf(
"%s '%s' %s",
gui.Tr.LcFilteringBy,
gui.State.Modes.Filtering.GetPath(),
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
)
},
reset: gui.exitFilterMode,
@ -50,9 +50,10 @@ func (gui *Gui) modeStatuses() []modeStatus {
{
isActive: gui.State.Modes.CherryPicking.Active,
description: func() string {
return utils.ColoredString(
fmt.Sprintf("%d commits copied %s", len(gui.State.Modes.CherryPicking.CherryPickedCommits), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
color.FgCyan,
return style.FgCyan.Sprintf(
"%d commits copied %s",
len(gui.State.Modes.CherryPicking.CherryPickedCommits),
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
)
},
reset: gui.exitCherryPickingMode,

View File

@ -3,8 +3,8 @@ package gui
import (
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -35,14 +35,11 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding {
}
func (gui *Gui) displayDescription(binding *Binding) string {
commandColor := color.New(color.FgCyan)
menuColor := color.New(color.FgMagenta)
if binding.OpensMenu {
return menuColor.Sprintf("%s...", binding.Description)
return style.FgMagenta.Sprintf("%s...", binding.Description)
}
return commandColor.Sprint(binding.Description)
return style.FgCyan.Sprint(binding.Description)
}
func (gui *Gui) handleCreateOptionsMenu() error {

View File

@ -4,10 +4,9 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string) [][]string {
@ -32,48 +31,48 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
if diffed {
nameColorAttr = theme.DiffTerminalColor
}
coloredName := utils.ColoredString(displayName, nameColorAttr)
coloredName := nameColorAttr.Sprint(displayName)
if b.IsTrackingRemote() {
coloredName = fmt.Sprintf("%s %s", coloredName, ColoredBranchStatus(b))
}
recencyColor := color.FgCyan
recencyColor := style.FgCyan
if b.Recency == " *" {
recencyColor = color.FgGreen
recencyColor = style.FgGreen
}
res := []string{recencyColor.Sprint(b.Recency), coloredName}
if fullDescription {
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
return append(res, style.FgYellow.Sprint(b.UpstreamName))
}
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName}
return res
}
// GetBranchColor branch color
func GetBranchColor(name string) color.Attribute {
func GetBranchColor(name string) style.TextStyle {
branchType := strings.Split(name, "/")[0]
switch branchType {
case "feature":
return color.FgGreen
return style.FgGreen
case "bugfix":
return color.FgYellow
return style.FgYellow
case "hotfix":
return color.FgRed
return style.FgRed
default:
return theme.DefaultTextColor
}
}
func ColoredBranchStatus(branch *models.Branch) string {
colour := color.FgYellow
colour := style.FgYellow
if branch.MatchesUpstream() {
colour = color.FgGreen
colour = style.FgGreen
} else if !branch.IsTrackingRemote() {
colour = color.FgRed
colour = style.FgRed
}
return utils.ColoredString(BranchStatus(branch), colour)
return colour.Sprint(BranchStatus(branch))
}
func BranchStatus(branch *models.Branch) string {

View File

@ -1,30 +1,23 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/patch"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFile, status patch.PatchStatus) string {
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
defaultColor := color.New(theme.DefaultTextColor)
diffTerminalColor := color.New(theme.DiffTerminalColor)
colour := defaultColor
colour := theme.DefaultTextColor
if diffName == name {
colour = diffTerminalColor
colour = theme.DiffTerminalColor
} else {
switch status {
case patch.UNSELECTED:
colour = defaultColor
case patch.WHOLE:
colour = green
colour = style.FgGreen
case patch.PART:
colour = yellow
colour = style.FgYellow
}
}
@ -33,21 +26,21 @@ func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFi
return colour.Sprint(name)
}
return utils.ColoredString(commitFile.ChangeStatus, getColorForChangeStatus(commitFile.ChangeStatus)) + " " + colour.Sprint(name)
return getColorForChangeStatus(commitFile.ChangeStatus).Sprint(commitFile.ChangeStatus) + " " + colour.Sprint(name)
}
func getColorForChangeStatus(changeStatus string) color.Attribute {
func getColorForChangeStatus(changeStatus string) style.TextStyle {
switch changeStatus {
case "A":
return color.FgGreen
return style.FgGreen
case "M", "R":
return color.FgYellow
return style.FgYellow
case "D":
return color.FgRed
return style.FgRed
case "C":
return color.FgCyan
return style.FgCyan
case "T":
return color.FgMagenta
return style.FgMagenta
default:
return theme.DefaultTextColor
}

View File

@ -3,8 +3,8 @@ package presentation
import (
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/kyokomi/emoji/v2"
@ -29,47 +29,35 @@ func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool,
}
func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
defaultColor := color.New(theme.DefaultTextColor)
diffedColor := color.New(theme.DiffTerminalColor)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
shaColor := theme.DefaultTextColor
switch c.Status {
case "unpushed":
shaColor = red
shaColor = style.FgRed
case "pushed":
shaColor = yellow
shaColor = style.FgYellow
case "merged":
shaColor = green
shaColor = style.FgGreen
case "rebasing":
shaColor = blue
shaColor = style.FgBlue
case "reflog":
shaColor = blue
default:
shaColor = defaultColor
shaColor = style.FgBlue
}
if diffed {
shaColor = diffedColor
shaColor = theme.DiffTerminalColor
} else if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
shaColor = style.FgCyan.SetColor(style.BgBlue)
}
tagString := ""
secondColumnString := blue.Sprint(utils.UnixToDate(c.UnixTimestamp))
secondColumnString := style.FgBlue.Sprint(utils.UnixToDate(c.UnixTimestamp))
if c.Action != "" {
secondColumnString = color.New(actionColorMap(c.Action)).Sprint(c.Action)
secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
} else if c.ExtraInfo != "" {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(c.ExtraInfo, tagColor) + " "
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(c.ExtraInfo) + " "
}
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
@ -79,51 +67,44 @@ func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCom
name = emoji.Sprint(name)
}
return []string{shaColor.Sprint(c.ShortSha()), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(name)}
return []string{
shaColor.Sprint(c.ShortSha()),
secondColumnString,
style.FgYellow.Sprint(truncatedAuthor),
tagString + theme.DefaultTextColor.Sprint(name),
}
}
func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
red := color.New(color.FgRed)
yellow := color.New(color.FgYellow)
green := color.New(color.FgGreen)
blue := color.New(color.FgBlue)
defaultColor := color.New(theme.DefaultTextColor)
diffedColor := color.New(theme.DiffTerminalColor)
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
copied := color.New(color.FgCyan, color.BgBlue)
var shaColor *color.Color
shaColor := theme.DefaultTextColor
switch c.Status {
case "unpushed":
shaColor = red
shaColor = style.FgRed
case "pushed":
shaColor = yellow
shaColor = style.FgYellow
case "merged":
shaColor = green
shaColor = style.FgGreen
case "rebasing":
shaColor = blue
shaColor = style.FgBlue
case "reflog":
shaColor = blue
default:
shaColor = defaultColor
shaColor = style.FgBlue
}
if diffed {
shaColor = diffedColor
shaColor = theme.DiffTerminalColor
} else if cherryPickedCommitShaMap[c.Sha] {
shaColor = copied
// for some reason, setting the background to blue pads out the other commits
// horizontally. For the sake of accessibility I'm considering this a feature,
// not a bug
shaColor = style.FgCyan.SetColor(style.BgBlue)
}
actionString := ""
tagString := ""
if c.Action != "" {
actionString = color.New(actionColorMap(c.Action)).Sprint(utils.WithPadding(c.Action, 7)) + " "
actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
} else if len(c.Tags) > 0 {
tagColor := color.New(color.FgMagenta, color.Bold)
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
tagString = theme.DiffTerminalColor.SetBold(true).Sprint(strings.Join(c.Tags, " ")) + " "
}
name := c.Name
@ -131,20 +112,23 @@ func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[s
name = emoji.Sprint(name)
}
return []string{shaColor.Sprint(c.ShortSha()), actionString + tagString + defaultColor.Sprint(name)}
}
func actionColorMap(str string) color.Attribute {
switch str {
case "pick":
return color.FgCyan
case "drop":
return color.FgRed
case "edit":
return color.FgGreen
case "fixup":
return color.FgMagenta
default:
return color.FgYellow
return []string{
shaColor.Sprint(c.ShortSha()),
actionString + tagString + theme.DefaultTextColor.Sprint(name),
}
}
func actionColorMap(str string) style.TextStyle {
switch str {
case "pick":
return style.FgCyan
case "drop":
return style.FgRed
case "edit":
return style.FgGreen
case "fixup":
return style.FgMagenta
default:
return style.FgYellow
}
}

View File

@ -1,8 +1,8 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -10,35 +10,30 @@ import (
func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, diffName string, submoduleConfigs []*models.SubmoduleConfig, file *models.File) string {
// potentially inefficient to be instantiating these color
// objects with each render
red := color.New(color.FgRed)
green := color.New(color.FgGreen)
diffColor := color.New(theme.DiffTerminalColor)
partiallyModifiedColor := color.New(color.FgYellow)
partiallyModifiedColor := style.FgYellow
var restColor *color.Color
restColor := style.FgGreen
if name == diffName {
restColor = diffColor
restColor = theme.DiffTerminalColor
} else if file == nil && hasStagedChanges && hasUnstagedChanges {
restColor = partiallyModifiedColor
} else if hasUnstagedChanges {
restColor = red
} else {
restColor = green
restColor = style.FgRed
}
output := ""
if file != nil {
// this is just making things look nice when the background attribute is 'reverse'
firstChar := file.ShortStatus[0:1]
firstCharCl := green
firstCharCl := style.FgGreen
if firstChar == "?" {
firstCharCl = red
firstCharCl = style.FgRed
} else if firstChar == " " {
firstCharCl = restColor
}
secondChar := file.ShortStatus[1:2]
secondCharCl := red
secondCharCl := style.FgRed
if secondChar == " " {
secondCharCl = restColor
}
@ -51,7 +46,7 @@ func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, di
output += restColor.Sprint(utils.EscapeSpecialChars(name))
if file != nil && file.IsSubmodule(submoduleConfigs) {
output += utils.ColoredString(" (submodule)", theme.DefaultTextColor)
output += theme.DefaultTextColor.Sprint(" (submodule)")
}
return output

View File

@ -1,8 +1,8 @@
package presentation
import (
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/kyokomi/emoji/v2"
@ -27,11 +27,9 @@ func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription
}
func coloredReflogSha(c *models.Commit, cherryPickedCommitShaMap map[string]bool) string {
var shaColor *color.Color
shaColor := style.FgBlue
if cherryPickedCommitShaMap[c.Sha] {
shaColor = color.New(color.FgCyan, color.BgBlue)
} else {
shaColor = color.New(color.FgBlue)
shaColor = style.FgCyan.SetColor(style.BgBlue)
}
return shaColor.Sprint(c.ShortSha())
@ -50,14 +48,12 @@ func getFullDescriptionDisplayStringsForReflogCommit(c *models.Commit, cherryPic
return []string{
coloredReflogSha(c, cherryPickedCommitShaMap),
utils.ColoredString(utils.UnixToDate(c.UnixTimestamp), color.FgMagenta),
utils.ColoredString(name, colorAttr),
style.FgMagenta.Sprint(utils.UnixToDate(c.UnixTimestamp)),
colorAttr.Sprint(name),
}
}
func getDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
defaultColor := color.New(theme.DefaultTextColor)
name := c.Name
if parseEmoji {
name = emoji.Sprint(name)
@ -65,6 +61,6 @@ func getDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap
return []string{
coloredReflogSha(c, cherryPickedCommitShaMap),
defaultColor.Sprint(name),
theme.DefaultTextColor.Sprint(name),
}
}

View File

@ -3,7 +3,6 @@ package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetRemoteBranchListDisplayStrings(branches []*models.RemoteBranch, diffName string) [][]string {
@ -24,7 +23,5 @@ func getRemoteBranchDisplayStrings(b *models.RemoteBranch, diffed bool) []string
nameColorAttr = theme.DiffTerminalColor
}
displayName := utils.ColoredString(b.Name, nameColorAttr)
return []string{displayName}
return []string{nameColorAttr.Sprint(b.Name)}
}

View File

@ -1,12 +1,9 @@
package presentation
import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetRemoteListDisplayStrings(remotes []*models.Remote, diffName string) [][]string {
@ -29,5 +26,5 @@ func getRemoteDisplayStrings(r *models.Remote, diffed bool) []string {
nameColorAttr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(r.Name, nameColorAttr), utils.ColoredString(fmt.Sprintf("%d branches", branchCount), color.FgBlue)}
return []string{nameColorAttr.Sprint(r.Name), style.FgBlue.Sprintf("%d branches", branchCount)}
}

View File

@ -3,7 +3,6 @@ package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetStashEntryListDisplayStrings(stashEntries []*models.StashEntry, diffName string) [][]string {
@ -23,5 +22,5 @@ func getStashEntryDisplayStrings(s *models.StashEntry, diffed bool) []string {
if diffed {
attr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(s.Name, attr)}
return []string{attr.Sprint(s.Name)}
}

View File

@ -3,7 +3,6 @@ package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]string {
@ -17,5 +16,5 @@ func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]st
}
func getSubmoduleDisplayStrings(s *models.SubmoduleConfig) []string {
return []string{utils.ColoredString(s.Name, theme.DefaultTextColor)}
return []string{theme.DefaultTextColor.Sprint(s.Name)}
}

View File

@ -3,7 +3,6 @@ package presentation
import (
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func GetTagListDisplayStrings(tags []*models.Tag, diffName string) [][]string {
@ -23,5 +22,5 @@ func getTagDisplayStrings(t *models.Tag, diffed bool) []string {
if diffed {
attr = theme.DiffTerminalColor
}
return []string{utils.ColoredString(t.Name, attr)}
return []string{attr.Sprint(t.Name)}
}

View File

@ -4,17 +4,17 @@ import (
"os"
"path/filepath"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) handleCreateRecentReposMenu() error {
recentRepoPaths := gui.Config.GetAppState().RecentRepos
reposCount := utils.Min(len(recentRepoPaths), 20)
yellow := color.New(color.FgMagenta)
// we won't show the current repo hence the -1
menuItems := make([]*menuItem, reposCount-1)
for i, path := range recentRepoPaths[1:reposCount] {
@ -22,7 +22,7 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
menuItems[i] = &menuItem{
displayStrings: []string{
filepath.Base(path),
yellow.Sprint(path),
style.FgMagenta.Sprint(path),
},
onPress: func() error {
// if we were in a submodule, we want to forget about that stack of repos

View File

@ -4,8 +4,8 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -26,7 +26,7 @@ func (gui *Gui) handleRemoteSelect() error {
if remote == nil {
task = NewRenderStringTask("No remotes")
} else {
task = NewRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
task = NewRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", style.FgGreen.Sprint(remote.Name), strings.Join(remote.Urls, "\n")))
}
return gui.refreshMainViews(refreshMainOpts{

View File

@ -3,8 +3,8 @@ package gui
import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) resetToRef(ref string, strength string, span string, options oscommands.RunCommandOptions) error {
@ -36,9 +36,7 @@ func (gui *Gui) createResetMenu(ref string) error {
menuItems[i] = &menuItem{
displayStrings: []string{
fmt.Sprintf("%s reset", strength),
color.New(color.FgRed).Sprint(
fmt.Sprintf("reset --%s %s", strength, ref),
),
style.FgRed.Sprintf("reset --%s %s", strength, ref),
},
onPress: func() error {
return gui.resetToRef(ref, strength, "Reset", oscommands.RunCommandOptions{})

View File

@ -4,7 +4,6 @@ import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/theme"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) handleOpenSearch(viewName string) error {
@ -53,10 +52,7 @@ func (gui *Gui) onSelectItemWrapper(innerFunc func(int) error) func(int, int, in
fmt.Sprintf(
"no matches for '%s' %s",
gui.State.Searching.searchString,
utils.ColoredString(
fmt.Sprintf("%s: exit search mode", gui.getKeyDisplay(keybindingConfig.Universal.Return)),
theme.OptionsFgColor,
),
theme.OptionsFgColor.Sprintf("%s: exit search mode", gui.getKeyDisplay(keybindingConfig.Universal.Return)),
),
)
return nil
@ -68,14 +64,11 @@ func (gui *Gui) onSelectItemWrapper(innerFunc func(int) error) func(int, int, in
gui.State.Searching.searchString,
index+1,
total,
utils.ColoredString(
fmt.Sprintf(
"%s: next match, %s: previous match, %s: exit search mode",
gui.getKeyDisplay(keybindingConfig.Universal.NextMatch),
gui.getKeyDisplay(keybindingConfig.Universal.PrevMatch),
gui.getKeyDisplay(keybindingConfig.Universal.Return),
),
theme.OptionsFgColor,
theme.OptionsFgColor.Sprintf(
"%s: next match, %s: previous match, %s: exit search mode",
gui.getKeyDisplay(keybindingConfig.Universal.NextMatch),
gui.getKeyDisplay(keybindingConfig.Universal.PrevMatch),
gui.getKeyDisplay(keybindingConfig.Universal.Return),
),
),
)

View File

@ -4,10 +4,10 @@ import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/constants"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@ -28,10 +28,10 @@ func (gui *Gui) refreshStatus() {
}
if gui.GitCommand.WorkingTreeState() != commands.REBASE_MODE_NORMAL {
status += utils.ColoredString(fmt.Sprintf("(%s) ", gui.GitCommand.WorkingTreeState()), color.FgYellow)
status += style.FgYellow.Sprintf("(%s) ", gui.GitCommand.WorkingTreeState())
}
name := utils.ColoredString(currentBranch.Name, presentation.GetBranchColor(currentBranch.Name))
name := presentation.GetBranchColor(currentBranch.Name).Sprint(currentBranch.Name)
repoName := utils.GetCurrentRepoName()
status += fmt.Sprintf("%s → %s ", repoName, name)
@ -94,8 +94,6 @@ func (gui *Gui) handleStatusSelect() error {
return nil
}
magenta := color.New(color.FgMagenta)
dashboardString := strings.Join(
[]string{
lazygitTitle(),
@ -105,7 +103,7 @@ func (gui *Gui) handleStatusSelect() error {
fmt.Sprintf("Tutorial: %s", constants.Links.Docs.Tutorial),
fmt.Sprintf("Raise an Issue: %s", constants.Links.Issues),
fmt.Sprintf("Release Notes: %s", constants.Links.Releases),
magenta.Sprintf("Become a sponsor (github is matching all donations for 12 months): %s", constants.Links.Donate), // caffeine ain't free
style.FgMagenta.Sprintf("Become a sponsor (github is matching all donations for 12 months): %s", constants.Links.Donate), // caffeine ain't free
}, "\n\n")
return gui.refreshMainViews(refreshMainOpts{

147
pkg/gui/style/basic.go Normal file
View File

@ -0,0 +1,147 @@
package style
import (
"fmt"
"github.com/gookit/color"
)
type BasicTextStyle struct {
fg color.Color
bg color.Color
opts []color.Color
style color.Style
}
func (b BasicTextStyle) Sprint(a ...interface{}) string {
return b.style.Sprint(a...)
}
func (b BasicTextStyle) Sprintf(format string, a ...interface{}) string {
return b.style.Sprintf(format, a...)
}
func (b BasicTextStyle) deriveStyle() BasicTextStyle {
// b.style[:0] makes sure to use the same slice memory
if b.fg == 0 {
// Fg is most of the time defined so we reverse the check
b.style = b.style[:0]
} else {
b.style = append(b.style[:0], b.fg)
}
if b.bg != 0 {
b.style = append(b.style, b.bg)
}
b.style = append(b.style, b.opts...)
return b
}
func (b BasicTextStyle) setOpt(opt color.Color, v bool, deriveIfChanged bool) BasicTextStyle {
if v {
// Add value
for _, listOpt := range b.opts {
if listOpt == opt {
// Option already added
return b
}
}
b.opts = append(b.opts, opt)
} else {
// Remove value
for idx, listOpt := range b.opts {
if listOpt == opt {
b.opts = append(b.opts[:idx], b.opts[idx+1:]...)
if deriveIfChanged {
return b.deriveStyle()
}
return b
}
}
}
if deriveIfChanged {
return b.deriveStyle()
}
return b
}
func (b BasicTextStyle) SetBold(v bool) TextStyle {
return b.setOpt(color.OpBold, v, true)
}
func (b BasicTextStyle) SetReverse(v bool) TextStyle {
return b.setOpt(color.OpReverse, v, true)
}
func (b BasicTextStyle) SetUnderline(v bool) TextStyle {
return b.setOpt(color.OpUnderscore, v, true)
}
func (b BasicTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle {
return b.convertToRGB().SetRGBColor(red, green, blue, background)
}
func (b BasicTextStyle) convertToRGB() RGBTextStyle {
res := RGBTextStyle{
fg: b.fg.RGB(),
fgSet: b.fg != 0,
opts: b.opts,
}
if b.bg != 0 {
// Need to convert bg to fg otherwise .RGB wont work
// for more info see https://github.com/gookit/color/issues/39
rgbBg := (b.bg - 10).RGB()
rgbBg[3] = 1
res.bg = &rgbBg
res.style = *color.NewRGBStyle(res.fg, rgbBg)
} else {
res.style = *color.NewRGBStyle(res.fg)
}
res.style.SetOpts(b.opts)
return res
}
func (b BasicTextStyle) SetColor(other TextStyle) TextStyle {
switch typedOther := other.(type) {
case BasicTextStyle:
if typedOther.fg != 0 {
b.fg = typedOther.fg
}
if typedOther.bg != 0 {
b.bg = typedOther.bg
}
for _, opt := range typedOther.opts {
b = b.setOpt(opt, true, false)
}
return b.deriveStyle()
case RGBTextStyle:
bAsRGB := b.convertToRGB()
for _, opt := range typedOther.opts {
bAsRGB.setOpt(opt, true)
}
if typedOther.fgSet {
bAsRGB.fg = typedOther.fg
bAsRGB.style.SetFg(typedOther.fg)
}
if typedOther.bg != nil {
// Making sure to copy the value
bAsRGB.bg = &color.RGBColor{}
*bAsRGB.bg = *typedOther.bg
bAsRGB.style.SetBg(*typedOther.bg)
}
return bAsRGB
default:
panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedOther))
}
}

111
pkg/gui/style/rgb.go Normal file
View File

@ -0,0 +1,111 @@
package style
import (
"fmt"
"github.com/gookit/color"
)
type RGBTextStyle struct {
opts color.Opts
fgSet bool
fg color.RGBColor
bg *color.RGBColor
style color.RGBStyle
}
func (b RGBTextStyle) Sprint(a ...interface{}) string {
return b.style.Sprint(a...)
}
func (b RGBTextStyle) Sprintf(format string, a ...interface{}) string {
return b.style.Sprintf(format, a...)
}
func (b RGBTextStyle) setOpt(opt color.Color, v bool) RGBTextStyle {
if v {
// Add value
for _, listOpt := range b.opts {
if listOpt == opt {
return b
}
}
b.opts = append(b.opts, opt)
} else {
// Remove value
for idx, listOpt := range b.opts {
if listOpt == opt {
b.opts = append(b.opts[:idx], b.opts[idx+1:]...)
return b
}
}
}
return b
}
func (b RGBTextStyle) SetBold(v bool) TextStyle {
b = b.setOpt(color.OpBold, v)
b.style.SetOpts(b.opts)
return b
}
func (b RGBTextStyle) SetReverse(v bool) TextStyle {
b = b.setOpt(color.OpReverse, v)
b.style.SetOpts(b.opts)
return b
}
func (b RGBTextStyle) SetUnderline(v bool) TextStyle {
b = b.setOpt(color.OpUnderscore, v)
b.style.SetOpts(b.opts)
return b
}
func (b RGBTextStyle) SetColor(style TextStyle) TextStyle {
var rgbStyle RGBTextStyle
switch typedStyle := style.(type) {
case BasicTextStyle:
rgbStyle = typedStyle.convertToRGB()
case RGBTextStyle:
rgbStyle = typedStyle
default:
panic(fmt.Sprintf("got %T but expected BasicTextStyle or RGBTextStyle", typedStyle))
}
for _, opt := range rgbStyle.GetOpts() {
b = b.setOpt(opt, true)
}
if rgbStyle.fgSet {
b.fg = rgbStyle.fg
b.style.SetFg(rgbStyle.fg)
b.fgSet = true
}
if rgbStyle.bg != nil {
// Making sure to copy value
b.bg = &color.RGBColor{}
*b.bg = *rgbStyle.bg
b.style.SetBg(*rgbStyle.bg)
}
return b
}
func (b RGBTextStyle) SetRGBColor(red, green, blue uint8, background bool) TextStyle {
parsedColor := color.Rgb(red, green, blue, background)
if background {
b.bg = &parsedColor
b.style.SetBg(parsedColor)
} else {
b.fg = parsedColor
b.style.SetFg(parsedColor)
b.fgSet = true
}
return b
}
func (b RGBTextStyle) GetOpts() color.Opts {
return b.opts
}

97
pkg/gui/style/style.go Normal file
View File

@ -0,0 +1,97 @@
package style
import (
"github.com/gookit/color"
"github.com/jesseduffield/lazygit/pkg/utils"
)
type TextStyle interface {
Sprint(a ...interface{}) string
Sprintf(format string, a ...interface{}) string
SetBold(v bool) TextStyle
SetReverse(v bool) TextStyle
SetUnderline(v bool) TextStyle
SetColor(style TextStyle) TextStyle
SetRGBColor(r, g, b uint8, background bool) TextStyle
}
var (
FgWhite = New(color.FgWhite, 0)
FgLightWhite = New(color.FgLightWhite, 0)
FgBlack = New(color.FgBlack, 0)
FgBlackLighter = New(color.FgBlack.Light(), 0)
FgCyan = New(color.FgCyan, 0)
FgRed = New(color.FgRed, 0)
FgGreen = New(color.FgGreen, 0)
FgBlue = New(color.FgBlue, 0)
FgYellow = New(color.FgYellow, 0)
FgMagenta = New(color.FgMagenta, 0)
BgWhite = New(0, color.BgWhite)
BgBlack = New(0, color.BgBlack)
BgRed = New(0, color.BgRed)
BgGreen = New(0, color.BgGreen)
BgYellow = New(0, color.BgYellow)
BgBlue = New(0, color.BgBlue)
BgMagenta = New(0, color.BgMagenta)
BgCyan = New(0, color.BgCyan)
AttrUnderline = New(0, 0).SetUnderline(true)
AttrBold = New(0, 0).SetUnderline(true)
)
func New(fg color.Color, bg color.Color, opts ...color.Color) TextStyle {
return BasicTextStyle{
fg: fg,
bg: bg,
opts: opts,
style: color.Style{},
}.deriveStyle()
}
func SetConfigStyles(s TextStyle, keys []string, background bool) TextStyle {
for _, key := range keys {
colorMap := map[string]struct {
forground TextStyle
background TextStyle
}{
"default": {FgWhite, BgBlack},
"black": {FgBlack, BgBlack},
"red": {FgRed, BgRed},
"green": {FgGreen, BgGreen},
"yellow": {FgYellow, BgYellow},
"blue": {FgBlue, BgBlue},
"magenta": {FgMagenta, BgMagenta},
"cyan": {FgCyan, BgCyan},
"white": {FgWhite, BgWhite},
}
value, present := colorMap[key]
if present {
if background {
s = s.SetColor(value.background)
} else {
s = s.SetColor(value.forground)
}
continue
}
if key == "bold" {
s = s.SetBold(true)
continue
} else if key == "reverse" {
s = s.SetReverse(true)
continue
} else if key == "underline" {
s = s.SetUnderline(true)
continue
}
r, g, b, validHexColor := utils.GetHexColorValues(key)
if validHexColor {
s = s.SetRGBColor(r, g, b, background)
continue
}
}
return s
}

314
pkg/gui/style/style_test.go Normal file
View File

@ -0,0 +1,314 @@
package style
import (
"testing"
"github.com/gookit/color"
"github.com/stretchr/testify/assert"
)
func TestNewStyle(t *testing.T) {
type scenario struct {
name string
fg, bg color.Color
expectedStyle color.Style
}
scenarios := []scenario{
{
"no color",
0, 0,
color.Style{},
},
{
"only fg color",
color.FgRed, 0,
color.Style{color.FgRed},
},
{
"only bg color",
0, color.BgRed,
color.Style{color.BgRed},
},
{
"fg and bg color",
color.FgBlue, color.BgRed,
color.Style{color.FgBlue, color.BgRed},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
style := New(s.fg, s.bg)
basicStyle, ok := style.(BasicTextStyle)
assert.True(t, ok, "New(..) should return a interface of type BasicTextStyle")
assert.Equal(t, s.fg, basicStyle.fg)
assert.Equal(t, s.bg, basicStyle.bg)
assert.Equal(t, []color.Color(nil), basicStyle.opts)
assert.Equal(t, s.expectedStyle, basicStyle.style)
})
}
}
func TestBasicSetColor(t *testing.T) {
type scenario struct {
name string
colorToSet BasicTextStyle
expect BasicTextStyle
}
scenarios := []scenario{
{
"empty color",
BasicTextStyle{},
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}}},
{
"set new fg color",
BasicTextStyle{fg: color.FgCyan},
BasicTextStyle{fg: color.FgCyan, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
},
{
"set new bg color",
BasicTextStyle{bg: color.BgGray},
BasicTextStyle{fg: color.FgRed, bg: color.BgGray, opts: []color.Color{color.OpBold}},
},
{
"set new fg and bg color",
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray},
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray, opts: []color.Color{color.OpBold}},
},
{
"add options",
BasicTextStyle{opts: []color.Color{color.OpUnderscore}},
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold, color.OpUnderscore}},
},
{
"add options that already exists",
BasicTextStyle{opts: []color.Color{color.OpBold}},
BasicTextStyle{fg: color.FgRed, bg: color.BgBlue, opts: []color.Color{color.OpBold}},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
style, ok := New(color.FgRed, color.BgBlue).
SetBold(true).
SetColor(s.colorToSet).(BasicTextStyle)
assert.True(t, ok, "SetColor should return a interface of type BasicTextStyle if the input was also BasicTextStyle")
style.style = nil
assert.Equal(t, s.expect, style)
})
}
}
func TestRGBSetColor(t *testing.T) {
type scenario struct {
name string
colorToSet TextStyle
expect RGBTextStyle
}
red := color.FgRed.RGB()
cyan := color.FgCyan.RGB()
blue := color.FgBlue.RGB()
gray := color.FgGray.RGB()
toBg := func(c color.RGBColor) *color.RGBColor {
c[3] = 1
return &c
}
scenarios := []scenario{
{
"empty RGBTextStyle input",
RGBTextStyle{},
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}},
},
{
"empty BasicTextStyle input",
BasicTextStyle{},
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}},
},
{
"set fg and bg color using BasicTextStyle",
BasicTextStyle{fg: color.FgCyan, bg: color.BgGray},
RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray), opts: []color.Color{color.OpBold}},
},
{
"set fg and bg color using RGBTextStyle",
RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray)},
RGBTextStyle{fgSet: true, fg: cyan, bg: toBg(gray), opts: []color.Color{color.OpBold}},
},
{
"add options",
RGBTextStyle{opts: []color.Color{color.OpUnderscore}},
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold, color.OpUnderscore}},
},
{
"add options using BasicTextStyle",
BasicTextStyle{opts: []color.Color{color.OpUnderscore}},
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold, color.OpUnderscore}},
},
{
"add options that already exists",
RGBTextStyle{opts: []color.Color{color.OpBold}},
RGBTextStyle{fgSet: true, fg: red, bg: toBg(blue), opts: []color.Color{color.OpBold}},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
style, ok := New(color.FgRed, color.BgBlue).SetBold(true).(BasicTextStyle)
assert.True(t, ok, "SetBold should return a interface of type BasicTextStyle")
rgbStyle, ok := style.convertToRGB().SetColor(s.colorToSet).(RGBTextStyle)
assert.True(t, ok, "SetColor should return a interface of type RGBTextColor")
rgbStyle.style = color.RGBStyle{}
assert.Equal(t, s.expect, rgbStyle)
})
}
}
func TestConvertBasicToRGB(t *testing.T) {
type scenario struct {
name string
test func(*testing.T)
}
scenarios := []scenario{
{
"convert to rgb with fg",
func(t *testing.T) {
basicStyle, ok := New(color.FgRed, 0).(BasicTextStyle)
assert.True(t, ok, "New(..) should return a interface of type BasicTextStyle")
rgbStyle := basicStyle.convertToRGB()
assert.True(t, rgbStyle.fgSet)
assert.Equal(t, color.RGB(197, 30, 20), rgbStyle.fg)
assert.Nil(t, rgbStyle.bg)
},
},
{
"convert to rgb with fg and bg",
func(t *testing.T) {
basicStyle, ok := New(color.FgRed, color.BgRed).(BasicTextStyle)
assert.True(t, ok, "New(..) should return a interface of type BasicTextStyle")
rgbStyle := basicStyle.convertToRGB()
assert.True(t, rgbStyle.fgSet)
assert.Equal(t, color.RGB(197, 30, 20), rgbStyle.fg)
assert.Equal(t, color.RGB(197, 30, 20, true), *rgbStyle.bg)
},
},
{
"convert to rgb using SetRGBColor",
func(t *testing.T) {
style := New(color.FgRed, 0)
rgbStyle, ok := style.SetRGBColor(255, 00, 255, true).(RGBTextStyle)
assert.True(t, ok, "SetRGBColor should return a interface of type RGBTextStyle")
assert.True(t, rgbStyle.fgSet)
assert.Equal(t, color.RGB(197, 30, 20), rgbStyle.fg)
assert.Equal(t, color.RGB(255, 0, 255, true), *rgbStyle.bg)
},
},
{
"convert to rgb using SetRGBColor multiple times",
func(t *testing.T) {
style := New(color.FgRed, 0)
rgbStyle, ok := style.SetRGBColor(00, 255, 255, false).SetRGBColor(255, 00, 255, true).(RGBTextStyle)
assert.True(t, ok, "SetRGBColor should return a interface of type RGBTextStyle")
assert.True(t, rgbStyle.fgSet)
assert.Equal(t, color.RGB(0, 255, 255), rgbStyle.fg)
assert.Equal(t, color.RGB(255, 0, 255, true), *rgbStyle.bg)
},
},
}
for _, s := range scenarios {
t.Run(s.name, s.test)
}
}
func TestSettingAtributes(t *testing.T) {
type scenario struct {
name string
test func(s TextStyle) TextStyle
expectedOpts []color.Color
}
scenarios := []scenario{
{
"no attributes",
func(s TextStyle) TextStyle {
return s
},
[]color.Color{},
},
{
"set single attribute",
func(s TextStyle) TextStyle {
return s.SetBold(true)
},
[]color.Color{color.OpBold},
},
{
"set multiple attributes",
func(s TextStyle) TextStyle {
return s.SetBold(true).SetUnderline(true)
},
[]color.Color{color.OpBold, color.OpUnderscore},
},
{
"unset a attributes",
func(s TextStyle) TextStyle {
return s.SetBold(true).SetBold(false)
},
[]color.Color{},
},
{
"unset a attributes with multiple attributes",
func(s TextStyle) TextStyle {
return s.SetBold(true).SetUnderline(true).SetBold(false)
},
[]color.Color{color.OpUnderscore},
},
{
"unset all attributes with multiple attributes",
func(s TextStyle) TextStyle {
return s.SetBold(true).SetUnderline(true).SetBold(false).SetUnderline(false)
},
[]color.Color{},
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
// Test basic style
style := New(color.FgRed, 0)
basicStyle, ok := style.(BasicTextStyle)
assert.True(t, ok, "New(..) should return a interface of type BasicTextStyle")
basicStyle, ok = s.test(basicStyle).(BasicTextStyle)
assert.True(t, ok, "underlaying type should not be changed after test")
assert.Len(t, basicStyle.opts, len(s.expectedOpts))
for _, opt := range basicStyle.opts {
assert.Contains(t, s.expectedOpts, opt)
}
for _, opt := range s.expectedOpts {
assert.Contains(t, basicStyle.style, opt)
}
// Test RGB style
rgbStyle := New(color.FgRed, 0).(BasicTextStyle).convertToRGB()
rgbStyle, ok = s.test(rgbStyle).(RGBTextStyle)
assert.True(t, ok, "underlaying type should not be changed after test")
assert.Len(t, rgbStyle.opts, len(s.expectedOpts))
for _, opt := range rgbStyle.opts {
assert.Contains(t, s.expectedOpts, opt)
}
})
}
}

View File

@ -6,9 +6,8 @@ import (
"path/filepath"
"strings"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) getSelectedSubmodule() *models.SubmoduleConfig {
@ -28,9 +27,9 @@ func (gui *Gui) handleSubmoduleSelect() error {
} else {
prefix := fmt.Sprintf(
"Name: %s\nPath: %s\nUrl: %s\n\n",
utils.ColoredString(submodule.Name, color.FgGreen),
utils.ColoredString(submodule.Path, color.FgYellow),
utils.ColoredString(submodule.Url, color.FgCyan),
style.FgGreen.Sprint(submodule.Name),
style.FgYellow.Sprint(submodule.Path),
style.FgCyan.Sprint(submodule.Url),
)
file := gui.fileForSubmodule(submodule)
@ -213,7 +212,7 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
menuItems := []*menuItem{
{
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkInitCmdStr(), color.FgGreen)},
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdStr())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
@ -225,7 +224,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkUpdateCmdStr(), color.FgYellow)},
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdStr())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
@ -237,7 +236,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, utils.ColoredString(fmt.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr()), color.FgRed)},
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.BulkStashAndResetSubmodules).ResetSubmodules(gui.State.Submodules); err != nil {
@ -249,7 +248,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
},
},
{
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkDeinitCmdStr(), color.FgRed)},
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdStr())},
onPress: func() error {
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {

View File

@ -3,11 +3,11 @@ package gui
import (
"fmt"
"github.com/fatih/color"
"github.com/jesseduffield/lazygit/pkg/gui/style"
)
func (gui *Gui) handleCreateResetMenu() error {
red := color.New(color.FgRed)
red := style.FgRed
nukeStr := "reset --hard HEAD && git clean -fd"
if len(gui.State.Submodules) > 0 {

View File

@ -1,19 +1,14 @@
package theme
import (
"encoding/hex"
"github.com/fatih/color"
"github.com/gookit/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
)
var (
// DefaultTextColor is the default text color
DefaultTextColor = color.FgWhite
// DefaultHiTextColor is the default highlighted text color
DefaultHiTextColor = color.FgHiWhite
// GocuiDefaultTextColor does the same as DefaultTextColor but this one only colors gocui default text colors
GocuiDefaultTextColor gocui.Attribute
@ -23,69 +18,55 @@ var (
// InactiveBorderColor is the border color of the inactive active frames
InactiveBorderColor gocui.Attribute
// SelectedLineBgColor is the background color for the selected line
SelectedLineBgColor color.Attribute
// SelectedRangeBgColor is the background color of the selected range of lines
SelectedRangeBgColor color.Attribute
// GocuiSelectedLineBgColor is the background color for the selected line in gocui
GocuiSelectedLineBgColor gocui.Attribute
OptionsFgColor color.Attribute
OptionsColor gocui.Attribute
DiffTerminalColor = color.FgMagenta
// DefaultTextColor is the default text color
DefaultTextColor = style.New(color.FgWhite, 0)
// DefaultHiTextColor is the default highlighted text color
DefaultHiTextColor = style.New(color.FgLightWhite, 0)
// SelectedLineBgColor is the background color for the selected line
SelectedLineBgColor = style.New(0, 0)
// SelectedRangeBgColor is the background color of the selected range of lines
SelectedRangeBgColor = style.New(0, 0)
OptionsFgColor = style.New(0, 0)
DiffTerminalColor = style.New(color.FgMagenta, 0)
)
// UpdateTheme updates all theme variables
func UpdateTheme(themeConfig config.ThemeConfig) {
ActiveBorderColor = GetGocuiColor(themeConfig.ActiveBorderColor)
InactiveBorderColor = GetGocuiColor(themeConfig.InactiveBorderColor)
SelectedLineBgColor = GetBgColor(themeConfig.SelectedLineBgColor)
SelectedRangeBgColor = GetBgColor(themeConfig.SelectedRangeBgColor)
SelectedLineBgColor = style.SetConfigStyles(SelectedLineBgColor, themeConfig.SelectedLineBgColor, true)
SelectedRangeBgColor = style.SetConfigStyles(SelectedRangeBgColor, themeConfig.SelectedRangeBgColor, true)
GocuiSelectedLineBgColor = GetGocuiColor(themeConfig.SelectedLineBgColor)
OptionsColor = GetGocuiColor(themeConfig.OptionsTextColor)
OptionsFgColor = GetFgColor(themeConfig.OptionsTextColor)
OptionsFgColor = style.SetConfigStyles(OptionsFgColor, themeConfig.OptionsTextColor, false)
isLightTheme := themeConfig.LightTheme
if isLightTheme {
DefaultTextColor = color.FgBlack
DefaultHiTextColor = color.FgHiBlack
DefaultTextColor = style.FgBlack
DefaultHiTextColor = style.FgBlackLighter
GocuiDefaultTextColor = gocui.ColorBlack
} else {
DefaultTextColor = color.FgWhite
DefaultHiTextColor = color.FgHiWhite
DefaultTextColor = style.FgWhite
DefaultHiTextColor = style.FgLightWhite
GocuiDefaultTextColor = gocui.ColorWhite
}
}
// getHexColorValues returns the rgb values of a hex color
func getHexColorValues(v string) (r int32, g int32, b int32, valid bool) {
if len(v) == 4 {
v = string([]byte{v[0], v[1], v[1], v[2], v[2], v[3], v[3]})
} else if len(v) != 7 {
return
}
if v[0] != '#' {
return
}
rgb, err := hex.DecodeString(v[1:])
if err != nil {
return
}
return int32(rgb[0]), int32(rgb[1]), int32(rgb[2]), true
}
// GetAttribute gets the gocui color attribute from the string
func GetGocuiAttribute(key string) gocui.Attribute {
r, g, b, validHexColor := getHexColorValues(key)
r, g, b, validHexColor := utils.GetHexColorValues(key)
if validHexColor {
return gocui.NewRGBColor(r, g, b)
return gocui.NewRGBColor(int32(r), int32(g), int32(b))
}
colorMap := map[string]gocui.Attribute{
@ -109,52 +90,6 @@ func GetGocuiAttribute(key string) gocui.Attribute {
return gocui.ColorWhite
}
// GetFgAttribute gets the color foreground attribute from the string
func GetFgAttribute(key string) color.Attribute {
colorMap := map[string]color.Attribute{
"default": color.FgWhite,
"black": color.FgBlack,
"red": color.FgRed,
"green": color.FgGreen,
"yellow": color.FgYellow,
"blue": color.FgBlue,
"magenta": color.FgMagenta,
"cyan": color.FgCyan,
"white": color.FgWhite,
"bold": color.Bold,
"reverse": color.ReverseVideo,
"underline": color.Underline,
}
value, present := colorMap[key]
if present {
return value
}
return color.FgWhite
}
// GetBgAttribute gets the color background attribute from the string
func GetBgAttribute(key string) color.Attribute {
colorMap := map[string]color.Attribute{
"default": color.BgWhite,
"black": color.BgBlack,
"red": color.BgRed,
"green": color.BgGreen,
"yellow": color.BgYellow,
"blue": color.BgBlue,
"magenta": color.BgMagenta,
"cyan": color.BgCyan,
"white": color.BgWhite,
"bold": color.Bold,
"reverse": color.ReverseVideo,
"underline": color.Underline,
}
value, present := colorMap[key]
if present {
return value
}
return color.FgWhite
}
// GetGocuiColor bitwise OR's a list of attributes obtained via the given keys
func GetGocuiColor(keys []string) gocui.Attribute {
var attribute gocui.Attribute
@ -163,21 +98,3 @@ func GetGocuiColor(keys []string) gocui.Attribute {
}
return attribute
}
// GetColor bitwise OR's a list of attributes obtained via the given keys
func GetBgColor(keys []string) color.Attribute {
var attribute color.Attribute
for _, key := range keys {
attribute |= GetBgAttribute(key)
}
return attribute
}
// GetColor bitwise OR's a list of attributes obtained via the given keys
func GetFgColor(keys []string) color.Attribute {
var attribute color.Attribute
for _, key := range keys {
attribute |= GetFgAttribute(key)
}
return attribute
}

View File

@ -1,25 +1,10 @@
package utils
import (
"fmt"
"encoding/hex"
"regexp"
"github.com/fatih/color"
)
// ColoredString takes a string and a colour attribute and returns a colored
// string with that attribute
func ColoredString(str string, colorAttributes ...color.Attribute) string {
colour := color.New(colorAttributes...)
return ColoredStringDirect(str, colour)
}
// ColoredStringDirect used for aggregating a few color attributes rather than
// just sending a single one
func ColoredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}
// Decolorise strips a string of color
func Decolorise(str string) string {
re := regexp.MustCompile(`\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]`)
@ -47,3 +32,23 @@ func getPadWidths(stringArrays [][]string) []int {
}
return padWidths
}
// GetHexColorValues returns the rgb values of a hex color
func GetHexColorValues(v string) (r uint8, g uint8, b uint8, valid bool) {
if len(v) == 4 {
v = string([]byte{v[0], v[1], v[1], v[2], v[2], v[3], v[3]})
} else if len(v) != 7 {
return
}
if v[0] != '#' {
return
}
rgb, err := hex.DecodeString(v[1:])
if err != nil {
return
}
return rgb[0], rgb[1], rgb[2], true
}

View File

@ -1,4 +1,4 @@
package theme
package utils
import (
"testing"
@ -51,7 +51,7 @@ func TestGetHexColorValues(t *testing.T) {
for _, s := range scenarios {
s := s
t.Run(s.name, func(t *testing.T) {
r, g, b, valid := getHexColorValues(s.hexColor)
r, g, b, valid := GetHexColorValues(s.hexColor)
assert.EqualValues(t, s.valid, valid, s.hexColor)
if valid {
assert.EqualValues(t, s.rgb[0], r, s.hexColor)

View File

@ -7,8 +7,8 @@ import (
"os/exec"
"path/filepath"
"github.com/fatih/color"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/integration"
"github.com/jesseduffield/lazygit/pkg/secureexec"
)
@ -287,7 +287,7 @@ func (app *App) runSubprocess(cmd *exec.Cmd) {
cmd.Stderr = nil
cmd.Stdout = nil
fmt.Fprintf(os.Stdout, "\n%s", coloredString("press enter to return", color.FgGreen))
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return"))
fmt.Scanln() // wait for enter press
if err := gocui.Screen.Resume(); err != nil {
@ -382,14 +382,3 @@ func (app *App) layout(g *gocui.Gui) error {
func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
func coloredString(str string, colorAttributes ...color.Attribute) string {
colour := color.New(colorAttributes...)
return coloredStringDirect(str, colour)
}
// coloredStringDirect used for aggregating a few color attributes rather than
// just sending a single one
func coloredStringDirect(str string, colour *color.Color) string {
return colour.SprintFunc()(fmt.Sprint(str))
}

20
vendor/github.com/gookit/color/.gitignore generated vendored Normal file
View File

@ -0,0 +1,20 @@
*.log
*.swp
.idea
*.patch
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.DS_Store
app
demo

20
vendor/github.com/gookit/color/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2016 inhere
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

468
vendor/github.com/gookit/color/README.md generated vendored Normal file
View File

@ -0,0 +1,468 @@
# CLI Color
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square)
[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color)
[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color)
[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color)
[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color)
A command-line color library with true color support, universal API methods and Windows support.
> **[中文说明](README.zh-CN.md)**
Basic color preview:
![basic-color](_examples/images/basic-color2.png)
Now, 256 colors and RGB colors have also been supported to work in Windows CMD and PowerShell:
![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg)
## Features
- Simple to use, zero dependencies
- Supports rich color output: 16-color (4-bit), 256-color (8-bit), true color (24-bit, RGB)
- 16-color output is the most commonly used and most widely supported, working on any Windows version
- Since `v1.2.4` **the 256-color (8-bit), true color (24-bit) support windows CMD and PowerShell**
- See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support
- Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
- Supports HTML tag-style color rendering, such as `<green>message</>`.
- In addition to using built-in tags, it also supports custom color attributes
- Custom color attributes support the use of 16 color names, 256 color values, rgb color values and hex color values
- Support working on Windows `cmd` and `powerShell` terminal
- Basic colors: `Bold`, `Black`, `White`, `Gray`, `Red`, `Green`, `Yellow`, `Blue`, `Magenta`, `Cyan`
- Additional styles: `Info`, `Note`, `Light`, `Error`, `Danger`, `Notice`, `Success`, `Comment`, `Primary`, `Warning`, `Question`, `Secondary`
- Support by set `NO_COLOR` for disable color or use `FORCE_COLOR` for force open color render.
- Support Rgb, 256, 16 color conversion
## GoDoc
- [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1)
- [godoc for github](https://pkg.go.dev/github.com/gookit/color)
## Install
```bash
go get github.com/gookit/color
```
## Quick start
```go
package main
import (
"fmt"
"github.com/gookit/color"
)
func main() {
// quick use package func
color.Redp("Simple to use color")
color.Redln("Simple to use color")
color.Greenp("Simple to use color\n")
color.Cyanln("Simple to use color")
color.Yellowln("Simple to use color")
// quick use like fmt.Print*
color.Red.Println("Simple to use color")
color.Green.Print("Simple to use color\n")
color.Cyan.Printf("Simple to use %s\n", "color")
color.Yellow.Printf("Simple to use %s\n", "color")
// use like func
red := color.FgRed.Render
green := color.FgGreen.Render
fmt.Printf("%s line %s library\n", red("Command"), green("color"))
// custom color
color.New(color.FgWhite, color.BgBlack).Println("custom color style")
// can also:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
// internal theme/style:
color.Info.Tips("message")
color.Info.Prompt("message")
color.Info.Println("message")
color.Warn.Println("message")
color.Error.Println("message")
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>\n")
// Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
// apply a style tag
color.Tag("info").Println("info style text")
// prompt message
color.Info.Prompt("prompt style message")
color.Warn.Prompt("prompt style message")
// tips message
color.Info.Tips("tips style message")
color.Warn.Tips("tips style message")
}
```
Run demo: `go run ./_examples/demo.go`
![colored-out](_examples/images/color-demo.jpg)
## Basic/16 color
Supported on any Windows version. Provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
```go
color.Bold.Println("bold message")
color.Black.Println("bold message")
color.White.Println("bold message")
color.Gray.Println("bold message")
color.Red.Println("yellow message")
color.Blue.Println("yellow message")
color.Cyan.Println("yellow message")
color.Yellow.Println("yellow message")
color.Magenta.Println("yellow message")
// Only use foreground color
color.FgCyan.Printf("Simple to use %s\n", "color")
// Only use background color
color.BgRed.Printf("Simple to use %s\n", "color")
```
Run demo: `go run ./_examples/color_16.go`
![basic-color](_examples/images/basic-color.png)
### Custom build color
```go
// Full custom: foreground, background, option
myStyle := color.New(color.FgWhite, color.BgBlack, color.OpBold)
myStyle.Println("custom color style")
// can also:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
```
custom set console settings:
```go
// set console color
color.Set(color.FgCyan)
// print message
fmt.Print("message")
// reset console settings
color.Reset()
```
### Additional styles
provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
print message use defined style:
```go
color.Info.Println("Info message")
color.Note.Println("Note message")
color.Notice.Println("Notice message")
color.Error.Println("Error message")
color.Danger.Println("Danger message")
color.Warn.Println("Warn message")
color.Debug.Println("Debug message")
color.Primary.Println("Primary message")
color.Question.Println("Question message")
color.Secondary.Println("Secondary message")
```
Run demo: `go run ./_examples/theme_basic.go`
![theme-basic](_examples/images/theme-basic.png)
**Tips style**
```go
color.Info.Tips("Info tips message")
color.Note.Tips("Note tips message")
color.Notice.Tips("Notice tips message")
color.Error.Tips("Error tips message")
color.Danger.Tips("Danger tips message")
color.Warn.Tips("Warn tips message")
color.Debug.Tips("Debug tips message")
color.Primary.Tips("Primary tips message")
color.Question.Tips("Question tips message")
color.Secondary.Tips("Secondary tips message")
```
Run demo: `go run ./_examples/theme_tips.go`
![theme-tips](_examples/images/theme-tips.png)
**Prompt Style**
```go
color.Info.Prompt("Info prompt message")
color.Note.Prompt("Note prompt message")
color.Notice.Prompt("Notice prompt message")
color.Error.Prompt("Error prompt message")
color.Danger.Prompt("Danger prompt message")
color.Warn.Prompt("Warn prompt message")
color.Debug.Prompt("Debug prompt message")
color.Primary.Prompt("Primary prompt message")
color.Question.Prompt("Question prompt message")
color.Secondary.Prompt("Secondary prompt message")
```
Run demo: `go run ./_examples/theme_prompt.go`
![theme-prompt](_examples/images/theme-prompt.png)
**Block Style**
```go
color.Info.Block("Info block message")
color.Note.Block("Note block message")
color.Notice.Block("Notice block message")
color.Error.Block("Error block message")
color.Danger.Block("Danger block message")
color.Warn.Block("Warn block message")
color.Debug.Block("Debug block message")
color.Primary.Block("Primary block message")
color.Question.Block("Question block message")
color.Secondary.Block("Secondary block message")
```
Run demo: `go run ./_examples/theme_block.go`
![theme-block](_examples/images/theme-block.png)
## 256-color usage
> 256 colors support Windows CMD, PowerShell environment after `v1.2.4`
### Set the foreground or background color
- `color.C256(val uint8, isBg ...bool) Color256`
```go
c := color.C256(132) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.C256(132, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 256-color style
Can be used to set foreground and background colors at the same time.
- `S256(fgAndBg ...uint8) *Style256`
```go
s := color.S256(32, 203)
s.Println("message")
s.Printf("format %s", "message")
```
with options:
```go
s := color.S256(32, 203)
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
Run demo: `go run ./_examples/color_256.go`
![color-tags](_examples/images/color-256.png)
## RGB/True color
> RGB colors support Windows `CMD`, `PowerShell` environment after `v1.2.4`
**Preview:**
> Run demo: `Run demo: go run ./_examples/color_rgb.go`
![color-rgb](_examples/images/color-rgb.png)
example:
```go
color.RGB(30, 144, 255).Println("message. use RGB number")
color.HEX("#1976D2").Println("blue-darken")
color.HEX("#D50000", true).Println("red-accent. use HEX style")
color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number")
color.HEXStyle("eee", "D50000").Println("deep-purple color")
```
### Set the foreground or background color
- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor`
```go
c := color.RGB(30,144,255) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.RGB(30,144,255, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
Create a style from an hexadecimal color string:
- `color.HEX(hex string, isBg ...bool) RGBColor`
```go
c := color.HEX("ccc") // can also: "cccccc" "#cccccc"
c.Println("message")
c.Printf("format %s", "message")
c = color.HEX("aabbcc", true) // as bg color
c.Println("message")
c.Printf("format %s", "message")
```
### RGB color style
Can be used to set the foreground and background colors at the same time.
- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle`
```go
s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23))
s.Println("message")
s.Printf("format %s", "message")
```
Create a style from an hexadecimal color string:
- `color.HEXStyle(fg string, bg ...string) *RGBStyle`
```go
s := color.HEXStyle("11aa23", "eee")
s.Println("message")
s.Printf("format %s", "message")
```
with options:
```go
s := color.HEXStyle("11aa23", "eee")
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
## HTML-like tag usage
**Supported** on Windows `cmd.exe` `PowerShell` .
```go
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
color.Println("<warning>hello</>")
// custom color attributes
color.Print("<fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")
// Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
```
- `color.Tag`
```go
// set a style tag
color.Tag("info").Print("info style text")
color.Tag("info").Printf("%s style text", "info")
color.Tag("info").Println("info style text")
```
Run demo: `go run ./_examples/color_tag.go`
![color-tags](_examples/images/color-tags.png)
## Color convert
Supports conversion between Rgb, 256, 16 colors, `Rgb <=> 256 <=> 16`
```go
basic := color.Red
basic.Println("basic color")
c256 := color.Red.C256()
c256.Println("256 color")
c256.C16().Println("basic color")
rgb := color.Red.RGB()
rgb.Println("rgb color")
rgb.C256().Println("256 color")
```
## Func refer
There are some useful functions reference
- `Disable()` disable color render
- `SetOutput(io.Writer)` custom set the colored text output writer
- `ForceOpenColor()` force open color render
- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3"
- `ClearCode(str string) string` Use for clear color codes
- `ClearTag(s string) string` clear all color html-tag for a string
- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin
- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers
- `RgbToHex(rgb []int) string` Convert RGB to hex code
- More useful func please see https://pkg.go.dev/github.com/gookit/color
## Project use
Check out these projects, which use https://github.com/gookit/color :
- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI
## Gookit packages
- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files
- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP
- [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands
- [gookit/slog](https://github.com/gookit/slog) Concise and extensible go log library
- [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go
- [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
- [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
- [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support
- [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data
- [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data
- [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
- More, please see https://github.com/gookit
## See also
- [inhere/console](https://github.com/inhere/php-console)
- [xo/terminfo](https://github.com/xo/terminfo)
- [beego/bee](https://github.com/beego/bee)
- [issue9/term](https://github.com/issue9/term)
- [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code)
- [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map)
- [Terminal Colors](https://gist.github.com/XVilka/8346728)
## License
[MIT](/LICENSE)

472
vendor/github.com/gookit/color/README.zh-CN.md generated vendored Normal file
View File

@ -0,0 +1,472 @@
# CLI Color
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square)
[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/51b28c5f7ffe4cc2b0f12ecf25ed247f)](https://app.codacy.com/app/inhere/color)
[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color)
[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color)
[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color)
Golang下的命令行色彩使用库, 拥有丰富的色彩渲染输出,通用的API方法,兼容Windows系统
> **[EN README](README.md)**
基本颜色预览:
![basic-color](_examples/images/basic-color2.png)
现在,256色和RGB色彩也已经支持windows CMD和PowerShell中工作:
![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg)
## 功能特色
- 使用简单方便
- 支持丰富的颜色输出, 16色(4bit),256色(8bit),RGB色彩(24bit, RGB)
- 16色(4bit)是最常用和支持最广的,支持Windows `cmd.exe`
- 自 `v1.2.4` 起 **256色(8bit),RGB色彩(24bit)均支持Windows CMD和PowerShell终端**
- 请查看 [this gist](https://gist.github.com/XVilka/8346728) 了解支持RGB色彩的终端
- 提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf`
- 同时支持html标签式的颜色渲染,除了使用内置标签,同时支持自定义颜色属性
- 例如: `this an <green>message</>` 标签内部的文本将会渲染为绿色字体
- 自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值
- 基础色彩: `Bold` `Black` `White` `Gray` `Red` `Green` `Yellow` `Blue` `Magenta` `Cyan`
- 扩展风格: `Info` `Note` `Light` `Error` `Danger` `Notice` `Success` `Comment` `Primary` `Warning` `Question` `Secondary`
- 支持通过设置环境变量 `NO_COLOR` 来禁用色彩,或者使用 `FORCE_COLOR` 来强制使用色彩渲染.
- 支持 Rgb, 256, 16 色彩之间的互相转换
- 支持Linux、Mac,同时兼容Windows系统环境
## GoDoc
- [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1)
- [godoc for github](https://pkg.go.dev/github.com/gookit/color)
## 安装
```bash
go get github.com/gookit/color
```
## 快速开始
如下,引入当前包就可以快速的使用
```go
package main
import (
"fmt"
"github.com/gookit/color"
)
func main() {
// 简单快速的使用,跟 fmt.Print* 类似
color.Redp("Simple to use color")
color.Redln("Simple to use color")
color.Greenp("Simple to use color\n")
color.Cyanln("Simple to use color")
color.Yellowln("Simple to use color")
// 简单快速的使用,跟 fmt.Print* 类似
color.Red.Println("Simple to use color")
color.Green.Print("Simple to use color\n")
color.Cyan.Printf("Simple to use %s\n", "color")
color.Yellow.Printf("Simple to use %s\n", "color")
// use like func
red := color.FgRed.Render
green := color.FgGreen.Render
fmt.Printf("%s line %s library\n", red("Command"), green("color"))
// 自定义颜色
color.New(color.FgWhite, color.BgBlack).Println("custom color style")
// 也可以:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
// internal style:
color.Info.Println("message")
color.Warn.Println("message")
color.Error.Println("message")
// 使用内置颜色标签
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>\n")
// 自定义标签: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
// apply a style tag
color.Tag("info").Println("info style text")
// prompt message
color.Info.Prompt("prompt style message")
color.Warn.Prompt("prompt style message")
// tips message
color.Info.Tips("tips style message")
color.Warn.Tips("tips style message")
}
```
> 运行 demo: `go run ./_examples/demo.go`
![colored-out](_examples/images/color-demo.jpg)
## 基础颜色(16-color)
提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf`
> 支持在windows `cmd.exe` `powerShell` 等终端使用
```go
color.Bold.Println("bold message")
color.Black.Println("bold message")
color.White.Println("bold message")
color.Gray.Println("bold message")
color.Red.Println("yellow message")
color.Blue.Println("yellow message")
color.Cyan.Println("yellow message")
color.Yellow.Println("yellow message")
color.Magenta.Println("yellow message")
// Only use foreground color
color.FgCyan.Printf("Simple to use %s\n", "color")
// Only use background color
color.BgRed.Printf("Simple to use %s\n", "color")
```
> 运行demo: `go run ./_examples/color_16.go`
![basic-color](_examples/images/basic-color.png)
### 构建风格
```go
// 仅设置前景色
color.FgCyan.Printf("Simple to use %s\n", "color")
// 仅设置背景色
color.BgRed.Printf("Simple to use %s\n", "color")
// 完全自定义: 前景色 背景色 选项
style := color.New(color.FgWhite, color.BgBlack, color.OpBold)
style.Println("custom color style")
// 也可以:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
```
直接设置控制台属性:
```go
// 设置console颜色
color.Set(color.FgCyan)
// 输出信息
fmt.Print("message")
// 重置console颜色
color.Reset()
```
> 当然,color已经内置丰富的色彩风格支持
### 扩展风格方法
提供通用的API方法:`Print` `Printf` `Println` `Sprint` `Sprintf`
> 支持在windows `cmd.exe` `powerShell` 等终端使用
基础使用:
```go
// print message
color.Info.Println("Info message")
color.Note.Println("Note message")
color.Notice.Println("Notice message")
color.Error.Println("Error message")
color.Danger.Println("Danger message")
color.Warn.Println("Warn message")
color.Debug.Println("Debug message")
color.Primary.Println("Primary message")
color.Question.Println("Question message")
color.Secondary.Println("Secondary message")
```
Run demo: `go run ./_examples/theme_basic.go`
![theme-basic](_examples/images/theme-basic.png)
**简约提示风格**
```go
color.Info.Tips("Info tips message")
color.Note.Tips("Note tips message")
color.Notice.Tips("Notice tips message")
color.Error.Tips("Error tips message")
color.Danger.Tips("Danger tips message")
color.Warn.Tips("Warn tips message")
color.Debug.Tips("Debug tips message")
color.Primary.Tips("Primary tips message")
color.Question.Tips("Question tips message")
color.Secondary.Tips("Secondary tips message")
```
Run demo: `go run ./_examples/theme_tips.go`
![theme-tips](_examples/images/theme-tips.png)
**着重提示风格**
```go
color.Info.Prompt("Info prompt message")
color.Note.Prompt("Note prompt message")
color.Notice.Prompt("Notice prompt message")
color.Error.Prompt("Error prompt message")
color.Danger.Prompt("Danger prompt message")
```
Run demo: `go run ./_examples/theme_prompt.go`
![theme-prompt](_examples/images/theme-prompt.png)
**强调提示风格**
```go
color.Warn.Block("Warn block message")
color.Debug.Block("Debug block message")
color.Primary.Block("Primary block message")
color.Question.Block("Question block message")
color.Secondary.Block("Secondary block message")
```
Run demo: `go run ./_examples/theme_block.go`
![theme-block](_examples/images/theme-block.png)
## 256 色彩使用
> 256色彩在 `v1.2.4` 后支持Windows CMD,PowerShell 环境
### 使用前景或后景色
- `color.C256(val uint8, isBg ...bool) Color256`
```go
c := color.C256(132) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.C256(132, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 使用256 色彩风格
> 可同时设置前景和背景色
- `color.S256(fgAndBg ...uint8) *Style256`
```go
s := color.S256(32, 203)
s.Println("message")
s.Printf("format %s", "message")
```
可以同时添加选项设置:
```go
s := color.S256(32, 203)
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
> 运行 demo: `go run ./_examples/color_256.go`
![color-tags](_examples/images/color-256.png)
## RGB/True色彩使用
> RGB色彩在 `v1.2.4` 后支持 Windows `CMD`, `PowerShell` 环境
**效果预览:**
> 运行 demo: `Run demo: go run ./_examples/color_rgb.go`
![color-rgb](_examples/images/color-rgb.png)
代码示例:
```go
color.RGB(30, 144, 255).Println("message. use RGB number")
color.HEX("#1976D2").Println("blue-darken")
color.HEX("#D50000", true).Println("red-accent. use HEX style")
color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number")
color.HEXStyle("eee", "D50000").Println("deep-purple color")
```
### 使用前景或后景色
- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor`
```go
c := color.RGB(30,144,255) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.RGB(30,144,255, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
- `color.HEX(hex string, isBg ...bool) RGBColor` 从16进制颜色创建
```go
c := color.HEX("ccc") // 也可以写为: "cccccc" "#cccccc"
c.Println("message")
c.Printf("format %s", "message")
c = color.HEX("aabbcc", true) // as bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 使用RGB风格
> 可同时设置前景和背景色
- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle`
```go
s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23))
s.Println("message")
s.Printf("format %s", "message")
```
- `color.HEXStyle(fg string, bg ...string) *RGBStyle` 从16进制颜色创建
```go
s := color.HEXStyle("11aa23", "eee")
s.Println("message")
s.Printf("format %s", "message")
```
- 可以同时添加选项设置:
```go
s := color.HEXStyle("11aa23", "eee")
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
## 使用颜色标签
> **支持** 在windows `cmd.exe` `PowerShell` 使用
使用内置的颜色标签,可以非常方便简单的构建自己需要的任何格式
> 同时支持自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值
```go
// 使用内置的 color tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
color.Println("<warning>hello</>")
// 自定义颜色属性
color.Print("<fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")
// 自定义颜色属性: 支持使用16色彩名称,256色彩值,rgb色彩值以及hex色彩值
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
```
- 使用 `color.Tag`
给后面输出的文本信息加上给定的颜色风格标签
```go
// set a style tag
color.Tag("info").Print("info style text")
color.Tag("info").Printf("%s style text", "info")
color.Tag("info").Println("info style text")
```
> 运行 demo: `go run ./_examples/color_tag.go`
![color-tags](_examples/images/color-tags.png)
## 颜色转换
支持 Rgb, 256, 16 色彩之间的互相转换 `Rgb <=> 256 <=> 16`
```go
basic := color.Red
basic.Println("basic color")
c256 := color.Red.C256()
c256.Println("256 color")
c256.C16().Println("basic color")
rgb := color.Red.RGB()
rgb.Println("rgb color")
rgb.C256().Println("256 color")
```
## 方法参考
一些有用的工具方法参考
- `Disable()` disable color render
- `SetOutput(io.Writer)` custom set the colored text output writer
- `ForceOpenColor()` force open color render
- `ClearCode(str string) string` Use for clear color codes
- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3"
- `ClearTag(s string) string` clear all color html-tag for a string
- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin
- `HexToRgb(hex string) (rgb []int)` Convert hex color string to RGB numbers
- `RgbToHex(rgb []int) string` Convert RGB to hex code
- 更多请查看文档 https://pkg.go.dev/github.com/gookit/color
## 使用color的项目
看看这些使用了 https://github.com/gookit/color 的项目:
- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI
## Gookit 工具包
- [gookit/ini](https://github.com/gookit/ini) INI配置读取管理,支持多文件加载,数据覆盖合并, 解析ENV变量, 解析变量引用
- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP
- [gookit/gcli](https://github.com/gookit/gcli) Go的命令行应用,工具库,运行CLI命令,支持命令行色彩,用户交互,进度显示,数据格式化显示
- [gookit/slog](https://github.com/gookit/slog) 简洁易扩展的go日志库
- [gookit/event](https://github.com/gookit/event) Go实现的轻量级的事件管理、调度程序库, 支持设置监听器的优先级, 支持对一组事件进行监听
- [gookit/cache](https://github.com/gookit/cache) 通用的缓存使用包装库,通过包装各种常用的驱动,来提供统一的使用API
- [gookit/config](https://github.com/gookit/config) Go应用配置管理,支持多种格式(JSON, YAML, TOML, INI, HCL, ENV, Flags),多文件加载,远程文件加载,数据合并
- [gookit/color](https://github.com/gookit/color) CLI 控制台颜色渲染工具库, 拥有简洁的使用API,支持16色,256色,RGB色彩渲染输出
- [gookit/filter](https://github.com/gookit/filter) 提供对Golang数据的过滤,净化,转换
- [gookit/validate](https://github.com/gookit/validate) Go通用的数据验证与过滤库,使用简单,内置大部分常用验证、过滤器
- [gookit/goutil](https://github.com/gookit/goutil) Go 的一些工具函数,格式化,特殊处理,常用信息获取等
- 更多请查看 https://github.com/gookit
## 参考项目
- [inhere/console](https://github.com/inhere/php-console)
- [xo/terminfo](https://github.com/xo/terminfo)
- [beego/bee](https://github.com/beego/bee)
- [issue9/term](https://github.com/issue9/term)
- [ANSI转义序列](https://zh.wikipedia.org/wiki/ANSI转义序列)
- [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map)
- [Terminal Colors](https://gist.github.com/XVilka/8346728)
## License
MIT

238
vendor/github.com/gookit/color/color.go generated vendored Normal file
View File

@ -0,0 +1,238 @@
/*
Package color is Command line color library.
Support rich color rendering output, universal API method, compatible with Windows system
Source code and other details for the project are available at GitHub:
https://github.com/gookit/color
More usage please see README and tests.
*/
package color
import (
"fmt"
"io"
"os"
"regexp"
"github.com/xo/terminfo"
)
// terminal color available level alias of the terminfo.ColorLevel*
const (
LevelNo = terminfo.ColorLevelNone // not support color.
Level16 = terminfo.ColorLevelBasic // 3/4 bit color supported
Level256 = terminfo.ColorLevelHundreds // 8 bit color supported
LevelRgb = terminfo.ColorLevelMillions // (24 bit)true color supported
)
// color render templates
// ESC 操作的表示:
// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制)
const (
SettingTpl = "\x1b[%sm"
FullColorTpl = "\x1b[%sm%s\x1b[0m"
)
// ResetSet Close all properties.
const ResetSet = "\x1b[0m"
// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m"
const CodeExpr = `\033\[[\d;?]+m`
var (
// Enable switch color render and display
//
// NOTICE:
// if ENV: NO_COLOR is not empty, will disable color render.
Enable = os.Getenv("NO_COLOR") == ""
// RenderTag render HTML tag on call color.Xprint, color.PrintX
RenderTag = true
// debug mode for development.
//
// set env:
// COLOR_DEBUG_MODE=on
// or:
// COLOR_DEBUG_MODE=on go run ./_examples/envcheck.go
debugMode = os.Getenv("COLOR_DEBUG_MODE") == "on"
// inner errors record on detect color level
innerErrs []error
// output the default io.Writer message print
output io.Writer = os.Stdout
// mark current env, It's like in `cmd.exe`
// if not in windows, it's always is False.
isLikeInCmd bool
// the color support level for current terminal
// needVTP - need enable VTP, only for windows OS
colorLevel, needVTP = detectTermColorLevel()
// match color codes
codeRegex = regexp.MustCompile(CodeExpr)
// mark current env is support color.
// Always: isLikeInCmd != supportColor
// supportColor = IsSupportColor()
)
// TermColorLevel value on current ENV
func TermColorLevel() terminfo.ColorLevel {
return colorLevel
}
// SupportColor on the current ENV
func SupportColor() bool {
return colorLevel > terminfo.ColorLevelNone
}
// Support16Color on the current ENV
// func Support16Color() bool {
// return colorLevel > terminfo.ColorLevelNone
// }
// Support256Color on the current ENV
func Support256Color() bool {
return colorLevel > terminfo.ColorLevelBasic
}
// SupportTrueColor on the current ENV
func SupportTrueColor() bool {
return colorLevel > terminfo.ColorLevelHundreds
}
/*************************************************************
* global settings
*************************************************************/
// Set set console color attributes
func Set(colors ...Color) (int, error) {
code := Colors2code(colors...)
err := SetTerminal(code)
return 0, err
}
// Reset reset console color attributes
func Reset() (int, error) {
err := ResetTerminal()
return 0, err
}
// Disable disable color output
func Disable() bool {
oldVal := Enable
Enable = false
return oldVal
}
// NotRenderTag on call color.Xprint, color.PrintX
func NotRenderTag() {
RenderTag = false
}
// SetOutput set default colored text output
func SetOutput(w io.Writer) {
output = w
}
// ResetOutput reset output
func ResetOutput() {
output = os.Stdout
}
// ResetOptions reset all package option setting
func ResetOptions() {
RenderTag = true
Enable = true
output = os.Stdout
}
// ForceColor force open color render
func ForceSetColorLevel(level terminfo.ColorLevel) terminfo.ColorLevel {
oldLevelVal := colorLevel
colorLevel = level
return oldLevelVal
}
// ForceColor force open color render
func ForceColor() terminfo.ColorLevel {
return ForceOpenColor()
}
// ForceOpenColor force open color render
func ForceOpenColor() terminfo.ColorLevel {
// TODO should set level to ?
return ForceSetColorLevel(terminfo.ColorLevelMillions)
}
// IsLikeInCmd check result
// Deprecated
func IsLikeInCmd() bool {
return isLikeInCmd
}
// InnerErrs info
func InnerErrs() []error {
return innerErrs
}
/*************************************************************
* render color code
*************************************************************/
// RenderCode render message by color code.
// Usage:
// msg := RenderCode("3;32;45", "some", "message")
func RenderCode(code string, args ...interface{}) string {
var message string
if ln := len(args); ln == 0 {
return ""
}
message = fmt.Sprint(args...)
if len(code) == 0 {
return message
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(message)
}
return fmt.Sprintf(FullColorTpl, code, message)
}
// RenderWithSpaces Render code with spaces.
// If the number of args is > 1, a space will be added between the args
func RenderWithSpaces(code string, args ...interface{}) string {
message := formatArgsForPrintln(args)
if len(code) == 0 {
return message
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(message)
}
return fmt.Sprintf(FullColorTpl, code, message)
}
// RenderString render a string with color code.
// Usage:
// msg := RenderString("3;32;45", "a message")
func RenderString(code string, str string) string {
if len(code) == 0 || str == "" {
return str
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(str)
}
return fmt.Sprintf(FullColorTpl, code, str)
}
// ClearCode clear color codes.
// eg: "\033[36;1mText\x1b[0m" -> "Text"
func ClearCode(str string) string {
return codeRegex.ReplaceAllString(str, "")
}

440
vendor/github.com/gookit/color/color_16.go generated vendored Normal file
View File

@ -0,0 +1,440 @@
package color
import (
"fmt"
"strconv"
)
// Color Color16, 16 color value type
// 3(2^3=8) OR 4(2^4=16) bite color.
type Color uint8
type Basic = Color // alias of Color
// Opts basic color options. code: 0 - 9
type Opts []Color
// Add option value
func (o *Opts) Add(ops ...Color) {
for _, op := range ops {
if uint8(op) < 10 {
*o = append(*o, op)
}
}
}
// IsValid options
func (o Opts) IsValid() bool {
return len(o) > 0
}
// IsEmpty options
func (o Opts) IsEmpty() bool {
return len(o) == 0
}
// String options to string. eg: "1;3"
func (o Opts) String() string {
return Colors2code(o...)
}
/*************************************************************
* Basic 16 color definition
*************************************************************/
// Base value for foreground/background color
const (
FgBase uint8 = 30
BgBase uint8 = 40
// hi color base code
HiFgBase uint8 = 90
HiBgBase uint8 = 100
)
// Foreground colors. basic foreground colors 30 - 37
const (
FgBlack Color = iota + 30
FgRed
FgGreen
FgYellow
FgBlue
FgMagenta // 品红
FgCyan // 青色
FgWhite
// FgDefault revert default FG
FgDefault Color = 39
)
// Extra foreground color 90 - 97(非标准)
const (
FgDarkGray Color = iota + 90 // 亮黑(灰)
FgLightRed
FgLightGreen
FgLightYellow
FgLightBlue
FgLightMagenta
FgLightCyan
FgLightWhite
// FgGray is alias of FgDarkGray
FgGray Color = 90 // 亮黑(灰)
)
// Background colors. basic background colors 40 - 47
const (
BgBlack Color = iota + 40
BgRed
BgGreen
BgYellow // BgBrown like yellow
BgBlue
BgMagenta
BgCyan
BgWhite
// BgDefault revert default BG
BgDefault Color = 49
)
// Extra background color 100 - 107(非标准)
const (
BgDarkGray Color = iota + 100
BgLightRed
BgLightGreen
BgLightYellow
BgLightBlue
BgLightMagenta
BgLightCyan
BgLightWhite
// BgGray is alias of BgDarkGray
BgGray Color = 100
)
// Option settings
const (
OpReset Color = iota // 0 重置所有设置
OpBold // 1 加粗
OpFuzzy // 2 模糊(不是所有的终端仿真器都支持)
OpItalic // 3 斜体(不是所有的终端仿真器都支持)
OpUnderscore // 4 下划线
OpBlink // 5 闪烁
OpFastBlink // 5 快速闪烁(未广泛支持)
OpReverse // 7 颠倒的 交换背景色与前景色
OpConcealed // 8 隐匿的
OpStrikethrough // 9 删除的,删除线(未广泛支持)
)
// There are basic and light foreground color aliases
const (
Red = FgRed
Cyan = FgCyan
Gray = FgDarkGray // is light Black
Blue = FgBlue
Black = FgBlack
Green = FgGreen
White = FgWhite
Yellow = FgYellow
Magenta = FgMagenta
// special
Bold = OpBold
Normal = FgDefault
// extra light
LightRed = FgLightRed
LightCyan = FgLightCyan
LightBlue = FgLightBlue
LightGreen = FgLightGreen
LightWhite = FgLightWhite
LightYellow = FgLightYellow
LightMagenta = FgLightMagenta
HiRed = FgLightRed
HiCyan = FgLightCyan
HiBlue = FgLightBlue
HiGreen = FgLightGreen
HiWhite = FgLightWhite
HiYellow = FgLightYellow
HiMagenta = FgLightMagenta
BgHiRed = BgLightRed
BgHiCyan = BgLightCyan
BgHiBlue = BgLightBlue
BgHiGreen = BgLightGreen
BgHiWhite = BgLightWhite
BgHiYellow = BgLightYellow
BgHiMagenta = BgLightMagenta
)
// Bit4 an method for create Color
func Bit4(code uint8) Color {
return Color(code)
}
/*************************************************************
* Color render methods
*************************************************************/
// Name get color code name.
func (c Color) Name() string {
name, ok := basic2nameMap[uint8(c)]
if ok {
return name
}
return "unknown"
}
// Text render a text message
func (c Color) Text(message string) string {
return RenderString(c.String(), message)
}
// Render messages by color setting
// Usage:
// green := color.FgGreen.Render
// fmt.Println(green("message"))
func (c Color) Render(a ...interface{}) string {
return RenderCode(c.String(), a...)
}
// Renderln messages by color setting.
// like Println, will add spaces for each argument
// Usage:
// green := color.FgGreen.Renderln
// fmt.Println(green("message"))
func (c Color) Renderln(a ...interface{}) string {
return RenderWithSpaces(c.String(), a...)
}
// Sprint render messages by color setting. is alias of the Render()
func (c Color) Sprint(a ...interface{}) string {
return RenderCode(c.String(), a...)
}
// Sprintf format and render message.
// Usage:
// green := color.Green.Sprintf
// colored := green("message")
func (c Color) Sprintf(format string, args ...interface{}) string {
return RenderString(c.String(), fmt.Sprintf(format, args...))
}
// Print messages.
// Usage:
// color.Green.Print("message")
// OR:
// green := color.FgGreen.Print
// green("message")
func (c Color) Print(args ...interface{}) {
doPrintV2(c.Code(), fmt.Sprint(args...))
}
// Printf format and print messages.
// Usage:
// color.Cyan.Printf("string %s", "arg0")
func (c Color) Printf(format string, a ...interface{}) {
doPrintV2(c.Code(), fmt.Sprintf(format, a...))
}
// Println messages with new line
func (c Color) Println(a ...interface{}) {
doPrintlnV2(c.String(), a)
}
// Light current color. eg: 36(FgCyan) -> 96(FgLightCyan).
// Usage:
// lightCyan := Cyan.Light()
// lightCyan.Print("message")
func (c Color) Light() Color {
val := int(c)
if val >= 30 && val <= 47 {
return Color(uint8(c) + 60)
}
// don't change
return c
}
// Darken current color. eg. 96(FgLightCyan) -> 36(FgCyan)
// Usage:
// cyan := LightCyan.Darken()
// cyan.Print("message")
func (c Color) Darken() Color {
val := int(c)
if val >= 90 && val <= 107 {
return Color(uint8(c) - 60)
}
// don't change
return c
}
// C256 convert 16 color to 256-color code.
func (c Color) C256() Color256 {
val := uint8(c)
if val < 10 { // is option code
return emptyC256 // empty
}
var isBg uint8
if val >= BgBase && val <= 47 { // is bg
isBg = AsBg
val = val - 10 // to fg code
} else if val >= HiBgBase && val <= 107 { // is hi bg
isBg = AsBg
val = val - 10 // to fg code
}
if c256, ok := basicTo256Map[val]; ok {
return Color256{c256, isBg}
}
// use raw value direct convert
return Color256{val}
}
// RGB convert 16 color to 256-color code.
func (c Color) RGB() RGBColor {
val := uint8(c)
if val < 10 { // is option code
return emptyRGBColor
}
return HEX(Basic2hex(val))
}
// Code convert to code string. eg "35"
func (c Color) Code() string {
// return fmt.Sprintf("%d", c)
return strconv.Itoa(int(c))
}
// String convert to code string. eg "35"
func (c Color) String() string {
// return fmt.Sprintf("%d", c)
return strconv.Itoa(int(c))
}
// IsValid color value
func (c Color) IsValid() bool {
return c < 107
}
/*************************************************************
* basic color maps
*************************************************************/
// FgColors foreground colors map
var FgColors = map[string]Color{
"black": FgBlack,
"red": FgRed,
"green": FgGreen,
"yellow": FgYellow,
"blue": FgBlue,
"magenta": FgMagenta,
"cyan": FgCyan,
"white": FgWhite,
"default": FgDefault,
}
// BgColors background colors map
var BgColors = map[string]Color{
"black": BgBlack,
"red": BgRed,
"green": BgGreen,
"yellow": BgYellow,
"blue": BgBlue,
"magenta": BgMagenta,
"cyan": BgCyan,
"white": BgWhite,
"default": BgDefault,
}
// ExFgColors extra foreground colors map
var ExFgColors = map[string]Color{
"darkGray": FgDarkGray,
"lightRed": FgLightRed,
"lightGreen": FgLightGreen,
"lightYellow": FgLightYellow,
"lightBlue": FgLightBlue,
"lightMagenta": FgLightMagenta,
"lightCyan": FgLightCyan,
"lightWhite": FgLightWhite,
}
// ExBgColors extra background colors map
var ExBgColors = map[string]Color{
"darkGray": BgDarkGray,
"lightRed": BgLightRed,
"lightGreen": BgLightGreen,
"lightYellow": BgLightYellow,
"lightBlue": BgLightBlue,
"lightMagenta": BgLightMagenta,
"lightCyan": BgLightCyan,
"lightWhite": BgLightWhite,
}
// Options color options map
// Deprecated
// NOTICE: please use AllOptions instead.
var Options = AllOptions
// AllOptions color options map
var AllOptions = map[string]Color{
"reset": OpReset,
"bold": OpBold,
"fuzzy": OpFuzzy,
"italic": OpItalic,
"underscore": OpUnderscore,
"blink": OpBlink,
"reverse": OpReverse,
"concealed": OpConcealed,
}
var (
// TODO basic name alias
// basicNameAlias = map[string]string{}
// basic color name to code
name2basicMap = initName2basicMap()
// basic2nameMap basic color code to name
basic2nameMap = map[uint8]string{
30: "black",
31: "red",
32: "green",
33: "yellow",
34: "blue",
35: "magenta",
36: "cyan",
37: "white",
// hi color code
90: "lightBlack",
91: "lightRed",
92: "lightGreen",
93: "lightYellow",
94: "lightBlue",
95: "lightMagenta",
96: "lightCyan",
97: "lightWhite",
// options
0: "reset",
1: "bold",
2: "fuzzy",
3: "italic",
4: "underscore",
5: "blink",
7: "reverse",
8: "concealed",
}
)
// Basic2nameMap data
func Basic2nameMap() map[uint8]string {
return basic2nameMap
}
func initName2basicMap() map[string]uint8 {
n2b := make(map[string]uint8, len(basic2nameMap))
for u, s := range basic2nameMap {
n2b[s] = u
}
return n2b
}

308
vendor/github.com/gookit/color/color_256.go generated vendored Normal file
View File

@ -0,0 +1,308 @@
package color
import (
"fmt"
"strconv"
"strings"
)
/*
from wikipedia, 256 color:
ESC[ 38;5;<n> m选择前景色
ESC[ 48;5;<n> m选择背景色
0- 7标准颜色 ESC[3037m
8- 15高强度颜色 ESC[9097m
16-2316 × 6 × 6 立方216: 16 + 36 × r + 6 × g + b (0 r, g, b 5)
232-255从黑到白的24阶灰度色
*/
// tpl for 8 bit 256 color(`2^8`)
//
// format:
// ESC[ … 38;5;<n> … m // 选择前景色
// ESC[ … 48;5;<n> … m // 选择背景色
//
// example:
// fg "\x1b[38;5;242m"
// bg "\x1b[48;5;208m"
// both "\x1b[38;5;242;48;5;208m"
//
// links:
// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位
const (
TplFg256 = "38;5;%d"
TplBg256 = "48;5;%d"
Fg256Pfx = "38;5;"
Bg256Pfx = "48;5;"
)
/*************************************************************
* 8bit(256) Color: Bit8Color Color256
*************************************************************/
// Color256 256 color (8 bit), uint8 range at 0 - 255
//
// 颜色值使用10进制和16进制都可 0x98 = 152
//
// The color consists of two uint8:
// 0: color value
// 1: color type; Fg=0, Bg=1, >1: unset value
//
// example:
// fg color: [152, 0]
// bg color: [152, 1]
//
// NOTICE: now support 256 color on windows CMD, PowerShell
// lint warn - Name starts with package name
type Color256 [2]uint8
type Bit8Color = Color256 // alias
var emptyC256 = Color256{1: 99}
// Bit8 create a color256
func Bit8(val uint8, isBg ...bool) Color256 {
return C256(val, isBg...)
}
// C256 create a color256
func C256(val uint8, isBg ...bool) Color256 {
bc := Color256{val}
// mark is bg color
if len(isBg) > 0 && isBg[0] {
bc[1] = AsBg
}
return bc
}
// Set terminal by 256 color code
func (c Color256) Set() error {
return SetTerminal(c.String())
}
// Reset terminal. alias of the ResetTerminal()
func (c Color256) Reset() error {
return ResetTerminal()
}
// Print print message
func (c Color256) Print(a ...interface{}) {
doPrintV2(c.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (c Color256) Printf(format string, a ...interface{}) {
doPrintV2(c.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (c Color256) Println(a ...interface{}) {
doPrintlnV2(c.String(), a)
}
// Sprint returns rendered message
func (c Color256) Sprint(a ...interface{}) string {
return RenderCode(c.String(), a...)
}
// Sprintf returns format and rendered message
func (c Color256) Sprintf(format string, a ...interface{}) string {
return RenderString(c.String(), fmt.Sprintf(format, a...))
}
// C16 convert color-256 to 16 color.
func (c Color256) C16() Color {
return c.Basic()
}
// Basic convert color-256 to basic 16 color.
func (c Color256) Basic() Color {
return Color(c[0]) // TODO
}
// RGB convert color-256 to RGB color.
func (c Color256) RGB() RGBColor {
return RGBFromSlice(C256ToRgb(c[0]), c[1] == AsBg)
}
// RGBColor convert color-256 to RGB color.
func (c Color256) RGBColor() RGBColor {
return c.RGB()
}
// Value return color value
func (c Color256) Value() uint8 {
return c[0]
}
// Code convert to color code string. eg: "12"
func (c Color256) Code() string {
return strconv.Itoa(int(c[0]))
}
// FullCode convert to color code string with prefix. eg: "38;5;12"
func (c Color256) FullCode() string {
return c.String()
}
// String convert to color code string with prefix. eg: "38;5;12"
func (c Color256) String() string {
if c[1] == AsFg { // 0 is Fg
// return fmt.Sprintf(TplFg256, c[0])
return Fg256Pfx + strconv.Itoa(int(c[0]))
}
if c[1] == AsBg { // 1 is Bg
// return fmt.Sprintf(TplBg256, c[0])
return Bg256Pfx + strconv.Itoa(int(c[0]))
}
return "" // empty
}
// IsFg color
func (c Color256) IsFg() bool {
return c[1] == AsFg
}
// ToFg 256 color
func (c Color256) ToFg() Color256 {
c[1] = AsFg
return c
}
// IsBg color
func (c Color256) IsBg() bool {
return c[1] == AsBg
}
// ToBg 256 color
func (c Color256) ToBg() Color256 {
c[1] = AsBg
return c
}
// IsEmpty value
func (c Color256) IsEmpty() bool {
return c[1] > 1
}
/*************************************************************
* 8bit(256) Style
*************************************************************/
// Style256 definition
//
// 前/背景色
// 都是由两位uint8组成, 第一位是色彩值;
// 第二位与 Bit8Color 不一样的是,在这里表示是否设置了值 0 未设置 !=0 已设置
type Style256 struct {
// p Printer
// Name of the style
Name string
// color options of the style
opts Opts
// fg and bg color
fg, bg Color256
}
// S256 create a color256 style
// Usage:
// s := color.S256()
// s := color.S256(132) // fg
// s := color.S256(132, 203) // fg and bg
func S256(fgAndBg ...uint8) *Style256 {
s := &Style256{}
vl := len(fgAndBg)
if vl > 0 { // with fg
s.fg = Color256{fgAndBg[0], 1}
if vl > 1 { // and with bg
s.bg = Color256{fgAndBg[1], 1}
}
}
return s
}
// Set fg and bg color value, can also with color options
func (s *Style256) Set(fgVal, bgVal uint8, opts ...Color) *Style256 {
s.fg = Color256{fgVal, 1}
s.bg = Color256{bgVal, 1}
s.opts.Add(opts...)
return s
}
// SetBg set bg color value
func (s *Style256) SetBg(bgVal uint8) *Style256 {
s.bg = Color256{bgVal, 1}
return s
}
// SetFg set fg color value
func (s *Style256) SetFg(fgVal uint8) *Style256 {
s.fg = Color256{fgVal, 1}
return s
}
// SetOpts set options
func (s *Style256) SetOpts(opts Opts) *Style256 {
s.opts = opts
return s
}
// AddOpts add options
func (s *Style256) AddOpts(opts ...Color) *Style256 {
s.opts.Add(opts...)
return s
}
// Print message
func (s *Style256) Print(a ...interface{}) {
doPrintV2(s.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (s *Style256) Printf(format string, a ...interface{}) {
doPrintV2(s.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (s *Style256) Println(a ...interface{}) {
doPrintlnV2(s.String(), a)
}
// Sprint returns rendered message
func (s *Style256) Sprint(a ...interface{}) string {
return RenderCode(s.Code(), a...)
}
// Sprintf returns format and rendered message
func (s *Style256) Sprintf(format string, a ...interface{}) string {
return RenderString(s.Code(), fmt.Sprintf(format, a...))
}
// Code convert to color code string
func (s *Style256) Code() string {
return s.String()
}
// String convert to color code string
func (s *Style256) String() string {
var ss []string
if s.fg[1] > 0 {
ss = append(ss, fmt.Sprintf(TplFg256, s.fg[0]))
}
if s.bg[1] > 0 {
ss = append(ss, fmt.Sprintf(TplBg256, s.bg[0]))
}
if s.opts.IsValid() {
ss = append(ss, s.opts.String())
}
return strings.Join(ss, ";")
}

391
vendor/github.com/gookit/color/color_rgb.go generated vendored Normal file
View File

@ -0,0 +1,391 @@
package color
import (
"fmt"
"strconv"
"strings"
)
// 24 bit RGB color
// RGB:
// R 0-255 G 0-255 B 0-255
// R 00-FF G 00-FF B 00-FF (16进制)
//
// Format:
// ESC[ … 38;2;<r>;<g>;<b> … m // Select RGB foreground color
// ESC[ … 48;2;<r>;<g>;<b> … m // Choose RGB background color
//
// links:
// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位
//
// example:
// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m
// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m
// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m
const (
TplFgRGB = "38;2;%d;%d;%d"
TplBgRGB = "48;2;%d;%d;%d"
FgRGBPfx = "38;2;"
BgRGBPfx = "48;2;"
)
// mark color is fg or bg.
const (
AsFg uint8 = iota
AsBg
)
// values from https://github.com/go-terminfo/terminfo
// var (
// RgbaBlack = image_color.RGBA{0, 0, 0, 255}
// Red = color.RGBA{205, 0, 0, 255}
// Green = color.RGBA{0, 205, 0, 255}
// Orange = color.RGBA{205, 205, 0, 255}
// Blue = color.RGBA{0, 0, 238, 255}
// Magenta = color.RGBA{205, 0, 205, 255}
// Cyan = color.RGBA{0, 205, 205, 255}
// LightGrey = color.RGBA{229, 229, 229, 255}
//
// DarkGrey = color.RGBA{127, 127, 127, 255}
// LightRed = color.RGBA{255, 0, 0, 255}
// LightGreen = color.RGBA{0, 255, 0, 255}
// Yellow = color.RGBA{255, 255, 0, 255}
// LightBlue = color.RGBA{92, 92, 255, 255}
// LightMagenta = color.RGBA{255, 0, 255, 255}
// LightCyan = color.RGBA{0, 255, 255, 255}
// White = color.RGBA{255, 255, 255, 255}
// )
/*************************************************************
* RGB Color(Bit24Color, TrueColor)
*************************************************************/
// RGBColor definition.
//
// The first to third digits represent the color value.
// The last digit represents the foreground(0), background(1), >1 is unset value
//
// Usage:
// // 0, 1, 2 is R,G,B.
// // 3rd: Fg=0, Bg=1, >1: unset value
// RGBColor{30,144,255, 0}
// RGBColor{30,144,255, 1}
//
// NOTICE: now support RGB color on windows CMD, PowerShell
type RGBColor [4]uint8
// create a empty RGBColor
var emptyRGBColor = RGBColor{3: 99}
// RGB color create.
// Usage:
// c := RGB(30,144,255)
// c := RGB(30,144,255, true)
// c.Print("message")
func RGB(r, g, b uint8, isBg ...bool) RGBColor {
rgb := RGBColor{r, g, b}
if len(isBg) > 0 && isBg[0] {
rgb[3] = AsBg
}
return rgb
}
// Rgb alias of the RGB()
func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
// Bit24 alias of the RGB()
func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
// RGBFromSlice quick RGBColor from slice
func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor {
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
}
// HEX create RGB color from a HEX color string.
// Usage:
// c := HEX("ccc") // rgb: [204 204 204]
// c := HEX("aabbcc") // rgb: [170 187 204]
// c := HEX("#aabbcc")
// c := HEX("0xaabbcc")
// c.Print("message")
func HEX(hex string, isBg ...bool) RGBColor {
if rgb := HexToRgb(hex); len(rgb) > 0 {
return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...)
}
// mark is empty
return emptyRGBColor
}
// Hex alias of the HEX()
func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) }
// RGBFromString create RGB color from a string.
// Usage:
// c := RGBFromString("170,187,204")
// c.Print("message")
func RGBFromString(rgb string, isBg ...bool) RGBColor {
ss := stringToArr(rgb, ",")
if len(ss) != 3 {
return emptyRGBColor
}
var ar [3]int
for i, val := range ss {
iv, err := strconv.Atoi(val)
if err != nil {
return emptyRGBColor
}
ar[i] = iv
}
return RGB(uint8(ar[0]), uint8(ar[1]), uint8(ar[2]), isBg...)
}
// Set terminal by rgb/true color code
func (c RGBColor) Set() error {
return SetTerminal(c.String())
}
// Reset terminal. alias of the ResetTerminal()
func (c RGBColor) Reset() error {
return ResetTerminal()
}
// Print print message
func (c RGBColor) Print(a ...interface{}) {
doPrintV2(c.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (c RGBColor) Printf(format string, a ...interface{}) {
doPrintV2(c.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (c RGBColor) Println(a ...interface{}) {
doPrintlnV2(c.String(), a)
}
// Sprint returns rendered message
func (c RGBColor) Sprint(a ...interface{}) string {
return RenderCode(c.String(), a...)
}
// Sprintf returns format and rendered message
func (c RGBColor) Sprintf(format string, a ...interface{}) string {
return RenderString(c.String(), fmt.Sprintf(format, a...))
}
// Values to RGB values
func (c RGBColor) Values() []int {
return []int{int(c[0]), int(c[1]), int(c[2])}
}
// Code to color code string without prefix. eg: "204;123;56"
func (c RGBColor) Code() string {
return fmt.Sprintf("%d;%d;%d", c[0], c[1], c[2])
}
// Hex color rgb to hex string. as in "ff0080".
func (c RGBColor) Hex() string {
return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2])
}
// FullCode to color code string with prefix
func (c RGBColor) FullCode() string {
return c.String()
}
// String to color code string with prefix. eg: "38;2;204;123;56"
func (c RGBColor) String() string {
if c[3] == AsFg {
return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2])
}
if c[3] == AsBg {
return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2])
}
// c[3] > 1 is empty
return ""
}
// IsEmpty value
func (c RGBColor) IsEmpty() bool {
return c[3] > AsBg
}
// IsValid value
// func (c RGBColor) IsValid() bool {
// return c[3] <= AsBg
// }
// C256 returns the closest approximate 256 (8 bit) color
func (c RGBColor) C256() Color256 {
return C256(RgbTo256(c[0], c[1], c[2]), c[3] == AsBg)
}
// Basic returns the closest approximate 16 (4 bit) color
func (c RGBColor) Basic() Color {
// return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg))
return Color(Rgb2basic(c[0], c[1], c[2], c[3] == AsBg))
}
// Color returns the closest approximate 16 (4 bit) color
func (c RGBColor) Color() Color { return c.Basic() }
// C16 returns the closest approximate 16 (4 bit) color
func (c RGBColor) C16() Color { return c.Basic() }
/*************************************************************
* RGB Style
*************************************************************/
// RGBStyle definition.
//
// Foreground/Background color
// All are composed of 4 digits uint8, the first three digits are the color value;
// The last bit is different from RGBColor, here it indicates whether the value is set.
// - 1 Has been set
// - ^1 Not set
type RGBStyle struct {
// Name of the style
Name string
// color options of the style
opts Opts
// fg and bg color
fg, bg RGBColor
}
// NewRGBStyle create a RGBStyle.
func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(bg[0])
}
return s.SetFg(fg)
}
// HEXStyle create a RGBStyle from HEX color string.
// Usage:
// s := HEXStyle("aabbcc", "eee")
// s.Print("message")
func HEXStyle(fg string, bg ...string) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(HEX(bg[0]))
}
if len(fg) > 0 {
s.SetFg(HEX(fg))
}
return s
}
// RGBStyleFromString create a RGBStyle from color value string.
// Usage:
// s := RGBStyleFromString("170,187,204", "70,87,4")
// s.Print("message")
func RGBStyleFromString(fg string, bg ...string) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(RGBFromString(bg[0]))
}
return s.SetFg(RGBFromString(fg))
}
// Set fg and bg color, can also with color options
func (s *RGBStyle) Set(fg, bg RGBColor, opts ...Color) *RGBStyle {
return s.SetFg(fg).SetBg(bg).SetOpts(opts)
}
// SetFg set fg color
func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle {
fg[3] = 1 // add fixed value, mark is valid
s.fg = fg
return s
}
// SetBg set bg color
func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle {
bg[3] = 1 // add fixed value, mark is valid
s.bg = bg
return s
}
// SetOpts set color options
func (s *RGBStyle) SetOpts(opts Opts) *RGBStyle {
s.opts = opts
return s
}
// AddOpts add options
func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle {
s.opts.Add(opts...)
return s
}
// Print print message
func (s *RGBStyle) Print(a ...interface{}) {
doPrintV2(s.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (s *RGBStyle) Printf(format string, a ...interface{}) {
doPrintV2(s.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (s *RGBStyle) Println(a ...interface{}) {
doPrintlnV2(s.String(), a)
}
// Sprint returns rendered message
func (s *RGBStyle) Sprint(a ...interface{}) string {
return RenderCode(s.String(), a...)
}
// Sprintf returns format and rendered message
func (s *RGBStyle) Sprintf(format string, a ...interface{}) string {
return RenderString(s.String(), fmt.Sprintf(format, a...))
}
// Code convert to color code string
func (s *RGBStyle) Code() string {
return s.String()
}
// FullCode convert to color code string
func (s *RGBStyle) FullCode() string {
return s.String()
}
// String convert to color code string
func (s *RGBStyle) String() string {
var ss []string
// last value ensure is enable.
if s.fg[3] == 1 {
ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2]))
}
if s.bg[3] == 1 {
ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2]))
}
if s.opts.IsValid() {
ss = append(ss, s.opts.String())
}
return strings.Join(ss, ";")
}
// IsEmpty style
func (s *RGBStyle) IsEmpty() bool {
return s.fg[3] != 1 && s.bg[3] != 1
}

427
vendor/github.com/gookit/color/color_tag.go generated vendored Normal file
View File

@ -0,0 +1,427 @@
package color
import (
"fmt"
"regexp"
"strings"
)
// output colored text like use html tag. (not support windows cmd)
const (
// MatchExpr regex to match color tags
//
// Notice: golang 不支持反向引用. 即不支持使用 \1 引用第一个匹配 ([a-z=;]+)
// MatchExpr = `<([a-z=;]+)>(.*?)<\/\1>`
// 所以调整一下 统一使用 `</>` 来结束标签,例如 "<info>some text</>"
//
// allow custom attrs, eg: "<fg=white;bg=blue;op=bold>content</>"
// (?s:...) s - 让 "." 匹配换行
MatchExpr = `<([0-9a-zA-Z_=,;]+)>(?s:(.*?))<\/>`
// AttrExpr regex to match custom color attributes
// eg: "<fg=white;bg=blue;op=bold>content</>"
AttrExpr = `(fg|bg|op)[\s]*=[\s]*([0-9a-zA-Z,]+);?`
// StripExpr regex used for removing color tags
// StripExpr = `<[\/]?[a-zA-Z=;]+>`
// 随着上面的做一些调整
StripExpr = `<[\/]?[0-9a-zA-Z_=,;]*>`
)
var (
attrRegex = regexp.MustCompile(AttrExpr)
matchRegex = regexp.MustCompile(MatchExpr)
stripRegex = regexp.MustCompile(StripExpr)
)
/*************************************************************
* internal defined color tags
*************************************************************/
// There are internal defined color tags
// Usage: <tag>content text</>
// @notice 加 0 在前面是为了防止之前的影响到现在的设置
var colorTags = map[string]string{
// basic tags
"red": "0;31",
"red1": "1;31", // with bold
"redB": "1;31",
"red_b": "1;31",
"blue": "0;34",
"blue1": "1;34", // with bold
"blueB": "1;34",
"blue_b": "1;34",
"cyan": "0;36",
"cyan1": "1;36", // with bold
"cyanB": "1;36",
"cyan_b": "1;36",
"green": "0;32",
"green1": "1;32", // with bold
"greenB": "1;32",
"green_b": "1;32",
"black": "0;30",
"white": "1;37",
"default": "0;39", // no color
"normal": "0;39", // no color
"brown": "0;33", // #A52A2A
"yellow": "0;33",
"ylw0": "0;33",
"yellowB": "1;33", // with bold
"ylw1": "1;33",
"ylwB": "1;33",
"magenta": "0;35",
"mga": "0;35", // short name
"magentaB": "1;35", // with bold
"mgb": "1;35",
"mgaB": "1;35",
// light/hi tags
"gray": "0;90",
"darkGray": "0;90",
"dark_gray": "0;90",
"lightYellow": "0;93",
"light_yellow": "0;93",
"hiYellow": "0;93",
"hi_yellow": "0;93",
"hiYellowB": "1;93", // with bold
"hi_yellow_b": "1;93",
"lightMagenta": "0;95",
"light_magenta": "0;95",
"hiMagenta": "0;95",
"hi_magenta": "0;95",
"lightMagentaB": "1;95", // with bold
"hiMagentaB": "1;95", // with bold
"hi_magenta_b": "1;95",
"lightRed": "0;91",
"light_red": "0;91",
"hiRed": "0;91",
"hi_red": "0;91",
"lightRedB": "1;91", // with bold
"light_red_b": "1;91",
"hi_red_b": "1;91",
"lightGreen": "0;92",
"light_green": "0;92",
"hiGreen": "0;92",
"hi_green": "0;92",
"lightGreenB": "1;92",
"light_green_b": "1;92",
"hi_green_b": "1;92",
"lightBlue": "0;94",
"light_blue": "0;94",
"hiBlue": "0;94",
"hi_blue": "0;94",
"lightBlueB": "1;94",
"light_blue_b": "1;94",
"hi_blue_b": "1;94",
"lightCyan": "0;96",
"light_cyan": "0;96",
"hiCyan": "0;96",
"hi_cyan": "0;96",
"lightCyanB": "1;96",
"light_cyan_b": "1;96",
"hi_cyan_b": "1;96",
"lightWhite": "0;97;40",
"light_white": "0;97;40",
// option
"bold": "1",
"b": "1",
"underscore": "4",
"us": "4", // short name for 'underscore'
"reverse": "7",
// alert tags, like bootstrap's alert
"suc": "1;32", // same "green" and "bold"
"success": "1;32",
"info": "0;32", // same "green",
"comment": "0;33", // same "brown"
"note": "36;1",
"notice": "36;4",
"warn": "0;1;33",
"warning": "0;30;43",
"primary": "0;34",
"danger": "1;31", // same "red" but add bold
"err": "97;41",
"error": "97;41", // fg light white; bg red
}
/*************************************************************
* parse color tags
*************************************************************/
var (
tagParser = TagParser{}
rxNumStr = regexp.MustCompile("^[0-9]{1,3}$")
rxHexCode = regexp.MustCompile("^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
)
// TagParser struct
type TagParser struct {
disable bool
}
// NewTagParser create
func NewTagParser() *TagParser {
return &TagParser{}
}
// func (tp *TagParser) Disable() *TagParser {
// tp.disable = true
// return tp
// }
// ParseByEnv parse given string. will check package setting.
func (tp *TagParser) ParseByEnv(str string) string {
// disable handler TAG
if !RenderTag {
return str
}
// disable OR not support color
if !Enable || !SupportColor() {
return ClearTag(str)
}
return tp.Parse(str)
}
// Parse parse given string, replace color tag and return rendered string
func (tp *TagParser) Parse(str string) string {
// not contains color tag
if !strings.Contains(str, "</>") {
return str
}
// find color tags by regex. str eg: "<fg=white;bg=blue;op=bold>content</>"
matched := matchRegex.FindAllStringSubmatch(str, -1)
// item: 0 full text 1 tag name 2 tag content
for _, item := range matched {
full, tag, content := item[0], item[1], item[2]
// use defined tag name: "<info>content</>" -> tag: "info"
if !strings.ContainsRune(tag, '=') {
code := colorTags[tag]
if len(code) > 0 {
now := RenderString(code, content)
// old := WrapTag(content, tag) is equals to var 'full'
str = strings.Replace(str, full, now, 1)
}
continue
}
// custom color in tag
// - basic: "fg=white;bg=blue;op=bold"
if code := ParseCodeFromAttr(tag); len(code) > 0 {
now := RenderString(code, content)
str = strings.Replace(str, full, now, 1)
}
}
return str
}
// func (tp *TagParser) ParseAttr(attr string) (code string) {
// return
// }
// ReplaceTag parse string, replace color tag and return rendered string
func ReplaceTag(str string) string {
return tagParser.ParseByEnv(str)
}
// ParseCodeFromAttr parse color attributes.
//
// attr format:
// // VALUE please see var: FgColors, BgColors, AllOptions
// "fg=VALUE;bg=VALUE;op=VALUE"
// 16 color:
// "fg=yellow"
// "bg=red"
// "op=bold,underscore" option is allow multi value
// "fg=white;bg=blue;op=bold"
// "fg=white;op=bold,underscore"
// 256 color:
// "fg=167"
// "fg=167;bg=23"
// "fg=167;bg=23;op=bold"
// true color:
// // hex
// "fg=fc1cac"
// "fg=fc1cac;bg=c2c3c4"
// // r,g,b
// "fg=23,45,214"
// "fg=23,45,214;bg=109,99,88"
func ParseCodeFromAttr(attr string) (code string) {
if !strings.ContainsRune(attr, '=') {
return
}
attr = strings.Trim(attr, ";=,")
if len(attr) == 0 {
return
}
var codes []string
matched := attrRegex.FindAllStringSubmatch(attr, -1)
for _, item := range matched {
pos, val := item[1], item[2]
switch pos {
case "fg":
if c, ok := FgColors[val]; ok { // basic
codes = append(codes, c.String())
} else if c, ok := ExFgColors[val]; ok { // extra
codes = append(codes, c.String())
} else if code := rgbHex256toCode(val, false); code != "" {
codes = append(codes, code)
}
case "bg":
if c, ok := BgColors[val]; ok { // basic bg
codes = append(codes, c.String())
} else if c, ok := ExBgColors[val]; ok { // extra bg
codes = append(codes, c.String())
} else if code := rgbHex256toCode(val, true); code != "" {
codes = append(codes, code)
}
case "op": // options allow multi value
if strings.Contains(val, ",") {
ns := strings.Split(val, ",")
for _, n := range ns {
if c, ok := AllOptions[n]; ok {
codes = append(codes, c.String())
}
}
} else if c, ok := AllOptions[val]; ok {
codes = append(codes, c.String())
}
}
}
return strings.Join(codes, ";")
}
func rgbHex256toCode(val string, isBg bool) (code string) {
if len(val) == 6 && rxHexCode.MatchString(val) { // hex: "fc1cac"
code = HEX(val, isBg).String()
} else if strings.ContainsRune(val, ',') { // rgb: "231,178,161"
code = strings.Replace(val, ",", ";", -1)
if isBg {
code = BgRGBPfx + code
} else {
code = FgRGBPfx + code
}
} else if len(val) < 4 && rxNumStr.MatchString(val) { // 256 code
if isBg {
code = Bg256Pfx + val
} else {
code = Fg256Pfx + val
}
}
return
}
// ClearTag clear all tag for a string
func ClearTag(s string) string {
if !strings.Contains(s, "</>") {
return s
}
return stripRegex.ReplaceAllString(s, "")
}
/*************************************************************
* helper methods
*************************************************************/
// GetTagCode get color code by tag name
func GetTagCode(name string) string {
return colorTags[name]
}
// ApplyTag for messages
func ApplyTag(tag string, a ...interface{}) string {
return RenderCode(GetTagCode(tag), a...)
}
// WrapTag wrap a tag for a string "<tag>content</>"
func WrapTag(s string, tag string) string {
if s == "" || tag == "" {
return s
}
return fmt.Sprintf("<%s>%s</>", tag, s)
}
// GetColorTags get all internal color tags
func GetColorTags() map[string]string {
return colorTags
}
// IsDefinedTag is defined tag name
func IsDefinedTag(name string) bool {
_, ok := colorTags[name]
return ok
}
/*************************************************************
* Tag extra
*************************************************************/
// Tag value is a defined style name
// Usage:
// Tag("info").Println("message")
type Tag string
// Print messages
func (tg Tag) Print(a ...interface{}) {
name := string(tg)
str := fmt.Sprint(a...)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Print(str)
} else {
doPrintV2(GetTagCode(name), str)
}
}
// Printf format and print messages
func (tg Tag) Printf(format string, a ...interface{}) {
name := string(tg)
str := fmt.Sprintf(format, a...)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Print(str)
} else {
doPrintV2(GetTagCode(name), str)
}
}
// Println messages line
func (tg Tag) Println(a ...interface{}) {
name := string(tg)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Println(a...)
} else {
doPrintlnV2(GetTagCode(name), a)
}
}
// Sprint render messages
func (tg Tag) Sprint(a ...interface{}) string {
name := string(tg)
// if stl := GetStyle(name); !stl.IsEmpty() {
// return stl.Render(args...)
// }
return RenderCode(GetTagCode(name), a...)
}
// Sprintf format and render messages
func (tg Tag) Sprintf(format string, a ...interface{}) string {
tag := string(tg)
str := fmt.Sprintf(format, a...)
return RenderString(GetTagCode(tag), str)
}

593
vendor/github.com/gookit/color/convert.go generated vendored Normal file
View File

@ -0,0 +1,593 @@
package color
import (
"fmt"
"math"
"strconv"
"strings"
)
var (
// ---------- basic(16) <=> 256 color convert ----------
basicTo256Map = map[uint8]uint8{
30: 0, // black 000000
31: 160, // red c51e14
32: 34, // green 1dc121
33: 184, // yellow c7c329
34: 20, // blue 0a2fc4
35: 170, // magenta c839c5
36: 44, // cyan 20c5c6
37: 188, // white c7c7c7
90: 59, // lightBlack 686868
91: 203, // lightRed fd6f6b
92: 83, // lightGreen 67f86f
93: 227, // lightYellow fffa72
94: 69, // lightBlue 6a76fb
95: 213, // lightMagenta fd7cfc
96: 87, // lightCyan 68fdfe
97: 15, // lightWhite ffffff
}
// ---------- basic(16) <=> RGB color convert ----------
// refer from Hyper app
basic2hexMap = map[uint8]string{
30: "000000", // black
31: "c51e14", // red
32: "1dc121", // green
33: "c7c329", // yellow
34: "0a2fc4", // blue
35: "c839c5", // magenta
36: "20c5c6", // cyan
37: "c7c7c7", // white
90: "686868", // lightBlack/darkGray
91: "fd6f6b", // lightRed
92: "67f86f", // lightGreen
93: "fffa72", // lightYellow
94: "6a76fb", // lightBlue
95: "fd7cfc", // lightMagenta
96: "68fdfe", // lightCyan
97: "ffffff", // lightWhite
}
// will convert data from basic2hexMap
hex2basicMap = initHex2basicMap()
// ---------- 256 <=> RGB color convert ----------
// adapted from https://gist.github.com/MicahElliott/719710
c256ToHexMap = init256ToHexMap()
// rgb to 256 color look-up table
// RGB hex => 256 code
hexTo256Table = map[string]uint8{
// Primary 3-bit (8 colors). Unique representation!
"000000": 0,
"800000": 1,
"008000": 2,
"808000": 3,
"000080": 4,
"800080": 5,
"008080": 6,
"c0c0c0": 7,
// Equivalent "bright" versions of original 8 colors.
"808080": 8,
"ff0000": 9,
"00ff00": 10,
"ffff00": 11,
"0000ff": 12,
"ff00ff": 13,
"00ffff": 14,
"ffffff": 15,
// values commented out below are duplicates from the prior sections
// Strictly ascending.
// "000000": 16,
"000001": 16, // up: avoid key conflicts, value + 1
"00005f": 17,
"000087": 18,
"0000af": 19,
"0000d7": 20,
// "0000ff": 21,
"0000fe": 21, // up: avoid key conflicts, value - 1
"005f00": 22,
"005f5f": 23,
"005f87": 24,
"005faf": 25,
"005fd7": 26,
"005fff": 27,
"008700": 28,
"00875f": 29,
"008787": 30,
"0087af": 31,
"0087d7": 32,
"0087ff": 33,
"00af00": 34,
"00af5f": 35,
"00af87": 36,
"00afaf": 37,
"00afd7": 38,
"00afff": 39,
"00d700": 40,
"00d75f": 41,
"00d787": 42,
"00d7af": 43,
"00d7d7": 44,
"00d7ff": 45,
// "00ff00": 46,
"00ff01": 46, // up: avoid key conflicts, value + 1
"00ff5f": 47,
"00ff87": 48,
"00ffaf": 49,
"00ffd7": 50,
// "00ffff": 51,
"00fffe": 51, // up: avoid key conflicts, value - 1
"5f0000": 52,
"5f005f": 53,
"5f0087": 54,
"5f00af": 55,
"5f00d7": 56,
"5f00ff": 57,
"5f5f00": 58,
"5f5f5f": 59,
"5f5f87": 60,
"5f5faf": 61,
"5f5fd7": 62,
"5f5fff": 63,
"5f8700": 64,
"5f875f": 65,
"5f8787": 66,
"5f87af": 67,
"5f87d7": 68,
"5f87ff": 69,
"5faf00": 70,
"5faf5f": 71,
"5faf87": 72,
"5fafaf": 73,
"5fafd7": 74,
"5fafff": 75,
"5fd700": 76,
"5fd75f": 77,
"5fd787": 78,
"5fd7af": 79,
"5fd7d7": 80,
"5fd7ff": 81,
"5fff00": 82,
"5fff5f": 83,
"5fff87": 84,
"5fffaf": 85,
"5fffd7": 86,
"5fffff": 87,
"870000": 88,
"87005f": 89,
"870087": 90,
"8700af": 91,
"8700d7": 92,
"8700ff": 93,
"875f00": 94,
"875f5f": 95,
"875f87": 96,
"875faf": 97,
"875fd7": 98,
"875fff": 99,
"878700": 100,
"87875f": 101,
"878787": 102,
"8787af": 103,
"8787d7": 104,
"8787ff": 105,
"87af00": 106,
"87af5f": 107,
"87af87": 108,
"87afaf": 109,
"87afd7": 110,
"87afff": 111,
"87d700": 112,
"87d75f": 113,
"87d787": 114,
"87d7af": 115,
"87d7d7": 116,
"87d7ff": 117,
"87ff00": 118,
"87ff5f": 119,
"87ff87": 120,
"87ffaf": 121,
"87ffd7": 122,
"87ffff": 123,
"af0000": 124,
"af005f": 125,
"af0087": 126,
"af00af": 127,
"af00d7": 128,
"af00ff": 129,
"af5f00": 130,
"af5f5f": 131,
"af5f87": 132,
"af5faf": 133,
"af5fd7": 134,
"af5fff": 135,
"af8700": 136,
"af875f": 137,
"af8787": 138,
"af87af": 139,
"af87d7": 140,
"af87ff": 141,
"afaf00": 142,
"afaf5f": 143,
"afaf87": 144,
"afafaf": 145,
"afafd7": 146,
"afafff": 147,
"afd700": 148,
"afd75f": 149,
"afd787": 150,
"afd7af": 151,
"afd7d7": 152,
"afd7ff": 153,
"afff00": 154,
"afff5f": 155,
"afff87": 156,
"afffaf": 157,
"afffd7": 158,
"afffff": 159,
"d70000": 160,
"d7005f": 161,
"d70087": 162,
"d700af": 163,
"d700d7": 164,
"d700ff": 165,
"d75f00": 166,
"d75f5f": 167,
"d75f87": 168,
"d75faf": 169,
"d75fd7": 170,
"d75fff": 171,
"d78700": 172,
"d7875f": 173,
"d78787": 174,
"d787af": 175,
"d787d7": 176,
"d787ff": 177,
"d7af00": 178,
"d7af5f": 179,
"d7af87": 180,
"d7afaf": 181,
"d7afd7": 182,
"d7afff": 183,
"d7d700": 184,
"d7d75f": 185,
"d7d787": 186,
"d7d7af": 187,
"d7d7d7": 188,
"d7d7ff": 189,
"d7ff00": 190,
"d7ff5f": 191,
"d7ff87": 192,
"d7ffaf": 193,
"d7ffd7": 194,
"d7ffff": 195,
// "ff0000": 196,
"ff0001": 196, // up: avoid key conflicts, value + 1
"ff005f": 197,
"ff0087": 198,
"ff00af": 199,
"ff00d7": 200,
// "ff00ff": 201,
"ff00fe": 201, // up: avoid key conflicts, value - 1
"ff5f00": 202,
"ff5f5f": 203,
"ff5f87": 204,
"ff5faf": 205,
"ff5fd7": 206,
"ff5fff": 207,
"ff8700": 208,
"ff875f": 209,
"ff8787": 210,
"ff87af": 211,
"ff87d7": 212,
"ff87ff": 213,
"ffaf00": 214,
"ffaf5f": 215,
"ffaf87": 216,
"ffafaf": 217,
"ffafd7": 218,
"ffafff": 219,
"ffd700": 220,
"ffd75f": 221,
"ffd787": 222,
"ffd7af": 223,
"ffd7d7": 224,
"ffd7ff": 225,
// "ffff00": 226,
"ffff01": 226, // up: avoid key conflicts, value + 1
"ffff5f": 227,
"ffff87": 228,
"ffffaf": 229,
"ffffd7": 230,
// "ffffff": 231,
"fffffe": 231, // up: avoid key conflicts, value - 1
// Gray-scale range.
"080808": 232,
"121212": 233,
"1c1c1c": 234,
"262626": 235,
"303030": 236,
"3a3a3a": 237,
"444444": 238,
"4e4e4e": 239,
"585858": 240,
"626262": 241,
"6c6c6c": 242,
"767676": 243,
// "808080": 244,
"808081": 244, // up: avoid key conflicts, value + 1
"8a8a8a": 245,
"949494": 246,
"9e9e9e": 247,
"a8a8a8": 248,
"b2b2b2": 249,
"bcbcbc": 250,
"c6c6c6": 251,
"d0d0d0": 252,
"dadada": 253,
"e4e4e4": 254,
"eeeeee": 255,
}
incs = []uint8{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
)
func initHex2basicMap() map[string]uint8 {
h2b := make(map[string]uint8, len(basic2hexMap))
// ini data map
for u, s := range basic2hexMap {
h2b[s] = u
}
return h2b
}
func init256ToHexMap() map[uint8]string {
c256toh := make(map[uint8]string, len(hexTo256Table))
// ini data map
for hex, c256 := range hexTo256Table {
c256toh[c256] = hex
}
return c256toh
}
// RgbTo256Table mapping data
func RgbTo256Table() map[string]uint8 {
return hexTo256Table
}
// Colors2code convert colors to code. return like "32;45;3"
func Colors2code(colors ...Color) string {
if len(colors) == 0 {
return ""
}
var codes []string
for _, color := range colors {
codes = append(codes, color.String())
}
return strings.Join(codes, ";")
}
/*************************************************************
* HEX code <=> RGB/True color code
*************************************************************/
// Hex2rgb alias of the HexToRgb()
func Hex2rgb(hex string) []int { return HexToRgb(hex) }
// HexToRGB alias of the HexToRgb()
func HexToRGB(hex string) []int { return HexToRgb(hex) }
// HexToRgb convert hex color string to RGB numbers
//
// Usage:
// rgb := HexToRgb("ccc") // rgb: [204 204 204]
// rgb := HexToRgb("aabbcc") // rgb: [170 187 204]
// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204]
// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204]
func HexToRgb(hex string) (rgb []int) {
hex = strings.TrimSpace(hex)
if hex == "" {
return
}
// like from css. eg "#ccc" "#ad99c0"
if hex[0] == '#' {
hex = hex[1:]
}
hex = strings.ToLower(hex)
switch len(hex) {
case 3: // "ccc"
hex = string([]byte{hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]})
case 8: // "0xad99c0"
hex = strings.TrimPrefix(hex, "0x")
}
// recheck
if len(hex) != 6 {
return
}
// convert string to int64
if i64, err := strconv.ParseInt(hex, 16, 32); err == nil {
color := int(i64)
// parse int
rgb = make([]int, 3)
rgb[0] = color >> 16
rgb[1] = (color & 0x00FF00) >> 8
rgb[2] = color & 0x0000FF
}
return
}
// Rgb2hex alias of the RgbToHex()
func Rgb2hex(rgb []int) string { return RgbToHex(rgb) }
// RgbToHex convert RGB-code to hex-code
//
// Usage:
// hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc"
func RgbToHex(rgb []int) string {
hexNodes := make([]string, len(rgb))
for _, v := range rgb {
hexNodes = append(hexNodes, strconv.FormatInt(int64(v), 16))
}
return strings.Join(hexNodes, "")
}
/*************************************************************
* 4bit(16) color <=> RGB/True color
*************************************************************/
// Basic2hex convert basic color to hex string.
func Basic2hex(val uint8) string {
return basic2hexMap[val]
}
// Hex2basic convert hex string to basic color code.
func Hex2basic(hex string) uint8 {
return hex2basicMap[hex]
}
// Rgb2basic alias of the RgbToAnsi()
func Rgb2basic(r, g, b uint8, isBg bool) uint8 {
// is basic color, direct use static map data.
hex := RgbToHex([]int{int(r), int(g), int(b)})
if val, ok := hex2basicMap[hex]; ok {
if isBg {
return val + 10
}
return val
}
return RgbToAnsi(r, g, b, isBg)
}
// Rgb2ansi alias of the RgbToAnsi()
func Rgb2ansi(r, g, b uint8, isBg bool) uint8 {
return RgbToAnsi(r, g, b, isBg)
}
// RgbToAnsi convert RGB-code to 16-code
// refer https://github.com/radareorg/radare2/blob/master/libr/cons/rgb.c#L249-L271
func RgbToAnsi(r, g, b uint8, isBg bool) uint8 {
var bright, c, k uint8
base := compareVal(isBg, BgBase, FgBase)
// eco bright-specific
if r == 0x80 && g == 0x80 && b == 0x80 { // 0x80=128
bright = 53
} else if r == 0xff || g == 0xff || b == 0xff { // 0xff=255
bright = 60
} // else bright = 0
if r == g && g == b {
// 0x7f=127
// r = (r > 0x7f) ? 1 : 0;
r = compareVal(r > 0x7f, 1, 0)
g = compareVal(g > 0x7f, 1, 0)
b = compareVal(b > 0x7f, 1, 0)
} else {
k = (r + g + b) / 3
// r = (r >= k) ? 1 : 0;
r = compareVal(r >= k, 1, 0)
g = compareVal(g >= k, 1, 0)
b = compareVal(b >= k, 1, 0)
}
// c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0))
c = compareVal(r > 0, 1, 0)
if g > 0 {
c += compareVal(b > 0, 6, 2)
} else {
c += compareVal(b > 0, 4, 0)
}
return base + bright + c
}
/*************************************************************
* 8bit(256) color <=> RGB/True color
*************************************************************/
// Rgb2short convert RGB-code to 256-code
func Rgb2short(r, g, b uint8) uint8 {
return RgbTo256(r, g, b)
}
// RgbTo256 convert RGB-code to 256-code
func RgbTo256(r, g, b uint8) uint8 {
res := make([]uint8, 3)
for partI, part := range [3]uint8{r, g, b} {
i := 0
for i < len(incs)-1 {
s, b := incs[i], incs[i+1] // smaller, bigger
if s <= part && part <= b {
s1 := math.Abs(float64(s) - float64(part))
b1 := math.Abs(float64(b) - float64(part))
var closest uint8
if s1 < b1 {
closest = s
} else {
closest = b
}
res[partI] = closest
break
}
i++
}
}
hex := fmt.Sprintf("%02x%02x%02x", res[0], res[1], res[2])
equiv := hexTo256Table[hex]
return equiv
}
// C256ToRgb convert an 256 color code to RGB numbers
func C256ToRgb(val uint8) (rgb []uint8) {
hex := c256ToHexMap[val]
// convert to rgb code
rgbInts := Hex2rgb(hex)
return []uint8{
uint8(rgbInts[0]),
uint8(rgbInts[1]),
uint8(rgbInts[2]),
}
}
// C256ToRgbV1 convert an 256 color code to RGB numbers
// refer https://github.com/torvalds/linux/commit/cec5b2a97a11ade56a701e83044d0a2a984c67b4
func C256ToRgbV1(val uint8) (rgb []uint8) {
var r, g, b uint8
if val < 8 { // Standard colours.
// r = val&1 ? 0xaa : 0x00;
r = compareVal(val&1 == 1, 0xaa, 0x00)
g = compareVal(val&2 == 2, 0xaa, 0x00)
b = compareVal(val&4 == 4, 0xaa, 0x00)
} else if val < 16 {
// r = val & 1 ? 0xff : 0x55;
r = compareVal(val&1 == 1, 0xff, 0x55)
g = compareVal(val&2 == 2, 0xff, 0x55)
b = compareVal(val&4 == 4, 0xff, 0x55)
} else if val < 232 { /* 6x6x6 colour cube. */
r = (val - 16) / 36 * 85 / 2
g = (val - 16) / 6 % 6 * 85 / 2
b = (val - 16) % 6 * 85 / 2
} else { /* Grayscale ramp. */
nv := uint8(int(val)*10 - 2312)
// set value
r, g, b = nv, nv, nv
}
return []uint8{r, g, b}
}

281
vendor/github.com/gookit/color/detect_env.go generated vendored Normal file
View File

@ -0,0 +1,281 @@
package color
import (
"io"
"io/ioutil"
"os"
"runtime"
"strconv"
"strings"
"syscall"
"github.com/xo/terminfo"
)
/*************************************************************
* helper methods for detect color supports
*************************************************************/
// DetectColorLevel for current env
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func DetectColorLevel() terminfo.ColorLevel {
level, _ := detectTermColorLevel()
return level
}
// detect terminal color support level
//
// refer https://github.com/Delta456/box-cli-maker
func detectTermColorLevel() (level terminfo.ColorLevel, needVTP bool) {
// on windows WSL:
// - runtime.GOOS == "Linux"
// - support true-color
// env:
// WSL_DISTRO_NAME=Debian
if val := os.Getenv("WSL_DISTRO_NAME"); val != "" {
// detect WSL as it has True Color support
if detectWSL() {
debugf("True Color support on WSL environment")
return terminfo.ColorLevelMillions, false
}
}
isWin := runtime.GOOS == "windows"
termVal := os.Getenv("TERM")
// on TERM=screen: not support true-color
if termVal != "screen" {
// On JetBrains Terminal
// - support true-color
// env:
// TERMINAL_EMULATOR=JetBrains-JediTerm
val := os.Getenv("TERMINAL_EMULATOR")
if val == "JetBrains-JediTerm" {
debugf("True Color support on JetBrains-JediTerm, is win: %v", isWin)
return terminfo.ColorLevelMillions, isWin
}
}
// level, err = terminfo.ColorLevelFromEnv()
level = detectColorLevelFromEnv(termVal, isWin)
debugf("color level by detectColorLevelFromEnv: %s", level.String())
// fallback: simple detect by TERM value string.
if level == terminfo.ColorLevelNone {
debugf("level none - fallback check special term color support")
// on Windows: enable VTP as it has True Color support
level, needVTP = detectSpecialTermColor(termVal)
}
return
}
// detectColorFromEnv returns the color level COLORTERM, FORCE_COLOR,
// TERM_PROGRAM, or determined from the TERM environment variable.
//
// refer the terminfo.ColorLevelFromEnv()
// https://en.wikipedia.org/wiki/Terminfo
func detectColorLevelFromEnv(termVal string, isWin bool) terminfo.ColorLevel {
// check for overriding environment variables
colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR")
switch {
case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit"):
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelMillions
case colorTerm != "" || forceColor != "":
return terminfo.ColorLevelBasic
case termProg == "Apple_Terminal":
return terminfo.ColorLevelHundreds
case termProg == "Terminus" || termProg == "Hyper":
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelMillions
case termProg == "iTerm.app":
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
// check iTerm version
ver := os.Getenv("TERM_PROGRAM_VERSION")
if ver != "" {
i, err := strconv.Atoi(strings.Split(ver, ".")[0])
if err != nil {
saveInternalError(terminfo.ErrInvalidTermProgramVersion)
// return terminfo.ColorLevelNone
return terminfo.ColorLevelHundreds
}
if i == 3 {
return terminfo.ColorLevelMillions
}
}
return terminfo.ColorLevelHundreds
}
// otherwise determine from TERM's max_colors capability
if !isWin && termVal != "" {
debugf("TERM=%s - check color level by load terminfo file", termVal)
ti, err := terminfo.Load(termVal)
if err != nil {
saveInternalError(err)
return terminfo.ColorLevelNone
}
debugf("the loaded term info file is: %s", ti.File)
v, ok := ti.Nums[terminfo.MaxColors]
switch {
case !ok || v <= 16:
return terminfo.ColorLevelNone
case ok && v >= 256:
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelBasic
}
// no TERM env value. default return none level
return terminfo.ColorLevelNone
// return terminfo.ColorLevelBasic
}
var detectedWSL bool
var wslContents string
// https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364
func detectWSL() bool {
if !detectedWSL {
b := make([]byte, 1024)
// `cat /proc/version`
// on mac:
// !not the file!
// on linux(debian,ubuntu,alpine):
// Linux version 4.19.121-linuxkit (root@18b3f92ade35) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Thu Jan 21 15:36:34 UTC 2021
// on win git bash, conEmu:
// MINGW64_NT-10.0-19042 version 3.1.7-340.x86_64 (@WIN-N0G619FD3UK) (gcc version 9.3.0 (GCC) ) 2020-10-23 13:08 UTC
// on WSL:
// Linux version 4.4.0-19041-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #488-Microsoft Mon Sep 01 13:43:00 PST 2020
f, err := os.Open("/proc/version")
if err == nil {
_, _ = f.Read(b) // ignore error
if err = f.Close(); err != nil {
saveInternalError(err)
}
wslContents = string(b)
}
detectedWSL = true
}
return strings.Contains(wslContents, "Microsoft")
}
// refer
// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_unix.go#L27-L45
// detect WSL as it has True Color support
func isWSL() bool {
// on windows WSL:
// - runtime.GOOS == "Linux"
// - support true-color
// WSL_DISTRO_NAME=Debian
if val := os.Getenv("WSL_DISTRO_NAME"); val == "" {
return false
}
// `cat /proc/sys/kernel/osrelease`
// on mac:
// !not the file!
// on linux:
// 4.19.121-linuxkit
// on WSL Output:
// 4.4.0-19041-Microsoft
wsl, err := ioutil.ReadFile("/proc/sys/kernel/osrelease")
if err != nil {
saveInternalError(err)
return false
}
// it gives "Microsoft" for WSL and "microsoft" for WSL 2
// it support True-color
content := strings.ToLower(string(wsl))
return strings.Contains(content, "microsoft")
}
/*************************************************************
* helper methods for check env
*************************************************************/
// IsWindows OS env
func IsWindows() bool {
return runtime.GOOS == "windows"
}
// IsConsole Determine whether w is one of stderr, stdout, stdin
func IsConsole(w io.Writer) bool {
o, ok := w.(*os.File)
if !ok {
return false
}
fd := o.Fd()
// fix: cannot use 'o == os.Stdout' to compare
return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr)
}
// IsMSys msys(MINGW64) environment, does not necessarily support color
func IsMSys() bool {
// like "MSYSTEM=MINGW64"
if len(os.Getenv("MSYSTEM")) > 0 {
return true
}
return false
}
// IsSupportColor check current console is support color.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupportColor() bool {
return IsSupport16Color()
}
// IsSupportColor check current console is support color.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupport16Color() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelNone
}
// IsSupport256Color render check
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupport256Color() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelBasic
}
// IsSupportRGBColor check. alias of the IsSupportTrueColor()
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupportRGBColor() bool {
return IsSupportTrueColor()
}
// IsSupportTrueColor render check.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
//
// ENV:
// "COLORTERM=truecolor"
// "COLORTERM=24bit"
func IsSupportTrueColor() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelHundreds
}

48
vendor/github.com/gookit/color/detect_nonwin.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
// +build !windows
// The method in the file has no effect
// Only for compatibility with non-Windows systems
package color
import (
"strings"
"syscall"
"github.com/xo/terminfo"
)
// detect special term color support
func detectSpecialTermColor(termVal string) (terminfo.ColorLevel, bool) {
if termVal == "" {
return terminfo.ColorLevelNone, false
}
debugf("terminfo check fail - fallback detect color by check TERM value")
// on TERM=screen:
// - support 256, not support true-color. test on macOS
if termVal == "screen" {
return terminfo.ColorLevelHundreds, false
}
if strings.Contains(termVal, "256color") {
return terminfo.ColorLevelHundreds, false
}
if strings.Contains(termVal, "xterm") {
return terminfo.ColorLevelHundreds, false
// return terminfo.ColorLevelBasic, false
}
// return terminfo.ColorLevelNone, nil
return terminfo.ColorLevelBasic, false
}
// IsTerminal returns true if the given file descriptor is a terminal.
//
// Usage:
// IsTerminal(os.Stdout.Fd())
func IsTerminal(fd uintptr) bool {
return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr)
}

243
vendor/github.com/gookit/color/detect_windows.go generated vendored Normal file
View File

@ -0,0 +1,243 @@
// +build windows
// Display color on windows
// refer:
// golang.org/x/sys/windows
// golang.org/x/crypto/ssh/terminal
// https://docs.microsoft.com/en-us/windows/console
package color
import (
"os"
"syscall"
"unsafe"
"github.com/xo/terminfo"
"golang.org/x/sys/windows"
)
// related docs
// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences
// https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples
var (
// isMSys bool
kernel32 *syscall.LazyDLL
procGetConsoleMode *syscall.LazyProc
procSetConsoleMode *syscall.LazyProc
)
func init() {
if !SupportColor() {
isLikeInCmd = true
return
}
// if disabled.
if !Enable {
return
}
// if at windows's ConEmu, Cmder, putty ... terminals not need VTP
// -------- try force enable colors on windows terminal -------
tryEnableVTP(needVTP)
// fetch console screen buffer info
// err := getConsoleScreenBufferInfo(uintptr(syscall.Stdout), &defScreenInfo)
}
// try force enable colors on windows terminal
func tryEnableVTP(enable bool) bool {
if !enable {
return false
}
debugf("True-Color by enable VirtualTerminalProcessing on windows")
initKernel32Proc()
// enable colors on windows terminal
if tryEnableOnCONOUT() {
return true
}
return tryEnableOnStdout()
}
func initKernel32Proc() {
if kernel32 != nil {
return
}
// load related windows dll
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
}
func tryEnableOnCONOUT() bool {
outHandle, err := syscall.Open("CONOUT$", syscall.O_RDWR, 0)
if err != nil {
saveInternalError(err)
return false
}
err = EnableVirtualTerminalProcessing(outHandle, true)
if err != nil {
saveInternalError(err)
return false
}
return true
}
func tryEnableOnStdout() bool {
// try direct open syscall.Stdout
err := EnableVirtualTerminalProcessing(syscall.Stdout, true)
if err != nil {
saveInternalError(err)
return false
}
return true
}
// Get the Windows Version and Build Number
var (
winVersion, _, buildNumber = windows.RtlGetNtVersionNumbers()
)
// refer
// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_windows.go#L30-L57
// https://github.com/gookit/color/issues/25#issuecomment-738727917
// detects the Color Level Supported on windows: cmd, powerShell
func detectSpecialTermColor(termVal string) (tl terminfo.ColorLevel, needVTP bool) {
if os.Getenv("ConEmuANSI") == "ON" {
debugf("support True Color by ConEmuANSI=ON")
// ConEmuANSI is "ON" for generic ANSI support
// but True Color option is enabled by default
// I am just assuming that people wouldn't have disabled it
// Even if it is not enabled then ConEmu will auto round off
// accordingly
return terminfo.ColorLevelMillions, false
}
// Before Windows 10 Build Number 10586, console never supported ANSI Colors
if buildNumber < 10586 || winVersion < 10 {
// Detect if using ANSICON on older systems
if os.Getenv("ANSICON") != "" {
conVersion := os.Getenv("ANSICON_VER")
// 8 bit Colors were only supported after v1.81 release
if conVersion >= "181" {
return terminfo.ColorLevelHundreds, false
}
return terminfo.ColorLevelBasic, false
}
return terminfo.ColorLevelNone, false
}
// True Color is not available before build 14931 so fallback to 8 bit color.
if buildNumber < 14931 {
return terminfo.ColorLevelHundreds, true
}
// Windows 10 build 14931 is the first release that supports 16m/TrueColor
debugf("support True Color on windows version is >= build 14931")
return terminfo.ColorLevelMillions, true
}
/*************************************************************
* render full color code on windows(8,16,24bit color)
*************************************************************/
// docs https://docs.microsoft.com/zh-cn/windows/console/getconsolemode#parameters
const (
// equals to docs page's ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
EnableVirtualTerminalProcessingMode uint32 = 0x4
)
// EnableVirtualTerminalProcessing Enable virtual terminal processing
//
// ref from github.com/konsorten/go-windows-terminal-sequences
// doc https://docs.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences#samples
//
// Usage:
// err := EnableVirtualTerminalProcessing(syscall.Stdout, true)
// // support print color text
// err = EnableVirtualTerminalProcessing(syscall.Stdout, false)
func EnableVirtualTerminalProcessing(stream syscall.Handle, enable bool) error {
var mode uint32
// Check if it is currently in the terminal
// err := syscall.GetConsoleMode(syscall.Stdout, &mode)
err := syscall.GetConsoleMode(stream, &mode)
if err != nil {
// fmt.Println("EnableVirtualTerminalProcessing", err)
return err
}
if enable {
mode |= EnableVirtualTerminalProcessingMode
} else {
mode &^= EnableVirtualTerminalProcessingMode
}
ret, _, err := procSetConsoleMode.Call(uintptr(stream), uintptr(mode))
if ret == 0 {
return err
}
return nil
}
// renderColorCodeOnCmd enable cmd color render.
// func renderColorCodeOnCmd(fn func()) {
// err := EnableVirtualTerminalProcessing(syscall.Stdout, true)
// // if is not in terminal, will clear color tag.
// if err != nil {
// // panic(err)
// fn()
// return
// }
//
// // force open color render
// old := ForceOpenColor()
// fn()
// // revert color setting
// supportColor = old
//
// err = EnableVirtualTerminalProcessing(syscall.Stdout, false)
// if err != nil {
// panic(err)
// }
// }
/*************************************************************
* render simple color code on windows
*************************************************************/
// IsTty returns true if the given file descriptor is a terminal.
func IsTty(fd uintptr) bool {
initKernel32Proc()
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}
// IsTerminal returns true if the given file descriptor is a terminal.
//
// Usage:
// fd := os.Stdout.Fd()
// fd := uintptr(syscall.Stdout) // for windows
// IsTerminal(fd)
func IsTerminal(fd uintptr) bool {
initKernel32Proc()
var st uint32
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
return r != 0 && e == 0
}

9
vendor/github.com/gookit/color/go.mod generated vendored Normal file
View File

@ -0,0 +1,9 @@
module github.com/gookit/color
go 1.12
require (
github.com/stretchr/testify v1.6.1
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
)

15
vendor/github.com/gookit/color/go.sum generated vendored Normal file
View File

@ -0,0 +1,15 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

122
vendor/github.com/gookit/color/printer.go generated vendored Normal file
View File

@ -0,0 +1,122 @@
package color
import "fmt"
/*************************************************************
* colored message Printer
*************************************************************/
// PrinterFace interface
type PrinterFace interface {
fmt.Stringer
Sprint(a ...interface{}) string
Sprintf(format string, a ...interface{}) string
Print(a ...interface{})
Printf(format string, a ...interface{})
Println(a ...interface{})
}
// Printer a generic color message printer.
//
// Usage:
// p := &Printer{Code: "32;45;3"}
// p.Print("message")
type Printer struct {
// NoColor disable color.
NoColor bool
// Code color code string. eg "32;45;3"
Code string
}
// NewPrinter instance
func NewPrinter(colorCode string) *Printer {
return &Printer{Code: colorCode}
}
// String returns color code string. eg: "32;45;3"
func (p *Printer) String() string {
// panic("implement me")
return p.Code
}
// Sprint returns rendering colored messages
func (p *Printer) Sprint(a ...interface{}) string {
return RenderCode(p.String(), a...)
}
// Sprintf returns format and rendering colored messages
func (p *Printer) Sprintf(format string, a ...interface{}) string {
return RenderString(p.String(), fmt.Sprintf(format, a...))
}
// Print rendering colored messages
func (p *Printer) Print(a ...interface{}) {
doPrintV2(p.String(), fmt.Sprint(a...))
}
// Printf format and rendering colored messages
func (p *Printer) Printf(format string, a ...interface{}) {
doPrintV2(p.String(), fmt.Sprintf(format, a...))
}
// Println rendering colored messages with newline
func (p *Printer) Println(a ...interface{}) {
doPrintlnV2(p.Code, a)
}
// IsEmpty color code
func (p *Printer) IsEmpty() bool {
return p.Code == ""
}
/*************************************************************
* SimplePrinter struct
*************************************************************/
// SimplePrinter use for quick use color print on inject to struct
type SimplePrinter struct{}
// Print message
func (s *SimplePrinter) Print(v ...interface{}) {
Print(v...)
}
// Printf message
func (s *SimplePrinter) Printf(format string, v ...interface{}) {
Printf(format, v...)
}
// Println message
func (s *SimplePrinter) Println(v ...interface{}) {
Println(v...)
}
// Infof message
func (s *SimplePrinter) Infof(format string, a ...interface{}) {
Info.Printf(format, a...)
}
// Infoln message
func (s *SimplePrinter) Infoln(a ...interface{}) {
Info.Println(a...)
}
// Warnf message
func (s *SimplePrinter) Warnf(format string, a ...interface{}) {
Warn.Printf(format, a...)
}
// Warnln message
func (s *SimplePrinter) Warnln(a ...interface{}) {
Warn.Println(a...)
}
// Errorf message
func (s *SimplePrinter) Errorf(format string, a ...interface{}) {
Error.Printf(format, a...)
}
// Errorln message
func (s *SimplePrinter) Errorln(a ...interface{}) {
Error.Println(a...)
}

109
vendor/github.com/gookit/color/quickstart.go generated vendored Normal file
View File

@ -0,0 +1,109 @@
package color
/*************************************************************
* quick use color print message
*************************************************************/
// Redp print message with Red color
func Redp(a ...interface{}) {
Red.Print(a...)
}
// Redln print message line with Red color
func Redln(a ...interface{}) {
Red.Println(a...)
}
// Bluep print message with Blue color
func Bluep(a ...interface{}) {
Blue.Print(a...)
}
// Blueln print message line with Blue color
func Blueln(a ...interface{}) {
Blue.Println(a...)
}
// Cyanp print message with Cyan color
func Cyanp(a ...interface{}) {
Cyan.Print(a...)
}
// Cyanln print message line with Cyan color
func Cyanln(a ...interface{}) {
Cyan.Println(a...)
}
// Grayp print message with Gray color
func Grayp(a ...interface{}) {
Gray.Print(a...)
}
// Grayln print message line with Gray color
func Grayln(a ...interface{}) {
Gray.Println(a...)
}
// Greenp print message with Green color
func Greenp(a ...interface{}) {
Green.Print(a...)
}
// Greenln print message line with Green color
func Greenln(a ...interface{}) {
Green.Println(a...)
}
// Yellowp print message with Yellow color
func Yellowp(a ...interface{}) {
Yellow.Print(a...)
}
// Yellowln print message line with Yellow color
func Yellowln(a ...interface{}) {
Yellow.Println(a...)
}
// Magentap print message with Magenta color
func Magentap(a ...interface{}) {
Magenta.Print(a...)
}
// Magentaln print message line with Magenta color
func Magentaln(a ...interface{}) {
Magenta.Println(a...)
}
/*************************************************************
* quick use style print message
*************************************************************/
// Infof print message with Info style
func Infof(format string, a ...interface{}) {
Info.Printf(format, a...)
}
// Infoln print message with Info style
func Infoln(a ...interface{}) {
Info.Println(a...)
}
// Errorf print message with Error style
func Errorf(format string, a ...interface{}) {
Error.Printf(format, a...)
}
// Errorln print message with Error style
func Errorln(a ...interface{}) {
Error.Println(a...)
}
// Warnf print message with Warn style
func Warnf(format string, a ...interface{}) {
Warn.Printf(format, a...)
}
// Warnln print message with Warn style
func Warnln(a ...interface{}) {
Warn.Println(a...)
}

315
vendor/github.com/gookit/color/style.go generated vendored Normal file
View File

@ -0,0 +1,315 @@
package color
import (
"fmt"
"strings"
)
/*************************************************************
* 16 color Style
*************************************************************/
// Style a 16 color style. can add: fg color, bg color, color options
//
// Example:
// color.Style{color.FgGreen}.Print("message")
type Style []Color
// New create a custom style
//
// Usage:
// color.New(color.FgGreen).Print("message")
// equals to:
// color.Style{color.FgGreen}.Print("message")
func New(colors ...Color) Style {
return colors
}
// Save to global styles map
func (s Style) Save(name string) {
AddStyle(name, s)
}
// Add to global styles map
func (s *Style) Add(cs ...Color) {
*s = append(*s, cs...)
}
// Render render text
// Usage:
// color.New(color.FgGreen).Render("text")
// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text")
func (s Style) Render(a ...interface{}) string {
return RenderCode(s.String(), a...)
}
// Renderln render text line.
// like Println, will add spaces for each argument
// Usage:
// color.New(color.FgGreen).Renderln("text", "more")
// color.New(color.FgGreen, color.BgBlack, color.OpBold).Render("text", "more")
func (s Style) Renderln(a ...interface{}) string {
return RenderWithSpaces(s.String(), a...)
}
// Sprint is alias of the 'Render'
func (s Style) Sprint(a ...interface{}) string {
return RenderCode(s.String(), a...)
}
// Sprintf format and render message.
func (s Style) Sprintf(format string, a ...interface{}) string {
return RenderString(s.String(), fmt.Sprintf(format, a...))
}
// Print render and Print text
func (s Style) Print(a ...interface{}) {
doPrintV2(s.String(), fmt.Sprint(a...))
}
// Printf render and print text
func (s Style) Printf(format string, a ...interface{}) {
doPrintV2(s.Code(), fmt.Sprintf(format, a...))
}
// Println render and print text line
func (s Style) Println(a ...interface{}) {
doPrintlnV2(s.String(), a)
}
// Code convert to code string. returns like "32;45;3"
func (s Style) Code() string {
return s.String()
}
// String convert to code string. returns like "32;45;3"
func (s Style) String() string {
return Colors2code(s...)
}
// IsEmpty style
func (s Style) IsEmpty() bool {
return len(s) == 0
}
/*************************************************************
* Theme(extended Style)
*************************************************************/
// Theme definition. extends from Style
type Theme struct {
// Name theme name
Name string
// Style for the theme
Style
}
// NewTheme instance
func NewTheme(name string, style Style) *Theme {
return &Theme{name, style}
}
// Save to themes map
func (t *Theme) Save() {
AddTheme(t.Name, t.Style)
}
// Tips use name as title, only apply style for name
func (t *Theme) Tips(format string, a ...interface{}) {
// only apply style for name
t.Print(strings.ToUpper(t.Name) + ": ")
Printf(format+"\n", a...)
}
// Prompt use name as title, and apply style for message
func (t *Theme) Prompt(format string, a ...interface{}) {
title := strings.ToUpper(t.Name) + ":"
t.Println(title, fmt.Sprintf(format, a...))
}
// Block like Prompt, but will wrap a empty line
func (t *Theme) Block(format string, a ...interface{}) {
title := strings.ToUpper(t.Name) + ":\n"
t.Println(title, fmt.Sprintf(format, a...))
}
/*************************************************************
* Theme: internal themes
*************************************************************/
// internal themes(like bootstrap style)
// Usage:
// color.Info.Print("message")
// color.Info.Printf("a %s message", "test")
// color.Warn.Println("message")
// color.Error.Println("message")
var (
// Info color style
Info = &Theme{"info", Style{OpReset, FgGreen}}
// Note color style
Note = &Theme{"note", Style{OpBold, FgLightCyan}}
// Warn color style
Warn = &Theme{"warning", Style{OpBold, FgYellow}}
// Light color style
Light = &Theme{"light", Style{FgLightWhite, BgBlack}}
// Error color style
Error = &Theme{"error", Style{FgLightWhite, BgRed}}
// Danger color style
Danger = &Theme{"danger", Style{OpBold, FgRed}}
// Debug color style
Debug = &Theme{"debug", Style{OpReset, FgCyan}}
// Notice color style
Notice = &Theme{"notice", Style{OpBold, FgCyan}}
// Comment color style
Comment = &Theme{"comment", Style{OpReset, FgYellow}}
// Success color style
Success = &Theme{"success", Style{OpBold, FgGreen}}
// Primary color style
Primary = &Theme{"primary", Style{OpReset, FgBlue}}
// Question color style
Question = &Theme{"question", Style{OpReset, FgMagenta}}
// Secondary color style
Secondary = &Theme{"secondary", Style{FgDarkGray}}
)
// Themes internal defined themes.
// Usage:
// color.Themes["info"].Println("message")
var Themes = map[string]*Theme{
"info": Info,
"note": Note,
"light": Light,
"error": Error,
"debug": Debug,
"danger": Danger,
"notice": Notice,
"success": Success,
"comment": Comment,
"primary": Primary,
"warning": Warn,
"question": Question,
"secondary": Secondary,
}
// AddTheme add a theme and style
func AddTheme(name string, style Style) {
Themes[name] = NewTheme(name, style)
Styles[name] = style
}
// GetTheme get defined theme by name
func GetTheme(name string) *Theme {
return Themes[name]
}
/*************************************************************
* internal styles
*************************************************************/
// Styles internal defined styles, like bootstrap styles.
// Usage:
// color.Styles["info"].Println("message")
var Styles = map[string]Style{
"info": {OpReset, FgGreen},
"note": {OpBold, FgLightCyan},
"light": {FgLightWhite, BgRed},
"error": {FgLightWhite, BgRed},
"danger": {OpBold, FgRed},
"notice": {OpBold, FgCyan},
"success": {OpBold, FgGreen},
"comment": {OpReset, FgMagenta},
"primary": {OpReset, FgBlue},
"warning": {OpBold, FgYellow},
"question": {OpReset, FgMagenta},
"secondary": {FgDarkGray},
}
// some style name alias
var styleAliases = map[string]string{
"err": "error",
"suc": "success",
"warn": "warning",
}
// AddStyle add a style
func AddStyle(name string, s Style) {
Styles[name] = s
}
// GetStyle get defined style by name
func GetStyle(name string) Style {
if s, ok := Styles[name]; ok {
return s
}
if realName, ok := styleAliases[name]; ok {
return Styles[realName]
}
// empty style
return New()
}
/*************************************************************
* color scheme
*************************************************************/
// Scheme struct
type Scheme struct {
Name string
Styles map[string]Style
}
// NewScheme create new Scheme
func NewScheme(name string, styles map[string]Style) *Scheme {
return &Scheme{Name: name, Styles: styles}
}
// NewDefaultScheme create an defuault color Scheme
func NewDefaultScheme(name string) *Scheme {
return NewScheme(name, map[string]Style{
"info": {OpReset, FgGreen},
"warn": {OpBold, FgYellow},
"error": {FgLightWhite, BgRed},
})
}
// Style get by name
func (s *Scheme) Style(name string) Style {
return s.Styles[name]
}
// Infof message print
func (s *Scheme) Infof(format string, a ...interface{}) {
s.Styles["info"].Printf(format, a...)
}
// Infoln message print
func (s *Scheme) Infoln(v ...interface{}) {
s.Styles["info"].Println(v...)
}
// Warnf message print
func (s *Scheme) Warnf(format string, a ...interface{}) {
s.Styles["warn"].Printf(format, a...)
}
// Warnln message print
func (s *Scheme) Warnln(v ...interface{}) {
s.Styles["warn"].Println(v...)
}
// Errorf message print
func (s *Scheme) Errorf(format string, a ...interface{}) {
s.Styles["error"].Printf(format, a...)
}
// Errorln message print
func (s *Scheme) Errorln(v ...interface{}) {
s.Styles["error"].Println(v...)
}

206
vendor/github.com/gookit/color/utils.go generated vendored Normal file
View File

@ -0,0 +1,206 @@
package color
import (
"fmt"
"io"
"log"
"strings"
)
// SetTerminal by given code.
func SetTerminal(code string) error {
if !Enable || !SupportColor() {
return nil
}
_, err := fmt.Fprintf(output, SettingTpl, code)
return err
}
// ResetTerminal terminal setting.
func ResetTerminal() error {
if !Enable || !SupportColor() {
return nil
}
_, err := fmt.Fprint(output, ResetSet)
return err
}
/*************************************************************
* print methods(will auto parse color tags)
*************************************************************/
// Print render color tag and print messages
func Print(a ...interface{}) {
Fprint(output, a...)
}
// Printf format and print messages
func Printf(format string, a ...interface{}) {
Fprintf(output, format, a...)
}
// Println messages with new line
func Println(a ...interface{}) {
Fprintln(output, a...)
}
// Fprint print rendered messages to writer
// Notice: will ignore print error
func Fprint(w io.Writer, a ...interface{}) {
_, err := fmt.Fprint(w, Render(a...))
saveInternalError(err)
// if isLikeInCmd {
// renderColorCodeOnCmd(func() {
// _, _ = fmt.Fprint(w, Render(a...))
// })
// } else {
// _, _ = fmt.Fprint(w, Render(a...))
// }
}
// Fprintf print format and rendered messages to writer.
// Notice: will ignore print error
func Fprintf(w io.Writer, format string, a ...interface{}) {
str := fmt.Sprintf(format, a...)
_, err := fmt.Fprint(w, ReplaceTag(str))
saveInternalError(err)
}
// Fprintln print rendered messages line to writer
// Notice: will ignore print error
func Fprintln(w io.Writer, a ...interface{}) {
str := formatArgsForPrintln(a)
_, err := fmt.Fprintln(w, ReplaceTag(str))
saveInternalError(err)
}
// Lprint passes colored messages to a log.Logger for printing.
// Notice: should be goroutine safe
func Lprint(l *log.Logger, a ...interface{}) {
l.Print(Render(a...))
}
// Render parse color tags, return rendered string.
// Usage:
// text := Render("<info>hello</> <cyan>world</>!")
// fmt.Println(text)
func Render(a ...interface{}) string {
if len(a) == 0 {
return ""
}
return ReplaceTag(fmt.Sprint(a...))
}
// Sprint parse color tags, return rendered string
func Sprint(a ...interface{}) string {
if len(a) == 0 {
return ""
}
return ReplaceTag(fmt.Sprint(a...))
}
// Sprintf format and return rendered string
func Sprintf(format string, a ...interface{}) string {
return ReplaceTag(fmt.Sprintf(format, a...))
}
// String alias of the ReplaceTag
func String(s string) string {
return ReplaceTag(s)
}
// Text alias of the ReplaceTag
func Text(s string) string {
return ReplaceTag(s)
}
/*************************************************************
* helper methods for print
*************************************************************/
// new implementation, support render full color code on pwsh.exe, cmd.exe
func doPrintV2(code, str string) {
_, err := fmt.Fprint(output, RenderString(code, str))
saveInternalError(err)
// if isLikeInCmd {
// renderColorCodeOnCmd(func() {
// _, _ = fmt.Fprint(output, RenderString(code, str))
// })
// } else {
// _, _ = fmt.Fprint(output, RenderString(code, str))
// }
}
// new implementation, support render full color code on pwsh.exe, cmd.exe
func doPrintlnV2(code string, args []interface{}) {
str := formatArgsForPrintln(args)
_, err := fmt.Fprintln(output, RenderString(code, str))
saveInternalError(err)
}
// if use Println, will add spaces for each arg
func formatArgsForPrintln(args []interface{}) (message string) {
if ln := len(args); ln == 0 {
message = ""
} else if ln == 1 {
message = fmt.Sprint(args[0])
} else {
message = fmt.Sprintln(args...)
// clear last "\n"
message = message[:len(message)-1]
}
return
}
/*************************************************************
* helper methods
*************************************************************/
// is on debug mode
// func isDebugMode() bool {
// return debugMode == "on"
// }
func debugf(f string, v ...interface{}) {
if debugMode {
fmt.Print("COLOR_DEBUG: ")
fmt.Printf(f, v...)
fmt.Println()
}
}
// equals: return ok ? val1 : val2
func compareVal(ok bool, val1, val2 uint8) uint8 {
if ok {
return val1
}
return val2
}
func saveInternalError(err error) {
if err != nil {
debugf("inner error: %s", err.Error())
innerErrs = append(innerErrs, err)
}
}
func stringToArr(str, sep string) (arr []string) {
str = strings.TrimSpace(str)
if str == "" {
return
}
ss := strings.Split(str, sep)
for _, val := range ss {
if val = strings.TrimSpace(val); val != "" {
arr = append(arr, val)
}
}
return
}

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2012-2018 Mat Ryer and Tyler Bunnell
Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -5,20 +5,28 @@ import (
"reflect"
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
type CompareType int
const (
compareLess CompareType = iota - 1
compareEqual
compareGreater
)
func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) {
switch kind {
case reflect.Int:
{
intobj1 := obj1.(int)
intobj2 := obj2.(int)
if intobj1 > intobj2 {
return -1, true
return compareGreater, true
}
if intobj1 == intobj2 {
return 0, true
return compareEqual, true
}
if intobj1 < intobj2 {
return 1, true
return compareLess, true
}
}
case reflect.Int8:
@ -26,13 +34,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
int8obj1 := obj1.(int8)
int8obj2 := obj2.(int8)
if int8obj1 > int8obj2 {
return -1, true
return compareGreater, true
}
if int8obj1 == int8obj2 {
return 0, true
return compareEqual, true
}
if int8obj1 < int8obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Int16:
@ -40,13 +48,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
int16obj1 := obj1.(int16)
int16obj2 := obj2.(int16)
if int16obj1 > int16obj2 {
return -1, true
return compareGreater, true
}
if int16obj1 == int16obj2 {
return 0, true
return compareEqual, true
}
if int16obj1 < int16obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Int32:
@ -54,13 +62,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
int32obj1 := obj1.(int32)
int32obj2 := obj2.(int32)
if int32obj1 > int32obj2 {
return -1, true
return compareGreater, true
}
if int32obj1 == int32obj2 {
return 0, true
return compareEqual, true
}
if int32obj1 < int32obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Int64:
@ -68,13 +76,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
int64obj1 := obj1.(int64)
int64obj2 := obj2.(int64)
if int64obj1 > int64obj2 {
return -1, true
return compareGreater, true
}
if int64obj1 == int64obj2 {
return 0, true
return compareEqual, true
}
if int64obj1 < int64obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Uint:
@ -82,13 +90,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
uintobj1 := obj1.(uint)
uintobj2 := obj2.(uint)
if uintobj1 > uintobj2 {
return -1, true
return compareGreater, true
}
if uintobj1 == uintobj2 {
return 0, true
return compareEqual, true
}
if uintobj1 < uintobj2 {
return 1, true
return compareLess, true
}
}
case reflect.Uint8:
@ -96,13 +104,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
uint8obj1 := obj1.(uint8)
uint8obj2 := obj2.(uint8)
if uint8obj1 > uint8obj2 {
return -1, true
return compareGreater, true
}
if uint8obj1 == uint8obj2 {
return 0, true
return compareEqual, true
}
if uint8obj1 < uint8obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Uint16:
@ -110,13 +118,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
uint16obj1 := obj1.(uint16)
uint16obj2 := obj2.(uint16)
if uint16obj1 > uint16obj2 {
return -1, true
return compareGreater, true
}
if uint16obj1 == uint16obj2 {
return 0, true
return compareEqual, true
}
if uint16obj1 < uint16obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Uint32:
@ -124,13 +132,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
uint32obj1 := obj1.(uint32)
uint32obj2 := obj2.(uint32)
if uint32obj1 > uint32obj2 {
return -1, true
return compareGreater, true
}
if uint32obj1 == uint32obj2 {
return 0, true
return compareEqual, true
}
if uint32obj1 < uint32obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Uint64:
@ -138,13 +146,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
uint64obj1 := obj1.(uint64)
uint64obj2 := obj2.(uint64)
if uint64obj1 > uint64obj2 {
return -1, true
return compareGreater, true
}
if uint64obj1 == uint64obj2 {
return 0, true
return compareEqual, true
}
if uint64obj1 < uint64obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Float32:
@ -152,13 +160,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
float32obj1 := obj1.(float32)
float32obj2 := obj2.(float32)
if float32obj1 > float32obj2 {
return -1, true
return compareGreater, true
}
if float32obj1 == float32obj2 {
return 0, true
return compareEqual, true
}
if float32obj1 < float32obj2 {
return 1, true
return compareLess, true
}
}
case reflect.Float64:
@ -166,13 +174,13 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
float64obj1 := obj1.(float64)
float64obj2 := obj2.(float64)
if float64obj1 > float64obj2 {
return -1, true
return compareGreater, true
}
if float64obj1 == float64obj2 {
return 0, true
return compareEqual, true
}
if float64obj1 < float64obj2 {
return 1, true
return compareLess, true
}
}
case reflect.String:
@ -180,18 +188,18 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
stringobj1 := obj1.(string)
stringobj2 := obj2.(string)
if stringobj1 > stringobj2 {
return -1, true
return compareGreater, true
}
if stringobj1 == stringobj2 {
return 0, true
return compareEqual, true
}
if stringobj1 < stringobj2 {
return 1, true
return compareLess, true
}
}
}
return 0, false
return compareEqual, false
}
// Greater asserts that the first element is greater than the second
@ -200,26 +208,7 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (int, bool) {
// assert.Greater(t, float64(2), float64(1))
// assert.Greater(t, "b", "a")
func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != -1 {
return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...)
}
return true
return compareTwoValues(t, e1, e2, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs)
}
// GreaterOrEqual asserts that the first element is greater than or equal to the second
@ -229,26 +218,7 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface
// assert.GreaterOrEqual(t, "b", "a")
// assert.GreaterOrEqual(t, "b", "b")
func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != -1 && res != 0 {
return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...)
}
return true
return compareTwoValues(t, e1, e2, []CompareType{compareGreater, compareEqual}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs)
}
// Less asserts that the first element is less than the second
@ -257,26 +227,7 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in
// assert.Less(t, float64(1), float64(2))
// assert.Less(t, "a", "b")
func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
e1Kind := reflect.ValueOf(e1).Kind()
e2Kind := reflect.ValueOf(e2).Kind()
if e1Kind != e2Kind {
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != 1 {
return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...)
}
return true
return compareTwoValues(t, e1, e2, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs)
}
// LessOrEqual asserts that the first element is less than or equal to the second
@ -286,6 +237,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{})
// assert.LessOrEqual(t, "a", "b")
// assert.LessOrEqual(t, "b", "b")
func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool {
return compareTwoValues(t, e1, e2, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs)
}
func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []CompareType, failMessage string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
@ -296,14 +251,24 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter
return Fail(t, "Elements should be the same type", msgAndArgs...)
}
res, isComparable := compare(e1, e2, e1Kind)
compareResult, isComparable := compare(e1, e2, e1Kind)
if !isComparable {
return Fail(t, fmt.Sprintf("Can not compare type \"%s\"", reflect.TypeOf(e1)), msgAndArgs...)
}
if res != 1 && res != 0 {
return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...)
if !containsValue(allowedComparesResults, compareResult) {
return Fail(t, fmt.Sprintf(failMessage, e1, e2), msgAndArgs...)
}
return true
}
func containsValue(values []CompareType, value CompareType) bool {
for _, v := range values {
if v == value {
return true
}
}
return false
}

View File

@ -32,7 +32,8 @@ func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args
return Contains(t, s, contains, append([]interface{}{msg}, args...)...)
}
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
// DirExistsf checks whether a directory exists in the given path. It also fails
// if the path is a file rather a directory or there is an error checking whether it exists.
func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -92,7 +93,7 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
// EqualValuesf asserts that two objects are equal or convertable to the same types
// and equal.
//
// assert.EqualValuesf(t, uint32(123, "error message %s", "formatted"), int32(123))
// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted")
func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -126,7 +127,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick
// Exactlyf asserts that two objects are equal in value and type.
//
// assert.Exactlyf(t, int32(123, "error message %s", "formatted"), int64(123))
// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted")
func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -160,7 +161,8 @@ func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool {
return False(t, value, append([]interface{}{msg}, args...)...)
}
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
// FileExistsf checks whether a file exists in the given path. It also fails if
// the path points to a directory or there is an error when trying to check the file.
func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -171,7 +173,7 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool
// Greaterf asserts that the first element is greater than the second
//
// assert.Greaterf(t, 2, 1, "error message %s", "formatted")
// assert.Greaterf(t, float64(2, "error message %s", "formatted"), float64(1))
// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted")
// assert.Greaterf(t, "b", "a", "error message %s", "formatted")
func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -223,7 +225,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u
//
// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
// Returns whether the assertion was successful (true) or not (false).
func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -235,7 +237,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string,
//
// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
// Returns whether the assertion was successful (true) or not (false).
func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -243,6 +245,18 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri
return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...)
}
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
//
// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...)
}
// HTTPSuccessf asserts that a specified handler returns a success status code.
//
// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted")
@ -257,7 +271,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin
// Implementsf asserts that an object is implemented by the specified interface.
//
// assert.Implementsf(t, (*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -267,7 +281,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms
// InDeltaf asserts that the two numerals are within delta of each other.
//
// assert.InDeltaf(t, math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -325,14 +339,6 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int
return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Lenf asserts that the specified object has specific length.
// Lenf also fails if the object has a type that len() not accept.
//
@ -347,7 +353,7 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf
// Lessf asserts that the first element is less than the second
//
// assert.Lessf(t, 1, 2, "error message %s", "formatted")
// assert.Lessf(t, float64(1, "error message %s", "formatted"), float64(2))
// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted")
// assert.Lessf(t, "a", "b", "error message %s", "formatted")
func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -369,6 +375,17 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args .
return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...)
}
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...)
}
// Nilf asserts that the specified object is nil.
//
// assert.Nilf(t, err, "error message %s", "formatted")
@ -379,6 +396,15 @@ func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool
return Nil(t, object, append([]interface{}{msg}, args...)...)
}
// NoDirExistsf checks whether a directory does not exist in the given path.
// It fails if the path points to an existing _directory_ only.
func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoDirExists(t, path, append([]interface{}{msg}, args...)...)
}
// NoErrorf asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
@ -392,6 +418,15 @@ func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool {
return NoError(t, err, append([]interface{}{msg}, args...)...)
}
// NoFileExistsf checks whether a file does not exist in a given path. It fails
// if the path points to an existing _file_ only.
func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NoFileExists(t, path, append([]interface{}{msg}, args...)...)
}
// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
@ -431,6 +466,16 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string,
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
//
// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted")
func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotNilf asserts that the specified object is not nil.
//
// assert.NotNilf(t, err, "error message %s", "formatted")
@ -453,7 +498,7 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo
// NotRegexpf asserts that a specified regexp does not match a string.
//
// assert.NotRegexpf(t, regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted")
func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -462,6 +507,19 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ..
return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...)
}
// NotSamef asserts that two pointers do not reference the same object.
//
// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted")
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// NotSubsetf asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
@ -491,6 +549,18 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool
return Panics(t, f, append([]interface{}{msg}, args...)...)
}
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc
// panics, and that the recovered panic value is an error that satisfies the
// EqualError comparison.
//
// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...)
}
// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
@ -504,7 +574,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str
// Regexpf asserts that a specified regexp matches a string.
//
// assert.Regexpf(t, regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted")
func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
@ -557,6 +627,14 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim
return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...)
}
// Zerof asserts that i is the zero value for its type.
func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool {
if h, ok := t.(tHelper); ok {

View File

@ -53,7 +53,8 @@ func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string,
return Containsf(a.t, s, contains, msg, args...)
}
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
// DirExists checks whether a directory exists in the given path. It also fails
// if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -61,7 +62,8 @@ func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool {
return DirExists(a.t, path, msgAndArgs...)
}
// DirExistsf checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
// DirExistsf checks whether a directory exists in the given path. It also fails
// if the path is a file rather a directory or there is an error checking whether it exists.
func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -167,7 +169,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn
// EqualValuesf asserts that two objects are equal or convertable to the same types
// and equal.
//
// a.EqualValuesf(uint32(123, "error message %s", "formatted"), int32(123))
// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted")
func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -249,7 +251,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg
// Exactlyf asserts that two objects are equal in value and type.
//
// a.Exactlyf(int32(123, "error message %s", "formatted"), int64(123))
// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted")
func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -309,7 +311,8 @@ func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool {
return Falsef(a.t, value, msg, args...)
}
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
// FileExists checks whether a file exists in the given path. It also fails if
// the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -317,7 +320,8 @@ func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool {
return FileExists(a.t, path, msgAndArgs...)
}
// FileExistsf checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
// FileExistsf checks whether a file exists in the given path. It also fails if
// the path points to a directory or there is an error when trying to check the file.
func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -366,7 +370,7 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string,
// Greaterf asserts that the first element is greater than the second
//
// a.Greaterf(2, 1, "error message %s", "formatted")
// a.Greaterf(float64(2, "error message %s", "formatted"), float64(1))
// a.Greaterf(float64(2), float64(1), "error message %s", "formatted")
// a.Greaterf("b", "a", "error message %s", "formatted")
func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
@ -443,7 +447,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri
//
// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -467,7 +471,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s
//
// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
//
// Returns whether the assertion was successful (true, "error message %s", "formatted") or not (false).
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -475,6 +479,30 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url
return HTTPRedirectf(a.t, handler, method, url, values, msg, args...)
}
// HTTPStatusCode asserts that a specified handler returns a specified status code.
//
// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501)
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...)
}
// HTTPStatusCodef asserts that a specified handler returns a specified status code.
//
// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted")
//
// Returns whether the assertion was successful (true) or not (false).
func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...)
}
// HTTPSuccess asserts that a specified handler returns a success status code.
//
// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
@ -511,7 +539,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{},
// Implementsf asserts that an object is implemented by the specified interface.
//
// a.Implementsf((*MyInterface, "error message %s", "formatted")(nil), new(MyObject))
// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted")
func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -521,7 +549,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}
// InDelta asserts that the two numerals are within delta of each other.
//
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
// a.InDelta(math.Pi, 22/7.0, 0.01)
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -563,7 +591,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del
// InDeltaf asserts that the two numerals are within delta of each other.
//
// a.InDeltaf(math.Pi, (22 / 7.0, "error message %s", "formatted"), 0.01)
// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted")
func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
@ -639,22 +667,6 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ..
return JSONEqf(a.t, expected, actual, msg, args...)
}
// YAMLEq asserts that two YAML strings are equivalent.
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return YAMLEq(a.t, expected, actual, msgAndArgs...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return YAMLEqf(a.t, expected, actual, msg, args...)
}
// Len asserts that the specified object has specific length.
// Len also fails if the object has a type that len() not accept.
//
@ -718,7 +730,7 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar
// Lessf asserts that the first element is less than the second
//
// a.Lessf(1, 2, "error message %s", "formatted")
// a.Lessf(float64(1, "error message %s", "formatted"), float64(2))
// a.Lessf(float64(1), float64(2), "error message %s", "formatted")
// a.Lessf("a", "b", "error message %s", "formatted")
func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
@ -727,6 +739,28 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i
return Lessf(a.t, e1, e2, msg, args...)
}
// Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond)
func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Never(a.t, condition, waitFor, tick, msgAndArgs...)
}
// Neverf asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted")
func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return Neverf(a.t, condition, waitFor, tick, msg, args...)
}
// Nil asserts that the specified object is nil.
//
// a.Nil(err)
@ -747,6 +781,24 @@ func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) b
return Nilf(a.t, object, msg, args...)
}
// NoDirExists checks whether a directory does not exist in the given path.
// It fails if the path points to an existing _directory_ only.
func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoDirExists(a.t, path, msgAndArgs...)
}
// NoDirExistsf checks whether a directory does not exist in the given path.
// It fails if the path points to an existing _directory_ only.
func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoDirExistsf(a.t, path, msg, args...)
}
// NoError asserts that a function returned no error (i.e. `nil`).
//
// actualObj, err := SomeFunction()
@ -773,6 +825,24 @@ func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool {
return NoErrorf(a.t, err, msg, args...)
}
// NoFileExists checks whether a file does not exist in a given path. It fails
// if the path points to an existing _file_ only.
func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoFileExists(a.t, path, msgAndArgs...)
}
// NoFileExistsf checks whether a file does not exist in a given path. It fails
// if the path points to an existing _file_ only.
func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NoFileExistsf(a.t, path, msg, args...)
}
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
// specified substring or element.
//
@ -838,6 +908,26 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr
return NotEqual(a.t, expected, actual, msgAndArgs...)
}
// NotEqualValues asserts that two objects are not equal even when converted to the same type
//
// a.NotEqualValues(obj1, obj2)
func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEqualValues(a.t, expected, actual, msgAndArgs...)
}
// NotEqualValuesf asserts that two objects are not equal even when converted to the same type
//
// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted")
func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotEqualValuesf(a.t, expected, actual, msg, args...)
}
// NotEqualf asserts that the specified values are NOT equal.
//
// a.NotEqualf(obj1, obj2, "error message %s", "formatted")
@ -904,7 +994,7 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in
// NotRegexpf asserts that a specified regexp does not match a string.
//
// a.NotRegexpf(regexp.MustCompile("starts", "error message %s", "formatted"), "it's starting")
// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted")
// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted")
func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
@ -913,6 +1003,32 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg
return NotRegexpf(a.t, rx, str, msg, args...)
}
// NotSame asserts that two pointers do not reference the same object.
//
// a.NotSame(ptr1, ptr2)
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotSame(a.t, expected, actual, msgAndArgs...)
}
// NotSamef asserts that two pointers do not reference the same object.
//
// a.NotSamef(ptr1, ptr2, "error message %s", "formatted")
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return NotSamef(a.t, expected, actual, msg, args...)
}
// NotSubset asserts that the specified list(array, slice...) contains not all
// elements given in the specified subset(array, slice...).
//
@ -961,6 +1077,30 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
return Panics(a.t, f, msgAndArgs...)
}
// PanicsWithError asserts that the code inside the specified PanicTestFunc
// panics, and that the recovered panic value is an error that satisfies the
// EqualError comparison.
//
// a.PanicsWithError("crazy error", func(){ GoCrazy() })
func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return PanicsWithError(a.t, errString, f, msgAndArgs...)
}
// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc
// panics, and that the recovered panic value is an error that satisfies the
// EqualError comparison.
//
// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted")
func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return PanicsWithErrorf(a.t, errString, f, msg, args...)
}
// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that
// the recovered panic value equals the expected panic value.
//
@ -1006,7 +1146,7 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter
// Regexpf asserts that a specified regexp matches a string.
//
// a.Regexpf(regexp.MustCompile("start", "error message %s", "formatted"), "it's starting")
// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted")
// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted")
func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
@ -1103,6 +1243,22 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta
return WithinDurationf(a.t, expected, actual, delta, msg, args...)
}
// YAMLEq asserts that two YAML strings are equivalent.
func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return YAMLEq(a.t, expected, actual, msgAndArgs...)
}
// YAMLEqf asserts that two YAML strings are equivalent.
func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {
h.Helper()
}
return YAMLEqf(a.t, expected, actual, msg, args...)
}
// Zero asserts that i is the zero value for its type.
func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
if h, ok := a.t.(tHelper); ok {

View File

@ -11,6 +11,7 @@ import (
"reflect"
"regexp"
"runtime"
"runtime/debug"
"strings"
"time"
"unicode"
@ -18,10 +19,10 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/pmezard/go-difflib/difflib"
yaml "gopkg.in/yaml.v2"
yaml "gopkg.in/yaml.v3"
)
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_format.go.tmpl
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
// TestingT is an interface wrapper around *testing.T
type TestingT interface {
@ -44,7 +45,7 @@ type BoolAssertionFunc func(TestingT, bool, ...interface{}) bool
// for table driven tests.
type ErrorAssertionFunc func(TestingT, error, ...interface{}) bool
// Comparison a custom function that returns true on success and false on failure
// Comparison is a custom function that returns true on success and false on failure
type Comparison func() (success bool)
/*
@ -103,11 +104,11 @@ the problem actually occurred in calling code.*/
// failed.
func CallerInfo() []string {
pc := uintptr(0)
file := ""
line := 0
ok := false
name := ""
var pc uintptr
var ok bool
var file string
var line int
var name string
callers := []string{}
for i := 0; ; i++ {
@ -351,6 +352,19 @@ func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{})
}
// validateEqualArgs checks whether provided arguments can be safely used in the
// Equal/NotEqual functions.
func validateEqualArgs(expected, actual interface{}) error {
if expected == nil && actual == nil {
return nil
}
if isFunction(expected) || isFunction(actual) {
return errors.New("cannot take func type as argument")
}
return nil
}
// Same asserts that two pointers reference the same object.
//
// assert.Same(t, ptr1, ptr2)
@ -362,18 +376,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
h.Helper()
}
expectedPtr, actualPtr := reflect.ValueOf(expected), reflect.ValueOf(actual)
if expectedPtr.Kind() != reflect.Ptr || actualPtr.Kind() != reflect.Ptr {
return Fail(t, "Invalid operation: both arguments must be pointers", msgAndArgs...)
}
expectedType, actualType := reflect.TypeOf(expected), reflect.TypeOf(actual)
if expectedType != actualType {
return Fail(t, fmt.Sprintf("Pointer expected to be of type %v, but was %v",
expectedType, actualType), msgAndArgs...)
}
if expected != actual {
if !samePointers(expected, actual) {
return Fail(t, fmt.Sprintf("Not same: \n"+
"expected: %p %#v\n"+
"actual : %p %#v", expected, expected, actual, actual), msgAndArgs...)
@ -382,6 +385,42 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
return true
}
// NotSame asserts that two pointers do not reference the same object.
//
// assert.NotSame(t, ptr1, ptr2)
//
// Both arguments must be pointer variables. Pointer variable sameness is
// determined based on the equality of both type and value.
func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if samePointers(expected, actual) {
return Fail(t, fmt.Sprintf(
"Expected and actual point to the same object: %p %#v",
expected, expected), msgAndArgs...)
}
return true
}
// samePointers compares two generic interface objects and returns whether
// they point to the same object
func samePointers(first, second interface{}) bool {
firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second)
if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr {
return false
}
firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second)
if firstType != secondType {
return false
}
// compare pointer addresses
return first == second
}
// formatUnequalValues takes two values of arbitrary types and returns string
// representations appropriate to be presented to the user.
//
@ -390,12 +429,27 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b
// to a type conversion in the Go grammar.
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
return fmt.Sprintf("%T(%#v)", expected, expected),
fmt.Sprintf("%T(%#v)", actual, actual)
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)),
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual))
}
switch expected.(type) {
case time.Duration:
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
}
return truncatingFormat(expected), truncatingFormat(actual)
}
return fmt.Sprintf("%#v", expected),
fmt.Sprintf("%#v", actual)
// truncatingFormat formats the data and truncates it if it's too long.
//
// This helps keep formatted error messages lines from exceeding the
// bufio.MaxScanTokenSize max line length that the go testing framework imposes.
func truncatingFormat(data interface{}) string {
value := fmt.Sprintf("%#v", data)
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.
if len(value) > max {
value = value[0:max] + "<... truncated>"
}
return value
}
// EqualValues asserts that two objects are equal or convertable to the same types
@ -442,12 +496,12 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}
//
// assert.NotNil(t, err)
func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if !isNil(object) {
return true
}
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
}
@ -488,12 +542,12 @@ func isNil(object interface{}) bool {
//
// assert.Nil(t, err)
func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if isNil(object) {
return true
}
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
}
@ -530,12 +584,11 @@ func isEmpty(object interface{}) bool {
//
// assert.Empty(t, obj)
func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
pass := isEmpty(object)
if !pass {
if h, ok := t.(tHelper); ok {
h.Helper()
}
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
}
@ -550,12 +603,11 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
// assert.Equal(t, "two", obj[1])
// }
func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
pass := !isEmpty(object)
if !pass {
if h, ok := t.(tHelper); ok {
h.Helper()
}
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
}
@ -598,16 +650,10 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{})
//
// assert.True(t, myBool)
func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if h, ok := t.(interface {
Helper()
}); ok {
h.Helper()
}
if value != true {
if !value {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, "Should be true", msgAndArgs...)
}
@ -619,11 +665,10 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
//
// assert.False(t, myBool)
func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if value != false {
if value {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, "Should be false", msgAndArgs...)
}
@ -654,6 +699,21 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{
}
// NotEqualValues asserts that two objects are not equal even when converted to the same type
//
// assert.NotEqualValues(t, obj1, obj2)
func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if ObjectsAreEqualValues(expected, actual) {
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
}
return true
}
// containsElement try loop over the list check if the list includes the element.
// return (false, false) if impossible.
// return (true, false) if element was not found.
@ -706,10 +766,10 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo
ok, found := includeElement(s, contains)
if !ok {
return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...)
}
if !found {
return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
return Fail(t, fmt.Sprintf("%#v does not contain %#v", s, contains), msgAndArgs...)
}
return true
@ -840,27 +900,39 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
return true
}
aKind := reflect.TypeOf(listA).Kind()
bKind := reflect.TypeOf(listB).Kind()
if aKind != reflect.Array && aKind != reflect.Slice {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...)
if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) {
return false
}
if bKind != reflect.Array && bKind != reflect.Slice {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...)
extraA, extraB := diffLists(listA, listB)
if len(extraA) == 0 && len(extraB) == 0 {
return true
}
return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...)
}
// isList checks that the provided value is array or slice.
func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) {
kind := reflect.TypeOf(list).Kind()
if kind != reflect.Array && kind != reflect.Slice {
return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind),
msgAndArgs...)
}
return true
}
// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B.
// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and
// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored.
func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) {
aValue := reflect.ValueOf(listA)
bValue := reflect.ValueOf(listB)
aLen := aValue.Len()
bLen := bValue.Len()
if aLen != bLen {
return Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...)
}
// Mark indexes in bValue that we already used
visited := make([]bool, bLen)
for i := 0; i < aLen; i++ {
@ -877,11 +949,38 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface
}
}
if !found {
return Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...)
extraA = append(extraA, element)
}
}
return true
for j := 0; j < bLen; j++ {
if visited[j] {
continue
}
extraB = append(extraB, bValue.Index(j).Interface())
}
return
}
func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string {
var msg bytes.Buffer
msg.WriteString("elements differ")
if len(extraA) > 0 {
msg.WriteString("\n\nextra elements in list A:\n")
msg.WriteString(spewConfig.Sdump(extraA))
}
if len(extraB) > 0 {
msg.WriteString("\n\nextra elements in list B:\n")
msg.WriteString(spewConfig.Sdump(extraB))
}
msg.WriteString("\n\nlistA:\n")
msg.WriteString(spewConfig.Sdump(listA))
msg.WriteString("\n\nlistB:\n")
msg.WriteString(spewConfig.Sdump(listB))
return msg.String()
}
// Condition uses a Comparison to assert a complex condition.
@ -901,15 +1000,17 @@ func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
type PanicTestFunc func()
// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
func didPanic(f PanicTestFunc) (bool, interface{}) {
func didPanic(f PanicTestFunc) (bool, interface{}, string) {
didPanic := false
var message interface{}
var stack string
func() {
defer func() {
if message = recover(); message != nil {
didPanic = true
stack = string(debug.Stack())
}
}()
@ -918,7 +1019,7 @@ func didPanic(f PanicTestFunc) (bool, interface{}) {
}()
return didPanic, message
return didPanic, message, stack
}
@ -930,7 +1031,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
h.Helper()
}
if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
}
@ -946,12 +1047,34 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr
h.Helper()
}
funcDidPanic, panicValue := didPanic(f)
funcDidPanic, panicValue, panickedStack := didPanic(f)
if !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
}
if panicValue != expected {
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v", f, expected, panicValue), msgAndArgs...)
return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...)
}
return true
}
// PanicsWithError asserts that the code inside the specified PanicTestFunc
// panics, and that the recovered panic value is an error that satisfies the
// EqualError comparison.
//
// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() })
func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
funcDidPanic, panicValue, panickedStack := didPanic(f)
if !funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...)
}
panicErr, ok := panicValue.(error)
if !ok || panicErr.Error() != errString {
return Fail(t, fmt.Sprintf("func %#v should panic with error message:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, errString, panicValue, panickedStack), msgAndArgs...)
}
return true
@ -965,8 +1088,8 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
h.Helper()
}
if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v", f, panicValue), msgAndArgs...)
if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic {
return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...)
}
return true
@ -993,6 +1116,8 @@ func toFloat(x interface{}) (float64, bool) {
xok := true
switch xn := x.(type) {
case uint:
xf = float64(xn)
case uint8:
xf = float64(xn)
case uint16:
@ -1014,7 +1139,7 @@ func toFloat(x interface{}) (float64, bool) {
case float32:
xf = float64(xn)
case float64:
xf = float64(xn)
xf = xn
case time.Duration:
xf = float64(xn)
default:
@ -1026,7 +1151,7 @@ func toFloat(x interface{}) (float64, bool) {
// InDelta asserts that the two numerals are within delta of each other.
//
// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
// assert.InDelta(t, math.Pi, 22/7.0, 0.01)
func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -1128,6 +1253,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
if !aok {
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
}
if math.IsNaN(af) {
return 0, errors.New("expected value must not be NaN")
}
if af == 0 {
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
}
@ -1135,6 +1263,9 @@ func calcRelativeError(expected, actual interface{}) (float64, error) {
if !bok {
return 0, fmt.Errorf("actual value %q cannot be converted to float", actual)
}
if math.IsNaN(bf) {
return 0, errors.New("actual value must not be NaN")
}
return math.Abs(af-bf) / math.Abs(af), nil
}
@ -1144,6 +1275,9 @@ func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAnd
if h, ok := t.(tHelper); ok {
h.Helper()
}
if math.IsNaN(epsilon) {
return Fail(t, "epsilon must not be NaN")
}
actualEpsilon, err := calcRelativeError(expected, actual)
if err != nil {
return Fail(t, err.Error(), msgAndArgs...)
@ -1191,10 +1325,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m
// assert.Equal(t, expectedObj, actualObj)
// }
func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err != nil {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...)
}
@ -1208,11 +1342,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
// assert.Equal(t, expectedError, err)
// }
func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
if err == nil {
if h, ok := t.(tHelper); ok {
h.Helper()
}
return Fail(t, "An error is expected but got nil.", msgAndArgs...)
}
@ -1314,7 +1447,8 @@ func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
return true
}
// FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file.
// FileExists checks whether a file exists in the given path. It also fails if
// the path points to a directory or there is an error when trying to check the file.
func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -1332,7 +1466,24 @@ func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
return true
}
// DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists.
// NoFileExists checks whether a file does not exist in a given path. It fails
// if the path points to an existing _file_ only.
func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
info, err := os.Lstat(path)
if err != nil {
return true
}
if info.IsDir() {
return true
}
return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...)
}
// DirExists checks whether a directory exists in the given path. It also fails
// if the path is a file rather a directory or there is an error checking whether it exists.
func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
@ -1350,6 +1501,25 @@ func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
return true
}
// NoDirExists checks whether a directory does not exist in the given path.
// It fails if the path points to an existing _directory_ only.
func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
info, err := os.Lstat(path)
if err != nil {
if os.IsNotExist(err) {
return true
}
return true
}
if !info.IsDir() {
return true
}
return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...)
}
// JSONEq asserts that two JSON strings are equivalent.
//
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
@ -1439,15 +1609,6 @@ func diff(expected interface{}, actual interface{}) string {
return "\n\nDiff:\n" + diff
}
// validateEqualArgs checks whether provided arguments can be safely used in the
// Equal/NotEqual functions.
func validateEqualArgs(expected, actual interface{}) error {
if isFunction(expected) || isFunction(actual) {
return errors.New("cannot take func type as argument")
}
return nil
}
func isFunction(arg interface{}) bool {
if arg == nil {
return false
@ -1460,6 +1621,7 @@ var spewConfig = spew.ConfigState{
DisablePointerAddresses: true,
DisableCapacities: true,
SortKeys: true,
DisableMethods: true,
}
type tHelper interface {
@ -1475,24 +1637,59 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
h.Helper()
}
ch := make(chan bool, 1)
timer := time.NewTimer(waitFor)
ticker := time.NewTicker(tick)
checkPassed := make(chan bool)
defer timer.Stop()
ticker := time.NewTicker(tick)
defer ticker.Stop()
defer close(checkPassed)
for {
for tick := ticker.C; ; {
select {
case <-timer.C:
return Fail(t, "Condition never satisfied", msgAndArgs...)
case result := <-checkPassed:
if result {
case <-tick:
tick = nil
go func() { ch <- condition() }()
case v := <-ch:
if v {
return true
}
case <-ticker.C:
go func() {
checkPassed <- condition()
}()
tick = ticker.C
}
}
}
// Never asserts that the given condition doesn't satisfy in waitFor time,
// periodically checking the target function each tick.
//
// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond)
func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
ch := make(chan bool, 1)
timer := time.NewTimer(waitFor)
defer timer.Stop()
ticker := time.NewTicker(tick)
defer ticker.Stop()
for tick := ticker.C; ; {
select {
case <-timer.C:
return true
case <-tick:
tick = nil
go func() { ch <- condition() }()
case v := <-ch:
if v {
return Fail(t, "Condition satisfied", msgAndArgs...)
}
tick = ticker.C
}
}
}

View File

@ -13,4 +13,4 @@ func New(t TestingT) *Assertions {
}
}
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs
//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs"

View File

@ -33,7 +33,6 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent
@ -56,7 +55,6 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
@ -79,7 +77,6 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
return false
}
isErrorCode := code >= http.StatusBadRequest
@ -90,6 +87,28 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values
return isErrorCode
}
// HTTPStatusCode asserts that a specified handler returns a specified status code.
//
// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501)
//
// Returns whether the assertion was successful (true) or not (false).
func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool {
if h, ok := t.(tHelper); ok {
h.Helper()
}
code, err := httpCode(handler, method, url, values)
if err != nil {
Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err))
}
successful := code == statuscode
if !successful {
Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code))
}
return successful
}
// HTTPBody is a helper that returns HTTP body of the response. It returns
// empty string if building a new request fails.
func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {

9
vendor/github.com/xo/terminfo/.gitignore generated vendored Normal file
View File

@ -0,0 +1,9 @@
/.cache/
/cmd/infocmp/infocmp
/cmd/infocmp/.out/
/infocmp
/.out/
*.txt

21
vendor/github.com/xo/terminfo/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Anmol Sethi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

139
vendor/github.com/xo/terminfo/README.md generated vendored Normal file
View File

@ -0,0 +1,139 @@
# About terminfo [![GoDoc][1]][2]
Package `terminfo` provides a pure-Go implementation of reading information
from the terminfo database.
`terminfo` is meant as a replacement for `ncurses` in simple Go programs.
## Installing
Install in the usual Go way:
```sh
$ go get -u github.com/xo/terminfo
```
## Using
Please see the [GoDoc API listing][2] for more information on using `terminfo`.
```go
// _examples/simple/main.go
package main
import (
"bytes"
"fmt"
"log"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"github.com/xo/terminfo"
)
func main() {
//r := rand.New(nil)
// load terminfo
ti, err := terminfo.LoadFromEnv()
if err != nil {
log.Fatal(err)
}
// cleanup
defer func() {
err := recover()
termreset(ti)
if err != nil {
log.Fatal(err)
}
}()
terminit(ti)
termtitle(ti, "simple example!")
termputs(ti, 3, 3, "Ctrl-C to exit")
maxColors := termcolors(ti)
if maxColors > 256 {
maxColors = 256
}
for i := 0; i < maxColors; i++ {
termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█"))
}
// wait for signal
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
<-sigs
}
// terminit initializes the special CA mode on the terminal, and makes the
// cursor invisible.
func terminit(ti *terminfo.Terminfo) {
buf := new(bytes.Buffer)
// set the cursor invisible
ti.Fprintf(buf, terminfo.CursorInvisible)
// enter special mode
ti.Fprintf(buf, terminfo.EnterCaMode)
// clear the screen
ti.Fprintf(buf, terminfo.ClearScreen)
os.Stdout.Write(buf.Bytes())
}
// termreset is the inverse of terminit.
func termreset(ti *terminfo.Terminfo) {
buf := new(bytes.Buffer)
ti.Fprintf(buf, terminfo.ExitCaMode)
ti.Fprintf(buf, terminfo.CursorNormal)
os.Stdout.Write(buf.Bytes())
}
// termputs puts a string at row, col, interpolating v.
func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) {
buf := new(bytes.Buffer)
ti.Fprintf(buf, terminfo.CursorAddress, row, col)
fmt.Fprintf(buf, s, v...)
os.Stdout.Write(buf.Bytes())
}
// sl is the status line terminfo.
var sl *terminfo.Terminfo
// termtitle sets the window title.
func termtitle(ti *terminfo.Terminfo, s string) {
var once sync.Once
once.Do(func() {
if ti.Has(terminfo.HasStatusLine) {
return
}
// load the sl xterm if terminal is an xterm or has COLORTERM
if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" {
sl, _ = terminfo.Load("xterm+sl")
}
})
if sl != nil {
ti = sl
}
if !ti.Has(terminfo.HasStatusLine) {
return
}
buf := new(bytes.Buffer)
ti.Fprintf(buf, terminfo.ToStatusLine)
fmt.Fprint(buf, s)
ti.Fprintf(buf, terminfo.FromStatusLine)
os.Stdout.Write(buf.Bytes())
}
// termcolors returns the maximum colors available for the terminal.
func termcolors(ti *terminfo.Terminfo) int {
if colors := ti.Num(terminfo.MaxColors); colors > 0 {
return colors
}
return int(terminfo.ColorLevelBasic)
}
```
[1]: https://godoc.org/github.com/xo/terminfo?status.svg
[2]: https://godoc.org/github.com/xo/terminfo

33
vendor/github.com/xo/terminfo/caps.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
package terminfo
//go:generate go run gen.go
// BoolCapName returns the bool capability name.
func BoolCapName(i int) string {
return boolCapNames[2*i]
}
// BoolCapNameShort returns the short bool capability name.
func BoolCapNameShort(i int) string {
return boolCapNames[2*i+1]
}
// NumCapName returns the num capability name.
func NumCapName(i int) string {
return numCapNames[2*i]
}
// NumCapNameShort returns the short num capability name.
func NumCapNameShort(i int) string {
return numCapNames[2*i+1]
}
// StringCapName returns the string capability name.
func StringCapName(i int) string {
return stringCapNames[2*i]
}
// StringCapNameShort returns the short string capability name.
func StringCapNameShort(i int) string {
return stringCapNames[2*i+1]
}

2023
vendor/github.com/xo/terminfo/capvals.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

91
vendor/github.com/xo/terminfo/color.go generated vendored Normal file
View File

@ -0,0 +1,91 @@
package terminfo
import (
"os"
"strconv"
"strings"
)
// ColorLevel is the color level supported by a terminal.
type ColorLevel uint
// ColorLevel values.
const (
ColorLevelNone ColorLevel = iota
ColorLevelBasic
ColorLevelHundreds
ColorLevelMillions
)
// String satisfies the Stringer interface.
func (c ColorLevel) String() string {
switch c {
case ColorLevelBasic:
return "basic"
case ColorLevelHundreds:
return "hundreds"
case ColorLevelMillions:
return "millions"
}
return "none"
}
// ChromaFormatterName returns the github.com/alecthomas/chroma compatible
// formatter name for the color level.
func (c ColorLevel) ChromaFormatterName() string {
switch c {
case ColorLevelBasic:
return "terminal"
case ColorLevelHundreds:
return "terminal256"
case ColorLevelMillions:
return "terminal16m"
}
return "noop"
}
// ColorLevelFromEnv returns the color level COLORTERM, FORCE_COLOR,
// TERM_PROGRAM, or determined from the TERM environment variable.
func ColorLevelFromEnv() (ColorLevel, error) {
// check for overriding environment variables
colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR")
switch {
case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit") || termProg == "Hyper":
return ColorLevelMillions, nil
case colorTerm != "" || forceColor != "":
return ColorLevelBasic, nil
case termProg == "Apple_Terminal":
return ColorLevelHundreds, nil
case termProg == "iTerm.app":
ver := os.Getenv("TERM_PROGRAM_VERSION")
if ver == "" {
return ColorLevelHundreds, nil
}
i, err := strconv.Atoi(strings.Split(ver, ".")[0])
if err != nil {
return ColorLevelNone, ErrInvalidTermProgramVersion
}
if i == 3 {
return ColorLevelMillions, nil
}
return ColorLevelHundreds, nil
}
// otherwise determine from TERM's max_colors capability
if term := os.Getenv("TERM"); term != "" {
ti, err := Load(term)
if err != nil {
return ColorLevelNone, err
}
v, ok := ti.Nums[MaxColors]
switch {
case !ok || v <= 16:
return ColorLevelNone, nil
case ok && v >= 256:
return ColorLevelHundreds, nil
}
}
return ColorLevelBasic, nil
}

3
vendor/github.com/xo/terminfo/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/xo/terminfo
go 1.15

0
vendor/github.com/xo/terminfo/go.sum generated vendored Normal file
View File

72
vendor/github.com/xo/terminfo/load.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
package terminfo
import (
"os"
"os/user"
"path"
"strings"
"sync"
)
// termCache is the terminfo cache.
var termCache = struct {
db map[string]*Terminfo
sync.RWMutex
}{
db: make(map[string]*Terminfo),
}
// Load follows the behavior described in terminfo(5) to find correct the
// terminfo file using the name, reads the file and then returns a Terminfo
// struct that describes the file.
func Load(name string) (*Terminfo, error) {
if name == "" {
return nil, ErrEmptyTermName
}
termCache.RLock()
ti, ok := termCache.db[name]
termCache.RUnlock()
if ok {
return ti, nil
}
var checkDirs []string
// check $TERMINFO
if dir := os.Getenv("TERMINFO"); dir != "" {
checkDirs = append(checkDirs, dir)
}
// check $HOME/.terminfo
u, err := user.Current()
if err != nil {
return nil, err
}
checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo"))
// check $TERMINFO_DIRS
if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" {
checkDirs = append(checkDirs, strings.Split(dirs, ":")...)
}
// check fallback directories
checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo")
for _, dir := range checkDirs {
ti, err = Open(dir, name)
if err != nil && err != ErrFileNotFound && !os.IsNotExist(err) {
return nil, err
} else if err == nil {
return ti, nil
}
}
return nil, ErrDatabaseDirectoryNotFound
}
// LoadFromEnv loads the terminal info based on the name contained in
// environment variable TERM.
func LoadFromEnv() (*Terminfo, error) {
return Load(os.Getenv("TERM"))
}

490
vendor/github.com/xo/terminfo/param.go generated vendored Normal file
View File

@ -0,0 +1,490 @@
package terminfo
import (
"bytes"
"fmt"
"io"
"strconv"
"strings"
"sync"
)
// parametizer represents the a scan state for a parameterized string.
type parametizer struct {
// z is the string to parameterize
z []byte
// pos is the current position in s.
pos int
// nest is the current nest level.
nest int
// s is the variable stack.
s stack
// skipElse keeps the state of skipping else.
skipElse bool
// buf is the result buffer.
buf *bytes.Buffer
// params are the parameters to interpolate.
params [9]interface{}
// vars are dynamic variables.
vars [26]interface{}
}
// staticVars are the static, global variables.
var staticVars = struct {
vars [26]interface{}
sync.Mutex
}{}
var parametizerPool = sync.Pool{
New: func() interface{} {
p := new(parametizer)
p.buf = bytes.NewBuffer(make([]byte, 0, 45))
return p
},
}
// newParametizer returns a new initialized parametizer from the pool.
func newParametizer(z []byte) *parametizer {
p := parametizerPool.Get().(*parametizer)
p.z = z
return p
}
// reset resets the parametizer.
func (p *parametizer) reset() {
p.pos, p.nest = 0, 0
p.s.reset()
p.buf.Reset()
p.params, p.vars = [9]interface{}{}, [26]interface{}{}
parametizerPool.Put(p)
}
// stateFn represents the state of the scanner as a function that returns the
// next state.
type stateFn func() stateFn
// exec executes the parameterizer, interpolating the supplied parameters.
func (p *parametizer) exec() string {
for state := p.scanTextFn; state != nil; {
state = state()
}
return p.buf.String()
}
// peek returns the next byte.
func (p *parametizer) peek() (byte, error) {
if p.pos >= len(p.z) {
return 0, io.EOF
}
return p.z[p.pos], nil
}
// writeFrom writes the characters from ppos to pos to the buffer.
func (p *parametizer) writeFrom(ppos int) {
if p.pos > ppos {
// append remaining characters.
p.buf.Write(p.z[ppos:p.pos])
}
}
func (p *parametizer) scanTextFn() stateFn {
ppos := p.pos
for {
ch, err := p.peek()
if err != nil {
p.writeFrom(ppos)
return nil
}
if ch == '%' {
p.writeFrom(ppos)
p.pos++
return p.scanCodeFn
}
p.pos++
}
}
func (p *parametizer) scanCodeFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
switch ch {
case '%':
p.buf.WriteByte('%')
case ':':
// this character is used to avoid interpreting "%-" and "%+" as operators.
// the next character is where the format really begins.
p.pos++
_, err = p.peek()
if err != nil {
return nil
}
return p.scanFormatFn
case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
return p.scanFormatFn
case 'o':
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8))
case 'd':
p.buf.WriteString(strconv.Itoa(p.s.popInt()))
case 'x':
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16))
case 'X':
p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16)))
case 's':
p.buf.WriteString(p.s.popString())
case 'c':
p.buf.WriteByte(p.s.popByte())
case 'p':
p.pos++
return p.pushParamFn
case 'P':
p.pos++
return p.setDsVarFn
case 'g':
p.pos++
return p.getDsVarFn
case '\'':
p.pos++
ch, err = p.peek()
if err != nil {
return nil
}
p.s.push(ch)
// skip the '\''
p.pos++
case '{':
p.pos++
return p.pushIntfn
case 'l':
p.s.push(len(p.s.popString()))
case '+':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai + bi)
case '-':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai - bi)
case '*':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai * bi)
case '/':
bi, ai := p.s.popInt(), p.s.popInt()
if bi != 0 {
p.s.push(ai / bi)
} else {
p.s.push(0)
}
case 'm':
bi, ai := p.s.popInt(), p.s.popInt()
if bi != 0 {
p.s.push(ai % bi)
} else {
p.s.push(0)
}
case '&':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai & bi)
case '|':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai | bi)
case '^':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai ^ bi)
case '=':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai == bi)
case '>':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai > bi)
case '<':
bi, ai := p.s.popInt(), p.s.popInt()
p.s.push(ai < bi)
case 'A':
bi, ai := p.s.popBool(), p.s.popBool()
p.s.push(ai && bi)
case 'O':
bi, ai := p.s.popBool(), p.s.popBool()
p.s.push(ai || bi)
case '!':
p.s.push(!p.s.popBool())
case '~':
p.s.push(^p.s.popInt())
case 'i':
for i := range p.params[:2] {
if n, ok := p.params[i].(int); ok {
p.params[i] = n + 1
}
}
case '?', ';':
case 't':
return p.scanThenFn
case 'e':
p.skipElse = true
return p.skipTextFn
}
p.pos++
return p.scanTextFn
}
func (p *parametizer) scanFormatFn() stateFn {
// the character was already read, so no need to check the error.
ch, _ := p.peek()
// 6 should be the maximum length of a format string, for example "%:-9.9d".
f := []byte{'%', ch, 0, 0, 0, 0}
var err error
for {
p.pos++
ch, err = p.peek()
if err != nil {
return nil
}
f = append(f, ch)
switch ch {
case 'o', 'd', 'x', 'X':
fmt.Fprintf(p.buf, string(f), p.s.popInt())
break
case 's':
fmt.Fprintf(p.buf, string(f), p.s.popString())
break
case 'c':
fmt.Fprintf(p.buf, string(f), p.s.popByte())
break
}
}
p.pos++
return p.scanTextFn
}
func (p *parametizer) pushParamFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) {
p.s.push(p.params[ai])
} else {
p.s.push(0)
}
// skip the '}'
p.pos++
return p.scanTextFn
}
func (p *parametizer) setDsVarFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
if ch >= 'A' && ch <= 'Z' {
staticVars.Lock()
staticVars.vars[int(ch-'A')] = p.s.pop()
staticVars.Unlock()
} else if ch >= 'a' && ch <= 'z' {
p.vars[int(ch-'a')] = p.s.pop()
}
p.pos++
return p.scanTextFn
}
func (p *parametizer) getDsVarFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
var a byte
if ch >= 'A' && ch <= 'Z' {
a = 'A'
} else if ch >= 'a' && ch <= 'z' {
a = 'a'
}
staticVars.Lock()
p.s.push(staticVars.vars[int(ch-a)])
staticVars.Unlock()
p.pos++
return p.scanTextFn
}
func (p *parametizer) pushIntfn() stateFn {
var ai int
for {
ch, err := p.peek()
if err != nil {
return nil
}
p.pos++
if ch < '0' || ch > '9' {
p.s.push(ai)
return p.scanTextFn
}
ai = (ai * 10) + int(ch-'0')
}
}
func (p *parametizer) scanThenFn() stateFn {
p.pos++
if p.s.popBool() {
return p.scanTextFn
}
p.skipElse = false
return p.skipTextFn
}
func (p *parametizer) skipTextFn() stateFn {
for {
ch, err := p.peek()
if err != nil {
return nil
}
p.pos++
if ch == '%' {
break
}
}
if p.skipElse {
return p.skipElseFn
}
return p.skipThenFn
}
func (p *parametizer) skipThenFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
p.pos++
switch ch {
case ';':
if p.nest == 0 {
return p.scanTextFn
}
p.nest--
case '?':
p.nest++
case 'e':
if p.nest == 0 {
return p.scanTextFn
}
}
return p.skipTextFn
}
func (p *parametizer) skipElseFn() stateFn {
ch, err := p.peek()
if err != nil {
return nil
}
p.pos++
switch ch {
case ';':
if p.nest == 0 {
return p.scanTextFn
}
p.nest--
case '?':
p.nest++
}
return p.skipTextFn
}
// Printf evaluates a parameterized terminfo value z, interpolating params.
func Printf(z []byte, params ...interface{}) string {
p := newParametizer(z)
defer p.reset()
// make sure we always have 9 parameters -- makes it easier
// later to skip checks and its faster
for i := 0; i < len(p.params) && i < len(params); i++ {
p.params[i] = params[i]
}
return p.exec()
}
// Fprintf evaluates a parameterized terminfo value z, interpolating params and
// writing to w.
func Fprintf(w io.Writer, z []byte, params ...interface{}) {
w.Write([]byte(Printf(z, params...)))
}

48
vendor/github.com/xo/terminfo/stack.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package terminfo
type stack []interface{}
func (s *stack) push(v interface{}) {
*s = append(*s, v)
}
func (s *stack) pop() interface{} {
if len(*s) == 0 {
return nil
}
v := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
return v
}
func (s *stack) popInt() int {
if i, ok := s.pop().(int); ok {
return i
}
return 0
}
func (s *stack) popBool() bool {
if b, ok := s.pop().(bool); ok {
return b
}
return false
}
func (s *stack) popByte() byte {
if b, ok := s.pop().(byte); ok {
return b
}
return 0
}
func (s *stack) popString() string {
if a, ok := s.pop().(string); ok {
return a
}
return ""
}
func (s *stack) reset() {
*s = (*s)[:0]
}

538
vendor/github.com/xo/terminfo/terminfo.go generated vendored Normal file
View File

@ -0,0 +1,538 @@
// Package terminfo implements reading terminfo files in pure go.
package terminfo
import (
"io"
"io/ioutil"
"path"
"strconv"
"strings"
)
// Error is a terminfo error.
type Error string
// Error satisfies the error interface.
func (err Error) Error() string {
return string(err)
}
const (
// ErrInvalidFileSize is the invalid file size error.
ErrInvalidFileSize Error = "invalid file size"
// ErrUnexpectedFileEnd is the unexpected file end error.
ErrUnexpectedFileEnd Error = "unexpected file end"
// ErrInvalidStringTable is the invalid string table error.
ErrInvalidStringTable Error = "invalid string table"
// ErrInvalidMagic is the invalid magic error.
ErrInvalidMagic Error = "invalid magic"
// ErrInvalidHeader is the invalid header error.
ErrInvalidHeader Error = "invalid header"
// ErrInvalidNames is the invalid names error.
ErrInvalidNames Error = "invalid names"
// ErrInvalidExtendedHeader is the invalid extended header error.
ErrInvalidExtendedHeader Error = "invalid extended header"
// ErrEmptyTermName is the empty term name error.
ErrEmptyTermName Error = "empty term name"
// ErrDatabaseDirectoryNotFound is the database directory not found error.
ErrDatabaseDirectoryNotFound Error = "database directory not found"
// ErrFileNotFound is the file not found error.
ErrFileNotFound Error = "file not found"
// ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error.
ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION"
)
// Terminfo describes a terminal's capabilities.
type Terminfo struct {
// File is the original source file.
File string
// Names are the provided cap names.
Names []string
// Bools are the bool capabilities.
Bools map[int]bool
// BoolsM are the missing bool capabilities.
BoolsM map[int]bool
// Nums are the num capabilities.
Nums map[int]int
// NumsM are the missing num capabilities.
NumsM map[int]bool
// Strings are the string capabilities.
Strings map[int][]byte
// StringsM are the missing string capabilities.
StringsM map[int]bool
// ExtBools are the extended bool capabilities.
ExtBools map[int]bool
// ExtBoolsNames is the map of extended bool capabilities to their index.
ExtBoolNames map[int][]byte
// ExtNums are the extended num capabilities.
ExtNums map[int]int
// ExtNumsNames is the map of extended num capabilities to their index.
ExtNumNames map[int][]byte
// ExtStrings are the extended string capabilities.
ExtStrings map[int][]byte
// ExtStringsNames is the map of extended string capabilities to their index.
ExtStringNames map[int][]byte
}
// Decode decodes the terminfo data contained in buf.
func Decode(buf []byte) (*Terminfo, error) {
var err error
// check max file length
if len(buf) >= maxFileLength {
return nil, ErrInvalidFileSize
}
d := &decoder{
buf: buf,
len: len(buf),
}
// read header
h, err := d.readInts(6, 16)
if err != nil {
return nil, err
}
var numWidth int
// check magic
if h[fieldMagic] == magic {
numWidth = 16
} else if h[fieldMagic] == magicExtended {
numWidth = 32
} else {
return nil, ErrInvalidMagic
}
// check header
if hasInvalidCaps(h) {
return nil, ErrInvalidHeader
}
// check remaining length
if d.len-d.pos < capLength(h) {
return nil, ErrUnexpectedFileEnd
}
// read names
names, err := d.readBytes(h[fieldNameSize])
if err != nil {
return nil, err
}
// check name is terminated properly
i := findNull(names, 0)
if i == -1 {
return nil, ErrInvalidNames
}
names = names[:i]
// read bool caps
bools, boolsM, err := d.readBools(h[fieldBoolCount])
if err != nil {
return nil, err
}
// read num caps
nums, numsM, err := d.readNums(h[fieldNumCount], numWidth)
if err != nil {
return nil, err
}
// read string caps
strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize])
if err != nil {
return nil, err
}
ti := &Terminfo{
Names: strings.Split(string(names), "|"),
Bools: bools,
BoolsM: boolsM,
Nums: nums,
NumsM: numsM,
Strings: strs,
StringsM: strsM,
}
// at the end of file, so no extended caps
if d.pos >= d.len {
return ti, nil
}
// decode extended header
eh, err := d.readInts(5, 16)
if err != nil {
return nil, err
}
// check extended offset field
if hasInvalidExtOffset(eh) {
return nil, ErrInvalidExtendedHeader
}
// check extended cap lengths
if d.len-d.pos != extCapLength(eh, numWidth) {
return nil, ErrInvalidExtendedHeader
}
// read extended bool caps
ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount])
if err != nil {
return nil, err
}
// read extended num caps
ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth)
if err != nil {
return nil, err
}
// read extended string data table indexes
extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16)
if err != nil {
return nil, err
}
// read string data table
extData, err := d.readBytes(eh[fieldExtTableSize])
if err != nil {
return nil, err
}
// precautionary check that exactly at end of file
if d.pos != d.len {
return nil, ErrUnexpectedFileEnd
}
var last int
// read extended string caps
ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
if err != nil {
return nil, err
}
extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:]
// read extended bool names
ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount])
if err != nil {
return nil, err
}
extIndexes = extIndexes[eh[fieldExtBoolCount]:]
// read extended num names
ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount])
if err != nil {
return nil, err
}
extIndexes = extIndexes[eh[fieldExtNumCount]:]
// read extended string names
ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
if err != nil {
return nil, err
}
//extIndexes = extIndexes[eh[fieldExtStringCount]:]
return ti, nil
}
// Open reads the terminfo file name from the specified directory dir.
func Open(dir, name string) (*Terminfo, error) {
var err error
var buf []byte
var filename string
for _, f := range []string{
path.Join(dir, name[0:1], name),
path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name),
} {
buf, err = ioutil.ReadFile(f)
if err == nil {
filename = f
break
}
}
if buf == nil {
return nil, ErrFileNotFound
}
// decode
ti, err := Decode(buf)
if err != nil {
return nil, err
}
// save original file name
ti.File = filename
// add to cache
termCache.Lock()
for _, n := range ti.Names {
termCache.db[n] = ti
}
termCache.Unlock()
return ti, nil
}
// boolCaps returns all bool and extended capabilities using f to format the
// index key.
func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool {
m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools))
if !extended {
for k, v := range ti.Bools {
m[f(k)] = v
}
} else {
for k, v := range ti.ExtBools {
m[string(ti.ExtBoolNames[k])] = v
}
}
return m
}
// BoolCaps returns all bool capabilities.
func (ti *Terminfo) BoolCaps() map[string]bool {
return ti.boolCaps(BoolCapName, false)
}
// BoolCapsShort returns all bool capabilities, using the short name as the
// index.
func (ti *Terminfo) BoolCapsShort() map[string]bool {
return ti.boolCaps(BoolCapNameShort, false)
}
// ExtBoolCaps returns all extended bool capabilities.
func (ti *Terminfo) ExtBoolCaps() map[string]bool {
return ti.boolCaps(BoolCapName, true)
}
// ExtBoolCapsShort returns all extended bool capabilities, using the short
// name as the index.
func (ti *Terminfo) ExtBoolCapsShort() map[string]bool {
return ti.boolCaps(BoolCapNameShort, true)
}
// numCaps returns all num and extended capabilities using f to format the
// index key.
func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int {
m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums))
if !extended {
for k, v := range ti.Nums {
m[f(k)] = v
}
} else {
for k, v := range ti.ExtNums {
m[string(ti.ExtNumNames[k])] = v
}
}
return m
}
// NumCaps returns all num capabilities.
func (ti *Terminfo) NumCaps() map[string]int {
return ti.numCaps(NumCapName, false)
}
// NumCapsShort returns all num capabilities, using the short name as the
// index.
func (ti *Terminfo) NumCapsShort() map[string]int {
return ti.numCaps(NumCapNameShort, false)
}
// ExtNumCaps returns all extended num capabilities.
func (ti *Terminfo) ExtNumCaps() map[string]int {
return ti.numCaps(NumCapName, true)
}
// ExtNumCapsShort returns all extended num capabilities, using the short
// name as the index.
func (ti *Terminfo) ExtNumCapsShort() map[string]int {
return ti.numCaps(NumCapNameShort, true)
}
// stringCaps returns all string and extended capabilities using f to format the
// index key.
func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte {
m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings))
if !extended {
for k, v := range ti.Strings {
m[f(k)] = v
}
} else {
for k, v := range ti.ExtStrings {
m[string(ti.ExtStringNames[k])] = v
}
}
return m
}
// StringCaps returns all string capabilities.
func (ti *Terminfo) StringCaps() map[string][]byte {
return ti.stringCaps(StringCapName, false)
}
// StringCapsShort returns all string capabilities, using the short name as the
// index.
func (ti *Terminfo) StringCapsShort() map[string][]byte {
return ti.stringCaps(StringCapNameShort, false)
}
// ExtStringCaps returns all extended string capabilities.
func (ti *Terminfo) ExtStringCaps() map[string][]byte {
return ti.stringCaps(StringCapName, true)
}
// ExtStringCapsShort returns all extended string capabilities, using the short
// name as the index.
func (ti *Terminfo) ExtStringCapsShort() map[string][]byte {
return ti.stringCaps(StringCapNameShort, true)
}
// Has determines if the bool cap i is present.
func (ti *Terminfo) Has(i int) bool {
return ti.Bools[i]
}
// Num returns the num cap i, or -1 if not present.
func (ti *Terminfo) Num(i int) int {
n, ok := ti.Nums[i]
if !ok {
return -1
}
return n
}
// Printf formats the string cap i, interpolating parameters v.
func (ti *Terminfo) Printf(i int, v ...interface{}) string {
return Printf(ti.Strings[i], v...)
}
// Fprintf prints the string cap i to writer w, interpolating parameters v.
func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) {
Fprintf(w, ti.Strings[i], v...)
}
// Color takes a foreground and background color and returns string that sets
// them for this terminal.
func (ti *Terminfo) Colorf(fg, bg int, str string) string {
maxColors := int(ti.Nums[MaxColors])
// map bright colors to lower versions if the color table only holds 8.
if maxColors == 8 {
if fg > 7 && fg < 16 {
fg -= 8
}
if bg > 7 && bg < 16 {
bg -= 8
}
}
var s string
if maxColors > fg && fg >= 0 {
s += ti.Printf(SetAForeground, fg)
}
if maxColors > bg && bg >= 0 {
s += ti.Printf(SetABackground, bg)
}
return s + str + ti.Printf(ExitAttributeMode)
}
// Goto returns a string suitable for addressing the cursor at the given
// row and column. The origin 0, 0 is in the upper left corner of the screen.
func (ti *Terminfo) Goto(row, col int) string {
return Printf(ti.Strings[CursorAddress], row, col)
}
// Puts emits the string to the writer, but expands inline padding indications
// (of the form $<[delay]> where [delay] is msec) to a suitable number of
// padding characters (usually null bytes) based upon the supplied baud. At
// high baud rates, more padding characters will be inserted.
/*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) {
var err error
for {
start := strings.Index(s, "$<")
if start == -1 {
// most strings don't need padding, which is good news!
return io.WriteString(w, s)
}
end := strings.Index(s, ">")
if end == -1 {
// unterminated... just emit bytes unadulterated.
return io.WriteString(w, "$<"+s)
}
var c int
c, err = io.WriteString(w, s[:start])
if err != nil {
return n + c, err
}
n += c
s = s[start+2:]
val := s[:end]
s = s[end+1:]
var ms int
var dot, mandatory, asterisk bool
unit := 1000
for _, ch := range val {
switch {
case ch >= '0' && ch <= '9':
ms = (ms * 10) + int(ch-'0')
if dot {
unit *= 10
}
case ch == '.' && !dot:
dot = true
case ch == '*' && !asterisk:
ms *= lines
asterisk = true
case ch == '/':
mandatory = true
default:
break
}
}
z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar]
b := make([]byte, len(pad)*z)
for bp := copy(b, pad); bp < len(b); bp *= 2 {
copy(b[bp:], b[:bp])
}
if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory {
c, err = w.Write(b)
if err != nil {
return n + c, err
}
n += c
}
}
return n, nil
}*/

266
vendor/github.com/xo/terminfo/util.go generated vendored Normal file
View File

@ -0,0 +1,266 @@
package terminfo
import (
"sort"
)
const (
// maxFileLength is the max file length.
maxFileLength = 4096
// magic is the file magic for terminfo files.
magic = 0432
// magicExtended is the file magic for terminfo files with the extended number format.
magicExtended = 01036
)
// header fields.
const (
fieldMagic = iota
fieldNameSize
fieldBoolCount
fieldNumCount
fieldStringCount
fieldTableSize
)
// header extended fields.
const (
fieldExtBoolCount = iota
fieldExtNumCount
fieldExtStringCount
fieldExtOffsetCount
fieldExtTableSize
)
// hasInvalidCaps determines if the capabilities in h are invalid.
func hasInvalidCaps(h []int) bool {
return h[fieldBoolCount] > CapCountBool ||
h[fieldNumCount] > CapCountNum ||
h[fieldStringCount] > CapCountString
}
// capLength returns the total length of the capabilities in bytes.
func capLength(h []int) int {
return h[fieldNameSize] +
h[fieldBoolCount] +
(h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align
h[fieldNumCount]*2 +
h[fieldStringCount]*2 +
h[fieldTableSize]
}
// hasInvalidExtOffset determines if the extended offset field is valid.
func hasInvalidExtOffset(h []int) bool {
return h[fieldExtBoolCount]+
h[fieldExtNumCount]+
h[fieldExtStringCount]*2 != h[fieldExtOffsetCount]
}
// extCapLength returns the total length of extended capabilities in bytes.
func extCapLength(h []int, numWidth int) int {
return h[fieldExtBoolCount] +
h[fieldExtBoolCount]%2 + // account for word align
h[fieldExtNumCount]*(numWidth/8) +
h[fieldExtOffsetCount]*2 +
h[fieldExtTableSize]
}
// findNull finds the position of null in buf.
func findNull(buf []byte, i int) int {
for ; i < len(buf); i++ {
if buf[i] == 0 {
return i
}
}
return -1
}
// readStrings decodes n strings from string data table buf using the indexes in idx.
func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) {
var last int
m := make(map[int][]byte)
for i := 0; i < n; i++ {
start := idx[i]
if start < 0 {
continue
}
if end := findNull(buf, start); end != -1 {
m[i], last = buf[start:end], end+1
} else {
return nil, 0, ErrInvalidStringTable
}
}
return m, last, nil
}
// decoder holds state info while decoding a terminfo file.
type decoder struct {
buf []byte
pos int
len int
}
// readBytes reads the next n bytes of buf, incrementing pos by n.
func (d *decoder) readBytes(n int) ([]byte, error) {
if d.len < d.pos+n {
return nil, ErrUnexpectedFileEnd
}
n, d.pos = d.pos, d.pos+n
return d.buf[n:d.pos], nil
}
// readInts reads n number of ints with width w.
func (d *decoder) readInts(n, w int) ([]int, error) {
w /= 8
l := n * w
buf, err := d.readBytes(l)
if err != nil {
return nil, err
}
// align
d.pos += d.pos % 2
z := make([]int, n)
for i, j := 0, 0; i < l; i, j = i+w, j+1 {
switch w {
case 1:
z[i] = int(buf[i])
case 2:
z[j] = int(int16(buf[i+1])<<8 | int16(buf[i]))
case 4:
z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i])
}
}
return z, nil
}
// readBools reads the next n bools.
func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) {
buf, err := d.readInts(n, 8)
if err != nil {
return nil, nil, err
}
// process
bools, boolsM := make(map[int]bool), make(map[int]bool)
for i, b := range buf {
bools[i] = b == 1
if int8(b) == -2 {
boolsM[i] = true
}
}
return bools, boolsM, nil
}
// readNums reads the next n nums.
func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) {
buf, err := d.readInts(n, w)
if err != nil {
return nil, nil, err
}
// process
nums, numsM := make(map[int]int), make(map[int]bool)
for i := 0; i < n; i++ {
nums[i] = buf[i]
if buf[i] == -2 {
numsM[i] = true
}
}
return nums, numsM, nil
}
// readStringTable reads the string data for n strings and the accompanying data
// table of length sz.
func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) {
buf, err := d.readInts(n, 16)
if err != nil {
return nil, nil, err
}
// read string data table
data, err := d.readBytes(sz)
if err != nil {
return nil, nil, err
}
// align
d.pos += d.pos % 2
// process
s := make([][]byte, n)
var m []int
for i := 0; i < n; i++ {
start := buf[i]
if start == -2 {
m = append(m, i)
} else if start >= 0 {
if end := findNull(data, start); end != -1 {
s[i] = data[start:end]
} else {
return nil, nil, ErrInvalidStringTable
}
}
}
return s, m, nil
}
// readStrings reads the next n strings and processes the string data table of
// length sz.
func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) {
s, m, err := d.readStringTable(n, sz)
if err != nil {
return nil, nil, err
}
strs := make(map[int][]byte)
for k, v := range s {
if k == AcsChars {
v = canonicalizeAscChars(v)
}
strs[k] = v
}
strsM := make(map[int]bool, len(m))
for _, k := range m {
strsM[k] = true
}
return strs, strsM, nil
}
// canonicalizeAscChars reorders chars to be unique, in order.
//
// see repair_ascc in ncurses-6.0/progs/dump_entry.c
func canonicalizeAscChars(z []byte) []byte {
var c chars
enc := make(map[byte]byte, len(z)/2)
for i := 0; i < len(z); i += 2 {
if _, ok := enc[z[i]]; !ok {
a, b := z[i], z[i+1]
//log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b)
c, enc[a] = append(c, b), b
}
}
sort.Sort(c)
r := make([]byte, 2*len(c))
for i := 0; i < len(c); i++ {
r[i*2], r[i*2+1] = c[i], enc[c[i]]
}
return r
}
type chars []byte
func (c chars) Len() int { return len(c) }
func (c chars) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c chars) Less(i, j int) bool { return c[i] < c[j] }

201
vendor/gopkg.in/yaml.v2/LICENSE generated vendored
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

390
vendor/gopkg.in/yaml.v2/encode.go generated vendored
View File

@ -1,390 +0,0 @@
package yaml
import (
"encoding"
"fmt"
"io"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)
// jsonNumber is the interface of the encoding/json.Number datatype.
// Repeating the interface here avoids a dependency on encoding/json, and also
// supports other libraries like jsoniter, which use a similar datatype with
// the same interface. Detecting this interface is useful when dealing with
// structures containing json.Number, which is a string under the hood. The
// encoder should prefer the use of Int64(), Float64() and string(), in that
// order, when encoding this type.
type jsonNumber interface {
Float64() (float64, error)
Int64() (int64, error)
String() string
}
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
// doneInit holds whether the initial stream_start_event has been
// emitted.
doneInit bool
}
func newEncoder() *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func newEncoderWithWriter(w io.Writer) *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_writer(&e.emitter, w)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func (e *encoder) init() {
if e.doneInit {
return
}
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
e.emit()
e.doneInit = true
}
func (e *encoder) finish() {
e.emitter.open_ended = false
yaml_stream_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
e.must(yaml_emitter_emit(&e.emitter, &e.event))
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
e.init()
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.emit()
e.marshal(tag, in)
yaml_document_end_event_initialize(&e.event, true)
e.emit()
}
func (e *encoder) marshal(tag string, in reflect.Value) {
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}
iface := in.Interface()
switch m := iface.(type) {
case jsonNumber:
integer, err := m.Int64()
if err == nil {
// In this case the json.Number is a valid int64
in = reflect.ValueOf(integer)
break
}
float, err := m.Float64()
if err == nil {
// In this case the json.Number is a valid float64
in = reflect.ValueOf(float)
break
}
// fallback case - no number could be obtained
in = reflect.ValueOf(m.String())
case time.Time, *time.Time:
// Although time.Time implements TextMarshaler,
// we don't want to treat it as a string for YAML
// purposes because YAML has special support for
// timestamps.
case Marshaler:
v, err := m.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
in = reflect.ValueOf(v)
case encoding.TextMarshaler:
text, err := m.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
case nil:
e.nilv()
return
}
switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
if in.Type() == ptrTimeType {
e.timev(tag, in.Elem())
} else {
e.marshal(tag, in.Elem())
}
case reflect.Struct:
if in.Type() == timeType {
e.timev(tag, in)
} else {
e.structv(tag, in)
}
case reflect.Slice, reflect.Array:
if in.Type().Elem() == mapItemType {
e.itemsv(tag, in)
} else {
e.slicev(tag, in)
}
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if in.Type() == durationType {
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
} else {
e.intv(tag, in)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) itemsv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
for _, item := range slice {
e.marshal("", reflect.ValueOf(item.Key))
e.marshal("", reflect.ValueOf(item.Value))
}
})
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = in.FieldByIndex(info.Inline)
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
e.emit()
f()
yaml_mapping_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
canUsePlain := true
switch {
case !utf8.ValidString(s):
if tag == yaml_BINARY_TAG {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = yaml_BINARY_TAG
s = encodeBase64(s)
case tag == "":
// Check to see if it would resolve to a specific
// tag when encoded unquoted. If it doesn't,
// there's no need to quote it.
rtag, _ := resolve("", s)
canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
}
// Note: it's possible for user code to emit invalid YAML
// if they explicitly specify a tag and a string containing
// text that's incompatible with that tag.
switch {
case strings.Contains(s, "\n"):
style = yaml_LITERAL_SCALAR_STYLE
case canUsePlain:
style = yaml_PLAIN_SCALAR_STYLE
default:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
precision := 64
if in.Kind() == reflect.Float32 {
precision = 32
}
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
implicit := tag == ""
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.emit()
}

26
vendor/gopkg.in/yaml.v2/writerc.go generated vendored
View File

@ -1,26 +0,0 @@
package yaml
// Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_WRITER_ERROR
emitter.problem = problem
return false
}
// Flush the output buffer.
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
if emitter.write_handler == nil {
panic("write handler not set")
}
// Check if the buffer is empty.
if emitter.buffer_pos == 0 {
return true
}
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
return true
}

View File

@ -1,16 +1,17 @@
This project is covered by two different licenses: MIT and Apache.
#### MIT License ####
The following files were ported to Go from C files of libyaml, and thus
are still covered by their original copyright and license:
are still covered by their original MIT license, with the additional
copyright staring in 2011 when the project was ported over:
apic.go
emitterc.go
parserc.go
readerc.go
scannerc.go
writerc.go
yamlh.go
yamlprivateh.go
apic.go emitterc.go parserc.go readerc.go scannerc.go
writerc.go yamlh.go yamlprivateh.go
Copyright (c) 2006 Kirill Simonov
Copyright (c) 2006-2010 Kirill Simonov
Copyright (c) 2006-2011 Kirill Simonov
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
@ -29,3 +30,21 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
### Apache License ###
All the remaining project files are covered by the Apache license:
Copyright (c) 2011-2019 Canonical Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -12,7 +12,23 @@ C library to parse and generate YAML data quickly and reliably.
Compatibility
-------------
The yaml package supports most of YAML 1.1 and 1.2, including support for
The yaml package supports most of YAML 1.2, but preserves some behavior
from 1.1 for backwards compatibility.
Specifically, as of v3 of the yaml package:
- YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being
decoded into a typed bool value. Otherwise they behave as a string. Booleans
in YAML 1.2 are _true/false_ only.
- Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_
as specified in YAML 1.2, because most parsers still use the old format.
Octals in the _0o777_ format are supported though, so new files work.
- Does not support base-60 floats. These are gone from YAML 1.2, and were
actually never supported by this package as it's clearly a poor choice.
and offers backwards
compatibility with YAML 1.1 in some cases.
1.2, including support for
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
implemented, and base-60 floats from YAML 1.1 are purposefully not
supported since they're a poor design and are gone in YAML 1.2.
@ -20,29 +36,30 @@ supported since they're a poor design and are gone in YAML 1.2.
Installation and usage
----------------------
The import path for the package is *gopkg.in/yaml.v2*.
The import path for the package is *gopkg.in/yaml.v3*.
To install it, run:
go get gopkg.in/yaml.v2
go get gopkg.in/yaml.v3
API documentation
-----------------
If opened in a browser, the import path itself leads to the API documentation:
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
- [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3)
API stability
-------------
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in).
License
-------
The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details.
The yaml package is licensed under the MIT and Apache License 2.0 licenses.
Please see the LICENSE file for details.
Example
@ -55,7 +72,7 @@ import (
"fmt"
"log"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
var data = `

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -86,7 +108,6 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
raw_buffer: make([]byte, 0, output_raw_buffer_size),
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
events: make([]yaml_event_t, 0, initial_queue_size),
best_width: -1,
}
}
@ -139,7 +160,7 @@ func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
emitter.canonical = canonical
}
//// Set the indentation increment.
// Set the indentation increment.
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
if indent < 2 || indent > 9 {
indent = 2
@ -289,29 +310,14 @@ func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
}
}
///*
// * Create ALIAS.
// */
//
//YAML_DECLARE(int)
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
//{
// mark yaml_mark_t = { 0, 0, 0 }
// anchor_copy *yaml_char_t = NULL
//
// assert(event) // Non-NULL event object is expected.
// assert(anchor) // Non-NULL anchor is expected.
//
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
//
// anchor_copy = yaml_strdup(anchor)
// if (!anchor_copy)
// return 0
//
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
//
// return 1
//}
// Create ALIAS.
func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
*event = yaml_event_t{
typ: yaml_ALIAS_EVENT,
anchor: anchor,
}
return true
}
// Create SCALAR.
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -11,33 +26,14 @@ import (
"time"
)
const (
documentNode = 1 << iota
mappingNode
sequenceNode
scalarNode
aliasNode
)
type node struct {
kind int
line, column int
tag string
// For an alias node, alias holds the resolved alias.
alias *node
value string
implicit bool
children []*node
anchors map[string]*node
}
// ----------------------------------------------------------------------------
// Parser, produces a node tree out of a libyaml event stream.
type parser struct {
parser yaml_parser_t
event yaml_event_t
doc *node
doc *Node
anchors map[string]*Node
doneInit bool
}
@ -66,6 +62,7 @@ func (p *parser) init() {
if p.doneInit {
return
}
p.anchors = make(map[string]*Node)
p.expect(yaml_STREAM_START_EVENT)
p.doneInit = true
}
@ -132,13 +129,14 @@ func (p *parser) fail() {
failf("%s%s", where, msg)
}
func (p *parser) anchor(n *node, anchor []byte) {
func (p *parser) anchor(n *Node, anchor []byte) {
if anchor != nil {
p.doc.anchors[string(anchor)] = n
n.Anchor = string(anchor)
p.anchors[n.Anchor] = n
}
}
func (p *parser) parse() *node {
func (p *parser) parse() *Node {
p.init()
switch p.peek() {
case yaml_SCALAR_EVENT:
@ -154,67 +152,145 @@ func (p *parser) parse() *node {
case yaml_STREAM_END_EVENT:
// Happens when attempting to decode an empty buffer.
return nil
case yaml_TAIL_COMMENT_EVENT:
panic("internal error: unexpected tail comment event (please report)")
default:
panic("attempted to parse unknown event: " + p.event.typ.String())
panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String())
}
}
func (p *parser) node(kind int) *node {
return &node{
kind: kind,
line: p.event.start_mark.line,
column: p.event.start_mark.column,
func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
var style Style
if tag != "" && tag != "!" {
tag = shortTag(tag)
style = TaggedStyle
} else if defaultTag != "" {
tag = defaultTag
} else if kind == ScalarNode {
tag, _ = resolve("", value)
}
return &Node{
Kind: kind,
Tag: tag,
Value: value,
Style: style,
Line: p.event.start_mark.line + 1,
Column: p.event.start_mark.column + 1,
HeadComment: string(p.event.head_comment),
LineComment: string(p.event.line_comment),
FootComment: string(p.event.foot_comment),
}
}
func (p *parser) document() *node {
n := p.node(documentNode)
n.anchors = make(map[string]*node)
func (p *parser) parseChild(parent *Node) *Node {
child := p.parse()
parent.Content = append(parent.Content, child)
return child
}
func (p *parser) document() *Node {
n := p.node(DocumentNode, "", "", "")
p.doc = n
p.expect(yaml_DOCUMENT_START_EVENT)
n.children = append(n.children, p.parse())
p.parseChild(n)
if p.peek() == yaml_DOCUMENT_END_EVENT {
n.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_DOCUMENT_END_EVENT)
return n
}
func (p *parser) alias() *node {
n := p.node(aliasNode)
n.value = string(p.event.anchor)
n.alias = p.doc.anchors[n.value]
if n.alias == nil {
failf("unknown anchor '%s' referenced", n.value)
func (p *parser) alias() *Node {
n := p.node(AliasNode, "", "", string(p.event.anchor))
n.Alias = p.anchors[n.Value]
if n.Alias == nil {
failf("unknown anchor '%s' referenced", n.Value)
}
p.expect(yaml_ALIAS_EVENT)
return n
}
func (p *parser) scalar() *node {
n := p.node(scalarNode)
n.value = string(p.event.value)
n.tag = string(p.event.tag)
n.implicit = p.event.implicit
func (p *parser) scalar() *Node {
var parsedStyle = p.event.scalar_style()
var nodeStyle Style
switch {
case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = DoubleQuotedStyle
case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
nodeStyle = SingleQuotedStyle
case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0:
nodeStyle = LiteralStyle
case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0:
nodeStyle = FoldedStyle
}
var nodeValue = string(p.event.value)
var nodeTag = string(p.event.tag)
var defaultTag string
if nodeStyle == 0 {
if nodeValue == "<<" {
defaultTag = mergeTag
}
} else {
defaultTag = strTag
}
n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue)
n.Style |= nodeStyle
p.anchor(n, p.event.anchor)
p.expect(yaml_SCALAR_EVENT)
return n
}
func (p *parser) sequence() *node {
n := p.node(sequenceNode)
func (p *parser) sequence() *Node {
n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_SEQUENCE_START_EVENT)
for p.peek() != yaml_SEQUENCE_END_EVENT {
n.children = append(n.children, p.parse())
p.parseChild(n)
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
p.expect(yaml_SEQUENCE_END_EVENT)
return n
}
func (p *parser) mapping() *node {
n := p.node(mappingNode)
func (p *parser) mapping() *Node {
n := p.node(MappingNode, mapTag, string(p.event.tag), "")
block := true
if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
block = false
n.Style |= FlowStyle
}
p.anchor(n, p.event.anchor)
p.expect(yaml_MAPPING_START_EVENT)
for p.peek() != yaml_MAPPING_END_EVENT {
n.children = append(n.children, p.parse(), p.parse())
k := p.parseChild(n)
if block && k.FootComment != "" {
// Must be a foot comment for the prior value when being dedented.
if len(n.Content) > 2 {
n.Content[len(n.Content)-3].FootComment = k.FootComment
k.FootComment = ""
}
}
v := p.parseChild(n)
if k.FootComment == "" && v.FootComment != "" {
k.FootComment = v.FootComment
v.FootComment = ""
}
if p.peek() == yaml_TAIL_COMMENT_EVENT {
if k.FootComment == "" {
k.FootComment = string(p.event.foot_comment)
}
p.expect(yaml_TAIL_COMMENT_EVENT)
}
}
n.LineComment = string(p.event.line_comment)
n.FootComment = string(p.event.foot_comment)
if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 {
n.Content[len(n.Content)-2].FootComment = n.FootComment
n.FootComment = ""
}
p.expect(yaml_MAPPING_END_EVENT)
return n
@ -224,48 +300,68 @@ func (p *parser) mapping() *node {
// Decoder, unmarshals a node into a provided value.
type decoder struct {
doc *node
aliases map[*node]bool
mapType reflect.Type
doc *Node
aliases map[*Node]bool
terrors []string
strict bool
stringMapType reflect.Type
generalMapType reflect.Type
knownFields bool
uniqueKeys bool
decodeCount int
aliasCount int
aliasDepth int
}
var (
mapItemType = reflect.TypeOf(MapItem{})
nodeType = reflect.TypeOf(Node{})
durationType = reflect.TypeOf(time.Duration(0))
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = defaultMapType.Elem()
stringMapType = reflect.TypeOf(map[string]interface{}{})
generalMapType = reflect.TypeOf(map[interface{}]interface{}{})
ifaceType = generalMapType.Elem()
timeType = reflect.TypeOf(time.Time{})
ptrTimeType = reflect.TypeOf(&time.Time{})
)
func newDecoder(strict bool) *decoder {
d := &decoder{mapType: defaultMapType, strict: strict}
d.aliases = make(map[*node]bool)
func newDecoder() *decoder {
d := &decoder{
stringMapType: stringMapType,
generalMapType: generalMapType,
uniqueKeys: true,
}
d.aliases = make(map[*Node]bool)
return d
}
func (d *decoder) terror(n *node, tag string, out reflect.Value) {
if n.tag != "" {
tag = n.tag
func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
if n.Tag != "" {
tag = n.Tag
}
value := n.value
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
value := n.Value
if tag != seqTag && tag != mapTag {
if len(value) > 10 {
value = " `" + value[:7] + "...`"
} else {
value = " `" + value + "`"
}
}
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type()))
}
func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
err := u.UnmarshalYAML(n)
if e, ok := err.(*TypeError); ok {
d.terrors = append(d.terrors, e.Errors...)
return false
}
if err != nil {
fail(err)
}
return true
}
func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
terrlen := len(d.terrors)
err := u.UnmarshalYAML(func(v interface{}) (err error) {
defer handleErr(&err)
@ -294,8 +390,8 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
// its types unmarshalled appropriately.
//
// If n holds a null value, prepare returns before doing anything.
func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) {
func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
if n.ShortTag() == nullTag {
return out, false, false
}
again := true
@ -309,15 +405,40 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm
again = true
}
if out.CanAddr() {
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
outi := out.Addr().Interface()
if u, ok := outi.(Unmarshaler); ok {
good = d.callUnmarshaler(n, u)
return out, true, good
}
if u, ok := outi.(obsoleteUnmarshaler); ok {
good = d.callObsoleteUnmarshaler(n, u)
return out, true, good
}
}
}
return out, false, false
}
func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) {
if n.ShortTag() == nullTag {
return reflect.Value{}
}
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
const (
// 400,000 decode operations is ~500kb of dense object declarations, or
// ~5kb of dense object declarations with 10000% alias expansion
@ -347,7 +468,7 @@ func allowedAliasRatio(decodeCount int) float64 {
}
}
func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
d.decodeCount++
if d.aliasDepth > 0 {
d.aliasCount++
@ -355,46 +476,50 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
failf("document contains excessive aliasing")
}
switch n.kind {
case documentNode:
if out.Type() == nodeType {
out.Set(reflect.ValueOf(n).Elem())
return true
}
switch n.Kind {
case DocumentNode:
return d.document(n, out)
case aliasNode:
case AliasNode:
return d.alias(n, out)
}
out, unmarshaled, good := d.prepare(n, out)
if unmarshaled {
return good
}
switch n.kind {
case scalarNode:
switch n.Kind {
case ScalarNode:
good = d.scalar(n, out)
case mappingNode:
case MappingNode:
good = d.mapping(n, out)
case sequenceNode:
case SequenceNode:
good = d.sequence(n, out)
default:
panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
panic("internal error: unknown node kind: " + strconv.Itoa(int(n.Kind)))
}
return good
}
func (d *decoder) document(n *node, out reflect.Value) (good bool) {
if len(n.children) == 1 {
func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
if len(n.Content) == 1 {
d.doc = n
d.unmarshal(n.children[0], out)
d.unmarshal(n.Content[0], out)
return true
}
return false
}
func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
if d.aliases[n] {
// TODO this could actually be allowed in some circumstances.
failf("anchor '%s' value contains itself", n.value)
failf("anchor '%s' value contains itself", n.Value)
}
d.aliases[n] = true
d.aliasDepth++
good = d.unmarshal(n.alias, out)
good = d.unmarshal(n.Alias, out)
d.aliasDepth--
delete(d.aliases, n)
return good
@ -408,15 +533,15 @@ func resetMap(out reflect.Value) {
}
}
func (d *decoder) scalar(n *node, out reflect.Value) bool {
func (d *decoder) scalar(n *Node, out reflect.Value) bool {
var tag string
var resolved interface{}
if n.tag == "" && !n.implicit {
tag = yaml_STR_TAG
resolved = n.value
if n.indicatedString() {
tag = strTag
resolved = n.Value
} else {
tag, resolved = resolve(n.tag, n.value)
if tag == yaml_BINARY_TAG {
tag, resolved = resolve(n.Tag, n.Value)
if tag == binaryTag {
data, err := base64.StdEncoding.DecodeString(resolved.(string))
if err != nil {
failf("!!binary value contains invalid base64 data")
@ -425,12 +550,14 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
}
}
if resolved == nil {
if out.Kind() == reflect.Map && !out.CanAddr() {
resetMap(out)
} else {
out.Set(reflect.Zero(out.Type()))
if out.CanAddr() {
switch out.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
out.Set(reflect.Zero(out.Type()))
return true
}
}
return true
return false
}
if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
// We've resolved to exactly the type we want, so use that.
@ -443,13 +570,13 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
if ok {
var text []byte
if tag == yaml_BINARY_TAG {
if tag == binaryTag {
text = []byte(resolved.(string))
} else {
// We let any value be unmarshaled into TextUnmarshaler.
// That might be more lax than we'd like, but the
// TextUnmarshaler itself should bowl out any dubious values.
text = []byte(n.value)
text = []byte(n.Value)
}
err := u.UnmarshalText(text)
if err != nil {
@ -460,47 +587,37 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
}
switch out.Kind() {
case reflect.String:
if tag == yaml_BINARY_TAG {
if tag == binaryTag {
out.SetString(resolved.(string))
return true
}
if resolved != nil {
out.SetString(n.value)
return true
}
out.SetString(n.Value)
return true
case reflect.Interface:
if resolved == nil {
out.Set(reflect.Zero(out.Type()))
} else if tag == yaml_TIMESTAMP_TAG {
// It looks like a timestamp but for backward compatibility
// reasons we set it as a string, so that code that unmarshals
// timestamp-like values into interface{} will continue to
// see a string and not a time.Time.
// TODO(v3) Drop this.
out.Set(reflect.ValueOf(n.value))
} else {
out.Set(reflect.ValueOf(resolved))
}
out.Set(reflect.ValueOf(resolved))
return true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
// This used to work in v2, but it's very unfriendly.
isDuration := out.Type() == durationType
switch resolved := resolved.(type) {
case int:
if !out.OverflowInt(int64(resolved)) {
if !isDuration && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case int64:
if !out.OverflowInt(resolved) {
if !isDuration && !out.OverflowInt(resolved) {
out.SetInt(resolved)
return true
}
case uint64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
case float64:
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
out.SetInt(int64(resolved))
return true
}
@ -541,6 +658,17 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
case bool:
out.SetBool(resolved)
return true
case string:
// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html).
// It only works if explicitly attempting to unmarshal into a typed bool value.
switch resolved {
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON":
out.SetBool(true)
return true
case "n", "N", "no", "No", "NO", "off", "Off", "OFF":
out.SetBool(false)
return true
}
}
case reflect.Float32, reflect.Float64:
switch resolved := resolved.(type) {
@ -563,13 +691,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool {
return true
}
case reflect.Ptr:
if out.Type().Elem() == reflect.TypeOf(resolved) {
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
elem := reflect.New(out.Type().Elem())
elem.Elem().Set(reflect.ValueOf(resolved))
out.Set(elem)
return true
}
panic("yaml internal error: please report the issue")
}
d.terror(n, tag, out)
return false
@ -582,8 +704,8 @@ func settableValueOf(i interface{}) reflect.Value {
return sv
}
func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
l := len(n.children)
func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
var iface reflect.Value
switch out.Kind() {
@ -598,7 +720,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
iface = out
out = settableValueOf(make([]interface{}, l))
default:
d.terror(n, yaml_SEQ_TAG, out)
d.terror(n, seqTag, out)
return false
}
et := out.Type().Elem()
@ -606,7 +728,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
j := 0
for i := 0; i < l; i++ {
e := reflect.New(et).Elem()
if ok := d.unmarshal(n.children[i], e); ok {
if ok := d.unmarshal(n.Content[i], e); ok {
out.Index(j).Set(e)
j++
}
@ -620,51 +742,65 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
return true
}
func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
l := len(n.Content)
if d.uniqueKeys {
nerrs := len(d.terrors)
for i := 0; i < l; i += 2 {
ni := n.Content[i]
for j := i + 2; j < l; j += 2 {
nj := n.Content[j]
if ni.Kind == nj.Kind && ni.Value == nj.Value {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
}
}
}
if len(d.terrors) > nerrs {
return false
}
}
switch out.Kind() {
case reflect.Struct:
return d.mappingStruct(n, out)
case reflect.Slice:
return d.mappingSlice(n, out)
case reflect.Map:
// okay
case reflect.Interface:
if d.mapType.Kind() == reflect.Map {
iface := out
out = reflect.MakeMap(d.mapType)
iface.Set(out)
iface := out
if isStringMap(n) {
out = reflect.MakeMap(d.stringMapType)
} else {
slicev := reflect.New(d.mapType).Elem()
if !d.mappingSlice(n, slicev) {
return false
}
out.Set(slicev)
return true
out = reflect.MakeMap(d.generalMapType)
}
iface.Set(out)
default:
d.terror(n, yaml_MAP_TAG, out)
d.terror(n, mapTag, out)
return false
}
outt := out.Type()
kt := outt.Key()
et := outt.Elem()
mapType := d.mapType
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
d.mapType = outt
stringMapType := d.stringMapType
generalMapType := d.generalMapType
if outt.Elem() == ifaceType {
if outt.Key().Kind() == reflect.String {
d.stringMapType = outt
} else if outt.Key() == ifaceType {
d.generalMapType = outt
}
}
if out.IsNil() {
out.Set(reflect.MakeMap(outt))
}
l := len(n.children)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
if isMerge(n.Content[i]) {
d.merge(n.Content[i+1], out)
continue
}
k := reflect.New(kt).Elem()
if d.unmarshal(n.children[i], k) {
if d.unmarshal(n.Content[i], k) {
kkind := k.Kind()
if kkind == reflect.Interface {
kkind = k.Elem().Kind()
@ -673,61 +809,34 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
failf("invalid map key: %#v", k.Interface())
}
e := reflect.New(et).Elem()
if d.unmarshal(n.children[i+1], e) {
d.setMapIndex(n.children[i+1], out, k, e)
if d.unmarshal(n.Content[i+1], e) {
out.SetMapIndex(k, e)
}
}
}
d.mapType = mapType
d.stringMapType = stringMapType
d.generalMapType = generalMapType
return true
}
func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) {
if d.strict && out.MapIndex(k) != zeroValue {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface()))
return
}
out.SetMapIndex(k, v)
}
func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
outt := out.Type()
if outt.Elem() != mapItemType {
d.terror(n, yaml_MAP_TAG, out)
func isStringMap(n *Node) bool {
if n.Kind != MappingNode {
return false
}
mapType := d.mapType
d.mapType = outt
var slice []MapItem
var l = len(n.children)
l := len(n.Content)
for i := 0; i < l; i += 2 {
if isMerge(n.children[i]) {
d.merge(n.children[i+1], out)
continue
}
item := MapItem{}
k := reflect.ValueOf(&item.Key).Elem()
if d.unmarshal(n.children[i], k) {
v := reflect.ValueOf(&item.Value).Elem()
if d.unmarshal(n.children[i+1], v) {
slice = append(slice, item)
}
if n.Content[i].ShortTag() != strTag {
return false
}
}
out.Set(reflect.ValueOf(slice))
d.mapType = mapType
return true
}
func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
sinfo, err := getStructInfo(out.Type())
if err != nil {
panic(err)
}
name := settableValueOf("")
l := len(n.children)
var inlineMap reflect.Value
var elemType reflect.Type
@ -737,23 +846,30 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
elemType = inlineMap.Type().Elem()
}
for _, index := range sinfo.InlineUnmarshalers {
field := d.fieldByIndex(n, out, index)
d.prepare(n, field)
}
var doneFields []bool
if d.strict {
if d.uniqueKeys {
doneFields = make([]bool, len(sinfo.FieldsList))
}
name := settableValueOf("")
l := len(n.Content)
for i := 0; i < l; i += 2 {
ni := n.children[i]
ni := n.Content[i]
if isMerge(ni) {
d.merge(n.children[i+1], out)
d.merge(n.Content[i+1], out)
continue
}
if !d.unmarshal(ni, name) {
continue
}
if info, ok := sinfo.FieldsMap[name.String()]; ok {
if d.strict {
if d.uniqueKeys {
if doneFields[info.Id] {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type()))
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
continue
}
doneFields[info.Id] = true
@ -762,18 +878,18 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) {
if info.Inline == nil {
field = out.Field(info.Num)
} else {
field = out.FieldByIndex(info.Inline)
field = d.fieldByIndex(n, out, info.Inline)
}
d.unmarshal(n.children[i+1], field)
d.unmarshal(n.Content[i+1], field)
} else if sinfo.InlineMap != -1 {
if inlineMap.IsNil() {
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
}
value := reflect.New(elemType).Elem()
d.unmarshal(n.children[i+1], value)
d.setMapIndex(n.children[i+1], inlineMap, name, value)
} else if d.strict {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type()))
d.unmarshal(n.Content[i+1], value)
inlineMap.SetMapIndex(name, value)
} else if d.knownFields {
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
}
}
return true
@ -783,24 +899,24 @@ func failWantMap() {
failf("map merge requires map or sequence of maps as the value")
}
func (d *decoder) merge(n *node, out reflect.Value) {
switch n.kind {
case mappingNode:
func (d *decoder) merge(n *Node, out reflect.Value) {
switch n.Kind {
case MappingNode:
d.unmarshal(n, out)
case aliasNode:
if n.alias != nil && n.alias.kind != mappingNode {
case AliasNode:
if n.Alias != nil && n.Alias.Kind != MappingNode {
failWantMap()
}
d.unmarshal(n, out)
case sequenceNode:
case SequenceNode:
// Step backwards as earlier nodes take precedence.
for i := len(n.children) - 1; i >= 0; i-- {
ni := n.children[i]
if ni.kind == aliasNode {
if ni.alias != nil && ni.alias.kind != mappingNode {
for i := len(n.Content) - 1; i >= 0; i-- {
ni := n.Content[i]
if ni.Kind == AliasNode {
if ni.Alias != nil && ni.Alias.Kind != MappingNode {
failWantMap()
}
} else if ni.kind != mappingNode {
} else if ni.Kind != MappingNode {
failWantMap()
}
d.unmarshal(ni, out)
@ -810,6 +926,6 @@ func (d *decoder) merge(n *node, out reflect.Value) {
}
}
func isMerge(n *node) bool {
return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
func isMerge(n *Node) bool {
return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
}

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -43,8 +65,13 @@ func put_break(emitter *yaml_emitter_t) bool {
default:
panic("unknown line break setting")
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and below and drop from everywhere else (see commented lines).
emitter.indention = true
return true
}
@ -97,8 +124,13 @@ func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
if !write(emitter, s, i) {
return false
}
if emitter.column == 0 {
emitter.space_above = true
}
emitter.column = 0
emitter.line++
// [Go] Do this here and above and drop from everywhere else (see commented lines).
emitter.indention = true
}
return true
}
@ -204,6 +236,10 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
}
} else if !indentless {
emitter.indent += emitter.best_indent
// [Go] If inside a block sequence item, discount the space taken by the indicator.
if emitter.best_indent > 2 && emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
emitter.indent -= 2
}
}
return true
}
@ -228,16 +264,22 @@ func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bo
return yaml_emitter_emit_document_end(emitter, event)
case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, true)
return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false)
case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true)
case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
return yaml_emitter_emit_flow_sequence_item(emitter, event, false)
return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, true)
return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false)
case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true)
case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
return yaml_emitter_emit_flow_mapping_key(emitter, event, false)
return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false)
case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
@ -298,6 +340,8 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t
emitter.column = 0
emitter.whitespace = true
emitter.indention = true
emitter.space_above = true
emitter.foot_indent = -1
if emitter.encoding != yaml_UTF8_ENCODING {
if !yaml_emitter_write_bom(emitter) {
@ -392,13 +436,22 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
return false
}
if emitter.canonical {
if emitter.canonical || true {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
}
if len(emitter.head_comment) > 0 {
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !put_break(emitter) {
return false
}
}
emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
return true
}
@ -425,7 +478,20 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
// Expect the root node.
func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
return yaml_emitter_emit_node(emitter, event, true, false, false, false)
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_emit_node(emitter, event, true, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect DOCUMENT-END.
@ -433,6 +499,12 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
if event.typ != yaml_DOCUMENT_END_EVENT {
return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
}
// [Go] Force document foot separation.
emitter.foot_indent = 0
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.foot_indent = -1
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -454,7 +526,7 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
}
// Expect a flow item node.
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
return false
@ -466,13 +538,15 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.canonical && !first {
if emitter.canonical && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.column == 0 || emitter.canonical && !first {
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -480,29 +554,62 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first {
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a flow key node.
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
if first {
if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
return false
@ -514,13 +621,18 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
}
if event.typ == yaml_MAPPING_END_EVENT {
if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
emitter.flow_level--
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
if emitter.canonical && !first {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -528,16 +640,33 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
emitter.state = emitter.states[len(emitter.states)-1]
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !first {
if !first && !trail {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if emitter.column == 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
}
if emitter.canonical || emitter.column > emitter.best_width {
if !yaml_emitter_write_indent(emitter) {
return false
@ -571,16 +700,41 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
return false
}
}
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE)
} else {
emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
}
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
return false
}
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block item node.
func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
if first {
if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) {
// [Go] The original logic here would not indent the sequence when inside a mapping.
// In Go we always indent it, but take the sequence indicator out of the indentation.
indentless := emitter.best_indent == 2 && emitter.mapping_context && (emitter.column == 0 || !emitter.indention)
original := emitter.indent
if !yaml_emitter_increase_indent(emitter, false, indentless) {
return false
}
if emitter.indent > original+2 {
emitter.indent -= 2
}
}
if event.typ == yaml_SEQUENCE_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
@ -589,6 +743,9 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
emitter.states = emitter.states[:len(emitter.states)-1]
return true
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !yaml_emitter_write_indent(emitter) {
return false
}
@ -596,7 +753,16 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
return false
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
return yaml_emitter_emit_node(emitter, event, false, true, false, false)
if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a block key node.
@ -606,6 +772,9 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
return false
}
}
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if event.typ == yaml_MAPPING_END_EVENT {
emitter.indent = emitter.indents[len(emitter.indents)-1]
emitter.indents = emitter.indents[:len(emitter.indents)-1]
@ -642,7 +811,16 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
}
}
emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
return yaml_emitter_emit_node(emitter, event, false, false, true, false)
if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
return false
}
if !yaml_emitter_process_line_comment(emitter) {
return false
}
if !yaml_emitter_process_foot_comment(emitter) {
return false
}
return true
}
// Expect a node.
@ -908,6 +1086,71 @@ func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
panic("unknown scalar style")
}
// Write a head comment.
func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
if len(emitter.tail_comment) > 0 {
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.tail_comment) {
return false
}
emitter.tail_comment = emitter.tail_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
}
if len(emitter.head_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.head_comment) {
return false
}
emitter.head_comment = emitter.head_comment[:0]
return true
}
// Write an line comment.
func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
if len(emitter.line_comment) == 0 {
return true
}
if !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
}
if !yaml_emitter_write_comment(emitter, emitter.line_comment) {
return false
}
emitter.line_comment = emitter.line_comment[:0]
return true
}
// Write a foot comment.
func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool {
if len(emitter.foot_comment) == 0 {
return true
}
if !yaml_emitter_write_indent(emitter) {
return false
}
if !yaml_emitter_write_comment(emitter, emitter.foot_comment) {
return false
}
emitter.foot_comment = emitter.foot_comment[:0]
emitter.foot_indent = emitter.indent
if emitter.foot_indent < 0 {
emitter.foot_indent = 0
}
return true
}
// Check if a %YAML directive is valid.
func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
if version_directive.major != 1 || version_directive.minor != 1 {
@ -987,6 +1230,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
flow_indicators = false
line_breaks = false
special_characters = false
tab_characters = false
leading_space = false
leading_break = false
@ -1055,7 +1299,9 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
}
}
if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
if value[i] == '\t' {
tab_characters = true
} else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
special_characters = true
}
if is_space(value, i) {
@ -1110,10 +1356,12 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || special_characters {
if space_break || tab_characters || special_characters {
emitter.scalar_data.flow_plain_allowed = false
emitter.scalar_data.block_plain_allowed = false
emitter.scalar_data.single_quoted_allowed = false
}
if space_break || special_characters {
emitter.scalar_data.block_allowed = false
}
if line_breaks {
@ -1137,6 +1385,19 @@ func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bo
emitter.tag_data.suffix = nil
emitter.scalar_data.value = nil
if len(event.head_comment) > 0 {
emitter.head_comment = event.head_comment
}
if len(event.line_comment) > 0 {
emitter.line_comment = event.line_comment
}
if len(event.foot_comment) > 0 {
emitter.foot_comment = event.foot_comment
}
if len(event.tail_comment) > 0 {
emitter.tail_comment = event.tail_comment
}
switch event.typ {
case yaml_ALIAS_EVENT:
if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
@ -1208,13 +1469,20 @@ func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
return false
}
}
if emitter.foot_indent == indent {
if !put_break(emitter) {
return false
}
}
for emitter.column < indent {
if !put(emitter, ' ') {
return false
}
}
emitter.whitespace = true
emitter.indention = true
//emitter.indention = true
emitter.space_above = false
emitter.foot_indent = -1
return true
}
@ -1311,7 +1579,7 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_
}
func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
if !emitter.whitespace {
if len(value) > 0 && !emitter.whitespace {
if !put(emitter, ' ') {
return false
}
@ -1341,7 +1609,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1358,7 +1626,9 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
}
}
emitter.whitespace = false
if len(value) > 0 {
emitter.whitespace = false
}
emitter.indention = false
if emitter.root_context {
emitter.open_ended = true
@ -1397,7 +1667,7 @@ func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []by
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1599,7 +1869,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !put_break(emitter) {
return false
}
emitter.indention = true
//emitter.indention = true
emitter.whitespace = true
breaks := true
for i := 0; i < len(value); {
@ -1607,7 +1877,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1637,7 +1907,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !put_break(emitter) {
return false
}
emitter.indention = true
//emitter.indention = true
emitter.whitespace = true
breaks := true
@ -1658,7 +1928,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
if !write_break(emitter, value, &i) {
return false
}
emitter.indention = true
//emitter.indention = true
breaks = true
} else {
if breaks {
@ -1683,3 +1953,40 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
}
return true
}
func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool {
breaks := false
pound := false
for i := 0; i < len(comment); {
if is_break(comment, i) {
if !write_break(emitter, comment, &i) {
return false
}
//emitter.indention = true
breaks = true
pound = false
} else {
if breaks && !yaml_emitter_write_indent(emitter) {
return false
}
if !pound {
if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) {
return false
}
pound = true
}
if !write(emitter, comment, &i) {
return false
}
emitter.indention = false
breaks = false
}
}
if !breaks && !put_break(emitter) {
return false
}
emitter.whitespace = true
//emitter.indention = true
return true
}

561
vendor/gopkg.in/yaml.v3/encode.go generated vendored Normal file
View File

@ -0,0 +1,561 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
"encoding"
"fmt"
"io"
"reflect"
"regexp"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
)
type encoder struct {
emitter yaml_emitter_t
event yaml_event_t
out []byte
flow bool
indent int
doneInit bool
}
func newEncoder() *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_string(&e.emitter, &e.out)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func newEncoderWithWriter(w io.Writer) *encoder {
e := &encoder{}
yaml_emitter_initialize(&e.emitter)
yaml_emitter_set_output_writer(&e.emitter, w)
yaml_emitter_set_unicode(&e.emitter, true)
return e
}
func (e *encoder) init() {
if e.doneInit {
return
}
if e.indent == 0 {
e.indent = 4
}
e.emitter.best_indent = e.indent
yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
e.emit()
e.doneInit = true
}
func (e *encoder) finish() {
e.emitter.open_ended = false
yaml_stream_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) destroy() {
yaml_emitter_delete(&e.emitter)
}
func (e *encoder) emit() {
// This will internally delete the e.event value.
e.must(yaml_emitter_emit(&e.emitter, &e.event))
}
func (e *encoder) must(ok bool) {
if !ok {
msg := e.emitter.problem
if msg == "" {
msg = "unknown problem generating YAML content"
}
failf("%s", msg)
}
}
func (e *encoder) marshalDoc(tag string, in reflect.Value) {
e.init()
var node *Node
if in.IsValid() {
node, _ = in.Interface().(*Node)
}
if node != nil && node.Kind == DocumentNode {
e.nodev(in)
} else {
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.emit()
e.marshal(tag, in)
yaml_document_end_event_initialize(&e.event, true)
e.emit()
}
}
func (e *encoder) marshal(tag string, in reflect.Value) {
tag = shortTag(tag)
if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
e.nilv()
return
}
iface := in.Interface()
switch value := iface.(type) {
case *Node:
e.nodev(in)
return
case time.Time:
e.timev(tag, in)
return
case *time.Time:
e.timev(tag, in.Elem())
return
case time.Duration:
e.stringv(tag, reflect.ValueOf(value.String()))
return
case Marshaler:
v, err := value.MarshalYAML()
if err != nil {
fail(err)
}
if v == nil {
e.nilv()
return
}
e.marshal(tag, reflect.ValueOf(v))
return
case encoding.TextMarshaler:
text, err := value.MarshalText()
if err != nil {
fail(err)
}
in = reflect.ValueOf(string(text))
case nil:
e.nilv()
return
}
switch in.Kind() {
case reflect.Interface:
e.marshal(tag, in.Elem())
case reflect.Map:
e.mapv(tag, in)
case reflect.Ptr:
e.marshal(tag, in.Elem())
case reflect.Struct:
e.structv(tag, in)
case reflect.Slice, reflect.Array:
e.slicev(tag, in)
case reflect.String:
e.stringv(tag, in)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.intv(tag, in)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
e.uintv(tag, in)
case reflect.Float32, reflect.Float64:
e.floatv(tag, in)
case reflect.Bool:
e.boolv(tag, in)
default:
panic("cannot marshal type: " + in.Type().String())
}
}
func (e *encoder) mapv(tag string, in reflect.Value) {
e.mappingv(tag, func() {
keys := keyList(in.MapKeys())
sort.Sort(keys)
for _, k := range keys {
e.marshal("", k)
e.marshal("", in.MapIndex(k))
}
})
}
func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
for _, num := range index {
for {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
}
v = v.Elem()
continue
}
break
}
v = v.Field(num)
}
return v
}
func (e *encoder) structv(tag string, in reflect.Value) {
sinfo, err := getStructInfo(in.Type())
if err != nil {
panic(err)
}
e.mappingv(tag, func() {
for _, info := range sinfo.FieldsList {
var value reflect.Value
if info.Inline == nil {
value = in.Field(info.Num)
} else {
value = e.fieldByIndex(in, info.Inline)
if !value.IsValid() {
continue
}
}
if info.OmitEmpty && isZero(value) {
continue
}
e.marshal("", reflect.ValueOf(info.Key))
e.flow = info.Flow
e.marshal("", value)
}
if sinfo.InlineMap >= 0 {
m := in.Field(sinfo.InlineMap)
if m.Len() > 0 {
e.flow = false
keys := keyList(m.MapKeys())
sort.Sort(keys)
for _, k := range keys {
if _, found := sinfo.FieldsMap[k.String()]; found {
panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
}
e.marshal("", k)
e.flow = false
e.marshal("", m.MapIndex(k))
}
}
}
})
}
func (e *encoder) mappingv(tag string, f func()) {
implicit := tag == ""
style := yaml_BLOCK_MAPPING_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)
e.emit()
f()
yaml_mapping_end_event_initialize(&e.event)
e.emit()
}
func (e *encoder) slicev(tag string, in reflect.Value) {
implicit := tag == ""
style := yaml_BLOCK_SEQUENCE_STYLE
if e.flow {
e.flow = false
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
e.emit()
n := in.Len()
for i := 0; i < n; i++ {
e.marshal("", in.Index(i))
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.emit()
}
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
//
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
// in YAML 1.2 and by this package, but these should be marshalled quoted for
// the time being for compatibility with other parsers.
func isBase60Float(s string) (result bool) {
// Fast path.
if s == "" {
return false
}
c := s[0]
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
return false
}
// Do the full match.
return base60float.MatchString(s)
}
// From http://yaml.org/type/float.html, except the regular expression there
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
// isOldBool returns whether s is bool notation as defined in YAML 1.1.
//
// We continue to force strings that YAML 1.1 would interpret as booleans to be
// rendered as quotes strings so that the marshalled output valid for YAML 1.1
// parsing.
func isOldBool(s string) (result bool) {
switch s {
case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
"n", "N", "no", "No", "NO", "off", "Off", "OFF":
return true
default:
return false
}
}
func (e *encoder) stringv(tag string, in reflect.Value) {
var style yaml_scalar_style_t
s := in.String()
canUsePlain := true
switch {
case !utf8.ValidString(s):
if tag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
s = encodeBase64(s)
case tag == "":
// Check to see if it would resolve to a specific
// tag when encoded unquoted. If it doesn't,
// there's no need to quote it.
rtag, _ := resolve("", s)
canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
}
// Note: it's possible for user code to emit invalid YAML
// if they explicitly specify a tag and a string containing
// text that's incompatible with that tag.
switch {
case strings.Contains(s, "\n"):
if e.flow {
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
} else {
style = yaml_LITERAL_SCALAR_STYLE
}
case canUsePlain:
style = yaml_PLAIN_SCALAR_STYLE
default:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
}
func (e *encoder) boolv(tag string, in reflect.Value) {
var s string
if in.Bool() {
s = "true"
} else {
s = "false"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) intv(tag string, in reflect.Value) {
s := strconv.FormatInt(in.Int(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) uintv(tag string, in reflect.Value) {
s := strconv.FormatUint(in.Uint(), 10)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) timev(tag string, in reflect.Value) {
t := in.Interface().(time.Time)
s := t.Format(time.RFC3339Nano)
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) floatv(tag string, in reflect.Value) {
// Issue #352: When formatting, use the precision of the underlying value
precision := 64
if in.Kind() == reflect.Float32 {
precision = 32
}
s := strconv.FormatFloat(in.Float(), 'g', -1, precision)
switch s {
case "+Inf":
s = ".inf"
case "-Inf":
s = "-.inf"
case "NaN":
s = ".nan"
}
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) nilv() {
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
}
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
implicit := tag == ""
if !implicit {
tag = longTag(tag)
}
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
e.event.head_comment = head
e.event.line_comment = line
e.event.foot_comment = foot
e.event.tail_comment = tail
e.emit()
}
func (e *encoder) nodev(in reflect.Value) {
e.node(in.Interface().(*Node), "")
}
func (e *encoder) node(node *Node, tail string) {
// If the tag was not explicitly requested, and dropping it won't change the
// implicit tag of the value, don't include it in the presentation.
var tag = node.Tag
var stag = shortTag(tag)
var rtag string
var forceQuoting bool
if tag != "" && node.Style&TaggedStyle == 0 {
if node.Kind == ScalarNode {
if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
tag = ""
} else {
rtag, _ = resolve("", node.Value)
if rtag == stag {
tag = ""
} else if stag == strTag {
tag = ""
forceQuoting = true
}
}
} else {
switch node.Kind {
case MappingNode:
rtag = mapTag
case SequenceNode:
rtag = seqTag
}
if rtag == stag {
tag = ""
}
}
}
switch node.Kind {
case DocumentNode:
yaml_document_start_event_initialize(&e.event, nil, nil, true)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
yaml_document_end_event_initialize(&e.event, true)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case SequenceNode:
style := yaml_BLOCK_SEQUENCE_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_SEQUENCE_STYLE
}
e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style))
e.event.head_comment = []byte(node.HeadComment)
e.emit()
for _, node := range node.Content {
e.node(node, "")
}
e.must(yaml_sequence_end_event_initialize(&e.event))
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case MappingNode:
style := yaml_BLOCK_MAPPING_STYLE
if node.Style&FlowStyle != 0 {
style = yaml_FLOW_MAPPING_STYLE
}
yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)
e.event.tail_comment = []byte(tail)
e.event.head_comment = []byte(node.HeadComment)
e.emit()
// The tail logic below moves the foot comment of prior keys to the following key,
// since the value for each key may be a nested structure and the foot needs to be
// processed only the entirety of the value is streamed. The last tail is processed
// with the mapping end event.
var tail string
for i := 0; i+1 < len(node.Content); i += 2 {
k := node.Content[i]
foot := k.FootComment
if foot != "" {
kopy := *k
kopy.FootComment = ""
k = &kopy
}
e.node(k, tail)
tail = foot
v := node.Content[i+1]
e.node(v, "")
}
yaml_mapping_end_event_initialize(&e.event)
e.event.tail_comment = []byte(tail)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case AliasNode:
yaml_alias_event_initialize(&e.event, []byte(node.Value))
e.event.head_comment = []byte(node.HeadComment)
e.event.line_comment = []byte(node.LineComment)
e.event.foot_comment = []byte(node.FootComment)
e.emit()
case ScalarNode:
value := node.Value
if !utf8.ValidString(value) {
if tag == binaryTag {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if tag != "" {
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
}
// It can't be encoded directly as YAML so use a binary tag
// and encode it as base64.
tag = binaryTag
value = encodeBase64(value)
}
style := yaml_PLAIN_SCALAR_STYLE
switch {
case node.Style&DoubleQuotedStyle != 0:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
case node.Style&SingleQuotedStyle != 0:
style = yaml_SINGLE_QUOTED_SCALAR_STYLE
case node.Style&LiteralStyle != 0:
style = yaml_LITERAL_SCALAR_STYLE
case node.Style&FoldedStyle != 0:
style = yaml_FOLDED_SCALAR_STYLE
case strings.Contains(value, "\n"):
style = yaml_LITERAL_SCALAR_STYLE
case forceQuoting:
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
}
e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
}
}

View File

@ -1,4 +1,4 @@
module "gopkg.in/yaml.v2"
module "gopkg.in/yaml.v3"
require (
"gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -45,11 +67,46 @@ import (
// Peek the next token in the token queue.
func peek_token(parser *yaml_parser_t) *yaml_token_t {
if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
return &parser.tokens[parser.tokens_head]
token := &parser.tokens[parser.tokens_head]
yaml_parser_unfold_comments(parser, token)
return token
}
return nil
}
// yaml_parser_unfold_comments walks through the comments queue and joins all
// comments behind the position of the provided token into the respective
// top-level comment slices in the parser.
func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) {
for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index {
comment := &parser.comments[parser.comments_head]
if len(comment.head) > 0 {
if token.typ == yaml_BLOCK_END_TOKEN {
// No heads on ends, so keep comment.head for a follow up token.
break
}
if len(parser.head_comment) > 0 {
parser.head_comment = append(parser.head_comment, '\n')
}
parser.head_comment = append(parser.head_comment, comment.head...)
}
if len(comment.foot) > 0 {
if len(parser.foot_comment) > 0 {
parser.foot_comment = append(parser.foot_comment, '\n')
}
parser.foot_comment = append(parser.foot_comment, comment.foot...)
}
if len(comment.line) > 0 {
if len(parser.line_comment) > 0 {
parser.line_comment = append(parser.line_comment, '\n')
}
parser.line_comment = append(parser.line_comment, comment.line...)
}
*comment = yaml_comment_t{}
parser.comments_head++
}
}
// Remove the next token from the queue (must be called after peek_token).
func skip_token(parser *yaml_parser_t) {
parser.token_available = false
@ -224,10 +281,32 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
parser.state = yaml_PARSE_BLOCK_NODE_STATE
var head_comment []byte
if len(parser.head_comment) > 0 {
// [Go] Scan the header comment backwards, and if an empty line is found, break
// the header so the part before the last empty line goes into the
// document header, while the bottom of it goes into a follow up event.
for i := len(parser.head_comment) - 1; i > 0; i-- {
if parser.head_comment[i] == '\n' {
if i == len(parser.head_comment)-1 {
head_comment = parser.head_comment[:i]
parser.head_comment = parser.head_comment[i+1:]
break
} else if parser.head_comment[i-1] == '\n' {
head_comment = parser.head_comment[:i-1]
parser.head_comment = parser.head_comment[i+1:]
break
}
}
}
}
*event = yaml_event_t{
typ: yaml_DOCUMENT_START_EVENT,
start_mark: token.start_mark,
end_mark: token.end_mark,
head_comment: head_comment,
}
} else if token.typ != yaml_STREAM_END_TOKEN {
@ -284,6 +363,7 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
if token == nil {
return false
}
if token.typ == yaml_VERSION_DIRECTIVE_TOKEN ||
token.typ == yaml_TAG_DIRECTIVE_TOKEN ||
token.typ == yaml_DOCUMENT_START_TOKEN ||
@ -327,9 +407,25 @@ func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t)
end_mark: end_mark,
implicit: implicit,
}
yaml_parser_set_event_comments(parser, event)
if len(event.head_comment) > 0 && len(event.foot_comment) == 0 {
event.foot_comment = event.head_comment
event.head_comment = nil
}
return true
}
func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) {
event.head_comment = parser.head_comment
event.line_comment = parser.line_comment
event.foot_comment = parser.foot_comment
parser.head_comment = nil
parser.line_comment = nil
parser.foot_comment = nil
parser.tail_comment = nil
parser.stem_comment = nil
}
// Parse the productions:
// block_node_or_indentless_sequence ::=
// ALIAS
@ -373,6 +469,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
end_mark: token.end_mark,
anchor: token.value,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -486,6 +583,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
quoted_implicit: quoted_implicit,
style: yaml_style_t(token.style),
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -502,6 +600,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
}
yaml_parser_set_event_comments(parser, event)
return true
}
if token.typ == yaml_FLOW_MAPPING_START_TOKEN {
@ -516,6 +615,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_FLOW_MAPPING_STYLE),
}
yaml_parser_set_event_comments(parser, event)
return true
}
if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
@ -530,6 +630,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
implicit: implicit,
style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
}
if parser.stem_comment != nil {
event.head_comment = parser.stem_comment
parser.stem_comment = nil
}
return true
}
if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN {
@ -590,11 +694,25 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
if token.typ == yaml_BLOCK_ENTRY_TOKEN {
mark := token.end_mark
prior_head := len(parser.head_comment)
skip_token(parser)
token = peek_token(parser)
if token == nil {
return false
}
if prior_head > 0 && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
// [Go] It's a sequence under a sequence entry, so the former head comment
// is for the list itself, not the first list item under it.
parser.stem_comment = parser.head_comment[:prior_head]
if len(parser.head_comment) == prior_head {
parser.head_comment = nil
} else {
// Copy suffix to prevent very strange bugs if someone ever appends
// further bytes to the prefix in the stem_comment slice above.
parser.head_comment = append([]byte(nil), parser.head_comment[prior_head+1:]...)
}
}
if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN {
parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE)
return yaml_parser_parse_node(parser, event, true, false)
@ -684,6 +802,19 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
return false
}
// [Go] A tail comment was left from the prior mapping value processed. Emit an event
// as it needs to be processed with that value and not the following key.
if len(parser.tail_comment) > 0 {
*event = yaml_event_t{
typ: yaml_TAIL_COMMENT_EVENT,
start_mark: token.start_mark,
end_mark: token.end_mark,
foot_comment: parser.tail_comment,
}
parser.tail_comment = nil
return true
}
if token.typ == yaml_KEY_TOKEN {
mark := token.end_mark
skip_token(parser)
@ -709,6 +840,7 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}
@ -820,6 +952,7 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
@ -959,6 +1092,7 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
return true
}

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -95,7 +117,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
// [Go] This function was changed to guarantee the requested length size at EOF.
// The fact we need to do this is pretty awful, but the description above implies
// for that to be the case, and there are tests
// for that to be the case, and there are tests
// If the EOF flag is set and the raw buffer is empty, do nothing.
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -34,18 +49,14 @@ func init() {
tag string
l []string
}{
{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", yaml_MERGE_TAG, []string{"<<"}},
{true, boolTag, []string{"true", "True", "TRUE"}},
{false, boolTag, []string{"false", "False", "FALSE"}},
{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
{"<<", mergeTag, []string{"<<"}},
}
m := resolveMap
@ -56,11 +67,37 @@ func init() {
}
}
const (
nullTag = "!!null"
boolTag = "!!bool"
strTag = "!!str"
intTag = "!!int"
floatTag = "!!float"
timestampTag = "!!timestamp"
seqTag = "!!seq"
mapTag = "!!map"
binaryTag = "!!binary"
mergeTag = "!!merge"
)
var longTags = make(map[string]string)
var shortTags = make(map[string]string)
func init() {
for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
ltag := longTag(stag)
longTags[stag] = ltag
shortTags[ltag] = stag
}
}
const longTagPrefix = "tag:yaml.org,2002:"
func shortTag(tag string) string {
// TODO This can easily be made faster and produce less garbage.
if strings.HasPrefix(tag, longTagPrefix) {
if stag, ok := shortTags[tag]; ok {
return stag
}
return "!!" + tag[len(longTagPrefix):]
}
return tag
@ -68,6 +105,9 @@ func shortTag(tag string) string {
func longTag(tag string) string {
if strings.HasPrefix(tag, "!!") {
if ltag, ok := longTags[tag]; ok {
return ltag
}
return longTagPrefix + tag[2:]
}
return tag
@ -75,7 +115,7 @@ func longTag(tag string) string {
func resolvableTag(tag string) bool {
switch tag {
case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG:
case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
return true
}
return false
@ -84,23 +124,24 @@ func resolvableTag(tag string) bool {
var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
func resolve(tag string, in string) (rtag string, out interface{}) {
tag = shortTag(tag)
if !resolvableTag(tag) {
return tag, in
}
defer func() {
switch tag {
case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
case "", rtag, strTag, binaryTag:
return
case yaml_FLOAT_TAG:
if rtag == yaml_INT_TAG {
case floatTag:
if rtag == intTag {
switch v := out.(type) {
case int64:
rtag = yaml_FLOAT_TAG
rtag = floatTag
out = float64(v)
return
case int:
rtag = yaml_FLOAT_TAG
rtag = floatTag
out = float64(v)
return
}
@ -115,7 +156,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
if in != "" {
hint = resolveTable[in[0]]
}
if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
if hint != 0 && tag != strTag && tag != binaryTag {
// Handle things we can lookup in a map.
if item, ok := resolveMap[in]; ok {
return item.tag, item.value
@ -133,17 +174,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
// Not in the map, so maybe a normal float.
floatv, err := strconv.ParseFloat(in, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
return floatTag, floatv
}
case 'D', 'S':
// Int, float, or timestamp.
// Only try values as a timestamp if the value is unquoted or there's an explicit
// !!timestamp tag.
if tag == "" || tag == yaml_TIMESTAMP_TAG {
if tag == "" || tag == timestampTag {
t, ok := parseTimestamp(in)
if ok {
return yaml_TIMESTAMP_TAG, t
return timestampTag, t
}
}
@ -151,49 +192,76 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
intv, err := strconv.ParseInt(plain, 0, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain, 0, 64)
if err == nil {
return yaml_INT_TAG, uintv
return intTag, uintv
}
if yamlStyleFloat.MatchString(plain) {
floatv, err := strconv.ParseFloat(plain, 64)
if err == nil {
return yaml_FLOAT_TAG, floatv
return floatTag, floatv
}
}
if strings.HasPrefix(plain, "0b") {
intv, err := strconv.ParseInt(plain[2:], 2, 64)
if err == nil {
if intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
if err == nil {
return yaml_INT_TAG, uintv
return intTag, uintv
}
} else if strings.HasPrefix(plain, "-0b") {
intv, err := strconv.ParseInt("-" + plain[3:], 2, 64)
intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
if err == nil {
if true || intv == int64(int(intv)) {
return yaml_INT_TAG, int(intv)
return intTag, int(intv)
} else {
return yaml_INT_TAG, intv
return intTag, intv
}
}
}
// Octals as introduced in version 1.2 of the spec.
// Octals from the 1.1 spec, spelled as 0777, are still
// decoded by default in v3 as well for compatibility.
// May be dropped in v4 depending on how usage evolves.
if strings.HasPrefix(plain, "0o") {
intv, err := strconv.ParseInt(plain[2:], 8, 64)
if err == nil {
if intv == int64(int(intv)) {
return intTag, int(intv)
} else {
return intTag, intv
}
}
uintv, err := strconv.ParseUint(plain[2:], 8, 64)
if err == nil {
return intTag, uintv
}
} else if strings.HasPrefix(plain, "-0o") {
intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
if err == nil {
if true || intv == int64(int(intv)) {
return intTag, int(intv)
} else {
return intTag, intv
}
}
}
default:
panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
}
}
return yaml_STR_TAG, in
return strTag, in
}
// encodeBase64 encodes s as base64 that is broken up into multiple lines

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -489,6 +511,9 @@ func cache(parser *yaml_parser_t, length int) bool {
// Advance the buffer pointer.
func skip(parser *yaml_parser_t) {
if !is_blank(parser.buffer, parser.buffer_pos) {
parser.newlines = 0
}
parser.mark.index++
parser.mark.column++
parser.unread--
@ -502,17 +527,22 @@ func skip_line(parser *yaml_parser_t) {
parser.mark.line++
parser.unread -= 2
parser.buffer_pos += 2
parser.newlines++
} else if is_break(parser.buffer, parser.buffer_pos) {
parser.mark.index++
parser.mark.column = 0
parser.mark.line++
parser.unread--
parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
parser.newlines++
}
}
// Copy a character to a string buffer and advance pointers.
func read(parser *yaml_parser_t, s []byte) []byte {
if !is_blank(parser.buffer, parser.buffer_pos) {
parser.newlines = 0
}
w := width(parser.buffer[parser.buffer_pos])
if w == 0 {
panic("invalid character sequence")
@ -564,6 +594,7 @@ func read_line(parser *yaml_parser_t, s []byte) []byte {
parser.mark.column = 0
parser.mark.line++
parser.unread--
parser.newlines++
return s
}
@ -626,9 +657,13 @@ func trace(args ...interface{}) func() {
func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
// While we need more tokens to fetch, do it.
for {
if parser.tokens_head != len(parser.tokens) {
// If queue is non-empty, check if any potential simple key may
// occupy the head position.
// [Go] The comment parsing logic requires a lookahead of two tokens
// so that foot comments may be parsed in time of associating them
// with the tokens that are parsed before them, and also for line
// comments to be transformed into head comments in some edge cases.
if parser.tokens_head < len(parser.tokens)-2 {
// If a potential simple key is at the head position, we need to fetch
// the next token to disambiguate it.
head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
if !ok {
break
@ -649,7 +684,7 @@ func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
}
// The dispatcher for token fetchers.
func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
// Ensure that the buffer is initialized.
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
@ -660,13 +695,19 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
return yaml_parser_fetch_stream_start(parser)
}
scan_mark := parser.mark
// Eat whitespaces and comments until we reach the next token.
if !yaml_parser_scan_to_next_token(parser) {
return false
}
// [Go] While unrolling indents, transform the head comments of prior
// indentation levels observed after scan_start into foot comments at
// the respective indexes.
// Check the indentation level against the current column.
if !yaml_parser_unroll_indent(parser, parser.mark.column) {
if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) {
return false
}
@ -699,6 +740,21 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)
}
comment_mark := parser.mark
if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') {
// Associate any following comments with the prior token.
comment_mark = parser.tokens[len(parser.tokens)-1].start_mark
}
defer func() {
if !ok {
return
}
if !yaml_parser_scan_line_comment(parser, comment_mark) {
ok = false
return
}
}()
// Is it the flow sequence start indicator?
if buf[pos] == '[' {
return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)
@ -792,7 +848,7 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
// if it is followed by a non-space character.
//
// The last rule is more restrictive than the specification requires.
// [Go] Make this logic more reasonable.
// [Go] TODO Make this logic more reasonable.
//switch parser.buffer[parser.buffer_pos] {
//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':
//}
@ -965,19 +1021,49 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml
// Pop indentation levels from the indents stack until the current level
// becomes less or equal to the column. For each indentation level, append
// the BLOCK-END token.
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool {
// In the flow context, do nothing.
if parser.flow_level > 0 {
return true
}
block_mark := scan_mark
block_mark.index--
// Loop through the indentation levels in the stack.
for parser.indent > column {
// [Go] Reposition the end token before potential following
// foot comments of parent blocks. For that, search
// backwards for recent comments that were at the same
// indent as the block that is ending now.
stop_index := block_mark.index
for i := len(parser.comments) - 1; i >= 0; i-- {
comment := &parser.comments[i]
if comment.end_mark.index < stop_index {
// Don't go back beyond the start of the comment/whitespace scan, unless column < 0.
// If requested indent column is < 0, then the document is over and everything else
// is a foot anyway.
break
}
if comment.start_mark.column == parser.indent+1 {
// This is a good match. But maybe there's a former comment
// at that same indent level, so keep searching.
block_mark = comment.start_mark
}
// While the end of the former comment matches with
// the start of the following one, we know there's
// nothing in between and scanning is still safe.
stop_index = comment.scan_mark.index
}
// Create a token and append it to the queue.
token := yaml_token_t{
typ: yaml_BLOCK_END_TOKEN,
start_mark: parser.mark,
end_mark: parser.mark,
start_mark: block_mark,
end_mark: block_mark,
}
yaml_insert_token(parser, -1, &token)
@ -1026,7 +1112,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
}
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1050,7 +1136,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1074,7 +1160,7 @@ func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
// Produce the DOCUMENT-START or DOCUMENT-END token.
func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {
// Reset the indentation level.
if !yaml_parser_unroll_indent(parser, -1) {
if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
return false
}
@ -1107,6 +1193,7 @@ func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_
// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {
// The indicators '[' and '{' may start a simple key.
if !yaml_parser_save_simple_key(parser) {
return false
@ -1442,6 +1529,8 @@ func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {
// Eat whitespaces and comments until the next token is found.
func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
scan_mark := parser.mark
// Until the next token is not found.
for {
// Allow the BOM mark to start a line.
@ -1468,13 +1557,33 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
}
}
// Check if we just had a line comment under a sequence entry that
// looks more like a header to the following content. Similar to this:
//
// - # The comment
// - Some data
//
// If so, transform the line comment to a head comment and reposition.
if len(parser.comments) > 0 && len(parser.tokens) > 1 {
tokenA := parser.tokens[len(parser.tokens)-2]
tokenB := parser.tokens[len(parser.tokens)-1]
comment := &parser.comments[len(parser.comments)-1]
if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) {
// If it was in the prior line, reposition so it becomes a
// header of the follow up token. Otherwise, keep it in place
// so it becomes a header of the former.
comment.head = comment.line
comment.line = nil
if comment.start_mark.line == parser.mark.line-1 {
comment.token_mark = parser.mark
}
}
}
// Eat a comment until a line break.
if parser.buffer[parser.buffer_pos] == '#' {
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if !yaml_parser_scan_comments(parser, scan_mark) {
return false
}
}
@ -1572,6 +1681,10 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool
}
if parser.buffer[parser.buffer_pos] == '#' {
// [Go] Discard this inline comment for the time being.
//if !yaml_parser_scan_line_comment(parser, start_mark) {
// return false
//}
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@ -1987,7 +2100,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte
// '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
// '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
// '%'.
// [Go] Convert this into more reasonable logic.
// [Go] TODO Convert this into more reasonable logic.
for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||
parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||
parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||
@ -2142,6 +2255,10 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
}
}
if parser.buffer[parser.buffer_pos] == '#' {
// TODO Test this and then re-enable it.
//if !yaml_parser_scan_line_comment(parser, start_mark) {
// return false
//}
for !is_breakz(parser.buffer, parser.buffer_pos) {
skip(parser)
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@ -2709,3 +2826,200 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b
}
return true
}
func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool {
if parser.newlines > 0 {
return true
}
var start_mark yaml_mark_t
var text []byte
for peek := 0; peek < 512; peek++ {
if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
break
}
if is_blank(parser.buffer, parser.buffer_pos+peek) {
continue
}
if parser.buffer[parser.buffer_pos+peek] == '#' {
seen := parser.mark.index+peek
for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if is_breakz(parser.buffer, parser.buffer_pos) {
if parser.mark.index >= seen {
break
}
if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
return false
}
skip_line(parser)
} else {
if parser.mark.index >= seen {
if len(text) == 0 {
start_mark = parser.mark
}
text = append(text, parser.buffer[parser.buffer_pos])
}
skip(parser)
}
}
}
break
}
if len(text) > 0 {
parser.comments = append(parser.comments, yaml_comment_t{
token_mark: token_mark,
start_mark: start_mark,
line: text,
})
}
return true
}
func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool {
token := parser.tokens[len(parser.tokens)-1]
if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 {
token = parser.tokens[len(parser.tokens)-2]
}
var token_mark = token.start_mark
var start_mark yaml_mark_t
var recent_empty = false
var first_empty = parser.newlines <= 1
var line = parser.mark.line
var column = parser.mark.column
var text []byte
// The foot line is the place where a comment must start to
// still be considered as a foot of the prior content.
// If there's some content in the currently parsed line, then
// the foot is the line below it.
var foot_line = -1
if scan_mark.line > 0 {
foot_line = parser.mark.line-parser.newlines+1
if parser.newlines == 0 && parser.mark.column > 1 {
foot_line++
}
}
var peek = 0
for ; peek < 512; peek++ {
if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
break
}
column++
if is_blank(parser.buffer, parser.buffer_pos+peek) {
continue
}
c := parser.buffer[parser.buffer_pos+peek]
if is_breakz(parser.buffer, parser.buffer_pos+peek) || parser.flow_level > 0 && (c == ']' || c == '}') {
// Got line break or terminator.
if !recent_empty {
if first_empty && (start_mark.line == foot_line || start_mark.column-1 < parser.indent) {
// This is the first empty line and there were no empty lines before,
// so this initial part of the comment is a foot of the prior token
// instead of being a head for the following one. Split it up.
if len(text) > 0 {
if start_mark.column-1 < parser.indent {
// If dedented it's unrelated to the prior token.
token_mark = start_mark
}
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: token_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
foot: text,
})
scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
token_mark = scan_mark
text = nil
}
} else {
if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 {
text = append(text, '\n')
}
}
}
if !is_break(parser.buffer, parser.buffer_pos+peek) {
break
}
first_empty = false
recent_empty = true
column = 0
line++
continue
}
if len(text) > 0 && column < parser.indent+1 && column != start_mark.column {
// The comment at the different indentation is a foot of the
// preceding data rather than a head of the upcoming one.
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: token_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek, line, column},
foot: text,
})
scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
token_mark = scan_mark
text = nil
}
if parser.buffer[parser.buffer_pos+peek] != '#' {
break
}
if len(text) == 0 {
start_mark = yaml_mark_t{parser.mark.index + peek, line, column}
} else {
text = append(text, '\n')
}
recent_empty = false
// Consume until after the consumed comment line.
seen := parser.mark.index+peek
for {
if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
return false
}
if is_breakz(parser.buffer, parser.buffer_pos) {
if parser.mark.index >= seen {
break
}
if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
return false
}
skip_line(parser)
} else {
if parser.mark.index >= seen {
text = append(text, parser.buffer[parser.buffer_pos])
}
skip(parser)
}
}
peek = 0
column = 0
line = parser.mark.line
}
if len(text) > 0 {
parser.comments = append(parser.comments, yaml_comment_t{
scan_mark: scan_mark,
token_mark: start_mark,
start_mark: start_mark,
end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column},
head: text,
})
}
return true
}

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package yaml
import (
@ -37,8 +52,10 @@ func (l keyList) Less(i, j int) bool {
return ak < bk
}
ar, br := []rune(a.String()), []rune(b.String())
digits := false
for i := 0; i < len(ar) && i < len(br); i++ {
if ar[i] == br[i] {
digits = unicode.IsDigit(ar[i])
continue
}
al := unicode.IsLetter(ar[i])
@ -47,12 +64,16 @@ func (l keyList) Less(i, j int) bool {
return ar[i] < br[i]
}
if al || bl {
return bl
if digits {
return al
} else {
return bl
}
}
var ai, bi int
var an, bn int64
if ar[i] == '0' || br[i] == '0' {
for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- {
if ar[j] != '0' {
an = 1
bn = 1

48
vendor/gopkg.in/yaml.v3/writerc.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
// Set the writer error and return false.
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
emitter.error = yaml_WRITER_ERROR
emitter.problem = problem
return false
}
// Flush the output buffer.
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
if emitter.write_handler == nil {
panic("write handler not set")
}
// Check if the buffer is empty.
if emitter.buffer_pos == 0 {
return true
}
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
}
emitter.buffer_pos = 0
return true
}

View File

@ -1,3 +1,18 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package yaml implements YAML support for the Go language.
//
// Source code and other details for the project are available at GitHub:
@ -13,23 +28,16 @@ import (
"reflect"
"strings"
"sync"
"unicode/utf8"
)
// MapSlice encodes and decodes as a YAML map.
// The order of keys is preserved when encoding and decoding.
type MapSlice []MapItem
// MapItem is an item in a MapSlice.
type MapItem struct {
Key, Value interface{}
// The Unmarshaler interface may be implemented by types to customize their
// behavior when being unmarshaled from a YAML document.
type Unmarshaler interface {
UnmarshalYAML(value *Node) error
}
// The Unmarshaler interface may be implemented by types to customize their
// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
// method receives a function that may be called to unmarshal the original
// YAML value into a field or variable. It is safe to call the unmarshal
// function parameter more than once if necessary.
type Unmarshaler interface {
type obsoleteUnmarshaler interface {
UnmarshalYAML(unmarshal func(interface{}) error) error
}
@ -81,18 +89,10 @@ func Unmarshal(in []byte, out interface{}) (err error) {
return unmarshal(in, out, false)
}
// UnmarshalStrict is like Unmarshal except that any fields that are found
// in the data that do not have corresponding struct members, or mapping
// keys that are duplicates, will result in
// an error.
func UnmarshalStrict(in []byte, out interface{}) (err error) {
return unmarshal(in, out, true)
}
// A Decoder reads and decodes YAML values from an input stream.
// A Decorder reads and decodes YAML values from an input stream.
type Decoder struct {
strict bool
parser *parser
parser *parser
knownFields bool
}
// NewDecoder returns a new decoder that reads from r.
@ -105,10 +105,10 @@ func NewDecoder(r io.Reader) *Decoder {
}
}
// SetStrict sets whether strict decoding behaviour is enabled when
// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict.
func (dec *Decoder) SetStrict(strict bool) {
dec.strict = strict
// KnownFields ensures that the keys in decoded mappings to
// exist as fields in the struct being decoded into.
func (dec *Decoder) KnownFields(enable bool) {
dec.knownFields = enable
}
// Decode reads the next YAML-encoded value from its input
@ -117,7 +117,8 @@ func (dec *Decoder) SetStrict(strict bool) {
// See the documentation for Unmarshal for details about the
// conversion of YAML into a Go value.
func (dec *Decoder) Decode(v interface{}) (err error) {
d := newDecoder(dec.strict)
d := newDecoder()
d.knownFields = dec.knownFields
defer handleErr(&err)
node := dec.parser.parse()
if node == nil {
@ -134,9 +135,27 @@ func (dec *Decoder) Decode(v interface{}) (err error) {
return nil
}
// Decode decodes the node and stores its data into the value pointed to by v.
//
// See the documentation for Unmarshal for details about the
// conversion of YAML into a Go value.
func (n *Node) Decode(v interface{}) (err error) {
d := newDecoder()
defer handleErr(&err)
out := reflect.ValueOf(v)
if out.Kind() == reflect.Ptr && !out.IsNil() {
out = out.Elem()
}
d.unmarshal(n, out)
if len(d.terrors) > 0 {
return &TypeError{d.terrors}
}
return nil
}
func unmarshal(in []byte, out interface{}, strict bool) (err error) {
defer handleErr(&err)
d := newDecoder(strict)
d := newDecoder()
p := newParser(in)
defer p.destroy()
node := p.parse()
@ -233,6 +252,14 @@ func (e *Encoder) Encode(v interface{}) (err error) {
return nil
}
// SetIndent changes the used indentation used when encoding.
func (e *Encoder) SetIndent(spaces int) {
if spaces < 0 {
panic("yaml: cannot indent to a negative number of spaces")
}
e.encoder.indent = spaces
}
// Close closes the encoder by writing any remaining data.
// It does not write a stream terminating string "...".
func (e *Encoder) Close() (err error) {
@ -275,6 +302,150 @@ func (e *TypeError) Error() string {
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
}
type Kind uint32
const (
DocumentNode Kind = 1 << iota
SequenceNode
MappingNode
ScalarNode
AliasNode
)
type Style uint32
const (
TaggedStyle Style = 1 << iota
DoubleQuotedStyle
SingleQuotedStyle
LiteralStyle
FoldedStyle
FlowStyle
)
// Node represents an element in the YAML document hierarchy. While documents
// are typically encoded and decoded into higher level types, such as structs
// and maps, Node is an intermediate representation that allows detailed
// control over the content being decoded or encoded.
//
// Values that make use of the Node type interact with the yaml package in the
// same way any other type would do, by encoding and decoding yaml data
// directly or indirectly into them.
//
// For example:
//
// var person struct {
// Name string
// Address yaml.Node
// }
// err := yaml.Unmarshal(data, &person)
//
// Or by itself:
//
// var person Node
// err := yaml.Unmarshal(data, &person)
//
type Node struct {
// Kind defines whether the node is a document, a mapping, a sequence,
// a scalar value, or an alias to another node. The specific data type of
// scalar nodes may be obtained via the ShortTag and LongTag methods.
Kind Kind
// Style allows customizing the apperance of the node in the tree.
Style Style
// Tag holds the YAML tag defining the data type for the value.
// When decoding, this field will always be set to the resolved tag,
// even when it wasn't explicitly provided in the YAML content.
// When encoding, if this field is unset the value type will be
// implied from the node properties, and if it is set, it will only
// be serialized into the representation if TaggedStyle is used or
// the implicit tag diverges from the provided one.
Tag string
// Value holds the unescaped and unquoted represenation of the value.
Value string
// Anchor holds the anchor name for this node, which allows aliases to point to it.
Anchor string
// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
Alias *Node
// Content holds contained nodes for documents, mappings, and sequences.
Content []*Node
// HeadComment holds any comments in the lines preceding the node and
// not separated by an empty line.
HeadComment string
// LineComment holds any comments at the end of the line where the node is in.
LineComment string
// FootComment holds any comments following the node and before empty lines.
FootComment string
// Line and Column hold the node position in the decoded YAML text.
// These fields are not respected when encoding the node.
Line int
Column int
}
// LongTag returns the long form of the tag that indicates the data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
func (n *Node) LongTag() string {
return longTag(n.ShortTag())
}
// ShortTag returns the short form of the YAML tag that indicates data type for
// the node. If the Tag field isn't explicitly defined, one will be computed
// based on the node properties.
func (n *Node) ShortTag() string {
if n.indicatedString() {
return strTag
}
if n.Tag == "" || n.Tag == "!" {
switch n.Kind {
case MappingNode:
return mapTag
case SequenceNode:
return seqTag
case AliasNode:
if n.Alias != nil {
return n.Alias.ShortTag()
}
case ScalarNode:
tag, _ := resolve("", n.Value)
return tag
}
return ""
}
return shortTag(n.Tag)
}
func (n *Node) indicatedString() bool {
return n.Kind == ScalarNode &&
(shortTag(n.Tag) == strTag ||
(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
}
// SetString is a convenience function that sets the node to a string value
// and defines its style in a pleasant way depending on its content.
func (n *Node) SetString(s string) {
n.Kind = ScalarNode
if utf8.ValidString(s) {
n.Value = s
n.Tag = strTag
} else {
n.Value = encodeBase64(s)
n.Tag = binaryTag
}
if strings.Contains(n.Value, "\n") {
n.Style = LiteralStyle
}
}
// --------------------------------------------------------------------------
// Maintain a mapping of keys to structure field indexes
@ -289,6 +460,10 @@ type structInfo struct {
// InlineMap is the number of the field in the struct that
// contains an ,inline map, or -1 if there's none.
InlineMap int
// InlineUnmarshalers holds indexes to inlined fields that
// contain unmarshaler values.
InlineUnmarshalers [][]int
}
type fieldInfo struct {
@ -306,6 +481,12 @@ type fieldInfo struct {
var structMap = make(map[reflect.Type]*structInfo)
var fieldMapMutex sync.RWMutex
var unmarshalerType reflect.Type
func init() {
var v Unmarshaler
unmarshalerType = reflect.ValueOf(&v).Elem().Type()
}
func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldMapMutex.RLock()
@ -319,6 +500,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
fieldsMap := make(map[string]fieldInfo)
fieldsList := make([]fieldInfo, 0, n)
inlineMap := -1
inlineUnmarshalers := [][]int(nil)
for i := 0; i != n; i++ {
field := st.Field(i)
if field.PkgPath != "" && !field.Anonymous {
@ -347,7 +529,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
case "inline":
inline = true
default:
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
}
}
tag = fields[0]
@ -357,34 +539,47 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
switch field.Type.Kind() {
case reflect.Map:
if inlineMap >= 0 {
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
return nil, errors.New("multiple ,inline maps in struct " + st.String())
}
if field.Type.Key() != reflect.TypeOf("") {
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
}
inlineMap = info.Num
case reflect.Struct:
sinfo, err := getStructInfo(field.Type)
if err != nil {
return nil, err
case reflect.Struct, reflect.Ptr:
ftype := field.Type
for ftype.Kind() == reflect.Ptr {
ftype = ftype.Elem()
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
if ftype.Kind() != reflect.Struct {
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
if reflect.PtrTo(ftype).Implements(unmarshalerType) {
inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
} else {
sinfo, err := getStructInfo(ftype)
if err != nil {
return nil, err
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
for _, index := range sinfo.InlineUnmarshalers {
inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
}
for _, finfo := range sinfo.FieldsList {
if _, found := fieldsMap[finfo.Key]; found {
msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
if finfo.Inline == nil {
finfo.Inline = []int{i, finfo.Num}
} else {
finfo.Inline = append([]int{i}, finfo.Inline...)
}
finfo.Id = len(fieldsList)
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
finfo.Id = len(fieldsList)
fieldsMap[finfo.Key] = finfo
fieldsList = append(fieldsList, finfo)
}
default:
//return nil, errors.New("Option ,inline needs a struct value or map field")
return nil, errors.New("Option ,inline needs a struct value field")
return nil, errors.New("option ,inline may only be used on a struct or map field")
}
continue
}
@ -396,7 +591,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
}
if _, found = fieldsMap[info.Key]; found {
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
msg := "duplicated key '" + info.Key + "' in struct " + st.String()
return nil, errors.New(msg)
}
@ -406,9 +601,10 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
}
sinfo = &structInfo{
FieldsMap: fieldsMap,
FieldsList: fieldsList,
InlineMap: inlineMap,
FieldsMap: fieldsMap,
FieldsList: fieldsList,
InlineMap: inlineMap,
InlineUnmarshalers: inlineUnmarshalers,
}
fieldMapMutex.Lock()

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
import (
@ -73,13 +95,13 @@ type yaml_scalar_style_t yaml_style_t
// Scalar styles.
const (
// Let the emitter choose the style.
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0
yaml_PLAIN_SCALAR_STYLE // The plain scalar style.
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style.
yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
yaml_LITERAL_SCALAR_STYLE // The literal scalar style.
yaml_FOLDED_SCALAR_STYLE // The folded scalar style.
)
type yaml_sequence_style_t yaml_style_t
@ -238,6 +260,7 @@ const (
yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event.
yaml_MAPPING_START_EVENT // A MAPPING-START event.
yaml_MAPPING_END_EVENT // A MAPPING-END event.
yaml_TAIL_COMMENT_EVENT
)
var eventStrings = []string{
@ -252,6 +275,7 @@ var eventStrings = []string{
yaml_SEQUENCE_END_EVENT: "sequence end",
yaml_MAPPING_START_EVENT: "mapping start",
yaml_MAPPING_END_EVENT: "mapping end",
yaml_TAIL_COMMENT_EVENT: "tail comment",
}
func (e yaml_event_type_t) String() string {
@ -279,6 +303,12 @@ type yaml_event_t struct {
// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
tag_directives []yaml_tag_directive_t
// The comments
head_comment []byte
line_comment []byte
foot_comment []byte
tail_comment []byte
// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
anchor []byte
@ -554,6 +584,8 @@ type yaml_parser_t struct {
unread int // The number of unread characters in the buffer.
newlines int // The number of line breaks since last non-break/non-blank character
raw_buffer []byte // The raw buffer.
raw_buffer_pos int // The current position of the buffer.
@ -562,6 +594,17 @@ type yaml_parser_t struct {
offset int // The offset of the current position (in bytes).
mark yaml_mark_t // The mark of the current position.
// Comments
head_comment []byte // The current head comments
line_comment []byte // The current line comments
foot_comment []byte // The current foot comments
tail_comment []byte // Foot comment that happens at the end of a block.
stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc)
comments []yaml_comment_t // The folded comments for all parsed tokens
comments_head int
// Scanner stuff
stream_start_produced bool // Have we started to scan the input stream?
@ -595,6 +638,18 @@ type yaml_parser_t struct {
document *yaml_document_t // The currently parsed document.
}
type yaml_comment_t struct {
scan_mark yaml_mark_t // Position where scanning for comments started
token_mark yaml_mark_t // Position after which tokens will be associated with this comment
start_mark yaml_mark_t // Position of '#' comment mark
end_mark yaml_mark_t // Position where comment terminated
head []byte
line []byte
foot []byte
}
// Emitter Definitions
// The prototype of a write handler.
@ -625,8 +680,10 @@ const (
yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document.
yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END.
yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence.
yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out
yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence.
yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out
yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping.
yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping.
@ -698,6 +755,9 @@ type yaml_emitter_t struct {
indention bool // If the last character was an indentation character (' ', '-', '?', ':')?
open_ended bool // If an explicit document end is required?
space_above bool // Is there's an empty line above?
foot_indent int // The indent used to write the foot comment above, or -1 if none.
// Anchor analysis.
anchor_data struct {
anchor []byte // The anchor value.
@ -721,6 +781,12 @@ type yaml_emitter_t struct {
style yaml_scalar_style_t // The output style.
}
// Comments
head_comment []byte
line_comment []byte
foot_comment []byte
tail_comment []byte
// Dumper stuff
opened bool // If the stream was already opened?

View File

@ -1,3 +1,25 @@
//
// Copyright (c) 2011-2019 Canonical Ltd
// Copyright (c) 2006-2010 Kirill Simonov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package yaml
const (
@ -114,8 +136,9 @@ func is_crlf(b []byte, i int) bool {
// Check if the character is a line break or NUL.
func is_breakz(b []byte, i int) bool {
//return is_break(b, i) || is_z(b, i)
return ( // is_break:
b[i] == '\r' || // CR (#xD)
return (
// is_break:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028)
@ -127,8 +150,9 @@ func is_breakz(b []byte, i int) bool {
// Check if the character is a line break, space, or NUL.
func is_spacez(b []byte, i int) bool {
//return is_space(b, i) || is_breakz(b, i)
return ( // is_space:
b[i] == ' ' ||
return (
// is_space:
b[i] == ' ' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)
@ -141,8 +165,9 @@ func is_spacez(b []byte, i int) bool {
// Check if the character is a line break, space, tab, or NUL.
func is_blankz(b []byte, i int) bool {
//return is_blank(b, i) || is_breakz(b, i)
return ( // is_blank:
b[i] == ' ' || b[i] == '\t' ||
return (
// is_blank:
b[i] == ' ' || b[i] == '\t' ||
// is_breakz:
b[i] == '\r' || // CR (#xD)
b[i] == '\n' || // LF (#xA)

11
vendor/modules.txt vendored
View File

@ -98,6 +98,9 @@ github.com/golang-collections/collections/stack
## explicit
# github.com/google/go-cmp v0.3.1
## explicit
# github.com/gookit/color v1.4.2
## explicit
github.com/gookit/color
# github.com/imdario/mergo v0.3.11
## explicit
github.com/imdario/mergo
@ -206,11 +209,13 @@ github.com/sirupsen/logrus
# github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
## explicit
github.com/spkg/bom
# github.com/stretchr/testify v1.4.0
# github.com/stretchr/testify v1.6.1
## explicit
github.com/stretchr/testify/assert
# github.com/xanzy/ssh-agent v0.2.1
github.com/xanzy/ssh-agent
# github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
github.com/xo/terminfo
# golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
## explicit
golang.org/x/crypto/blowfish
@ -253,6 +258,6 @@ golang.org/x/text/encoding/internal/identifier
golang.org/x/text/transform
# gopkg.in/warnings.v0 v0.1.2
gopkg.in/warnings.v0
# gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
gopkg.in/yaml.v3
# github.com/go-git/go-git/v5 => github.com/jesseduffield/go-git/v5 v5.1.1