1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-02-01 13:17:53 +02:00

lots more generics

This commit is contained in:
Jesse Duffield 2022-03-19 15:36:46 +11:00
parent c7a629c440
commit eda8f4a5d4
19 changed files with 384 additions and 299 deletions

4
go.mod
View File

@ -14,7 +14,7 @@ require (
github.com/gookit/color v1.4.2
github.com/imdario/mergo v0.3.11
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220318214805-3397e5e19e9f
github.com/jesseduffield/generics v0.0.0-20220319042131-63614a800d5f
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
github.com/jesseduffield/gocui v0.3.1-0.20220227022729-69f0c798eec8
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
@ -32,7 +32,6 @@ require (
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
github.com/stretchr/testify v1.7.0
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8
gopkg.in/ozeidan/fuzzy-patricia.v3 v3.0.0
)
@ -61,6 +60,7 @@ require (
github.com/sergi/go-diff v1.1.0 // indirect
github.com/xanzy/ssh-agent v0.2.1 // indirect
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
golang.org/x/exp v0.0.0-20220318154914-8dddf5d87bd8 // indirect
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect

4
go.sum
View File

@ -66,8 +66,8 @@ github.com/integrii/flaggy v1.4.0 h1:A1x7SYx4jqu5NSrY14z8Z+0UyX2S5ygfJJrfolWR3zM
github.com/integrii/flaggy v1.4.0/go.mod h1:tnTxHeTJbah0gQ6/K0RW0J7fMUBk9MCF5blhm43LNpI=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jesseduffield/generics v0.0.0-20220318214805-3397e5e19e9f h1:9USuZttmg5ioHsjFyXboiGSbncpAqcKkq9qb4ga5PD0=
github.com/jesseduffield/generics v0.0.0-20220318214805-3397e5e19e9f/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/generics v0.0.0-20220319042131-63614a800d5f h1:VZkNxrfkR344djm4Ju7QuKLXxZlaaOaNCrAWVRc1gvU=
github.com/jesseduffield/generics v0.0.0-20220319042131-63614a800d5f/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20220227022729-69f0c798eec8 h1:9N08i5kjvOfkzMj6THmIM110wPTQLdVYEOHMHT2DFiI=

View File

@ -12,8 +12,8 @@ import (
"fmt"
"log"
"os"
"sort"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/app"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui"
@ -180,9 +180,9 @@ outer:
groupedBindings = append(groupedBindings, groupedBindingsType{contextAndView: contextAndView, bindings: contextBindings})
}
sort.Slice(groupedBindings, func(i, j int) bool {
first := groupedBindings[i].contextAndView
second := groupedBindings[j].contextAndView
slices.SortFunc(groupedBindings, func(a, b groupedBindingsType) bool {
first := a.contextAndView
second := b.contextAndView
if first.title == "" {
return true
}

View File

@ -10,7 +10,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
"github.com/jesseduffield/generics/slices"
)
// This package is for handling logic specific to a git hosting service like github, gitlab, bitbucket, etc.

View File

@ -5,6 +5,7 @@ import (
"strings"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/go-git/v5/config"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/common"
@ -78,8 +79,7 @@ outer:
if branch.Head {
foundHead = true
branch.Recency = " *"
branches = append(branches[0:i], branches[i+1:]...)
branches = append([]*models.Branch{branch}, branches...)
branches = slices.Move(branches, i, 0)
break
}
}
@ -88,7 +88,7 @@ outer:
if err != nil {
return nil, err
}
branches = append([]*models.Branch{{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"}}, branches...)
branches = slices.Prepend(branches, &models.Branch{Name: currentBranchName, DisplayName: currentBranchDisplayName, Head: true, Recency: " *"})
}
configBranches, err := self.config.Branches()
@ -158,10 +158,10 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
trimmedOutput := strings.TrimSpace(output)
outputLines := strings.Split(trimmedOutput, "\n")
branches := make([]*models.Branch, 0, len(outputLines))
for _, line := range outputLines {
branches := slices.FilterMap(outputLines, func(line string) (*models.Branch, bool) {
if line == "" {
continue
return nil, false
}
split := strings.Split(line, SEPARATION_CHAR)
@ -169,12 +169,11 @@ func (self *BranchLoader) obtainBranches() []*models.Branch {
// Ignore line if it isn't separated into 4 parts
// This is probably a warning message, for more info see:
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
continue
return nil, false
}
branch := obtainBranch(split)
branches = append(branches, branch)
}
return obtainBranch(split), true
})
return branches
}

View File

@ -3,13 +3,14 @@ package loaders
import (
"fmt"
"regexp"
"sort"
"strings"
"github.com/jesseduffield/generics/slices"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/samber/lo"
)
type RemoteLoader struct {
@ -42,37 +43,35 @@ func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
}
// first step is to get our remotes from go-git
remotes := make([]*models.Remote, len(goGitRemotes))
for i, goGitRemote := range goGitRemotes {
remotes := lo.Map(goGitRemotes, func(goGitRemote *gogit.Remote, _ int) *models.Remote {
remoteName := goGitRemote.Config().Name
re := regexp.MustCompile(fmt.Sprintf(`(?m)^\s*%s\/([\S]+)`, remoteName))
matches := re.FindAllStringSubmatch(remoteBranchesStr, -1)
branches := make([]*models.RemoteBranch, len(matches))
for j, match := range matches {
branches[j] = &models.RemoteBranch{
branches := lo.Map(matches, func(match []string, _ int) *models.RemoteBranch {
return &models.RemoteBranch{
Name: match[1],
RemoteName: remoteName,
}
}
})
remotes[i] = &models.Remote{
return &models.Remote{
Name: goGitRemote.Config().Name,
Urls: goGitRemote.Config().URLs,
Branches: branches,
}
}
})
// now lets sort our remotes by name alphabetically
sort.Slice(remotes, func(i, j int) bool {
slices.SortFunc(remotes, func(a, b *models.Remote) bool {
// we want origin at the top because we'll be most likely to want it
if remotes[i].Name == "origin" {
if a.Name == "origin" {
return true
}
if remotes[j].Name == "origin" {
if b.Name == "origin" {
return false
}
return strings.ToLower(remotes[i].Name) < strings.ToLower(remotes[j].Name)
return strings.ToLower(a.Name) < strings.ToLower(b.Name)
})
return remotes, nil

View File

@ -1,7 +1,7 @@
package controllers
import (
"github.com/jesseduffield/generics/list"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
@ -57,7 +57,7 @@ func (self *GlobalController) customCommand() error {
func (self *GlobalController) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
// reversing so that we display the latest command first
history := list.Reverse(self.c.GetAppState().CustomCommandsHistory)
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
return helpers.FuzzySearchFunc(history)
}

View File

@ -2,6 +2,7 @@ package helpers
import (
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/context"
@ -118,12 +119,12 @@ func (self *CherryPickHelper) add(selectedCommit *models.Commit, commitsList []*
commitSet := self.CherryPickedCommitShaSet()
commitSet.Add(selectedCommit.Sha)
commitsInSet := lo.Filter(commitsList, func(commit *models.Commit, _ int) bool {
return commitSet.Includes(commit.Sha)
})
newCommits := lo.Map(commitsInSet, func(commit *models.Commit, _ int) *models.Commit {
return &models.Commit{Name: commit.Name, Sha: commit.Sha}
})
newCommits := slices.FilterThenMap(commitsList,
func(commit *models.Commit) bool { return commitSet.Includes(commit.Sha) },
func(commit *models.Commit) *models.Commit {
return &models.Commit{Name: commit.Name, Sha: commit.Sha}
},
)
self.getData().CherryPickedCommits = newCommits
}

View File

@ -1,8 +1,6 @@
package filetree
import (
"sort"
)
import "github.com/jesseduffield/generics/slices"
type INode interface {
IsNil() bool
@ -27,19 +25,17 @@ func sortChildren(node INode) {
return
}
children := node.GetChildren()
sortedChildren := make([]INode, len(children))
copy(sortedChildren, children)
sortedChildren := slices.Clone(node.GetChildren())
sort.Slice(sortedChildren, func(i, j int) bool {
if !sortedChildren[i].IsLeaf() && sortedChildren[j].IsLeaf() {
slices.SortFunc(sortedChildren, func(a, b INode) bool {
if !a.IsLeaf() && b.IsLeaf() {
return true
}
if sortedChildren[i].IsLeaf() && !sortedChildren[j].IsLeaf() {
if a.IsLeaf() && !b.IsLeaf() {
return false
}
return sortedChildren[i].GetPath() < sortedChildren[j].GetPath()
return a.GetPath() < b.GetPath()
})
// TODO: think about making this in-place

View File

@ -2,10 +2,10 @@ package graph
import (
"runtime"
"sort"
"strings"
"sync"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
@ -265,11 +265,11 @@ func getNextPipes(prevPipes []*Pipe, commit *models.Commit, getStyle func(c *mod
}
// not efficient but doing it for now: sorting my pipes by toPos, then by kind
sort.Slice(newPipes, func(i, j int) bool {
if newPipes[i].toPos == newPipes[j].toPos {
return newPipes[i].kind < newPipes[j].kind
slices.SortFunc(newPipes, func(a, b *Pipe) bool {
if a.toPos == b.toPos {
return a.kind < b.kind
}
return newPipes[i].toPos < newPipes[j].toPos
return a.toPos < b.toPos
})
return newPipes

View File

@ -1,49 +0,0 @@
package list
import (
"golang.org/x/exp/slices"
)
type ComparableList[T comparable] struct {
*List[T]
}
func NewComparable[T comparable]() *ComparableList[T] {
return &ComparableList[T]{List: New[T]()}
}
func NewComparableFromSlice[T comparable](slice []T) *ComparableList[T] {
return &ComparableList[T]{List: NewFromSlice(slice)}
}
func (l *ComparableList[T]) Equal(other *ComparableList[T]) bool {
return l.EqualSlice(other.ToSlice())
}
func (l *ComparableList[T]) EqualSlice(other []T) bool {
return slices.Equal(l.ToSlice(), other)
}
func (l *ComparableList[T]) Compact() {
l.slice = slices.Compact(l.slice)
}
func (l *ComparableList[T]) Index(needle T) int {
return slices.Index(l.slice, needle)
}
func (l *ComparableList[T]) Contains(needle T) bool {
return slices.Contains(l.slice, needle)
}
func (l *ComparableList[T]) SortFuncInPlace(test func(a T, b T) bool) {
slices.SortFunc(l.slice, test)
}
func (l *ComparableList[T]) SortFunc(test func(a T, b T) bool) *ComparableList[T] {
newSlice := slices.Clone(l.slice)
slices.SortFunc(newSlice, test)
return NewComparableFromSlice(newSlice)
}

View File

@ -1,72 +0,0 @@
package list
func Some[T any](slice []T, test func(T) bool) bool {
for _, value := range slice {
if test(value) {
return true
}
}
return false
}
func Every[T any](slice []T, test func(T) bool) bool {
for _, value := range slice {
if !test(value) {
return false
}
}
return true
}
func Map[T any, V any](slice []T, f func(T) V) []V {
result := make([]V, len(slice))
for i, value := range slice {
result[i] = f(value)
}
return result
}
func MapInPlace[T any](slice []T, f func(T) T) {
for i, value := range slice {
slice[i] = f(value)
}
}
func Filter[T any](slice []T, test func(T) bool) []T {
result := make([]T, 0)
for _, element := range slice {
if test(element) {
result = append(result, element)
}
}
return result
}
func FilterInPlace[T any](slice []T, test func(T) bool) []T {
newLength := 0
for _, element := range slice {
if test(element) {
slice[newLength] = element
newLength++
}
}
return slice[:newLength]
}
func Reverse[T any](slice []T) []T {
result := make([]T, len(slice))
for i := range slice {
result[i] = slice[len(slice)-1-i]
}
return result
}
func ReverseInPlace[T any](slice []T) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}

View File

@ -1,117 +0,0 @@
package list
import (
"golang.org/x/exp/slices"
)
type List[T any] struct {
slice []T
}
func New[T any]() *List[T] {
return &List[T]{}
}
func NewFromSlice[T any](slice []T) *List[T] {
return &List[T]{slice: slice}
}
func (l *List[T]) ToSlice() []T {
return l.slice
}
// Mutative methods
func (l *List[T]) Push(v T) {
l.slice = append(l.slice, v)
}
func (l *List[T]) Pop() {
l.slice = l.slice[0 : len(l.slice)-1]
}
func (l *List[T]) Insert(index int, values ...T) {
l.slice = slices.Insert(l.slice, index, values...)
}
func (l *List[T]) Append(values ...T) {
l.slice = append(l.slice, values...)
}
func (l *List[T]) Prepend(values ...T) {
l.slice = append(values, l.slice...)
}
func (l *List[T]) Remove(index int) {
l.Delete(index, index+1)
}
func (l *List[T]) Delete(from int, to int) {
l.slice = slices.Delete(l.slice, from, to)
}
func (l *List[T]) FilterInPlace(test func(value T) bool) {
l.slice = FilterInPlace(l.slice, test)
}
func (l *List[T]) MapInPlace(f func(value T) T) {
MapInPlace(l.slice, f)
}
func (l *List[T]) ReverseInPlace() {
ReverseInPlace(l.slice)
}
// Non-mutative methods
// Similar to Append but we leave the original slice untouched and return a new list
func (l *List[T]) Concat(values ...T) *List[T] {
newSlice := make([]T, 0, len(l.slice)+len(values))
newSlice = append(newSlice, l.slice...)
newSlice = append(newSlice, values...)
return &List[T]{slice: newSlice}
}
func (l *List[T]) Filter(test func(value T) bool) *List[T] {
return NewFromSlice(Filter(l.slice, test))
}
// Unfortunately this does not support mapping from one type to another
// because Go does not yet (and may never) support methods defining their own
// type parameters. For that functionality you'll need to use the standalone
// Map function instead
func (l *List[T]) Map(f func(value T) T) *List[T] {
return NewFromSlice(Map(l.slice, f))
}
func (l *List[T]) Clone() *List[T] {
return NewFromSlice(slices.Clone(l.slice))
}
func (l *List[T]) Some(test func(value T) bool) bool {
return Some(l.slice, test)
}
func (l *List[T]) Every(test func(value T) bool) bool {
return Every(l.slice, test)
}
func (l *List[T]) IndexFunc(f func(T) bool) int {
return slices.IndexFunc(l.slice, f)
}
func (l *List[T]) ContainsFunc(f func(T) bool) bool {
return l.IndexFunc(f) != -1
}
func (l *List[T]) Reverse() *List[T] {
return NewFromSlice(Reverse(l.slice))
}
func (l *List[T]) IsEmpty() bool {
return len(l.slice) == 0
}
func (l *List[T]) Len() int {
return len(l.slice)
}

View File

@ -1,4 +1,4 @@
package hashmap
package maps
func Keys[Key comparable, Value any](m map[Key]Value) []Key {
keys := make([]Key, 0, len(m))

View File

@ -1,6 +1,6 @@
package set
import "github.com/jesseduffield/generics/hashmap"
import "github.com/jesseduffield/generics/maps"
type Set[T comparable] struct {
hashMap map[T]bool
@ -45,5 +45,5 @@ func (s *Set[T]) Includes(value T) bool {
// output slice is not necessarily in the same order that items were added
func (s *Set[T]) ToSlice() []T {
return hashmap.Keys(s.hashMap)
return maps.Keys(s.hashMap)
}

View File

@ -0,0 +1,117 @@
package slices
import (
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
// This file delegates to the official slices package, so that we end up with a superset of the official API.
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in increasing index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[E comparable](s1, s2 []E) bool {
return slices.Equal(s1, s2)
}
// EqualFunc reports whether two slices are equal using a comparison
// function on each pair of elements. If the lengths are different,
// EqualFunc returns false. Otherwise, the elements are compared in
// increasing index order, and the comparison stops at the first index
// for which eq returns false.
func EqualFunc[E1, E2 any](s1 []E1, s2 []E2, eq func(E1, E2) bool) bool {
return slices.EqualFunc(s1, s2, eq)
}
// Compare compares the elements of s1 and s2.
// The elements are compared sequentially, starting at index 0,
// until one element is not equal to the other.
// The result of comparing the first non-matching elements is returned.
// If both slices are equal until one of them ends, the shorter slice is
// considered less than the longer one.
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
// Comparisons involving floating point NaNs are ignored.
func Compare[E constraints.Ordered](s1, s2 []E) int {
return slices.Compare(s1, s2)
}
// CompareFunc is like Compare but uses a comparison function
// on each pair of elements. The elements are compared in increasing
// index order, and the comparisons stop after the first time cmp
// returns non-zero.
// The result is the first non-zero result of cmp; if cmp always
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
// and +1 if len(s1) > len(s2).
func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int {
return slices.CompareFunc(s1, s2, cmp)
}
// Index returns the index of the first occurrence of v in s,
// or -1 if not present.
func Index[E comparable](s []E, v E) int {
return slices.Index(s, v)
}
// IndexFunc returns the first index i satisfying f(s[i]),
// or -1 if none do.
func IndexFunc[E any](s []E, f func(E) bool) int {
return slices.IndexFunc(s, f)
}
// Contains reports whether v is present in s.
func Contains[E comparable](s []E, v E) bool {
return slices.Contains(s, v)
}
// Insert inserts the values v... into s at index i,
// returning the modified slice.
// In the returned slice r, r[i] == v[0].
// Insert panics if i is out of range.
// This function is O(len(s) + len(v)).
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
return slices.Insert(s, i, v...)
}
// Delete removes the elements s[i:j] from s, returning the modified slice.
// Delete panics if s[i:j] is not a valid slice of s.
// Delete modifies the contents of the slice s; it does not create a new slice.
// Delete is O(len(s)-(j-i)), so if many items must be deleted, it is better to
// make a single call deleting them all together than to delete one at a time.
func Delete[S ~[]E, E any](s S, i, j int) S {
return slices.Delete(s, i, j)
}
// Clone returns a copy of the slice.
// The elements are copied using assignment, so this is a shallow clone.
func Clone[S ~[]E, E any](s S) S {
return slices.Clone(s)
}
// Compact replaces consecutive runs of equal elements with a single copy.
// This is like the uniq command found on Unix.
// Compact modifies the contents of the slice s; it does not create a new slice.
// Intended usage is to assign the result back to the input slice.
func Compact[S ~[]E, E comparable](s S) S {
return slices.Compact(s)
}
// CompactFunc is like Compact but uses a comparison function.
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
return slices.CompactFunc(s, eq)
}
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. Grow may modify elements of the
// slice between the length and the capacity. If n is negative or too large to
// allocate the memory, Grow panics.
func Grow[S ~[]E, E any](s S, n int) S {
return slices.Grow(s, n)
}
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
func Clip[S ~[]E, E any](s S) S {
return slices.Clip(s)
}

View File

@ -0,0 +1,57 @@
package slices
import (
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
// This file delegates to the official slices package, so that we end up with a superset of the official API.
// Sort sorts a slice of any ordered type in ascending order.
func Sort[E constraints.Ordered](x []E) {
slices.Sort(x)
}
// Sort sorts the slice x in ascending order as determined by the less function.
// This sort is not guaranteed to be stable.
func SortFunc[E any](x []E, less func(a, b E) bool) {
slices.SortFunc(x, less)
}
// SortStable sorts the slice x while keeping the original order of equal
// elements, using less to compare elements.
func SortStableFunc[E any](x []E, less func(a, b E) bool) {
slices.SortStableFunc(x, less)
}
// IsSorted reports whether x is sorted in ascending order.
func IsSorted[E constraints.Ordered](x []E) bool {
return slices.IsSorted(x)
}
// IsSortedFunc reports whether x is sorted in ascending order, with less as the
// comparison function.
func IsSortedFunc[E any](x []E, less func(a, b E) bool) bool {
return slices.IsSortedFunc(x, less)
}
// BinarySearch searches for target in a sorted slice and returns the smallest
// index at which target is found. If the target is not found, the index at
// which it could be inserted into the slice is returned; therefore, if the
// intention is to find target itself a separate check for equality with the
// element at the returned index is required.
func BinarySearch[E constraints.Ordered](x []E, target E) int {
return slices.BinarySearch(x, target)
}
// BinarySearchFunc uses binary search to find and return the smallest index i
// in [0, n) at which ok(i) is true, assuming that on the range [0, n),
// ok(i) == true implies ok(i+1) == true. That is, BinarySearchFunc requires
// that ok is false for some (possibly empty) prefix of the input range [0, n)
// and then true for the (possibly empty) remainder; BinarySearchFunc returns
// the first true index. If there is no such index, BinarySearchFunc returns n.
// (Note that the "not found" return value is not -1 as in, for instance,
// strings.Index.) Search calls ok(i) only for i in the range [0, n).
func BinarySearchFunc[E any](x []E, ok func(E) bool) int {
return slices.BinarySearchFunc(x, ok)
}

View File

@ -0,0 +1,154 @@
package slices
import (
"golang.org/x/exp/slices"
)
// This file contains the new functions that do not live in the official slices package.
func Some[T any](slice []T, test func(T) bool) bool {
for _, value := range slice {
if test(value) {
return true
}
}
return false
}
func Every[T any](slice []T, test func(T) bool) bool {
for _, value := range slice {
if !test(value) {
return false
}
}
return true
}
// Produces a new slice, leaves the input slice untouched.
func Map[T any, V any](slice []T, f func(T) V) []V {
result := make([]V, len(slice))
for i, value := range slice {
result[i] = f(value)
}
return result
}
func MapInPlace[T any](slice []T, f func(T) T) {
for i, value := range slice {
slice[i] = f(value)
}
}
// Produces a new slice, leaves the input slice untouched.
func Filter[T any](slice []T, test func(T) bool) []T {
result := make([]T, 0)
for _, element := range slice {
if test(element) {
result = append(result, element)
}
}
return result
}
// Mutates original slice. Intended usage is to reassign the slice result to the input slice.
func FilterInPlace[T any](slice []T, test func(T) bool) []T {
newLength := 0
for _, element := range slice {
if test(element) {
slice[newLength] = element
newLength++
}
}
return slice[:newLength]
}
// Produces a new slice, leaves the input slice untouched
func Reverse[T any](slice []T) []T {
result := make([]T, len(slice))
for i := range slice {
result[i] = slice[len(slice)-1-i]
}
return result
}
func ReverseInPlace[T any](slice []T) {
for i, j := 0, len(slice)-1; i < j; i, j = i+1, j-1 {
slice[i], slice[j] = slice[j], slice[i]
}
}
// Produces a new slice, leaves the input slice untouched.
func FilterMap[T any, E any](slice []T, test func(T) (bool, E)) []E {
result := make([]E, 0, len(slice))
for _, element := range slice {
ok, mapped := test(element)
if ok {
result = append(result, mapped)
}
}
return result
}
// Produces a new slice, leaves the input slice untouched.
func FilterThenMap[T any, E any](slice []T, test func(T) bool, mapFn func(T) E) []E {
result := make([]E, 0, len(slice))
for _, element := range slice {
if test(element) {
result = append(result, mapFn(element))
}
}
return result
}
// Prepends items to the beginning of a slice.
// E.g. Prepend([]int{1,2}, 3, 4) = []int{3,4,1,2}
// Mutates original slice. Intended usage is to reassign the slice result to the input slice.
func Prepend[T any](slice []T, values ...T) []T {
return append(values, slice...)
}
// Removes the element at the given index. Intended usage is to reassign the result to the input slice.
func Remove[T any](slice []T, index int) []T {
return slices.Delete(slice, index, index+1)
}
// Operates on the input slice. Expected use is to reassign the result to the input slice.
func Move[T any](slice []T, fromIndex int, toIndex int) []T {
item := slice[fromIndex]
slice = Remove(slice, fromIndex)
return slices.Insert(slice, toIndex, item)
}
// Similar to Append but we leave the original slice untouched and return a new slice
func Concat[T any](slice []T, values ...T) []T {
newSlice := make([]T, 0, len(slice)+len(values))
newSlice = append(newSlice, slice...)
newSlice = append(newSlice, values...)
return newSlice
}
func ContainsFunc[T any](slice []T, f func(T) bool) bool {
return IndexFunc(slice, f) != -1
}
// Pops item from the end of the slice and returns it, along with the updated slice
// Mutates original slice. Intended usage is to reassign the slice result to the input slice.
func Pop[T any](slice []T) (T, []T) {
index := len(slice) - 1
value := slice[index]
slice = slice[0:index]
return value, slice
}
// Shifts item from the beginning of the slice and returns it, along with the updated slice.
// Mutates original slice. Intended usage is to reassign the slice result to the input slice.
func Shift[T any](slice []T) (T, []T) {
value := slice[0]
slice = slice[1:]
return value, slice
}

6
vendor/modules.txt vendored
View File

@ -120,11 +120,11 @@ github.com/integrii/flaggy
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
## explicit
github.com/jbenet/go-context/io
# github.com/jesseduffield/generics v0.0.0-20220318214805-3397e5e19e9f
# github.com/jesseduffield/generics v0.0.0-20220319042131-63614a800d5f
## explicit; go 1.18
github.com/jesseduffield/generics/hashmap
github.com/jesseduffield/generics/list
github.com/jesseduffield/generics/maps
github.com/jesseduffield/generics/set
github.com/jesseduffield/generics/slices
# github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
## explicit; go 1.13
github.com/jesseduffield/go-git/v5