Skip to content

Commit

Permalink
refactor: better sort package
Browse files Browse the repository at this point in the history
  • Loading branch information
pd93 committed Jan 26, 2025
1 parent cee2029 commit 9dbd04a
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 78 deletions.
6 changes: 3 additions & 3 deletions cmd/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ func run() error {
dir = home
}

var taskSorter sort.TaskSorter
var taskSorter sort.Sorter
switch flags.TaskSort {
case "none":
taskSorter = &sort.Noop{}
taskSorter = nil
case "alphanumeric":
taskSorter = &sort.AlphaNumeric{}
taskSorter = sort.AlphaNumeric
}

e := task.Executor{
Expand Down
14 changes: 8 additions & 6 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,20 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
w = e.Stdout
}

// Get the list of tasks and sort them
tasks := e.Taskfile.Tasks.Values()

// Sort the tasks
if e.TaskSorter == nil {
e.TaskSorter = &sort.AlphaNumericWithRootTasksFirst{}
e.TaskSorter = sort.AlphaNumericWithRootTasksFirst
}
e.TaskSorter.Sort(tasks)
keys := e.Taskfile.Tasks.Keys()
e.TaskSorter(keys, nil)

// Create a list of task names
taskNames := make([]string, 0, e.Taskfile.Tasks.Len())
for _, task := range tasks {
for _, key := range keys {
task, ok := e.Taskfile.Tasks.Get(key)
if !ok {
continue
}
if (allTasks || task.Desc != "") && !task.Internal {
taskNames = append(taskNames, strings.TrimRight(task.Task, ":"))
for _, alias := range task.Aliases {
Expand Down
46 changes: 21 additions & 25 deletions internal/sort/sorter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,38 @@ package sort
import (
"sort"
"strings"

"github.com/go-task/task/v3/taskfile/ast"
)

type TaskSorter interface {
Sort([]*ast.Task)
}

type Noop struct{}
// A Sorter is any function that sorts a set of tasks.
type Sorter func(items []string, namespaces []string) []string

func (s *Noop) Sort(tasks []*ast.Task) {}

type AlphaNumeric struct{}

// Tasks that are not namespaced should be listed before tasks that are.
// We detect this by searching for a ':' in the task name.
func (s *AlphaNumeric) Sort(tasks []*ast.Task) {
sort.Slice(tasks, func(i, j int) bool {
return tasks[i].Task < tasks[j].Task
// AlphaNumeric sorts the JSON output so that tasks are in alpha numeric order
// by task name.
func AlphaNumeric(items []string, namespaces []string) []string {
sort.Slice(items, func(i, j int) bool {
return items[i] < items[j]
})
return items
}

type AlphaNumericWithRootTasksFirst struct{}

// Tasks that are not namespaced should be listed before tasks that are.
// We detect this by searching for a ':' in the task name.
func (s *AlphaNumericWithRootTasksFirst) Sort(tasks []*ast.Task) {
sort.Slice(tasks, func(i, j int) bool {
iContainsColon := strings.Contains(tasks[i].Task, ":")
jContainsColon := strings.Contains(tasks[j].Task, ":")
// AlphaNumericWithRootTasksFirst sorts the JSON output so that tasks are in
// alpha numeric order by task name. It will also ensure that tasks that are not
// namespaced will be listed before tasks that are. We detect this by searching
// for a ':' in the task name.
func AlphaNumericWithRootTasksFirst(items []string, namespaces []string) []string {
if len(namespaces) > 0 {
return AlphaNumeric(items, namespaces)
}
sort.Slice(items, func(i, j int) bool {
iContainsColon := strings.Contains(items[i], ":")
jContainsColon := strings.Contains(items[j], ":")
if iContainsColon == jContainsColon {
return tasks[i].Task < tasks[j].Task
return items[i] < items[j]
}
if !iContainsColon && jContainsColon {
return true
}
return false
})
return items
}
68 changes: 32 additions & 36 deletions internal/sort/sorter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,82 +4,78 @@ import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/go-task/task/v3/taskfile/ast"
)

func TestAlphaNumericWithRootTasksFirst_Sort(t *testing.T) {
t.Parallel()

task1 := &ast.Task{Task: "task1"}
task2 := &ast.Task{Task: "task2"}
task3 := &ast.Task{Task: "ns1:task3"}
task4 := &ast.Task{Task: "ns2:task4"}
task5 := &ast.Task{Task: "task5"}
task6 := &ast.Task{Task: "ns3:task6"}
item1 := "a-item1"
item2 := "m-item2"
item3 := "ns1:item3"
item4 := "ns2:item4"
item5 := "z-item5"
item6 := "ns3:item6"

tests := []struct {
name string
tasks []*ast.Task
want []*ast.Task
items []string
want []string
}{
{
name: "no namespace tasks sorted alphabetically first",
tasks: []*ast.Task{task3, task2, task1},
want: []*ast.Task{task1, task2, task3},
name: "no namespace items sorted alphabetically first",
items: []string{item3, item2, item1},
want: []string{item1, item2, item3},
},
{
name: "namespace tasks sorted alphabetically after non-namespaced tasks",
tasks: []*ast.Task{task3, task4, task5},
want: []*ast.Task{task5, task3, task4},
name: "namespace items sorted alphabetically after non-namespaced items",
items: []string{item3, item4, item5},
want: []string{item5, item3, item4},
},
{
name: "all tasks sorted alphabetically with root tasks first",
tasks: []*ast.Task{task6, task5, task4, task3, task2, task1},
want: []*ast.Task{task1, task2, task5, task3, task4, task6},
name: "all items sorted alphabetically with root items first",
items: []string{item6, item5, item4, item3, item2, item1},
want: []string{item1, item2, item5, item3, item4, item6},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

s := &AlphaNumericWithRootTasksFirst{}
s.Sort(tt.tasks)
assert.Equal(t, tt.want, tt.tasks)
AlphaNumericWithRootTasksFirst(tt.items, nil)
assert.Equal(t, tt.want, tt.items)
})
}
}

func TestAlphaNumeric_Sort(t *testing.T) {
t.Parallel()

task1 := &ast.Task{Task: "task1"}
task2 := &ast.Task{Task: "task2"}
task3 := &ast.Task{Task: "ns1:task3"}
task4 := &ast.Task{Task: "ns2:task4"}
task5 := &ast.Task{Task: "task5"}
task6 := &ast.Task{Task: "ns3:task6"}
item1 := "a-item1"
item2 := "m-item2"
item3 := "ns1:item3"
item4 := "ns2:item4"
item5 := "z-item5"
item6 := "ns3:item6"

tests := []struct {
name string
tasks []*ast.Task
want []*ast.Task
items []string
want []string
}{
{
name: "all tasks sorted alphabetically",
tasks: []*ast.Task{task3, task2, task5, task1, task4, task6},
want: []*ast.Task{task3, task4, task6, task1, task2, task5},
name: "all items sorted alphabetically",
items: []string{item3, item2, item5, item1, item4, item6},
want: []string{item1, item2, item3, item4, item6, item5},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

s := &AlphaNumeric{}
s.Sort(tt.tasks)
assert.Equal(t, tt.tasks, tt.want)
AlphaNumeric(tt.items, nil)
assert.Equal(t, tt.want, tt.items)
})
}
}
21 changes: 13 additions & 8 deletions task.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type Executor struct {
Compiler *compiler.Compiler
Output output.Output
OutputStyle ast.Output
TaskSorter sort.TaskSorter
TaskSorter sort.Sorter
UserWorkingDir string
EnableVersionCheck bool

Expand Down Expand Up @@ -506,8 +506,19 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*ast.Task, error) {
// Create an error group to wait for each task to be compiled
var g errgroup.Group

// Sort the tasks
if e.TaskSorter == nil {
e.TaskSorter = sort.AlphaNumericWithRootTasksFirst
}
keys := e.Taskfile.Tasks.Keys()
e.TaskSorter(keys, nil)

// Filter tasks based on the given filter functions
for _, task := range e.Taskfile.Tasks.Values() {
for _, key := range keys {
task, ok := e.Taskfile.Tasks.Get(key)
if !ok {
continue
}
var shouldFilter bool
for _, filter := range filters {
if filter(task) {
Expand Down Expand Up @@ -536,12 +547,6 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*ast.Task, error) {
return nil, err
}

// Sort the tasks
if e.TaskSorter == nil {
e.TaskSorter = &sort.AlphaNumericWithRootTasksFirst{}
}
e.TaskSorter.Sort(tasks)

return tasks, nil
}

Expand Down

0 comments on commit 9dbd04a

Please sign in to comment.