mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	Merge branch 'feat/suggest-similar-task'
This commit is contained in:
		| @@ -2,6 +2,9 @@ | ||||
|  | ||||
| ## Unreleased | ||||
|  | ||||
| - Add a "Did you mean ...?" suggestion when a task does not exits another one | ||||
|   with a similar name is found | ||||
|   ([#867](https://github.com/go-task/task/issues/867), [#880](https://github.com/go-task/task/pull/880)). | ||||
| - Now YAML parse errors will print which Taskfile failed to parse | ||||
|   ([#885](https://github.com/go-task/task/issues/885), [#887](https://github.com/go-task/task/pull/887)). | ||||
| - Add ability to set `aliases` for tasks and namespaces ([#268](https://github.com/go-task/task/pull/268), [#340](https://github.com/go-task/task/pull/340), [#879](https://github.com/go-task/task/pull/879)). | ||||
|   | ||||
							
								
								
									
										13
									
								
								errors.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								errors.go
									
									
									
									
									
								
							| @@ -14,11 +14,20 @@ var ( | ||||
| ) | ||||
|  | ||||
| type taskNotFoundError struct { | ||||
| 	taskName string | ||||
| 	taskName   string | ||||
| 	didYouMean string | ||||
| } | ||||
|  | ||||
| func (err *taskNotFoundError) Error() string { | ||||
| 	return fmt.Sprintf(`task: Task %q not found`, err.taskName) | ||||
| 	if err.didYouMean != "" { | ||||
| 		return fmt.Sprintf( | ||||
| 			`task: Task %q does not exist. Did you mean %q?`, | ||||
| 			err.taskName, | ||||
| 			err.didYouMean, | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	return fmt.Sprintf(`task: Task %q does not exist`, err.taskName) | ||||
| } | ||||
|  | ||||
| type multipleTasksWithAliasError struct { | ||||
|   | ||||
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ require ( | ||||
| 	github.com/mattn/go-zglob v0.0.3 | ||||
| 	github.com/mitchellh/hashstructure/v2 v2.0.2 | ||||
| 	github.com/radovskyb/watcher v1.0.7 | ||||
| 	github.com/sajari/fuzzy v1.0.0 | ||||
| 	github.com/spf13/pflag v1.0.5 | ||||
| 	github.com/stretchr/testify v1.8.0 | ||||
| 	golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 | ||||
|   | ||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
								
							| @@ -26,6 +26,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN | ||||
| github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= | ||||
| github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg= | ||||
| github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= | ||||
| github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= | ||||
| github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
|   | ||||
							
								
								
									
										2
									
								
								help.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								help.go
									
									
									
									
									
								
							| @@ -81,7 +81,7 @@ func (e *Executor) tasksWithDesc() (tasks []*taskfile.Task) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // PrintTaskNames prints only the task names in a Taskfile. | ||||
| // ListTaskNames prints only the task names in a Taskfile. | ||||
| // Only tasks with a non-empty description are printed if allTasks is false. | ||||
| // Otherwise, all task names are printed. | ||||
| func (e *Executor) ListTaskNames(allTasks bool) { | ||||
|   | ||||
							
								
								
									
										23
									
								
								setup.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								setup.go
									
									
									
									
									
								
							| @@ -17,6 +17,8 @@ import ( | ||||
| 	"github.com/go-task/task/v3/internal/output" | ||||
| 	"github.com/go-task/task/v3/taskfile" | ||||
| 	"github.com/go-task/task/v3/taskfile/read" | ||||
|  | ||||
| 	"github.com/sajari/fuzzy" | ||||
| ) | ||||
|  | ||||
| func (e *Executor) Setup() error { | ||||
| @@ -28,6 +30,8 @@ func (e *Executor) Setup() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	e.setupFuzzyModel() | ||||
|  | ||||
| 	v, err := e.Taskfile.ParsedVersion() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -85,6 +89,25 @@ func (e *Executor) readTaskfile() error { | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (e *Executor) setupFuzzyModel() { | ||||
| 	if e.Taskfile != nil { | ||||
| 		model := fuzzy.NewModel() | ||||
| 		model.SetThreshold(1) // because we want to build grammar based on every task name | ||||
|  | ||||
| 		var words []string | ||||
| 		for taskName := range e.Taskfile.Tasks { | ||||
| 			words = append(words, taskName) | ||||
|  | ||||
| 			for _, task := range e.Taskfile.Tasks { | ||||
| 				words = append(words, task.Aliases...) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		model.Train(words) | ||||
| 		e.fuzzyModel = model | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *Executor) setupTempDir() error { | ||||
| 	if e.TempDir != "" { | ||||
| 		return nil | ||||
|   | ||||
							
								
								
									
										12
									
								
								task.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								task.go
									
									
									
									
									
								
							| @@ -16,6 +16,7 @@ import ( | ||||
| 	"github.com/go-task/task/v3/internal/templater" | ||||
| 	"github.com/go-task/task/v3/taskfile" | ||||
|  | ||||
| 	"github.com/sajari/fuzzy" | ||||
| 	"golang.org/x/exp/slices" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| ) | ||||
| @@ -53,7 +54,8 @@ type Executor struct { | ||||
| 	Output      output.Output | ||||
| 	OutputStyle taskfile.Output | ||||
|  | ||||
| 	taskvars *taskfile.Vars | ||||
| 	taskvars   *taskfile.Vars | ||||
| 	fuzzyModel *fuzzy.Model | ||||
|  | ||||
| 	concurrencySemaphore chan struct{} | ||||
| 	taskCallCount        map[string]*int32 | ||||
| @@ -71,6 +73,7 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error { | ||||
| 			e.ListTasksWithDesc() | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		if task.Internal { | ||||
| 			e.ListTasksWithDesc() | ||||
| 			return &taskInternalError{taskName: call.Task} | ||||
| @@ -359,8 +362,13 @@ func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) { | ||||
| 	} | ||||
| 	// If we found no tasks | ||||
| 	if len(aliasedTasks) == 0 { | ||||
| 		didYouMean := "" | ||||
| 		if e.fuzzyModel != nil { | ||||
| 			didYouMean = e.fuzzyModel.SpellCheck(call.Task) | ||||
| 		} | ||||
| 		return nil, &taskNotFoundError{ | ||||
| 			taskName: call.Task, | ||||
| 			taskName:   call.Task, | ||||
| 			didYouMean: didYouMean, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user