Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func run() error {
flags.ListAll,
flags.ListJson,
flags.NoStatus,
flags.Nested,
)
if listOptions.ShouldListTasks() {
if flags.Silent {
Expand Down
65 changes: 38 additions & 27 deletions help.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ type ListOptions struct {
ListAllTasks bool
FormatTaskListAsJSON bool
NoStatus bool
Nested bool
}

// NewListOptions creates a new ListOptions instance
func NewListOptions(list, listAll, listAsJson, noStatus bool) ListOptions {
func NewListOptions(list, listAll, listAsJson, noStatus, nested bool) ListOptions {
return ListOptions{
ListOnlyTasksWithDescriptions: list,
ListAllTasks: listAll,
FormatTaskListAsJSON: listAsJson,
NoStatus: noStatus,
Nested: nested,
}
}

Expand Down Expand Up @@ -63,7 +65,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
return false, err
}
if o.FormatTaskListAsJSON {
output, err := e.ToEditorOutput(tasks, o.NoStatus)
output, err := e.ToEditorOutput(tasks, o.NoStatus, o.Nested)
if err != nil {
return false, err
}
Expand Down Expand Up @@ -135,33 +137,17 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
return nil
}

func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Taskfile, error) {
o := &editors.Taskfile{
Tasks: make([]editors.Task, len(tasks)),
Location: e.Taskfile.Location,
}
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool) (*editors.Namespace, error) {
var g errgroup.Group
editorTasks := make([]editors.Task, len(tasks))

// Look over each task in parallel and turn it into an editor task
for i := range tasks {
aliases := []string{}
if len(tasks[i].Aliases) > 0 {
aliases = tasks[i].Aliases
}
g.Go(func() error {
o.Tasks[i] = editors.Task{
Name: tasks[i].Name(),
Task: tasks[i].Task,
Desc: tasks[i].Desc,
Summary: tasks[i].Summary,
Aliases: aliases,
UpToDate: false,
Location: &editors.Location{
Line: tasks[i].Location.Line,
Column: tasks[i].Location.Column,
Taskfile: tasks[i].Location.Taskfile,
},
}
editorTask := editors.NewTask(tasks[i])

if noStatus {
editorTasks[i] = editorTask
return nil
}

Expand All @@ -180,10 +166,35 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Ta
return err
}

o.Tasks[i].UpToDate = upToDate

editorTask.UpToDate = &upToDate
editorTasks[i] = editorTask
return nil
})
}
return o, g.Wait()
if err := g.Wait(); err != nil {
return nil, err
}

// Create the root namespace
var tasksLen int
if !nested {
tasksLen = len(editorTasks)
}
rootNamespace := &editors.Namespace{
Tasks: make([]editors.Task, tasksLen),
Location: e.Taskfile.Location,
}

// Recursively add namespaces to the root namespace or if nesting is
// disabled add them all to the root namespace
for i, task := range editorTasks {
taskNamespacePath := strings.Split(task.Task, ast.NamespaceSeparator)
if nested {
rootNamespace.AddNamespace(taskNamespacePath, task)
} else {
rootNamespace.Tasks[i] = task
}
}

return rootNamespace, g.Wait()
}
71 changes: 66 additions & 5 deletions internal/editors/output.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package editors

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

type (
// Taskfile wraps task list output for use in editor integrations (e.g. VSCode, etc)
Taskfile struct {
Tasks []Task `json:"tasks"`
Location string `json:"location"`
// Namespace wraps task list output for use in editor integrations (e.g. VSCode, etc)
Namespace struct {
Tasks []Task `json:"tasks"`
Namespaces map[string]*Namespace `json:"namespaces,omitempty"`
Location string `json:"location,omitempty"`
}
// Task describes a single task
Task struct {
Expand All @@ -13,7 +18,7 @@ type (
Desc string `json:"desc"`
Summary string `json:"summary"`
Aliases []string `json:"aliases"`
UpToDate bool `json:"up_to_date"`
UpToDate *bool `json:"up_to_date,omitempty"`
Location *Location `json:"location"`
}
// Location describes a task's location in a taskfile
Expand All @@ -23,3 +28,59 @@ type (
Taskfile string `json:"taskfile"`
}
)

func NewTask(task *ast.Task) Task {
aliases := []string{}
if len(task.Aliases) > 0 {
aliases = task.Aliases
}
return Task{
Name: task.Name(),
Task: task.Task,
Desc: task.Desc,
Summary: task.Summary,
Aliases: aliases,
Location: &Location{
Line: task.Location.Line,
Column: task.Location.Column,
Taskfile: task.Location.Taskfile,
},
}
}

func (parent *Namespace) AddNamespace(namespacePath []string, task Task) {
if len(namespacePath) == 0 {
return
}

// If there are no child namespaces, then we have found a task and we can
// simply add it to the current namespace
if len(namespacePath) == 1 {
parent.Tasks = append(parent.Tasks, task)
return
}

// Get the key of the current namespace in the path
namespaceKey := namespacePath[0]

// Add the namespace to the parent namespaces map using the namespace key
if parent.Namespaces == nil {
parent.Namespaces = make(map[string]*Namespace, 0)
}

// Search for the current namespace in the parent namespaces map
// If it doesn't exist, create it
namespace, ok := parent.Namespaces[namespaceKey]
if !ok {
namespace = &Namespace{}
parent.Namespaces[namespaceKey] = namespace
}

// Remove the current namespace key from the namespace path.
childNamespacePath := namespacePath[1:]

// If there are no child namespaces in the task name, then we have found the
// namespace of the task and we can add it to the current namespace.
// Otherwise, we need to go deeper
namespace.AddNamespace(childNamespacePath, task)
}
6 changes: 6 additions & 0 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ var (
TaskSort string
Status bool
NoStatus bool
Nested bool
Insecure bool
Force bool
ForceAll bool
Expand Down Expand Up @@ -117,6 +118,7 @@ func init() {
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
pflag.BoolVar(&Nested, "nested", false, "Nest namespaces when listing tasks as JSON")
pflag.BoolVar(&Insecure, "insecure", getConfig(config, config.Remote.Insecure, false), "Forces Task to download Taskfiles over insecure connections.")
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, config.Verbose, false), "Enables verbose mode.")
Expand Down Expand Up @@ -194,6 +196,10 @@ func Validate() error {
return errors.New("task: --no-status only applies to --json with --list or --list-all")
}

if Nested && !ListJson {
return errors.New("task: --nested only applies to --json with --list or --list-all")
}

return nil
}

Expand Down
Loading