From e1bbe648088b29af388f7250a290d0a0e0ec7774 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Mar 2023 21:23:42 +0100 Subject: [PATCH] chore: bump github.com/charmbracelet/bubbletea from 0.23.1 to 0.23.2 (#1) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +- go.sum | 14 +- .../charmbracelet/bubbles/help/help.go | 233 --- .../charmbracelet/bubbles/list/defaultitem.go | 227 --- .../charmbracelet/bubbles/list/keys.go | 97 -- .../charmbracelet/bubbles/list/list.go | 1264 ----------------- .../charmbracelet/bubbles/list/style.go | 99 -- .../bubbles/paginator/paginator.go | 202 --- .../charmbracelet/bubbletea/README.md | 53 +- .../github.com/charmbracelet/bubbletea/tea.go | 21 +- vendor/github.com/muesli/termenv/README.md | 104 +- .../github.com/muesli/termenv/ansi_compat.md | 8 + vendor/github.com/muesli/termenv/color.go | 12 +- vendor/github.com/muesli/termenv/copy.go | 19 +- vendor/github.com/muesli/termenv/hyperlink.go | 6 +- .../github.com/muesli/termenv/notification.go | 11 + vendor/github.com/muesli/termenv/output.go | 28 +- vendor/github.com/muesli/termenv/screen.go | 55 +- vendor/github.com/muesli/termenv/termenv.go | 13 +- .../muesli/termenv/termenv_other.go | 14 +- .../github.com/muesli/termenv/termenv_unix.go | 67 +- vendor/github.com/sahilm/fuzzy/.editorconfig | 18 - vendor/github.com/sahilm/fuzzy/.gitignore | 2 - vendor/github.com/sahilm/fuzzy/.travis.yml | 5 - .../github.com/sahilm/fuzzy/CONTRIBUTING.md | 1 - vendor/github.com/sahilm/fuzzy/Gopkg.lock | 20 - vendor/github.com/sahilm/fuzzy/Gopkg.toml | 4 - vendor/github.com/sahilm/fuzzy/LICENSE | 21 - vendor/github.com/sahilm/fuzzy/Makefile | 57 - vendor/github.com/sahilm/fuzzy/README.md | 184 --- vendor/github.com/sahilm/fuzzy/fuzzy.go | 235 --- vendor/golang.org/x/sync/LICENSE | 27 + vendor/golang.org/x/sync/PATENTS | 22 + vendor/golang.org/x/sync/errgroup/errgroup.go | 132 ++ vendor/modules.txt | 11 +- 35 files changed, 485 insertions(+), 2807 deletions(-) delete mode 100644 vendor/github.com/charmbracelet/bubbles/help/help.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/list/defaultitem.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/list/keys.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/list/list.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/list/style.go delete mode 100644 vendor/github.com/charmbracelet/bubbles/paginator/paginator.go create mode 100644 vendor/github.com/muesli/termenv/notification.go delete mode 100644 vendor/github.com/sahilm/fuzzy/.editorconfig delete mode 100644 vendor/github.com/sahilm/fuzzy/.gitignore delete mode 100644 vendor/github.com/sahilm/fuzzy/.travis.yml delete mode 100644 vendor/github.com/sahilm/fuzzy/CONTRIBUTING.md delete mode 100644 vendor/github.com/sahilm/fuzzy/Gopkg.lock delete mode 100644 vendor/github.com/sahilm/fuzzy/Gopkg.toml delete mode 100644 vendor/github.com/sahilm/fuzzy/LICENSE delete mode 100644 vendor/github.com/sahilm/fuzzy/Makefile delete mode 100644 vendor/github.com/sahilm/fuzzy/README.md delete mode 100644 vendor/github.com/sahilm/fuzzy/fuzzy.go create mode 100644 vendor/golang.org/x/sync/LICENSE create mode 100644 vendor/golang.org/x/sync/PATENTS create mode 100644 vendor/golang.org/x/sync/errgroup/errgroup.go diff --git a/go.mod b/go.mod index 08155a4..206dfa7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/charmbracelet/bubbles v0.14.0 - github.com/charmbracelet/bubbletea v0.23.1 + github.com/charmbracelet/bubbletea v0.23.2 github.com/charmbracelet/lipgloss v0.5.0 github.com/muesli/reflow v0.3.0 ) @@ -19,9 +19,9 @@ require ( github.com/mattn/go-runewidth v0.0.14 // indirect github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.13.0 // indirect + github.com/muesli/termenv v0.14.0 // indirect github.com/rivo/uniseg v0.4.3 // indirect - github.com/sahilm/fuzzy v0.1.0 // indirect + golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.4.0 // indirect golang.org/x/term v0.4.0 // indirect golang.org/x/text v0.6.0 // indirect diff --git a/go.sum b/go.sum index 63c8681..9bd7892 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,21 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E= github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck= -github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= +github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps= +github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -39,14 +36,15 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/muesli/termenv v0.14.0 h1:8x9NFfOe8lmIWK4pgy3IfVEy47f+ppe3tUqdPZG2Uy0= +github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/charmbracelet/bubbles/help/help.go b/vendor/github.com/charmbracelet/bubbles/help/help.go deleted file mode 100644 index 90971ac..0000000 --- a/vendor/github.com/charmbracelet/bubbles/help/help.go +++ /dev/null @@ -1,233 +0,0 @@ -package help - -import ( - "strings" - - "github.com/charmbracelet/bubbles/key" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// KeyMap is a map of keybindings used to generate help. Since it's an -// interface it can be any type, though struct or a map[string][]key.Binding -// are likely candidates. -// -// Note that if a key is disabled (via key.Binding.SetEnabled) it will not be -// rendered in the help view, so in theory generated help should self-manage. -type KeyMap interface { - - // ShortHelp returns a slice of bindings to be displayed in the short - // version of the help. The help bubble will render help in the order in - // which the help items are returned here. - ShortHelp() []key.Binding - - // MoreHelp returns an extended group of help items, grouped by columns. - // The help bubble will render the help in the order in which the help - // items are returned here. - FullHelp() [][]key.Binding -} - -// Styles is a set of available style definitions for the Help bubble. -type Styles struct { - Ellipsis lipgloss.Style - - // Styling for the short help - ShortKey lipgloss.Style - ShortDesc lipgloss.Style - ShortSeparator lipgloss.Style - - // Styling for the full help - FullKey lipgloss.Style - FullDesc lipgloss.Style - FullSeparator lipgloss.Style -} - -// Model contains the state of the help view. -type Model struct { - Width int - ShowAll bool // if true, render the "full" help menu - - ShortSeparator string - FullSeparator string - - // The symbol we use in the short help when help items have been truncated - // due to width. Periods of ellipsis by default. - Ellipsis string - - Styles Styles -} - -// New creates a new help view with some useful defaults. -func New() Model { - keyStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{ - Light: "#909090", - Dark: "#626262", - }) - - descStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{ - Light: "#B2B2B2", - Dark: "#4A4A4A", - }) - - sepStyle := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{ - Light: "#DDDADA", - Dark: "#3C3C3C", - }) - - return Model{ - ShortSeparator: " • ", - FullSeparator: " ", - Ellipsis: "…", - Styles: Styles{ - ShortKey: keyStyle, - ShortDesc: descStyle, - ShortSeparator: sepStyle, - Ellipsis: sepStyle.Copy(), - FullKey: keyStyle.Copy(), - FullDesc: descStyle.Copy(), - FullSeparator: sepStyle.Copy(), - }, - } -} - -// NewModel creates a new help view with some useful defaults. -// -// Deprecated. Use New instead. -var NewModel = New - -// Update helps satisfy the Bubble Tea Model interface. It's a no-op. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - return m, nil -} - -// View renders the help view's current state. -func (m Model) View(k KeyMap) string { - if m.ShowAll { - return m.FullHelpView(k.FullHelp()) - } - return m.ShortHelpView(k.ShortHelp()) -} - -// ShortHelpView renders a single line help view from a slice of keybindings. -// If the line is longer than the maximum width it will be gracefully -// truncated, showing only as many help items as possible. -func (m Model) ShortHelpView(bindings []key.Binding) string { - if len(bindings) == 0 { - return "" - } - - var b strings.Builder - var totalWidth int - var separator = m.Styles.ShortSeparator.Inline(true).Render(m.ShortSeparator) - - for i, kb := range bindings { - if !kb.Enabled() { - continue - } - - var sep string - if totalWidth > 0 && i < len(bindings) { - sep = separator - } - - str := sep + - m.Styles.ShortKey.Inline(true).Render(kb.Help().Key) + " " + - m.Styles.ShortDesc.Inline(true).Render(kb.Help().Desc) - - w := lipgloss.Width(str) - - // If adding this help item would go over the available width, stop - // drawing. - if m.Width > 0 && totalWidth+w > m.Width { - // Although if there's room for an ellipsis, print that. - tail := " " + m.Styles.Ellipsis.Inline(true).Render(m.Ellipsis) - tailWidth := lipgloss.Width(tail) - - if totalWidth+tailWidth < m.Width { - b.WriteString(tail) - } - - break - } - - totalWidth += w - b.WriteString(str) - } - - return b.String() -} - -// FullHelpView renders help columns from a slice of key binding slices. Each -// top level slice entry renders into a column. -func (m Model) FullHelpView(groups [][]key.Binding) string { - if len(groups) == 0 { - return "" - } - - // Linter note: at this time we don't think it's worth the additional - // code complexity involved in preallocating this slice. - //nolint:prealloc - var ( - out []string - - totalWidth int - sep = m.Styles.FullSeparator.Render(m.FullSeparator) - sepWidth = lipgloss.Width(sep) - ) - - // Iterate over groups to build columns - for i, group := range groups { - if group == nil || !shouldRenderColumn(group) { - continue - } - - var ( - keys []string - descriptions []string - ) - - // Separate keys and descriptions into different slices - for _, kb := range group { - if !kb.Enabled() { - continue - } - keys = append(keys, kb.Help().Key) - descriptions = append(descriptions, kb.Help().Desc) - } - - col := lipgloss.JoinHorizontal(lipgloss.Top, - m.Styles.FullKey.Render(strings.Join(keys, "\n")), - m.Styles.FullKey.Render(" "), - m.Styles.FullDesc.Render(strings.Join(descriptions, "\n")), - ) - - // Column - totalWidth += lipgloss.Width(col) - if totalWidth > m.Width { - break - } - - out = append(out, col) - - // Separator - if i < len(group)-1 { - totalWidth += sepWidth - if totalWidth > m.Width { - break - } - } - - out = append(out, sep) - } - - return lipgloss.JoinHorizontal(lipgloss.Top, out...) -} - -func shouldRenderColumn(b []key.Binding) (ok bool) { - for _, v := range b { - if v.Enabled() { - return true - } - } - return false -} diff --git a/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go b/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go deleted file mode 100644 index 74e4ee2..0000000 --- a/vendor/github.com/charmbracelet/bubbles/list/defaultitem.go +++ /dev/null @@ -1,227 +0,0 @@ -package list - -import ( - "fmt" - "io" - "strings" - - "github.com/charmbracelet/bubbles/key" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/muesli/reflow/truncate" -) - -// DefaultItemStyles defines styling for a default list item. -// See DefaultItemView for when these come into play. -type DefaultItemStyles struct { - // The Normal state. - NormalTitle lipgloss.Style - NormalDesc lipgloss.Style - - // The selected item state. - SelectedTitle lipgloss.Style - SelectedDesc lipgloss.Style - - // The dimmed state, for when the filter input is initially activated. - DimmedTitle lipgloss.Style - DimmedDesc lipgloss.Style - - // Charcters matching the current filter, if any. - FilterMatch lipgloss.Style -} - -// NewDefaultItemStyles returns style definitions for a default item. See -// DefaultItemView for when these come into play. -func NewDefaultItemStyles() (s DefaultItemStyles) { - s.NormalTitle = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}). - Padding(0, 0, 0, 2) - - s.NormalDesc = s.NormalTitle.Copy(). - Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"}) - - s.SelectedTitle = lipgloss.NewStyle(). - Border(lipgloss.NormalBorder(), false, false, false, true). - BorderForeground(lipgloss.AdaptiveColor{Light: "#F793FF", Dark: "#AD58B4"}). - Foreground(lipgloss.AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"}). - Padding(0, 0, 0, 1) - - s.SelectedDesc = s.SelectedTitle.Copy(). - Foreground(lipgloss.AdaptiveColor{Light: "#F793FF", Dark: "#AD58B4"}) - - s.DimmedTitle = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"}). - Padding(0, 0, 0, 2) - - s.DimmedDesc = s.DimmedTitle.Copy(). - Foreground(lipgloss.AdaptiveColor{Light: "#C2B8C2", Dark: "#4D4D4D"}) - - s.FilterMatch = lipgloss.NewStyle().Underline(true) - - return s -} - -// DefaultItem describes an items designed to work with DefaultDelegate. -type DefaultItem interface { - Item - Title() string - Description() string -} - -// DefaultDelegate is a standard delegate designed to work in lists. It's -// styled by DefaultItemStyles, which can be customized as you like. -// -// The description line can be hidden by setting Description to false, which -// renders the list as single-line-items. The spacing between items can be set -// with the SetSpacing method. -// -// Setting UpdateFunc is optional. If it's set it will be called when the -// ItemDelegate called, which is called when the list's Update function is -// invoked. -// -// Settings ShortHelpFunc and FullHelpFunc is optional. They can can be set to -// include items in the list's default short and full help menus. -type DefaultDelegate struct { - ShowDescription bool - Styles DefaultItemStyles - UpdateFunc func(tea.Msg, *Model) tea.Cmd - ShortHelpFunc func() []key.Binding - FullHelpFunc func() [][]key.Binding - height int - spacing int -} - -// NewDefaultDelegate creates a new delegate with default styles. -func NewDefaultDelegate() DefaultDelegate { - return DefaultDelegate{ - ShowDescription: true, - Styles: NewDefaultItemStyles(), - height: 2, - spacing: 1, - } -} - -// SetHeight sets delegate's preferred height. -func (d *DefaultDelegate) SetHeight(i int) { - d.height = i -} - -// Height returns the delegate's preferred height. -// This has effect only if ShowDescription is true, -// otherwise height is always 1. -func (d DefaultDelegate) Height() int { - if d.ShowDescription { - return d.height - } - return 1 -} - -// SetSpacing set the delegate's spacing. -func (d *DefaultDelegate) SetSpacing(i int) { - d.spacing = i -} - -// Spacing returns the delegate's spacing. -func (d DefaultDelegate) Spacing() int { - return d.spacing -} - -// Update checks whether the delegate's UpdateFunc is set and calls it. -func (d DefaultDelegate) Update(msg tea.Msg, m *Model) tea.Cmd { - if d.UpdateFunc == nil { - return nil - } - return d.UpdateFunc(msg, m) -} - -// Render prints an item. -func (d DefaultDelegate) Render(w io.Writer, m Model, index int, item Item) { - var ( - title, desc string - matchedRunes []int - s = &d.Styles - ) - - if i, ok := item.(DefaultItem); ok { - title = i.Title() - desc = i.Description() - } else { - return - } - - if m.width <= 0 { - // short-circuit - return - } - - // Prevent text from exceeding list width - textwidth := uint(m.width - s.NormalTitle.GetPaddingLeft() - s.NormalTitle.GetPaddingRight()) - title = truncate.StringWithTail(title, textwidth, ellipsis) - if d.ShowDescription { - var lines []string - for i, line := range strings.Split(desc, "\n") { - if i >= d.height-1 { - break - } - lines = append(lines, truncate.StringWithTail(line, textwidth, ellipsis)) - } - desc = strings.Join(lines, "\n") - } - - // Conditions - var ( - isSelected = index == m.Index() - emptyFilter = m.FilterState() == Filtering && m.FilterValue() == "" - isFiltered = m.FilterState() == Filtering || m.FilterState() == FilterApplied - ) - - if isFiltered && index < len(m.filteredItems) { - // Get indices of matched characters - matchedRunes = m.MatchesForItem(index) - } - - if emptyFilter { - title = s.DimmedTitle.Render(title) - desc = s.DimmedDesc.Render(desc) - } else if isSelected && m.FilterState() != Filtering { - if isFiltered { - // Highlight matches - unmatched := s.SelectedTitle.Inline(true) - matched := unmatched.Copy().Inherit(s.FilterMatch) - title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched) - } - title = s.SelectedTitle.Render(title) - desc = s.SelectedDesc.Render(desc) - } else { - if isFiltered { - // Highlight matches - unmatched := s.NormalTitle.Inline(true) - matched := unmatched.Copy().Inherit(s.FilterMatch) - title = lipgloss.StyleRunes(title, matchedRunes, matched, unmatched) - } - title = s.NormalTitle.Render(title) - desc = s.NormalDesc.Render(desc) - } - - if d.ShowDescription { - fmt.Fprintf(w, "%s\n%s", title, desc) - return - } - fmt.Fprintf(w, "%s", title) -} - -// ShortHelp returns the delegate's short help. -func (d DefaultDelegate) ShortHelp() []key.Binding { - if d.ShortHelpFunc != nil { - return d.ShortHelpFunc() - } - return nil -} - -// FullHelp returns the delegate's full help. -func (d DefaultDelegate) FullHelp() [][]key.Binding { - if d.FullHelpFunc != nil { - return d.FullHelpFunc() - } - return nil -} diff --git a/vendor/github.com/charmbracelet/bubbles/list/keys.go b/vendor/github.com/charmbracelet/bubbles/list/keys.go deleted file mode 100644 index 421a247..0000000 --- a/vendor/github.com/charmbracelet/bubbles/list/keys.go +++ /dev/null @@ -1,97 +0,0 @@ -package list - -import "github.com/charmbracelet/bubbles/key" - -// KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which -// is used to render the menu menu. -type KeyMap struct { - // Keybindings used when browsing the list. - CursorUp key.Binding - CursorDown key.Binding - NextPage key.Binding - PrevPage key.Binding - GoToStart key.Binding - GoToEnd key.Binding - Filter key.Binding - ClearFilter key.Binding - - // Keybindings used when setting a filter. - CancelWhileFiltering key.Binding - AcceptWhileFiltering key.Binding - - // Help toggle keybindings. - ShowFullHelp key.Binding - CloseFullHelp key.Binding - - // The quit keybinding. This won't be caught when filtering. - Quit key.Binding - - // The quit-no-matter-what keybinding. This will be caught when filtering. - ForceQuit key.Binding -} - -// DefaultKeyMap returns a default set of keybindings. -func DefaultKeyMap() KeyMap { - return KeyMap{ - // Browsing. - CursorUp: key.NewBinding( - key.WithKeys("up", "k"), - key.WithHelp("↑/k", "up"), - ), - CursorDown: key.NewBinding( - key.WithKeys("down", "j"), - key.WithHelp("↓/j", "down"), - ), - PrevPage: key.NewBinding( - key.WithKeys("left", "h", "pgup", "b", "u"), - key.WithHelp("←/h/pgup", "prev page"), - ), - NextPage: key.NewBinding( - key.WithKeys("right", "l", "pgdown", "f", "d"), - key.WithHelp("→/l/pgdn", "next page"), - ), - GoToStart: key.NewBinding( - key.WithKeys("home", "g"), - key.WithHelp("g/home", "go to start"), - ), - GoToEnd: key.NewBinding( - key.WithKeys("end", "G"), - key.WithHelp("G/end", "go to end"), - ), - Filter: key.NewBinding( - key.WithKeys("/"), - key.WithHelp("/", "filter"), - ), - ClearFilter: key.NewBinding( - key.WithKeys("esc"), - key.WithHelp("esc", "clear filter"), - ), - - // Filtering. - CancelWhileFiltering: key.NewBinding( - key.WithKeys("esc"), - key.WithHelp("esc", "cancel"), - ), - AcceptWhileFiltering: key.NewBinding( - key.WithKeys("enter", "tab", "shift+tab", "ctrl+k", "up", "ctrl+j", "down"), - key.WithHelp("enter", "apply filter"), - ), - - // Toggle help. - ShowFullHelp: key.NewBinding( - key.WithKeys("?"), - key.WithHelp("?", "more"), - ), - CloseFullHelp: key.NewBinding( - key.WithKeys("?"), - key.WithHelp("?", "close help"), - ), - - // Quitting. - Quit: key.NewBinding( - key.WithKeys("q", "esc"), - key.WithHelp("q", "quit"), - ), - ForceQuit: key.NewBinding(key.WithKeys("ctrl+c")), - } -} diff --git a/vendor/github.com/charmbracelet/bubbles/list/list.go b/vendor/github.com/charmbracelet/bubbles/list/list.go deleted file mode 100644 index bec5e6f..0000000 --- a/vendor/github.com/charmbracelet/bubbles/list/list.go +++ /dev/null @@ -1,1264 +0,0 @@ -// Package list provides a feature-rich Bubble Tea component for browsing -// a general purpose list of items. It features optional filtering, pagination, -// help, status messages, and a spinner to indicate activity. -package list - -import ( - "fmt" - "io" - "sort" - "strings" - "time" - - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/paginator" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/muesli/reflow/ansi" - "github.com/muesli/reflow/truncate" - "github.com/sahilm/fuzzy" -) - -// Item is an item that appears in the list. -type Item interface { - // Filter value is the value we use when filtering against this item when - // we're filtering the list. - FilterValue() string -} - -// ItemDelegate encapsulates the general functionality for all list items. The -// benefit to separating this logic from the item itself is that you can change -// the functionality of items without changing the actual items themselves. -// -// Note that if the delegate also implements help.KeyMap delegate-related -// help items will be added to the help view. -type ItemDelegate interface { - // Render renders the item's view. - Render(w io.Writer, m Model, index int, item Item) - - // Height is the height of the list item. - Height() int - - // Spacing is the size of the horizontal gap between list items in cells. - Spacing() int - - // Update is the update loop for items. All messages in the list's update - // loop will pass through here except when the user is setting a filter. - // Use this method to perform item-level updates appropriate to this - // delegate. - Update(msg tea.Msg, m *Model) tea.Cmd -} - -type filteredItem struct { - item Item // item matched - matches []int // rune indices of matched items -} - -type filteredItems []filteredItem - -func (f filteredItems) items() []Item { - agg := make([]Item, len(f)) - for i, v := range f { - agg[i] = v.item - } - return agg -} - -// FilterMatchesMsg contains data about items matched during filtering. The -// message should be routed to Update for processing. -type FilterMatchesMsg []filteredItem - -// FilterFunc takes a term and a list of strings to search through -// (defined by Item#FilterValue). -// It should return a sorted list of ranks. -type FilterFunc func(string, []string) []Rank - -// Rank defines a rank for a given item. -type Rank struct { - // The index of the item in the original input. - Index int - // Indices of the actual word that were matched against the filter term. - MatchedIndexes []int -} - -// DefaultFilter uses the sahilm/fuzzy to filter through the list. -// This is set by default. -func DefaultFilter(term string, targets []string) []Rank { - var ranks = fuzzy.Find(term, targets) - sort.Stable(ranks) - result := make([]Rank, len(ranks)) - for i, r := range ranks { - result[i] = Rank{ - Index: r.Index, - MatchedIndexes: r.MatchedIndexes, - } - } - return result -} - -type statusMessageTimeoutMsg struct{} - -// FilterState describes the current filtering state on the model. -type FilterState int - -// Possible filter states. -const ( - Unfiltered FilterState = iota // no filter set - Filtering // user is actively setting a filter - FilterApplied // a filter is applied and user is not editing filter -) - -// String returns a human-readable string of the current filter state. -func (f FilterState) String() string { - return [...]string{ - "unfiltered", - "filtering", - "filter applied", - }[f] -} - -// Model contains the state of this component. -type Model struct { - showTitle bool - showFilter bool - showStatusBar bool - showPagination bool - showHelp bool - filteringEnabled bool - - itemNameSingular string - itemNamePlural string - - Title string - Styles Styles - - // Key mappings for navigating the list. - KeyMap KeyMap - - // Filter is used to filter the list. - Filter FilterFunc - - disableQuitKeybindings bool - - // Additional key mappings for the short and full help views. This allows - // you to add additional key mappings to the help menu without - // re-implementing the help component. Of course, you can also disable the - // list's help component and implement a new one if you need more - // flexibility. - AdditionalShortHelpKeys func() []key.Binding - AdditionalFullHelpKeys func() []key.Binding - - spinner spinner.Model - showSpinner bool - width int - height int - Paginator paginator.Model - cursor int - Help help.Model - FilterInput textinput.Model - filterState FilterState - - // How long status messages should stay visible. By default this is - // 1 second. - StatusMessageLifetime time.Duration - - statusMessage string - statusMessageTimer *time.Timer - - // The master set of items we're working with. - items []Item - - // Filtered items we're currently displaying. Filtering, toggles and so on - // will alter this slice so we can show what is relevant. For that reason, - // this field should be considered ephemeral. - filteredItems filteredItems - - delegate ItemDelegate -} - -// New returns a new model with sensible defaults. -func New(items []Item, delegate ItemDelegate, width, height int) Model { - styles := DefaultStyles() - - sp := spinner.NewModel() - sp.Spinner = spinner.Line - sp.Style = styles.Spinner - - filterInput := textinput.NewModel() - filterInput.Prompt = "Filter: " - filterInput.PromptStyle = styles.FilterPrompt - filterInput.CursorStyle = styles.FilterCursor - filterInput.CharLimit = 64 - filterInput.Focus() - - p := paginator.NewModel() - p.Type = paginator.Dots - p.ActiveDot = styles.ActivePaginationDot.String() - p.InactiveDot = styles.InactivePaginationDot.String() - - m := Model{ - showTitle: true, - showFilter: true, - showStatusBar: true, - showPagination: true, - showHelp: true, - itemNameSingular: "item", - itemNamePlural: "items", - filteringEnabled: true, - KeyMap: DefaultKeyMap(), - Filter: DefaultFilter, - Styles: styles, - Title: "List", - FilterInput: filterInput, - StatusMessageLifetime: time.Second, - - width: width, - height: height, - delegate: delegate, - items: items, - Paginator: p, - spinner: sp, - Help: help.NewModel(), - } - - m.updatePagination() - m.updateKeybindings() - return m -} - -// NewModel returns a new model with sensible defaults. -// -// Deprecated. Use New instead. -var NewModel = New - -// SetFilteringEnabled enables or disables filtering. Note that this is different -// from ShowFilter, which merely hides or shows the input view. -func (m *Model) SetFilteringEnabled(v bool) { - m.filteringEnabled = v - if !v { - m.resetFiltering() - } - m.updateKeybindings() -} - -// FilteringEnabled returns whether or not filtering is enabled. -func (m Model) FilteringEnabled() bool { - return m.filteringEnabled -} - -// SetShowTitle shows or hides the title bar. -func (m *Model) SetShowTitle(v bool) { - m.showTitle = v - m.updatePagination() -} - -// ShowTitle returns whether or not the title bar is set to be rendered. -func (m Model) ShowTitle() bool { - return m.showTitle -} - -// SetShowFilter shows or hides the filer bar. Note that this does not disable -// filtering, it simply hides the built-in filter view. This allows you to -// use the FilterInput to render the filtering UI differently without having to -// re-implement filtering from scratch. -// -// To disable filtering entirely use EnableFiltering. -func (m *Model) SetShowFilter(v bool) { - m.showFilter = v - m.updatePagination() -} - -// ShowFilter returns whether or not the filter is set to be rendered. Note -// that this is separate from FilteringEnabled, so filtering can be hidden yet -// still invoked. This allows you to render filtering differently without -// having to re-implement it from scratch. -func (m Model) ShowFilter() bool { - return m.showFilter -} - -// SetShowStatusBar shows or hides the view that displays metadata about the -// list, such as item counts. -func (m *Model) SetShowStatusBar(v bool) { - m.showStatusBar = v - m.updatePagination() -} - -// ShowStatusBar returns whether or not the status bar is set to be rendered. -func (m Model) ShowStatusBar() bool { - return m.showStatusBar -} - -// SetStatusBarItemName defines a replacement for the items identifier. -// Defaults to item/items. -func (m *Model) SetStatusBarItemName(singular, plural string) { - m.itemNameSingular = singular - m.itemNamePlural = plural -} - -// StatusBarItemName returns singular and plural status bar item names. -func (m Model) StatusBarItemName() (string, string) { - return m.itemNameSingular, m.itemNamePlural -} - -// SetShowPagination hides or shoes the paginator. Note that pagination will -// still be active, it simply won't be displayed. -func (m *Model) SetShowPagination(v bool) { - m.showPagination = v - m.updatePagination() -} - -// ShowPagination returns whether the pagination is visible. -func (m *Model) ShowPagination() bool { - return m.showPagination -} - -// SetShowHelp shows or hides the help view. -func (m *Model) SetShowHelp(v bool) { - m.showHelp = v - m.updatePagination() -} - -// ShowHelp returns whether or not the help is set to be rendered. -func (m Model) ShowHelp() bool { - return m.showHelp -} - -// Items returns the items in the list. -func (m Model) Items() []Item { - return m.items -} - -// Set the items available in the list. This returns a command. -func (m *Model) SetItems(i []Item) tea.Cmd { - var cmd tea.Cmd - m.items = i - - if m.filterState != Unfiltered { - m.filteredItems = nil - cmd = filterItems(*m) - } - - m.updatePagination() - m.updateKeybindings() - return cmd -} - -// Select selects the given index of the list and goes to its respective page. -func (m *Model) Select(index int) { - m.Paginator.Page = index / m.Paginator.PerPage - m.cursor = index % m.Paginator.PerPage -} - -// ResetSelected resets the selected item to the first item in the first page of the list. -func (m *Model) ResetSelected() { - m.Select(0) -} - -// ResetFilter resets the current filtering state. -func (m *Model) ResetFilter() { - m.resetFiltering() -} - -// Replace an item at the given index. This returns a command. -func (m *Model) SetItem(index int, item Item) tea.Cmd { - var cmd tea.Cmd - m.items[index] = item - - if m.filterState != Unfiltered { - cmd = filterItems(*m) - } - - m.updatePagination() - return cmd -} - -// Insert an item at the given index. If index is out of the upper bound, the -// item will be appended. This returns a command. -func (m *Model) InsertItem(index int, item Item) tea.Cmd { - var cmd tea.Cmd - m.items = insertItemIntoSlice(m.items, item, index) - - if m.filterState != Unfiltered { - cmd = filterItems(*m) - } - - m.updatePagination() - m.updateKeybindings() - return cmd -} - -// RemoveItem removes an item at the given index. If the index is out of bounds -// this will be a no-op. O(n) complexity, which probably won't matter in the -// case of a TUI. -func (m *Model) RemoveItem(index int) { - m.items = removeItemFromSlice(m.items, index) - if m.filterState != Unfiltered { - m.filteredItems = removeFilterMatchFromSlice(m.filteredItems, index) - if len(m.filteredItems) == 0 { - m.resetFiltering() - } - } - m.updatePagination() -} - -// Set the item delegate. -func (m *Model) SetDelegate(d ItemDelegate) { - m.delegate = d - m.updatePagination() -} - -// VisibleItems returns the total items available to be shown. -func (m Model) VisibleItems() []Item { - if m.filterState != Unfiltered { - return m.filteredItems.items() - } - return m.items -} - -// SelectedItems returns the current selected item in the list. -func (m Model) SelectedItem() Item { - i := m.Index() - - items := m.VisibleItems() - if i < 0 || len(items) == 0 || len(items) <= i { - return nil - } - - return items[i] -} - -// MatchesForItem returns rune positions matched by the current filter, if any. -// Use this to style runes matched by the active filter. -// -// See DefaultItemView for a usage example. -func (m Model) MatchesForItem(index int) []int { - if m.filteredItems == nil || index >= len(m.filteredItems) { - return nil - } - return m.filteredItems[index].matches -} - -// Index returns the index of the currently selected item as it appears in the -// entire slice of items. -func (m Model) Index() int { - return m.Paginator.Page*m.Paginator.PerPage + m.cursor -} - -// Cursor returns the index of the cursor on the current page. -func (m Model) Cursor() int { - return m.cursor -} - -// CursorUp moves the cursor up. This can also move the state to the previous -// page. -func (m *Model) CursorUp() { - m.cursor-- - - // If we're at the start, stop - if m.cursor < 0 && m.Paginator.Page == 0 { - m.cursor = 0 - return - } - - // Move the cursor as normal - if m.cursor >= 0 { - return - } - - // Go to the previous page - m.Paginator.PrevPage() - m.cursor = m.Paginator.ItemsOnPage(len(m.VisibleItems())) - 1 -} - -// CursorDown moves the cursor down. This can also advance the state to the -// next page. -func (m *Model) CursorDown() { - itemsOnPage := m.Paginator.ItemsOnPage(len(m.VisibleItems())) - - m.cursor++ - - // If we're at the end, stop - if m.cursor < itemsOnPage { - return - } - - // Go to the next page - if !m.Paginator.OnLastPage() { - m.Paginator.NextPage() - m.cursor = 0 - return - } - - // During filtering the cursor position can exceed the number of - // itemsOnPage. It's more intuitive to start the cursor at the - // topmost position when moving it down in this scenario. - if m.cursor > itemsOnPage { - m.cursor = 0 - return - } - - m.cursor = itemsOnPage - 1 -} - -// PrevPage moves to the previous page, if available. -func (m Model) PrevPage() { - m.Paginator.PrevPage() -} - -// NextPage moves to the next page, if available. -func (m Model) NextPage() { - m.Paginator.NextPage() -} - -// FilterState returns the current filter state. -func (m Model) FilterState() FilterState { - return m.filterState -} - -// FilterValue returns the current value of the filter. -func (m Model) FilterValue() string { - return m.FilterInput.Value() -} - -// SettingFilter returns whether or not the user is currently editing the -// filter value. It's purely a convenience method for the following: -// -// m.FilterState() == Filtering -// -// It's included here because it's a common thing to check for when -// implementing this component. -func (m Model) SettingFilter() bool { - return m.filterState == Filtering -} - -// IsFiltered returns whether or not the list is currently filtered. -// It's purely a convenience method for the following: -// -// m.FilterState() == FilterApplied -// -func (m Model) IsFiltered() bool { - return m.filterState == FilterApplied -} - -// Width returns the current width setting. -func (m Model) Width() int { - return m.width -} - -// Height returns the current height setting. -func (m Model) Height() int { - return m.height -} - -// SetSpinner allows to set the spinner style. -func (m *Model) SetSpinner(spinner spinner.Spinner) { - m.spinner.Spinner = spinner -} - -// Toggle the spinner. Note that this also returns a command. -func (m *Model) ToggleSpinner() tea.Cmd { - if !m.showSpinner { - return m.StartSpinner() - } - m.StopSpinner() - return nil -} - -// StartSpinner starts the spinner. Note that this returns a command. -func (m *Model) StartSpinner() tea.Cmd { - m.showSpinner = true - return spinner.Tick -} - -// StopSpinner stops the spinner. -func (m *Model) StopSpinner() { - m.showSpinner = false -} - -// Helper for disabling the keybindings used for quitting, in case you want to -// handle this elsewhere in your application. -func (m *Model) DisableQuitKeybindings() { - m.disableQuitKeybindings = true - m.KeyMap.Quit.SetEnabled(false) - m.KeyMap.ForceQuit.SetEnabled(false) -} - -// NewStatusMessage sets a new status message, which will show for a limited -// amount of time. Note that this also returns a command. -func (m *Model) NewStatusMessage(s string) tea.Cmd { - m.statusMessage = s - if m.statusMessageTimer != nil { - m.statusMessageTimer.Stop() - } - - m.statusMessageTimer = time.NewTimer(m.StatusMessageLifetime) - - // Wait for timeout - return func() tea.Msg { - <-m.statusMessageTimer.C - return statusMessageTimeoutMsg{} - } -} - -// SetSize sets the width and height of this component. -func (m *Model) SetSize(width, height int) { - m.setSize(width, height) -} - -// SetWidth sets the width of this component. -func (m *Model) SetWidth(v int) { - m.setSize(v, m.height) -} - -// SetHeight sets the height of this component. -func (m *Model) SetHeight(v int) { - m.setSize(m.width, v) -} - -func (m *Model) setSize(width, height int) { - promptWidth := lipgloss.Width(m.Styles.Title.Render(m.FilterInput.Prompt)) - - m.width = width - m.height = height - m.Help.Width = width - m.FilterInput.Width = width - promptWidth - lipgloss.Width(m.spinnerView()) - m.updatePagination() -} - -func (m *Model) resetFiltering() { - if m.filterState == Unfiltered { - return - } - - m.filterState = Unfiltered - m.FilterInput.Reset() - m.filteredItems = nil - m.updatePagination() - m.updateKeybindings() -} - -func (m Model) itemsAsFilterItems() filteredItems { - fi := make([]filteredItem, len(m.items)) - for i, item := range m.items { - fi[i] = filteredItem{ - item: item, - } - } - return filteredItems(fi) -} - -// Set keybindings according to the filter state. -func (m *Model) updateKeybindings() { - switch m.filterState { - case Filtering: - m.KeyMap.CursorUp.SetEnabled(false) - m.KeyMap.CursorDown.SetEnabled(false) - m.KeyMap.NextPage.SetEnabled(false) - m.KeyMap.PrevPage.SetEnabled(false) - m.KeyMap.GoToStart.SetEnabled(false) - m.KeyMap.GoToEnd.SetEnabled(false) - m.KeyMap.Filter.SetEnabled(false) - m.KeyMap.ClearFilter.SetEnabled(false) - m.KeyMap.CancelWhileFiltering.SetEnabled(true) - m.KeyMap.AcceptWhileFiltering.SetEnabled(m.FilterInput.Value() != "") - m.KeyMap.Quit.SetEnabled(false) - m.KeyMap.ShowFullHelp.SetEnabled(false) - m.KeyMap.CloseFullHelp.SetEnabled(false) - - default: - hasItems := len(m.items) != 0 - m.KeyMap.CursorUp.SetEnabled(hasItems) - m.KeyMap.CursorDown.SetEnabled(hasItems) - - hasPages := m.Paginator.TotalPages > 1 - m.KeyMap.NextPage.SetEnabled(hasPages) - m.KeyMap.PrevPage.SetEnabled(hasPages) - - m.KeyMap.GoToStart.SetEnabled(hasItems) - m.KeyMap.GoToEnd.SetEnabled(hasItems) - - m.KeyMap.Filter.SetEnabled(m.filteringEnabled && hasItems) - m.KeyMap.ClearFilter.SetEnabled(m.filterState == FilterApplied) - m.KeyMap.CancelWhileFiltering.SetEnabled(false) - m.KeyMap.AcceptWhileFiltering.SetEnabled(false) - m.KeyMap.Quit.SetEnabled(!m.disableQuitKeybindings) - - if m.Help.ShowAll { - m.KeyMap.ShowFullHelp.SetEnabled(true) - m.KeyMap.CloseFullHelp.SetEnabled(true) - } else { - minHelp := countEnabledBindings(m.FullHelp()) > 1 - m.KeyMap.ShowFullHelp.SetEnabled(minHelp) - m.KeyMap.CloseFullHelp.SetEnabled(minHelp) - } - } -} - -// Update pagination according to the amount of items for the current state. -func (m *Model) updatePagination() { - index := m.Index() - availHeight := m.height - - if m.showTitle || (m.showFilter && m.filteringEnabled) { - availHeight -= lipgloss.Height(m.titleView()) - } - if m.showStatusBar { - availHeight -= lipgloss.Height(m.statusView()) - } - if m.showPagination { - availHeight -= lipgloss.Height(m.paginationView()) - } - if m.showHelp { - availHeight -= lipgloss.Height(m.helpView()) - } - - m.Paginator.PerPage = max(1, availHeight/(m.delegate.Height()+m.delegate.Spacing())) - - if pages := len(m.VisibleItems()); pages < 1 { - m.Paginator.SetTotalPages(1) - } else { - m.Paginator.SetTotalPages(pages) - } - - // Restore index - m.Paginator.Page = index / m.Paginator.PerPage - m.cursor = index % m.Paginator.PerPage - - // Make sure the page stays in bounds - if m.Paginator.Page >= m.Paginator.TotalPages-1 { - m.Paginator.Page = max(0, m.Paginator.TotalPages-1) - } -} - -func (m *Model) hideStatusMessage() { - m.statusMessage = "" - if m.statusMessageTimer != nil { - m.statusMessageTimer.Stop() - } -} - -// Update is the Bubble Tea update loop. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - var cmds []tea.Cmd - - switch msg := msg.(type) { - case tea.KeyMsg: - if key.Matches(msg, m.KeyMap.ForceQuit) { - return m, tea.Quit - } - - case FilterMatchesMsg: - m.filteredItems = filteredItems(msg) - return m, nil - - case spinner.TickMsg: - newSpinnerModel, cmd := m.spinner.Update(msg) - m.spinner = newSpinnerModel - if m.showSpinner { - cmds = append(cmds, cmd) - } - - case statusMessageTimeoutMsg: - m.hideStatusMessage() - } - - if m.filterState == Filtering { - cmds = append(cmds, m.handleFiltering(msg)) - } else { - cmds = append(cmds, m.handleBrowsing(msg)) - } - - return m, tea.Batch(cmds...) -} - -// Updates for when a user is browsing the list. -func (m *Model) handleBrowsing(msg tea.Msg) tea.Cmd { - var cmds []tea.Cmd - numItems := len(m.VisibleItems()) - - switch msg := msg.(type) { - case tea.KeyMsg: - switch { - // Note: we match clear filter before quit because, by default, they're - // both mapped to escape. - case key.Matches(msg, m.KeyMap.ClearFilter): - m.resetFiltering() - - case key.Matches(msg, m.KeyMap.Quit): - return tea.Quit - - case key.Matches(msg, m.KeyMap.CursorUp): - m.CursorUp() - - case key.Matches(msg, m.KeyMap.CursorDown): - m.CursorDown() - - case key.Matches(msg, m.KeyMap.PrevPage): - m.Paginator.PrevPage() - - case key.Matches(msg, m.KeyMap.NextPage): - m.Paginator.NextPage() - - case key.Matches(msg, m.KeyMap.GoToStart): - m.Paginator.Page = 0 - m.cursor = 0 - - case key.Matches(msg, m.KeyMap.GoToEnd): - m.Paginator.Page = m.Paginator.TotalPages - 1 - m.cursor = m.Paginator.ItemsOnPage(numItems) - 1 - - case key.Matches(msg, m.KeyMap.Filter): - m.hideStatusMessage() - if m.FilterInput.Value() == "" { - // Populate filter with all items only if the filter is empty. - m.filteredItems = m.itemsAsFilterItems() - } - m.Paginator.Page = 0 - m.cursor = 0 - m.filterState = Filtering - m.FilterInput.CursorEnd() - m.FilterInput.Focus() - m.updateKeybindings() - return textinput.Blink - - case key.Matches(msg, m.KeyMap.ShowFullHelp): - fallthrough - case key.Matches(msg, m.KeyMap.CloseFullHelp): - m.Help.ShowAll = !m.Help.ShowAll - m.updatePagination() - } - } - - cmd := m.delegate.Update(msg, m) - cmds = append(cmds, cmd) - - // Keep the index in bounds when paginating - itemsOnPage := m.Paginator.ItemsOnPage(len(m.VisibleItems())) - if m.cursor > itemsOnPage-1 { - m.cursor = max(0, itemsOnPage-1) - } - - return tea.Batch(cmds...) -} - -// Updates for when a user is in the filter editing interface. -func (m *Model) handleFiltering(msg tea.Msg) tea.Cmd { - var cmds []tea.Cmd - - // Handle keys - if msg, ok := msg.(tea.KeyMsg); ok { - switch { - case key.Matches(msg, m.KeyMap.CancelWhileFiltering): - m.resetFiltering() - m.KeyMap.Filter.SetEnabled(true) - m.KeyMap.ClearFilter.SetEnabled(false) - - case key.Matches(msg, m.KeyMap.AcceptWhileFiltering): - m.hideStatusMessage() - - if len(m.items) == 0 { - break - } - - h := m.VisibleItems() - - // If we've filtered down to nothing, clear the filter - if len(h) == 0 { - m.resetFiltering() - break - } - - m.FilterInput.Blur() - m.filterState = FilterApplied - m.updateKeybindings() - - if m.FilterInput.Value() == "" { - m.resetFiltering() - } - } - } - - // Update the filter text input component - newFilterInputModel, inputCmd := m.FilterInput.Update(msg) - filterChanged := m.FilterInput.Value() != newFilterInputModel.Value() - m.FilterInput = newFilterInputModel - cmds = append(cmds, inputCmd) - - // If the filtering input has changed, request updated filtering - if filterChanged { - cmds = append(cmds, filterItems(*m)) - m.KeyMap.AcceptWhileFiltering.SetEnabled(m.FilterInput.Value() != "") - } - - // Update pagination - m.updatePagination() - - return tea.Batch(cmds...) -} - -// ShortHelp returns bindings to show in the abbreviated help view. It's part -// of the help.KeyMap interface. -func (m Model) ShortHelp() []key.Binding { - kb := []key.Binding{ - m.KeyMap.CursorUp, - m.KeyMap.CursorDown, - } - - filtering := m.filterState == Filtering - - // If the delegate implements the help.KeyMap interface add the short help - // items to the short help after the cursor movement keys. - if !filtering { - if b, ok := m.delegate.(help.KeyMap); ok { - kb = append(kb, b.ShortHelp()...) - } - } - - kb = append(kb, - m.KeyMap.Filter, - m.KeyMap.ClearFilter, - m.KeyMap.AcceptWhileFiltering, - m.KeyMap.CancelWhileFiltering, - ) - - if !filtering && m.AdditionalShortHelpKeys != nil { - kb = append(kb, m.AdditionalShortHelpKeys()...) - } - - return append(kb, - m.KeyMap.Quit, - m.KeyMap.ShowFullHelp, - ) -} - -// FullHelp returns bindings to show the full help view. It's part of the -// help.KeyMap interface. -func (m Model) FullHelp() [][]key.Binding { - kb := [][]key.Binding{{ - m.KeyMap.CursorUp, - m.KeyMap.CursorDown, - m.KeyMap.NextPage, - m.KeyMap.PrevPage, - m.KeyMap.GoToStart, - m.KeyMap.GoToEnd, - }} - - filtering := m.filterState == Filtering - - // If the delegate implements the help.KeyMap interface add full help - // keybindings to a special section of the full help. - if !filtering { - if b, ok := m.delegate.(help.KeyMap); ok { - kb = append(kb, b.FullHelp()...) - } - } - - listLevelBindings := []key.Binding{ - m.KeyMap.Filter, - m.KeyMap.ClearFilter, - m.KeyMap.AcceptWhileFiltering, - m.KeyMap.CancelWhileFiltering, - } - - if !filtering && m.AdditionalFullHelpKeys != nil { - listLevelBindings = append(listLevelBindings, m.AdditionalFullHelpKeys()...) - } - - return append(kb, - listLevelBindings, - []key.Binding{ - m.KeyMap.Quit, - m.KeyMap.CloseFullHelp, - }) -} - -// View renders the component. -func (m Model) View() string { - var ( - sections []string - availHeight = m.height - ) - - if m.showTitle || (m.showFilter && m.filteringEnabled) { - v := m.titleView() - sections = append(sections, v) - availHeight -= lipgloss.Height(v) - } - - if m.showStatusBar { - v := m.statusView() - sections = append(sections, v) - availHeight -= lipgloss.Height(v) - } - - var pagination string - if m.showPagination { - pagination = m.paginationView() - availHeight -= lipgloss.Height(pagination) - } - - var help string - if m.showHelp { - help = m.helpView() - availHeight -= lipgloss.Height(help) - } - - content := lipgloss.NewStyle().Height(availHeight).Render(m.populatedView()) - sections = append(sections, content) - - if m.showPagination { - sections = append(sections, pagination) - } - - if m.showHelp { - sections = append(sections, help) - } - - return lipgloss.JoinVertical(lipgloss.Left, sections...) -} - -func (m Model) titleView() string { - var ( - view string - titleBarStyle = m.Styles.TitleBar.Copy() - - // We need to account for the size of the spinner, even if we don't - // render it, to reserve some space for it should we turn it on later. - spinnerView = m.spinnerView() - spinnerWidth = lipgloss.Width(spinnerView) - spinnerLeftGap = " " - spinnerOnLeft = titleBarStyle.GetPaddingLeft() >= spinnerWidth+lipgloss.Width(spinnerLeftGap) && m.showSpinner - ) - - // If the filter's showing, draw that. Otherwise draw the title. - if m.showFilter && m.filterState == Filtering { - view += m.FilterInput.View() - } else if m.showTitle { - if m.showSpinner && spinnerOnLeft { - view += spinnerView + spinnerLeftGap - titleBarGap := titleBarStyle.GetPaddingLeft() - titleBarStyle = titleBarStyle.PaddingLeft(titleBarGap - spinnerWidth - lipgloss.Width(spinnerLeftGap)) - } - - view += m.Styles.Title.Render(m.Title) - - // Status message - if m.filterState != Filtering { - view += " " + m.statusMessage - view = truncate.StringWithTail(view, uint(m.width-spinnerWidth), ellipsis) - } - } - - // Spinner - if m.showSpinner && !spinnerOnLeft { - // Place spinner on the right - availSpace := m.width - lipgloss.Width(m.Styles.TitleBar.Render(view)) - if availSpace > spinnerWidth { - view += strings.Repeat(" ", availSpace-spinnerWidth) - view += spinnerView - } - } - - if len(view) > 0 { - return titleBarStyle.Render(view) - } - return view -} - -func (m Model) statusView() string { - var status string - - totalItems := len(m.items) - visibleItems := len(m.VisibleItems()) - - var itemName string - if visibleItems != 1 { - itemName = m.itemNamePlural - } else { - itemName = m.itemNameSingular - } - - itemsDisplay := fmt.Sprintf("%d %s", visibleItems, itemName) - - if m.filterState == Filtering { - // Filter results - if visibleItems == 0 { - status = m.Styles.StatusEmpty.Render("Nothing matched") - } else { - status = itemsDisplay - } - } else if len(m.items) == 0 { - // Not filtering: no items. - status = m.Styles.StatusEmpty.Render("No " + m.itemNamePlural) - } else { - // Normal - filtered := m.FilterState() == FilterApplied - - if filtered { - f := strings.TrimSpace(m.FilterInput.Value()) - f = truncate.StringWithTail(f, 10, "…") - status += fmt.Sprintf("“%s” ", f) - } - - status += itemsDisplay - } - - numFiltered := totalItems - visibleItems - if numFiltered > 0 { - status += m.Styles.DividerDot.String() - status += m.Styles.StatusBarFilterCount.Render(fmt.Sprintf("%d filtered", numFiltered)) - } - - return m.Styles.StatusBar.Render(status) -} - -func (m Model) paginationView() string { - if m.Paginator.TotalPages < 2 { //nolint:gomnd - return "" - } - - s := m.Paginator.View() - - // If the dot pagination is wider than the width of the window - // use the arabic paginator. - if ansi.PrintableRuneWidth(s) > m.width { - m.Paginator.Type = paginator.Arabic - s = m.Styles.ArabicPagination.Render(m.Paginator.View()) - } - - style := m.Styles.PaginationStyle - if m.delegate.Spacing() == 0 && style.GetMarginTop() == 0 { - style = style.Copy().MarginTop(1) - } - - return style.Render(s) -} - -func (m Model) populatedView() string { - items := m.VisibleItems() - - var b strings.Builder - - // Empty states - if len(items) == 0 { - if m.filterState == Filtering { - return "" - } - return m.Styles.NoItems.Render("No " + m.itemNamePlural + " found.") - } - - if len(items) > 0 { - start, end := m.Paginator.GetSliceBounds(len(items)) - docs := items[start:end] - - for i, item := range docs { - m.delegate.Render(&b, m, i+start, item) - if i != len(docs)-1 { - fmt.Fprint(&b, strings.Repeat("\n", m.delegate.Spacing()+1)) - } - } - } - - // If there aren't enough items to fill up this page (always the last page) - // then we need to add some newlines to fill up the space where items would - // have been. - itemsOnPage := m.Paginator.ItemsOnPage(len(items)) - if itemsOnPage < m.Paginator.PerPage { - n := (m.Paginator.PerPage - itemsOnPage) * (m.delegate.Height() + m.delegate.Spacing()) - if len(items) == 0 { - n -= m.delegate.Height() - 1 - } - fmt.Fprint(&b, strings.Repeat("\n", n)) - } - - return b.String() -} - -func (m Model) helpView() string { - return m.Styles.HelpStyle.Render(m.Help.View(m)) -} - -func (m Model) spinnerView() string { - return m.spinner.View() -} - -func filterItems(m Model) tea.Cmd { - return func() tea.Msg { - if m.FilterInput.Value() == "" || m.filterState == Unfiltered { - return FilterMatchesMsg(m.itemsAsFilterItems()) // return nothing - } - - targets := []string{} - items := m.items - - for _, t := range items { - targets = append(targets, t.FilterValue()) - } - - filterMatches := []filteredItem{} - for _, r := range m.Filter(m.FilterInput.Value(), targets) { - filterMatches = append(filterMatches, filteredItem{ - item: items[r.Index], - matches: r.MatchedIndexes, - }) - } - - return FilterMatchesMsg(filterMatches) - } -} - -func insertItemIntoSlice(items []Item, item Item, index int) []Item { - if items == nil { - return []Item{item} - } - if index >= len(items) { - return append(items, item) - } - - index = max(0, index) - - items = append(items, nil) - copy(items[index+1:], items[index:]) - items[index] = item - return items -} - -// Remove an item from a slice of items at the given index. This runs in O(n). -func removeItemFromSlice(i []Item, index int) []Item { - if index >= len(i) { - return i // noop - } - copy(i[index:], i[index+1:]) - i[len(i)-1] = nil - return i[:len(i)-1] -} - -func removeFilterMatchFromSlice(i []filteredItem, index int) []filteredItem { - if index >= len(i) { - return i // noop - } - copy(i[index:], i[index+1:]) - i[len(i)-1] = filteredItem{} - return i[:len(i)-1] -} - -func countEnabledBindings(groups [][]key.Binding) (agg int) { - for _, group := range groups { - for _, kb := range group { - if kb.Enabled() { - agg++ - } - } - } - return agg -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} diff --git a/vendor/github.com/charmbracelet/bubbles/list/style.go b/vendor/github.com/charmbracelet/bubbles/list/style.go deleted file mode 100644 index e4451f8..0000000 --- a/vendor/github.com/charmbracelet/bubbles/list/style.go +++ /dev/null @@ -1,99 +0,0 @@ -package list - -import ( - "github.com/charmbracelet/lipgloss" -) - -const ( - bullet = "•" - ellipsis = "…" -) - -// Styles contains style definitions for this list component. By default, these -// values are generated by DefaultStyles. -type Styles struct { - TitleBar lipgloss.Style - Title lipgloss.Style - Spinner lipgloss.Style - FilterPrompt lipgloss.Style - FilterCursor lipgloss.Style - - // Default styling for matched characters in a filter. This can be - // overridden by delegates. - DefaultFilterCharacterMatch lipgloss.Style - - StatusBar lipgloss.Style - StatusEmpty lipgloss.Style - StatusBarActiveFilter lipgloss.Style - StatusBarFilterCount lipgloss.Style - - NoItems lipgloss.Style - - PaginationStyle lipgloss.Style - HelpStyle lipgloss.Style - - // Styled characters. - ActivePaginationDot lipgloss.Style - InactivePaginationDot lipgloss.Style - ArabicPagination lipgloss.Style - DividerDot lipgloss.Style -} - -// DefaultStyles returns a set of default style definitions for this list -// component. -func DefaultStyles() (s Styles) { - verySubduedColor := lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"} - subduedColor := lipgloss.AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"} - - s.TitleBar = lipgloss.NewStyle().Padding(0, 0, 1, 2) - - s.Title = lipgloss.NewStyle(). - Background(lipgloss.Color("62")). - Foreground(lipgloss.Color("230")). - Padding(0, 1) - - s.Spinner = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#8E8E8E", Dark: "#747373"}) - - s.FilterPrompt = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"}) - - s.FilterCursor = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"}) - - s.DefaultFilterCharacterMatch = lipgloss.NewStyle().Underline(true) - - s.StatusBar = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"}). - Padding(0, 0, 1, 2) - - s.StatusEmpty = lipgloss.NewStyle().Foreground(subduedColor) - - s.StatusBarActiveFilter = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}) - - s.StatusBarFilterCount = lipgloss.NewStyle().Foreground(verySubduedColor) - - s.NoItems = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#909090", Dark: "#626262"}) - - s.ArabicPagination = lipgloss.NewStyle().Foreground(subduedColor) - - s.PaginationStyle = lipgloss.NewStyle().PaddingLeft(2) //nolint:gomnd - - s.HelpStyle = lipgloss.NewStyle().Padding(1, 0, 0, 2) - - s.ActivePaginationDot = lipgloss.NewStyle(). - Foreground(lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"}). - SetString(bullet) - - s.InactivePaginationDot = lipgloss.NewStyle(). - Foreground(verySubduedColor). - SetString(bullet) - - s.DividerDot = lipgloss.NewStyle(). - Foreground(verySubduedColor). - SetString(" " + bullet + " ") - - return s -} diff --git a/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go b/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go deleted file mode 100644 index a84819a..0000000 --- a/vendor/github.com/charmbracelet/bubbles/paginator/paginator.go +++ /dev/null @@ -1,202 +0,0 @@ -// Package paginator provides a Bubble Tea package for calulating pagination -// and rendering pagination info. Note that this package does not render actual -// pages: it's purely for handling keystrokes related to pagination, and -// rendering pagination status. -package paginator - -import ( - "fmt" - - tea "github.com/charmbracelet/bubbletea" -) - -// Type specifies the way we render pagination. -type Type int - -// Pagination rendering options. -const ( - Arabic Type = iota - Dots -) - -// Model is the Bubble Tea model for this user interface. -type Model struct { - Type Type - Page int - PerPage int - TotalPages int - ActiveDot string - InactiveDot string - ArabicFormat string - UsePgUpPgDownKeys bool - UseLeftRightKeys bool - UseUpDownKeys bool - UseHLKeys bool - UseJKKeys bool -} - -// SetTotalPages is a helper function for calculating the total number of pages -// from a given number of items. It's use is optional since this pager can be -// used for other things beyond navigating sets. Note that it both returns the -// number of total pages and alters the model. -func (m *Model) SetTotalPages(items int) int { - if items < 1 { - return m.TotalPages - } - n := items / m.PerPage - if items%m.PerPage > 0 { - n++ - } - m.TotalPages = n - return n -} - -// ItemsOnPage is a helper function for returning the numer of items on the -// current page given the total numer of items passed as an argument. -func (m Model) ItemsOnPage(totalItems int) int { - if totalItems < 1 { - return 0 - } - start, end := m.GetSliceBounds(totalItems) - return end - start -} - -// GetSliceBounds is a helper function for paginating slices. Pass the length -// of the slice you're rendering and you'll receive the start and end bounds -// corresponding the to pagination. For example: -// -// bunchOfStuff := []stuff{...} -// start, end := model.GetSliceBounds(len(bunchOfStuff)) -// sliceToRender := bunchOfStuff[start:end] -// -func (m *Model) GetSliceBounds(length int) (start int, end int) { - start = m.Page * m.PerPage - end = min(m.Page*m.PerPage+m.PerPage, length) - return start, end -} - -// PrevPage is a number function for navigating one page backward. It will not -// page beyond the first page (i.e. page 0). -func (m *Model) PrevPage() { - if m.Page > 0 { - m.Page-- - } -} - -// NextPage is a helper function for navigating one page forward. It will not -// page beyond the last page (i.e. totalPages - 1). -func (m *Model) NextPage() { - if !m.OnLastPage() { - m.Page++ - } -} - -// OnLastPage returns whether or not we're on the last page. -func (m Model) OnLastPage() bool { - return m.Page == m.TotalPages-1 -} - -// New creates a new model with defaults. -func New() Model { - return Model{ - Type: Arabic, - Page: 0, - PerPage: 1, - TotalPages: 1, - ActiveDot: "•", - InactiveDot: "○", - ArabicFormat: "%d/%d", - UsePgUpPgDownKeys: true, - UseLeftRightKeys: true, - UseUpDownKeys: false, - UseHLKeys: true, - UseJKKeys: false, - } -} - -// NewModel creates a new model with defaults. -// -// Deprecated. Use New instead. -var NewModel = New - -// Update is the Tea update function which binds keystrokes to pagination. -func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - if m.UsePgUpPgDownKeys { - switch msg.String() { - case "pgup": - m.PrevPage() - case "pgdown": - m.NextPage() - } - } - if m.UseLeftRightKeys { - switch msg.String() { - case "left": - m.PrevPage() - case "right": - m.NextPage() - } - } - if m.UseUpDownKeys { - switch msg.String() { - case "up": - m.PrevPage() - case "down": - m.NextPage() - } - } - if m.UseHLKeys { - switch msg.String() { - case "h": - m.PrevPage() - case "l": - m.NextPage() - } - } - if m.UseJKKeys { - switch msg.String() { - case "j": - m.PrevPage() - case "k": - m.NextPage() - } - } - } - - return m, nil -} - -// View renders the pagination to a string. -func (m Model) View() string { - switch m.Type { - case Dots: - return m.dotsView() - default: - return m.arabicView() - } -} - -func (m Model) dotsView() string { - var s string - for i := 0; i < m.TotalPages; i++ { - if i == m.Page { - s += m.ActiveDot - continue - } - s += m.InactiveDot - } - return s -} - -func (m Model) arabicView() string { - return fmt.Sprintf(m.ArabicFormat, m.Page+1, m.TotalPages) -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} diff --git a/vendor/github.com/charmbracelet/bubbletea/README.md b/vendor/github.com/charmbracelet/bubbletea/README.md index fe1b5cf..3bad838 100644 --- a/vendor/github.com/charmbracelet/bubbletea/README.md +++ b/vendor/github.com/charmbracelet/bubbletea/README.md @@ -316,51 +316,58 @@ your program in another window. For some Bubble Tea programs in production, see: -* [AT CLI](https://github.com/daskycodes/at_cli): a utility for executing AT Commands via serial port connections +* [AT CLI](https://github.com/daskycodes/at_cli): execute AT Commands via serial port connections * [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform +* [brows](https://github.com/rubysolo/brows): a GitHub release browser * [Canard](https://github.com/mrusme/canard): an RSS client * [charm](https://github.com/charmbracelet/charm): the official Charm user account manager -* [chezmoi](https://github.com/twpayne/chezmoi): manage your dotfiles across multiple machines, securely -* [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in your terminal -* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone for your terminal +* [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines +* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving terminal +* [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal +* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone +* [cLive](https://github.com/koki-develop/clive): automate terminal operations and view them live in a browser * [container-canary](https://github.com/NVIDIA/container-canary): a container validator -* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53. Expose your EC2 quickly, securely and privately +* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53: expose your EC2 quickly, securely and privately +* [enola](https://github.com/sherlock-project/enola): hunt down social media accounts by username across social networks * [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI! * [fm](https://github.com/knipferrc/fm): a terminal-based file manager -* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): cleans up old and inactive forks in your GitHub account -* [fztea](https://github.com/jon4hz/fztea): connect to your Flipper's UI over serial or make it accessible via SSH -* [gambit](https://github.com/maaslalani/gambit): play chess in the terminal +* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): clean up old and inactive forks in your GitHub account +* [fztea](https://github.com/jon4hz/fztea): a Flipper Zero TUI +* [gambit](https://github.com/maaslalani/gambit): chess in the terminal * [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser -* [gh-b](https://github.com/joaom00/gh-b): GitHub CLI extension to easily manage your branches -* [gh-dash](https://www.github.com/dlvhdr/gh-dash): GitHub CLI extension to display a dashboard of PRs and issues +* [gh-b](https://github.com/joaom00/gh-b): a GitHub CLI extension for managing branches +* [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues * [gitflow-toolkit](https://github.com/mritd/gitflow-toolkit): a GitFlow submission tool -* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser and online markdown stash +* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash * [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI * [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app build on top of simplytranslate's APIs * [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool -* [IDNT](https://github.com/r-darwish/idnt): batch software uninstaller +* [IDNT](https://github.com/r-darwish/idnt): a batch software uninstaller * [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game -* [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): Multiplatform terminal mandelbrot set explorer +* [mandelbrot-cli](https://github.com/MicheleFiladelfia/mandelbrot-cli): a multiplatform terminal mandelbrot set explorer * [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client * [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories -* [Noted](https://github.com/torbratsberg/noted): Note viewer and manager -* [pathos](https://github.com/chip/pathos): pathos - CLI for editing a PATH env variable -* [portal](https://github.com/ZinoKader/portal): securely send transfer between computers -* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): browse Redis databases -* [sku](https://github.com/fedeztk/sku): a simple TUI for playing sudoku inside the terminal +* [Neon Modem Overdrive](https://github.com/mrusme/neonmodem): a BBS-style TUI client for Discourse, Lemmy, Lobste.rs and Hacker News +* [Noted](https://github.com/torbratsberg/noted): a note viewer and manager +* [pathos](https://github.com/chip/pathos): a PATH env variable editor +* [portal](https://github.com/ZinoKader/portal): secure transfers between computers +* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis databases browser +* [sku](https://github.com/fedeztk/sku): Sudoku on the CLI * [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool +* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager TUI * [Soft Serve](https://github.com/charmbracelet/soft-serve): a command-line-first Git server that runs a TUI over SSH +* [solitaire-tui](https://github.com/brianstrauch/solitaire-tui): Klondike Solitaire for the terminal * [StormForge Optimize Controller](https://github.com/thestormforge/optimize-controller): a tool for experimenting with application configurations in Kubernetes -* [STTG](https://github.com/wille1101/sttg): teletext client for SVT, Sweden’s national public television station -* [sttr](https://github.com/abhimanyu003/sttr): run various text transformations +* [STTG](https://github.com/wille1101/sttg): a teletext client for SVT, Sweden’s national public television station +* [sttr](https://github.com/abhimanyu003/sttr): a general-purpose text transformer * [tasktimer](https://github.com/caarlos0/tasktimer): a dead-simple task timer * [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser -* [ticker](https://github.com/achannarasappa/ticker): a terminal stock watcher and stock position tracker -* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal][portal]) +* [ticker](https://github.com/achannarasappa/ticker): a terminal stock viewer and stock position tracker +* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal][https://github.com/ZinoKader/portal]) * [Typer](https://github.com/maaslalani/typer): a typing test * [tz](https://github.com/oz/tz): an aid for scheduling across multiple time zones * [ugm](https://github.com/ariasmn/ugm): a unix user and group browser -* [wander](https://github.com/robinovitch61/wander): HashiCorp Nomad terminal client +* [wander](https://github.com/robinovitch61/wander): a HashiCorp Nomad terminal client * [wishlist](https://github.com/charmbracelet/wishlist): an SSH directory ## Feedback diff --git a/vendor/github.com/charmbracelet/bubbletea/tea.go b/vendor/github.com/charmbracelet/bubbletea/tea.go index 03734bc..f5f452b 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tea.go +++ b/vendor/github.com/charmbracelet/bubbletea/tea.go @@ -24,6 +24,7 @@ import ( isatty "github.com/mattn/go-isatty" "github.com/muesli/cancelreader" "github.com/muesli/termenv" + "golang.org/x/sync/errgroup" ) // ErrProgramKilled is returned by [Program.Run] when the program got killed. @@ -310,7 +311,25 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { go func() { // Execute commands one at a time, in order. for _, cmd := range msg { - p.Send(cmd()) + if cmd == nil { + continue + } + msg := cmd() + if batchMsg, ok := msg.(BatchMsg); ok { + g, _ := errgroup.WithContext(p.ctx) + for _, cmd := range batchMsg { + cmd := cmd + g.Go(func() error { + p.Send(cmd()) + return nil + }) + } + //nolint:errcheck + g.Wait() // wait for all commands from batch msg to finish + continue + } else { + p.Send(msg) + } } }() } diff --git a/vendor/github.com/muesli/termenv/README.md b/vendor/github.com/muesli/termenv/README.md index 1fc63dd..29dcf01 100644 --- a/vendor/github.com/muesli/termenv/README.md +++ b/vendor/github.com/muesli/termenv/README.md @@ -248,6 +248,15 @@ output.HideCursor() // Show the cursor output.ShowCursor() + +// Copy to clipboard +output.Copy(message) + +// Copy to primary clipboard (X11) +output.CopyPrimary(message) + +// Trigger notification +output.Notify(title, body) ``` ## Mouse @@ -294,30 +303,72 @@ termenv.EnableBracketedPaste() termenv.DisableBracketedPaste() ``` -## Optional Feature Support - -| Terminal | Alt Screen | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | Copy (OSC52) | Hyperlinks (OSC8) | Bracketed Paste | -| ---------------- | :--------: | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | :----------: | :---------------: | :-------------: | -| alacritty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌[^alacritty] | ✅ | -| foot | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| kitty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| Konsole | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌[^konsole] | ✅ | ✅ | -| rxvt | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | -| urxvt | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅[^urxvt] | ❌ | ✅ | -| screen | ✅ | ⛔[^mux] | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌[^screen] | ❌ | -| st | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | -| tmux | ✅ | ⛔[^mux] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌[^tmux] | ✅ | -| vte-based[^vte] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌[^vte] | ✅ | ✅ | -| wezterm | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| xterm | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | -| Linux Console | ✅ | ❌ | ✅ | ⛔ | ❌ | ❌ | ❌ | ⛔ | ⛔ | ❌ | -| Apple Terminal | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅[^apple] | ❌ | ✅ | -| iTerm | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ | -| Windows cmd | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | -| Windows Terminal | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +## Terminal Feature Support -[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. OSC52 is not supported, see [issue#2495](https://gitlab.gnome.org/GNOME/vte/-/issues/2495). +### Color Support + +- 24-bit (RGB): alacritty, foot, iTerm, kitty, Konsole, st, tmux, vte-based, wezterm, Windows Terminal +- 8-bit (256): rxvt, screen, xterm, Apple Terminal +- 4-bit (16): Linux Console + +### Control Sequences + +
+Click to show feature matrix + +| Terminal | Query Color Scheme | Query Cursor Position | Set Window Title | Change Cursor Color | Change Default Foreground Setting | Change Default Background Setting | Bracketed Paste | Extended Mouse (SGR) | Pixels Mouse (SGR-Pixels) | +| ---------------- | :----------------: | :-------------------: | :--------------: | :-----------------: | :-------------------------------: | :-------------------------------: | :-------------: | :------------------: | :-----------------------: | +| alacritty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| foot | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| kitty | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Konsole | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| rxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | +| urxvt | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| screen | ⛔[^mux] | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | +| st | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| tmux | ⛔[^mux] | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | +| vte-based[^vte] | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | +| wezterm | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| xterm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | +| Linux Console | ❌ | ✅ | ⛔ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Apple Terminal | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ❌ | +| iTerm | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | +| Windows cmd | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| Windows Terminal | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | + +[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. [^mux]: Unavailable as multiplexers (like tmux or screen) can be connected to multiple terminals (with different color settings) at the same time. + +You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request. + +
+ +### System Commands + +
+Click to show feature matrix + +| Terminal | Copy to Clipboard (OSC52) | Hyperlinks (OSC8) | Notifications (OSC777) | +| ---------------- | :-----------------------: | :---------------: | :--------------------: | +| alacritty | ✅ | ❌[^alacritty] | ❌ | +| foot | ✅ | ✅ | ✅ | +| kitty | ✅ | ✅ | ✅ | +| Konsole | ❌[^konsole] | ✅ | ❌ | +| rxvt | ❌ | ❌ | ❌ | +| urxvt | ✅[^urxvt] | ❌ | ✅ | +| screen | ✅ | ❌[^screen] | ❌ | +| st | ✅ | ❌ | ❌ | +| tmux | ✅ | ❌[^tmux] | ❌ | +| vte-based[^vte] | ❌[^vte] | ✅ | ❌ | +| wezterm | ✅ | ✅ | ❌ | +| xterm | ✅ | ❌ | ❌ | +| Linux Console | ⛔ | ⛔ | ❌ | +| Apple Terminal | ✅[^apple] | ❌ | ❌ | +| iTerm | ✅ | ✅ | ❌ | +| Windows cmd | ❌ | ❌ | ❌ | +| Windows Terminal | ✅ | ✅ | ❌ | + +[^vte]: This covers all vte-based terminals, including Gnome Terminal, guake, Pantheon Terminal, Terminator, Tilix, XFCE Terminal. OSC52 is not supported, see [issue#2495](https://gitlab.gnome.org/GNOME/vte/-/issues/2495). [^urxvt]: Workaround for urxvt not supporting OSC52. See [this](https://unix.stackexchange.com/a/629485) for more information. [^konsole]: OSC52 is not supported, for more info see [bug#372116](https://bugs.kde.org/show_bug.cgi?id=372116). [^apple]: OSC52 works with a [workaround](https://github.com/roy2220/osc52pty). @@ -325,13 +376,7 @@ termenv.DisableBracketedPaste() [^screen]: OSC8 is not supported, for more info see [bug#50952](https://savannah.gnu.org/bugs/index.php?50952). [^alacritty]: OSC8 is not supported, for more info see [issue#922](https://github.com/alacritty/alacritty/issues/922). -You can help improve this list! Check out [how to](ansi_compat.md) and open an issue or pull request. - -### Color Support - -- 24-bit (RGB): alacritty, foot, iTerm, kitty, Konsole, st, tmux, vte-based, wezterm, Windows Terminal -- 8-bit (256): rxvt, screen, xterm, Apple Terminal -- 4-bit (16): Linux Console +
## Platform Support @@ -350,7 +395,6 @@ you need to enable ANSI processing in your application first: The above code is safe to include on non-Windows systems or when os.Stdout does not refer to a terminal (e.g. in tests). - ## Color Chart ![ANSI color chart](https://github.com/muesli/termenv/raw/master/examples/color-chart/color-chart.png) diff --git a/vendor/github.com/muesli/termenv/ansi_compat.md b/vendor/github.com/muesli/termenv/ansi_compat.md index b403d74..6b68a3a 100644 --- a/vendor/github.com/muesli/termenv/ansi_compat.md +++ b/vendor/github.com/muesli/termenv/ansi_compat.md @@ -55,3 +55,11 @@ displayed on the terminal should contain the codes `200~` and `201~`: ```bash echo -ne "\033[?2004h" && sleep 10 ``` + +## Trigger Notification + +This command should trigger a notification: + +```bash +echo -ne "\033]777;notify;Title;Body\033\\" +``` diff --git a/vendor/github.com/muesli/termenv/color.go b/vendor/github.com/muesli/termenv/color.go index 885ad51..0d11f43 100644 --- a/vendor/github.com/muesli/termenv/color.go +++ b/vendor/github.com/muesli/termenv/color.go @@ -117,12 +117,12 @@ func xTermColor(s string) (RGBColor, error) { } switch { - case strings.HasSuffix(s, "\a"): - s = strings.TrimSuffix(s, "\a") - case strings.HasSuffix(s, "\033"): - s = strings.TrimSuffix(s, "\033") - case strings.HasSuffix(s, "\033\\"): - s = strings.TrimSuffix(s, "\033\\") + case strings.HasSuffix(s, string(BEL)): + s = strings.TrimSuffix(s, string(BEL)) + case strings.HasSuffix(s, string(ESC)): + s = strings.TrimSuffix(s, string(ESC)) + case strings.HasSuffix(s, ST): + s = strings.TrimSuffix(s, ST) default: return RGBColor(""), ErrInvalidColor } diff --git a/vendor/github.com/muesli/termenv/copy.go b/vendor/github.com/muesli/termenv/copy.go index 5c975d9..f65f5b6 100644 --- a/vendor/github.com/muesli/termenv/copy.go +++ b/vendor/github.com/muesli/termenv/copy.go @@ -4,13 +4,28 @@ import ( "github.com/aymanbagabas/go-osc52" ) +func (o Output) osc52Output() *osc52.Output { + return osc52.NewOutput(o.tty, o.environ.Environ()) +} + // Copy copies text to clipboard using OSC 52 escape sequence. func (o Output) Copy(str string) { - out := osc52.NewOutput(o.tty, o.environ.Environ()) - out.Copy(str) + o.osc52Output().Copy(str) +} + +// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape +// sequence. +func (o Output) CopyPrimary(str string) { + o.osc52Output().CopyPrimary(str) } // Copy copies text to clipboard using OSC 52 escape sequence. func Copy(str string) { output.Copy(str) } + +// CopyPrimary copies text to primary clipboard (X11) using OSC 52 escape +// sequence. +func CopyPrimary(str string) { + output.CopyPrimary(str) +} diff --git a/vendor/github.com/muesli/termenv/hyperlink.go b/vendor/github.com/muesli/termenv/hyperlink.go index 9d45450..97e760a 100644 --- a/vendor/github.com/muesli/termenv/hyperlink.go +++ b/vendor/github.com/muesli/termenv/hyperlink.go @@ -1,9 +1,5 @@ package termenv -import ( - "fmt" -) - // Hyperlink creates a hyperlink using OSC8. func Hyperlink(link, name string) string { return output.Hyperlink(link, name) @@ -11,5 +7,5 @@ func Hyperlink(link, name string) string { // Hyperlink creates a hyperlink using OSC8. func (o *Output) Hyperlink(link, name string) string { - return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", link, name) + return OSC + "8;;" + link + ST + name + OSC + "8;;" + ST } diff --git a/vendor/github.com/muesli/termenv/notification.go b/vendor/github.com/muesli/termenv/notification.go new file mode 100644 index 0000000..2a8cf06 --- /dev/null +++ b/vendor/github.com/muesli/termenv/notification.go @@ -0,0 +1,11 @@ +package termenv + +// Notify triggers a notification using OSC777. +func Notify(title, body string) { + output.Notify(title, body) +} + +// Notify triggers a notification using OSC777. +func (o *Output) Notify(title, body string) { + _, _ = o.WriteString(OSC + "777;notify;" + title + ";" + body + ST) +} diff --git a/vendor/github.com/muesli/termenv/output.go b/vendor/github.com/muesli/termenv/output.go index 5477892..37a0db9 100644 --- a/vendor/github.com/muesli/termenv/output.go +++ b/vendor/github.com/muesli/termenv/output.go @@ -8,7 +8,7 @@ import ( var ( // output is the default global output. - output = NewOutput(os.Stdout) + output = NewOutput(nil) ) // File represents a file descriptor. @@ -17,12 +17,16 @@ type File interface { Fd() uintptr } +// OutputOption sets an option on Output. +type OutputOption = func(*Output) + // Output is a terminal output. type Output struct { Profile tty io.Writer environ Environ + unsafe bool cache bool fgSync *sync.Once fgColor Color @@ -52,7 +56,7 @@ func DefaultOutput() *Output { } // NewOutput returns a new Output for the given file descriptor. -func NewOutput(tty io.Writer, opts ...func(*Output)) *Output { +func NewOutput(tty io.Writer, opts ...OutputOption) *Output { o := &Output{ tty: tty, environ: &osEnviron{}, @@ -69,19 +73,22 @@ func NewOutput(tty io.Writer, opts ...func(*Output)) *Output { if o.Profile < 0 { o.Profile = o.EnvColorProfile() } + if o.tty == nil { + o.tty = os.Stdout + } return o } // WithEnvironment returns a new Output for the given environment. -func WithEnvironment(environ Environ) func(*Output) { +func WithEnvironment(environ Environ) OutputOption { return func(o *Output) { o.environ = environ } } // WithProfile returns a new Output for the given profile. -func WithProfile(profile Profile) func(*Output) { +func WithProfile(profile Profile) OutputOption { return func(o *Output) { o.Profile = profile } @@ -89,7 +96,7 @@ func WithProfile(profile Profile) func(*Output) { // WithColorCache returns a new Output with fore- and background color values // pre-fetched and cached. -func WithColorCache(v bool) func(*Output) { +func WithColorCache(v bool) OutputOption { return func(o *Output) { o.cache = v @@ -99,6 +106,17 @@ func WithColorCache(v bool) func(*Output) { } } +// WithUnsafe returns a new Output with unsafe mode enabled. Unsafe mode doesn't +// check whether or not the terminal is a TTY. +// +// This is useful when mocking console output and enforcing ANSI escape output +// e.g. on SSH sessions. +func WithUnsafe() OutputOption { + return func(o *Output) { + o.unsafe = true + } +} + // ForegroundColor returns the terminal's default foreground color. func (o *Output) ForegroundColor() Color { f := func() { diff --git a/vendor/github.com/muesli/termenv/screen.go b/vendor/github.com/muesli/termenv/screen.go index 1bd3307..a71181b 100644 --- a/vendor/github.com/muesli/termenv/screen.go +++ b/vendor/github.com/muesli/termenv/screen.go @@ -32,16 +32,20 @@ const ( EraseEntireLineSeq = "2K" // Mouse. - EnableMousePressSeq = "?9h" // press only (X10) - DisableMousePressSeq = "?9l" - EnableMouseSeq = "?1000h" // press, release, wheel - DisableMouseSeq = "?1000l" - EnableMouseHiliteSeq = "?1001h" // highlight - DisableMouseHiliteSeq = "?1001l" - EnableMouseCellMotionSeq = "?1002h" // press, release, move on pressed, wheel - DisableMouseCellMotionSeq = "?1002l" - EnableMouseAllMotionSeq = "?1003h" // press, release, move, wheel - DisableMouseAllMotionSeq = "?1003l" + EnableMousePressSeq = "?9h" // press only (X10) + DisableMousePressSeq = "?9l" + EnableMouseSeq = "?1000h" // press, release, wheel + DisableMouseSeq = "?1000l" + EnableMouseHiliteSeq = "?1001h" // highlight + DisableMouseHiliteSeq = "?1001l" + EnableMouseCellMotionSeq = "?1002h" // press, release, move on pressed, wheel + DisableMouseCellMotionSeq = "?1002l" + EnableMouseAllMotionSeq = "?1003h" // press, release, move, wheel + DisableMouseAllMotionSeq = "?1003l" + EnableMouseExtendedModeSeq = "?1006h" // press, release, move, wheel, extended coordinates + DisableMouseExtendedModeSeq = "?1006l" + EnableMousePixelsModeSeq = "?1016h" // press, release, move, wheel, extended pixel coordinates + DisableMousePixelsModeSeq = "?1016l" // Screen. RestoreScreenSeq = "?47l" @@ -57,10 +61,10 @@ const ( EndBracketedPasteSeq = "201~" // Session. - SetWindowTitleSeq = "2;%s\007" - SetForegroundColorSeq = "10;%s\007" - SetBackgroundColorSeq = "11;%s\007" - SetCursorColorSeq = "12;%s\007" + SetWindowTitleSeq = "2;%s" + string(BEL) + SetForegroundColorSeq = "10;%s" + string(BEL) + SetBackgroundColorSeq = "11;%s" + string(BEL) + SetCursorColorSeq = "12;%s" + string(BEL) ShowCursorSeq = "?25h" HideCursorSeq = "?25l" ) @@ -259,6 +263,29 @@ func (o Output) DisableMouseAllMotion() { fmt.Fprint(o.tty, CSI+DisableMouseAllMotionSeq) } +// EnableMouseExtendedMotion enables Extended Mouse mode (SGR). This should be +// enabled in conjunction with EnableMouseCellMotion, and EnableMouseAllMotion. +func (o Output) EnableMouseExtendedMode() { + fmt.Fprint(o.tty, CSI+EnableMouseExtendedModeSeq) +} + +// DisableMouseExtendedMotion disables Extended Mouse mode (SGR). +func (o Output) DisableMouseExtendedMode() { + fmt.Fprint(o.tty, CSI+DisableMouseExtendedModeSeq) +} + +// EnableMousePixelsMotion enables Pixel Motion Mouse mode (SGR-Pixels). This +// should be enabled in conjunction with EnableMouseCellMotion, and +// EnableMouseAllMotion. +func (o Output) EnableMousePixelsMode() { + fmt.Fprint(o.tty, CSI+EnableMousePixelsModeSeq) +} + +// DisableMousePixelsMotion disables Pixel Motion Mouse mode (SGR-Pixels). +func (o Output) DisableMousePixelsMode() { + fmt.Fprint(o.tty, CSI+DisableMousePixelsModeSeq) +} + // SetWindowTitle sets the terminal window title. func (o Output) SetWindowTitle(title string) { fmt.Fprintf(o.tty, OSC+SetWindowTitleSeq, title) diff --git a/vendor/github.com/muesli/termenv/termenv.go b/vendor/github.com/muesli/termenv/termenv.go index 5031f03..e39ccc5 100644 --- a/vendor/github.com/muesli/termenv/termenv.go +++ b/vendor/github.com/muesli/termenv/termenv.go @@ -12,13 +12,22 @@ var ( ) const ( + // Escape character + ESC = '\x1b' + // Bell + BEL = '\a' // Control Sequence Introducer - CSI = "\x1b[" + CSI = string(ESC) + "[" // Operating System Command - OSC = "\x1b]" + OSC = string(ESC) + "]" + // String Terminator + ST = string(ESC) + `\` ) func (o *Output) isTTY() bool { + if o.unsafe { + return true + } if len(o.environ.Getenv("CI")) > 0 { return false } diff --git a/vendor/github.com/muesli/termenv/termenv_other.go b/vendor/github.com/muesli/termenv/termenv_other.go index 3c4b703..93a43b6 100644 --- a/vendor/github.com/muesli/termenv/termenv_other.go +++ b/vendor/github.com/muesli/termenv/termenv_other.go @@ -3,7 +3,11 @@ package termenv -func colorProfile() Profile { +import "io" + +// ColorProfile returns the supported color profile: +// ANSI256 +func (o Output) ColorProfile() Profile { return ANSI256 } @@ -16,3 +20,11 @@ func (o Output) backgroundColor() Color { // default black return ANSIColor(0) } + +// EnableVirtualTerminalProcessing enables virtual terminal processing on +// Windows for w and returns a function that restores w to its previous state. +// On non-Windows platforms, or if w does not refer to a terminal, then it +// returns a non-nil no-op function and no error. +func EnableVirtualTerminalProcessing(w io.Writer) (func() error, error) { + return func() error { return nil }, nil +} diff --git a/vendor/github.com/muesli/termenv/termenv_unix.go b/vendor/github.com/muesli/termenv/termenv_unix.go index 778c354..11746d2 100644 --- a/vendor/github.com/muesli/termenv/termenv_unix.go +++ b/vendor/github.com/muesli/termenv/termenv_unix.go @@ -109,7 +109,8 @@ func (o Output) backgroundColor() Color { return ANSIColor(0) } -func waitForData(fd uintptr, timeout time.Duration) error { +func (o *Output) waitForData(timeout time.Duration) error { + fd := o.TTY().Fd() tv := unix.NsecToTimeval(int64(timeout)) var readfds unix.FdSet readfds.Set(int(fd)) @@ -132,13 +133,15 @@ func waitForData(fd uintptr, timeout time.Duration) error { return nil } -func readNextByte(f File) (byte, error) { - if err := waitForData(f.Fd(), OSCTimeout); err != nil { - return 0, err +func (o *Output) readNextByte() (byte, error) { + if !o.unsafe { + if err := o.waitForData(OSCTimeout); err != nil { + return 0, err + } } var b [1]byte - n, err := f.Read(b[:]) + n, err := o.TTY().Read(b[:]) if err != nil { return 0, err } @@ -153,15 +156,15 @@ func readNextByte(f File) (byte, error) { // readNextResponse reads either an OSC response or a cursor position response: // - OSC response: "\x1b]11;rgb:1111/1111/1111\x1b\\" // - cursor position response: "\x1b[42;1R" -func readNextResponse(fd File) (response string, isOSC bool, err error) { - start, err := readNextByte(fd) +func (o *Output) readNextResponse() (response string, isOSC bool, err error) { + start, err := o.readNextByte() if err != nil { return "", false, err } // first byte must be ESC - for start != '\033' { - start, err = readNextByte(fd) + for start != ESC { + start, err = o.readNextByte() if err != nil { return "", false, err } @@ -170,7 +173,7 @@ func readNextResponse(fd File) (response string, isOSC bool, err error) { response += string(start) // next byte is either '[' (cursor position response) or ']' (OSC response) - tpe, err := readNextByte(fd) + tpe, err := o.readNextByte() if err != nil { return "", false, err } @@ -188,7 +191,7 @@ func readNextResponse(fd File) (response string, isOSC bool, err error) { } for { - b, err := readNextByte(fd) + b, err := o.readNextByte() if err != nil { return "", false, err } @@ -197,7 +200,7 @@ func readNextResponse(fd File) (response string, isOSC bool, err error) { if oscResponse { // OSC can be terminated by BEL (\a) or ST (ESC) - if b == '\a' || strings.HasSuffix(response, "\033") { + if b == BEL || strings.HasSuffix(response, string(ESC)) { return response, true, nil } } else { @@ -229,33 +232,35 @@ func (o Output) termStatusReport(sequence int) (string, error) { return "", ErrStatusReport } - fd := int(tty.Fd()) - // if in background, we can't control the terminal - if !isForeground(fd) { - return "", ErrStatusReport - } + if !o.unsafe { + fd := int(tty.Fd()) + // if in background, we can't control the terminal + if !isForeground(fd) { + return "", ErrStatusReport + } - t, err := unix.IoctlGetTermios(fd, tcgetattr) - if err != nil { - return "", fmt.Errorf("%s: %s", ErrStatusReport, err) - } - defer unix.IoctlSetTermios(fd, tcsetattr, t) //nolint:errcheck + t, err := unix.IoctlGetTermios(fd, tcgetattr) + if err != nil { + return "", fmt.Errorf("%s: %s", ErrStatusReport, err) + } + defer unix.IoctlSetTermios(fd, tcsetattr, t) //nolint:errcheck - noecho := *t - noecho.Lflag = noecho.Lflag &^ unix.ECHO - noecho.Lflag = noecho.Lflag &^ unix.ICANON - if err := unix.IoctlSetTermios(fd, tcsetattr, &noecho); err != nil { - return "", fmt.Errorf("%s: %s", ErrStatusReport, err) + noecho := *t + noecho.Lflag = noecho.Lflag &^ unix.ECHO + noecho.Lflag = noecho.Lflag &^ unix.ICANON + if err := unix.IoctlSetTermios(fd, tcsetattr, &noecho); err != nil { + return "", fmt.Errorf("%s: %s", ErrStatusReport, err) + } } // first, send OSC query, which is ignored by terminal which do not support it - fmt.Fprintf(tty, "\033]%d;?\033\\", sequence) + fmt.Fprintf(tty, OSC+"%d;?"+ST, sequence) // then, query cursor position, should be supported by all terminals - fmt.Fprintf(tty, "\033[6n") + fmt.Fprintf(tty, CSI+"6n") // read the next response - res, isOSC, err := readNextResponse(tty) + res, isOSC, err := o.readNextResponse() if err != nil { return "", fmt.Errorf("%s: %s", ErrStatusReport, err) } @@ -266,7 +271,7 @@ func (o Output) termStatusReport(sequence int) (string, error) { } // read the cursor query response next and discard the result - _, _, err = readNextResponse(tty) + _, _, err = o.readNextResponse() if err != nil { return "", err } diff --git a/vendor/github.com/sahilm/fuzzy/.editorconfig b/vendor/github.com/sahilm/fuzzy/.editorconfig deleted file mode 100644 index 8a8f6a5..0000000 --- a/vendor/github.com/sahilm/fuzzy/.editorconfig +++ /dev/null @@ -1,18 +0,0 @@ -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -indent_style = space -indent_size = 2 - -[*.sh] -indent_size = 4 - -[Makefile] -indent_style = tab -indent_size = 4 - -[*.go] -indent_style = tab -indent_size = 4 diff --git a/vendor/github.com/sahilm/fuzzy/.gitignore b/vendor/github.com/sahilm/fuzzy/.gitignore deleted file mode 100644 index d6c59ee..0000000 --- a/vendor/github.com/sahilm/fuzzy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -vendor/ -coverage/ diff --git a/vendor/github.com/sahilm/fuzzy/.travis.yml b/vendor/github.com/sahilm/fuzzy/.travis.yml deleted file mode 100644 index 6756d80..0000000 --- a/vendor/github.com/sahilm/fuzzy/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: go -go: - - 1.x -script: - - make diff --git a/vendor/github.com/sahilm/fuzzy/CONTRIBUTING.md b/vendor/github.com/sahilm/fuzzy/CONTRIBUTING.md deleted file mode 100644 index 7068ce1..0000000 --- a/vendor/github.com/sahilm/fuzzy/CONTRIBUTING.md +++ /dev/null @@ -1 +0,0 @@ -Everyone is welcome to contribute. Please send me a pull request or file an issue. I promise to respond promptly. diff --git a/vendor/github.com/sahilm/fuzzy/Gopkg.lock b/vendor/github.com/sahilm/fuzzy/Gopkg.lock deleted file mode 100644 index 6e3a7fe..0000000 --- a/vendor/github.com/sahilm/fuzzy/Gopkg.lock +++ /dev/null @@ -1,20 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:ee97ec8a00b2424570c1ce53d7b410e96fbd4c241b29df134276ff6aa3750335" - name = "github.com/kylelemons/godebug" - packages = [ - "diff", - "pretty", - ] - pruneopts = "" - revision = "d65d576e9348f5982d7f6d83682b694e731a45c6" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = ["github.com/kylelemons/godebug/pretty"] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/vendor/github.com/sahilm/fuzzy/Gopkg.toml b/vendor/github.com/sahilm/fuzzy/Gopkg.toml deleted file mode 100644 index 8f96b11..0000000 --- a/vendor/github.com/sahilm/fuzzy/Gopkg.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Test dependency -[[constraint]] - branch = "master" - name = "github.com/kylelemons/godebug" diff --git a/vendor/github.com/sahilm/fuzzy/LICENSE b/vendor/github.com/sahilm/fuzzy/LICENSE deleted file mode 100644 index f848719..0000000 --- a/vendor/github.com/sahilm/fuzzy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Sahil Muthoo - -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. diff --git a/vendor/github.com/sahilm/fuzzy/Makefile b/vendor/github.com/sahilm/fuzzy/Makefile deleted file mode 100644 index 7fa2be4..0000000 --- a/vendor/github.com/sahilm/fuzzy/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -.PHONY: all -all: setup lint test - -.PHONY: test -test: setup - go test -bench ./... - -.PHONY: cover -cover: setup - mkdir -p coverage - gocov test ./... | gocov-html > coverage/coverage.html - -sources = $(shell find . -name '*.go' -not -path './vendor/*') -.PHONY: goimports -goimports: setup - goimports -w $(sources) - -.PHONY: lint -lint: setup - gometalinter ./... --enable=goimports --disable=gocyclo --vendor -t - -.PHONY: install -install: setup - go install - -BIN_DIR := $(GOPATH)/bin -GOIMPORTS := $(BIN_DIR)/goimports -GOMETALINTER := $(BIN_DIR)/gometalinter -DEP := $(BIN_DIR)/dep -GOCOV := $(BIN_DIR)/gocov -GOCOV_HTML := $(BIN_DIR)/gocov-html - -$(GOIMPORTS): - go get -u golang.org/x/tools/cmd/goimports - -$(GOMETALINTER): - go get -u github.com/alecthomas/gometalinter - gometalinter --install &> /dev/null - -$(GOCOV): - go get -u github.com/axw/gocov/gocov - -$(GOCOV_HTML): - go get -u gopkg.in/matm/v1/gocov-html - -$(DEP): - go get -u github.com/golang/dep/cmd/dep - -tools: $(GOIMPORTS) $(GOMETALINTER) $(GOCOV) $(GOCOV_HTML) $(DEP) - -vendor: $(DEP) - dep ensure - -setup: tools vendor - -updatedeps: - dep ensure -update diff --git a/vendor/github.com/sahilm/fuzzy/README.md b/vendor/github.com/sahilm/fuzzy/README.md deleted file mode 100644 index c632da5..0000000 --- a/vendor/github.com/sahilm/fuzzy/README.md +++ /dev/null @@ -1,184 +0,0 @@ -gopher looking for stuff gopher found stuff - -# fuzzy -[![Build Status](https://travis-ci.org/sahilm/fuzzy.svg?branch=master)](https://travis-ci.org/sahilm/fuzzy) -[![Documentation](https://godoc.org/github.com/sahilm/fuzzy?status.svg)](https://godoc.org/github.com/sahilm/fuzzy) - -Go library that provides fuzzy string matching optimized for filenames and code symbols in the style of Sublime Text, -VSCode, IntelliJ IDEA et al. This library is external dependency-free. It only depends on the Go standard library. - -## Features - -- Intuitive matching. Results are returned in descending order of match quality. Quality is determined by: - - The first character in the pattern matches the first character in the match string. - - The matched character is camel cased. - - The matched character follows a separator such as an underscore character. - - The matched character is adjacent to a previous match. - -- Speed. Matches are returned in milliseconds. It's perfect for interactive search boxes. - -- The positions of matches is returned. Allows you to highlight matching characters. - -- Unicode aware. - -## Demo - -Here is a [demo](_example/main.go) of matching various patterns against ~16K files from the Unreal Engine 4 codebase. - -![demo](assets/demo.gif) - -You can run the demo yourself like so: - -``` -cd _example/ -go get github.com/jroimartin/gocui -go run main.go -``` - -## Usage - -The following example prints out matches with the matched chars in bold. - -```go -package main - -import ( - "fmt" - - "github.com/sahilm/fuzzy" -) - -func main() { - const bold = "\033[1m%s\033[0m" - pattern := "mnr" - data := []string{"game.cpp", "moduleNameResolver.ts", "my name is_Ramsey"} - - matches := fuzzy.Find(pattern, data) - - for _, match := range matches { - for i := 0; i < len(match.Str); i++ { - if contains(i, match.MatchedIndexes) { - fmt.Print(fmt.Sprintf(bold, string(match.Str[i]))) - } else { - fmt.Print(string(match.Str[i])) - } - } - fmt.Println() - } -} - -func contains(needle int, haystack []int) bool { - for _, i := range haystack { - if needle == i { - return true - } - } - return false -} -``` -If the data you want to match isn't a slice of strings, you can use `FindFromSource` by implementing -the provided `Source` interface. Here's an example: - -```go -package main - -import ( - "fmt" - - "github.com/sahilm/fuzzy" -) - -type employee struct { - name string - age int -} - -type employees []employee - -func (e employees) String(i int) string { - return e[i].name -} - -func (e employees) Len() int { - return len(e) -} - -func main() { - emps := employees{ - { - name: "Alice", - age: 45, - }, - { - name: "Bob", - age: 35, - }, - { - name: "Allie", - age: 35, - }, - } - results := fuzzy.FindFrom("al", emps) - fmt.Println(results) -} -``` - -Check out the [godoc](https://godoc.org/github.com/sahilm/fuzzy) for detailed documentation. - -## Installation - -`go get github.com/sahilm/fuzzy` or use your favorite dependency management tool. - -## Speed - -Here are a few benchmark results on a normal laptop. - -``` -BenchmarkFind/with_unreal_4_(~16K_files)-4 100 12915315 ns/op -BenchmarkFind/with_linux_kernel_(~60K_files)-4 50 30885038 ns/op -``` - -Matching a pattern against ~60K files from the Linux kernel takes about 30ms. - -## Contributing - -Everyone is welcome to contribute. Please send me a pull request or file an issue. I promise -to respond promptly. - -## Credits - -* [@ericpauley](https://github.com/ericpauley) & [@lunixbochs](https://github.com/lunixbochs) contributed Unicode awareness and various performance optimisations. - -* The algorithm is based of the awesome work of [forrestthewoods](https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.js). -See [this](https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb#.d05n81yjy) -blog post for details of the algorithm. - -* The artwork is by my lovely wife Sanah. It's based on the Go Gopher. - -* The Go gopher was designed by Renee French (http://reneefrench.blogspot.com/). -The design is licensed under the Creative Commons 3.0 Attributions license. - -## License - -The MIT License (MIT) - -Copyright (c) 2017 Sahil Muthoo - -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. - diff --git a/vendor/github.com/sahilm/fuzzy/fuzzy.go b/vendor/github.com/sahilm/fuzzy/fuzzy.go deleted file mode 100644 index bd66ee6..0000000 --- a/vendor/github.com/sahilm/fuzzy/fuzzy.go +++ /dev/null @@ -1,235 +0,0 @@ -/* -Package fuzzy provides fuzzy string matching optimized -for filenames and code symbols in the style of Sublime Text, -VSCode, IntelliJ IDEA et al. -*/ -package fuzzy - -import ( - "sort" - "unicode" - "unicode/utf8" -) - -// Match represents a matched string. -type Match struct { - // The matched string. - Str string - // The index of the matched string in the supplied slice. - Index int - // The indexes of matched characters. Useful for highlighting matches. - MatchedIndexes []int - // Score used to rank matches - Score int -} - -const ( - firstCharMatchBonus = 10 - matchFollowingSeparatorBonus = 20 - camelCaseMatchBonus = 20 - adjacentMatchBonus = 5 - unmatchedLeadingCharPenalty = -5 - maxUnmatchedLeadingCharPenalty = -15 -) - -var separators = []rune("/-_ .\\") - -// Matches is a slice of Match structs -type Matches []Match - -func (a Matches) Len() int { return len(a) } -func (a Matches) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a Matches) Less(i, j int) bool { return a[i].Score >= a[j].Score } - -// Source represents an abstract source of a list of strings. Source must be iterable type such as a slice. -// The source will be iterated over till Len() with String(i) being called for each element where i is the -// index of the element. You can find a working example in the README. -type Source interface { - // The string to be matched at position i. - String(i int) string - // The length of the source. Typically is the length of the slice of things that you want to match. - Len() int -} - -type stringSource []string - -func (ss stringSource) String(i int) string { - return ss[i] -} - -func (ss stringSource) Len() int { return len(ss) } - -/* -Find looks up pattern in data and returns matches -in descending order of match quality. Match quality -is determined by a set of bonus and penalty rules. - -The following types of matches apply a bonus: - -* The first character in the pattern matches the first character in the match string. - -* The matched character is camel cased. - -* The matched character follows a separator such as an underscore character. - -* The matched character is adjacent to a previous match. - -Penalties are applied for every character in the search string that wasn't matched and all leading -characters upto the first match. -*/ -func Find(pattern string, data []string) Matches { - return FindFrom(pattern, stringSource(data)) -} - -/* -FindFrom is an alternative implementation of Find using a Source -instead of a list of strings. -*/ -func FindFrom(pattern string, data Source) Matches { - if len(pattern) == 0 { - return nil - } - runes := []rune(pattern) - var matches Matches - var matchedIndexes []int - for i := 0; i < data.Len(); i++ { - var match Match - match.Str = data.String(i) - match.Index = i - if matchedIndexes != nil { - match.MatchedIndexes = matchedIndexes - } else { - match.MatchedIndexes = make([]int, 0, len(runes)) - } - var score int - patternIndex := 0 - bestScore := -1 - matchedIndex := -1 - currAdjacentMatchBonus := 0 - var last rune - var lastIndex int - nextc, nextSize := utf8.DecodeRuneInString(data.String(i)) - var candidate rune - var candidateSize int - for j := 0; j < len(data.String(i)); j += candidateSize { - candidate, candidateSize = nextc, nextSize - if equalFold(candidate, runes[patternIndex]) { - score = 0 - if j == 0 { - score += firstCharMatchBonus - } - if unicode.IsLower(last) && unicode.IsUpper(candidate) { - score += camelCaseMatchBonus - } - if j != 0 && isSeparator(last) { - score += matchFollowingSeparatorBonus - } - if len(match.MatchedIndexes) > 0 { - lastMatch := match.MatchedIndexes[len(match.MatchedIndexes)-1] - bonus := adjacentCharBonus(lastIndex, lastMatch, currAdjacentMatchBonus) - score += bonus - // adjacent matches are incremental and keep increasing based on previous adjacent matches - // thus we need to maintain the current match bonus - currAdjacentMatchBonus += bonus - } - if score > bestScore { - bestScore = score - matchedIndex = j - } - } - var nextp rune - if patternIndex < len(runes)-1 { - nextp = runes[patternIndex+1] - } - if j+candidateSize < len(data.String(i)) { - if data.String(i)[j+candidateSize] < utf8.RuneSelf { // Fast path for ASCII - nextc, nextSize = rune(data.String(i)[j+candidateSize]), 1 - } else { - nextc, nextSize = utf8.DecodeRuneInString(data.String(i)[j+candidateSize:]) - } - } else { - nextc, nextSize = 0, 0 - } - // We apply the best score when we have the next match coming up or when the search string has ended. - // Tracking when the next match is coming up allows us to exhaustively find the best match and not necessarily - // the first match. - // For example given the pattern "tk" and search string "The Black Knight", exhaustively matching allows us - // to match the second k thus giving this string a higher score. - if equalFold(nextp, nextc) || nextc == 0 { - if matchedIndex > -1 { - if len(match.MatchedIndexes) == 0 { - penalty := matchedIndex * unmatchedLeadingCharPenalty - bestScore += max(penalty, maxUnmatchedLeadingCharPenalty) - } - match.Score += bestScore - match.MatchedIndexes = append(match.MatchedIndexes, matchedIndex) - score = 0 - bestScore = -1 - patternIndex++ - } - } - lastIndex = j - last = candidate - } - // apply penalty for each unmatched character - penalty := len(match.MatchedIndexes) - len(data.String(i)) - match.Score += penalty - if len(match.MatchedIndexes) == len(runes) { - matches = append(matches, match) - matchedIndexes = nil - } else { - matchedIndexes = match.MatchedIndexes[:0] // Recycle match index slice - } - } - sort.Stable(matches) - return matches -} - -// Taken from strings.EqualFold -func equalFold(tr, sr rune) bool { - if tr == sr { - return true - } - if tr < sr { - tr, sr = sr, tr - } - // Fast check for ASCII. - if tr < utf8.RuneSelf { - // ASCII, and sr is upper case. tr must be lower case. - if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' { - return true - } - return false - } - - // General case. SimpleFold(x) returns the next equivalent rune > x - // or wraps around to smaller values. - r := unicode.SimpleFold(sr) - for r != sr && r < tr { - r = unicode.SimpleFold(r) - } - return r == tr -} - -func adjacentCharBonus(i int, lastMatch int, currentBonus int) int { - if lastMatch == i { - return currentBonus*2 + adjacentMatchBonus - } - return 0 -} - -func isSeparator(s rune) bool { - for _, sep := range separators { - if s == sep { - return true - } - } - return false -} - -func max(x int, y int) int { - if x > y { - return x - } - return y -} diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 0000000..7330990 --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google 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, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 0000000..cbee7a4 --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,132 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "context" + "fmt" + "sync" +) + +type token struct{} + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid, has no limit on the number of active goroutines, +// and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + sem chan token + + errOnce sync.Once + err error +} + +func (g *Group) done() { + if g.sem != nil { + <-g.sem + } + g.wg.Done() +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// It blocks until the new goroutine can be added without the number of +// active goroutines in the group exceeding the configured limit. +// +// The first call to return a non-nil error cancels the group's context, if the +// group was created by calling WithContext. The error will be returned by Wait. +func (g *Group) Go(f func() error) { + if g.sem != nil { + g.sem <- token{} + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +} + +// TryGo calls the given function in a new goroutine only if the number of +// active goroutines in the group is currently below the configured limit. +// +// The return value reports whether the goroutine was started. +func (g *Group) TryGo(f func() error) bool { + if g.sem != nil { + select { + case g.sem <- token{}: + // Note: this allows barging iff channels in general allow barging. + default: + return false + } + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() + return true +} + +// SetLimit limits the number of active goroutines in this group to at most n. +// A negative value indicates no limit. +// +// Any subsequent call to the Go method will block until it can add an active +// goroutine without exceeding the configured limit. +// +// The limit must not be modified while any goroutines in the group are active. +func (g *Group) SetLimit(n int) { + if n < 0 { + g.sem = nil + return + } + if len(g.sem) != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + } + g.sem = make(chan token, n) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index defc002..b1ff5f2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -6,14 +6,11 @@ github.com/atotto/clipboard github.com/aymanbagabas/go-osc52 # github.com/charmbracelet/bubbles v0.14.0 ## explicit; go 1.13 -github.com/charmbracelet/bubbles/help github.com/charmbracelet/bubbles/key -github.com/charmbracelet/bubbles/list -github.com/charmbracelet/bubbles/paginator github.com/charmbracelet/bubbles/spinner github.com/charmbracelet/bubbles/textinput github.com/charmbracelet/bubbles/viewport -# github.com/charmbracelet/bubbletea v0.23.1 +# github.com/charmbracelet/bubbletea v0.23.2 ## explicit; go 1.16 github.com/charmbracelet/bubbletea # github.com/charmbracelet/lipgloss v0.5.0 @@ -47,15 +44,15 @@ github.com/muesli/reflow/ansi github.com/muesli/reflow/truncate github.com/muesli/reflow/wordwrap github.com/muesli/reflow/wrap -# github.com/muesli/termenv v0.13.0 +# github.com/muesli/termenv v0.14.0 ## explicit; go 1.13 github.com/muesli/termenv # github.com/rivo/uniseg v0.4.3 ## explicit; go 1.18 github.com/rivo/uniseg -# github.com/sahilm/fuzzy v0.1.0 +# golang.org/x/sync v0.1.0 ## explicit -github.com/sahilm/fuzzy +golang.org/x/sync/errgroup # golang.org/x/sys v0.4.0 ## explicit; go 1.17 golang.org/x/sys/internal/unsafeheader