From 48b8d280b938360c7e6c1365264feafbac76d057 Mon Sep 17 00:00:00 2001
From: Pete Davison <pd93.uk@outlook.com>
Date: Sun, 30 Jun 2024 02:07:02 +0000
Subject: [PATCH] feat: completion command

---
 cmd/task/task.go                              |  9 ++
 completion.go                                 | 34 +++++++
 internal/flags/flags.go                       |  2 +
 .../docs/deprecations/completion_scripts.mdx  | 25 ++++++
 website/docs/installation.mdx                 | 88 +++++++++++--------
 5 files changed, 121 insertions(+), 37 deletions(-)
 create mode 100644 completion.go
 create mode 100644 website/docs/deprecations/completion_scripts.mdx

diff --git a/cmd/task/task.go b/cmd/task/task.go
index 0de9075054..439ca9c5a7 100644
--- a/cmd/task/task.go
+++ b/cmd/task/task.go
@@ -83,6 +83,15 @@ func run() error {
 		return nil
 	}
 
+	if flags.Completion != "" {
+		script, err := task.Completion(flags.Completion)
+		if err != nil {
+			return err
+		}
+		fmt.Println(script)
+		return nil
+	}
+
 	if flags.Global {
 		home, err := os.UserHomeDir()
 		if err != nil {
diff --git a/completion.go b/completion.go
new file mode 100644
index 0000000000..84191874de
--- /dev/null
+++ b/completion.go
@@ -0,0 +1,34 @@
+package task
+
+import (
+	_ "embed"
+	"fmt"
+)
+
+//go:embed completion/bash/task.bash
+var bash string
+
+//go:embed completion/fish/task.fish
+var fish string
+
+//go:embed completion/ps/task.ps1
+var powershell string
+
+//go:embed completion/zsh/_task
+var zsh string
+
+func Completion(completion string) (string, error) {
+	// Get the file extension for the selected shell
+	switch completion {
+	case "bash":
+		return bash, nil
+	case "fish":
+		return fish, nil
+	case "powershell":
+		return powershell, nil
+	case "zsh":
+		return zsh, nil
+	default:
+		return "", fmt.Errorf("unknown shell: %s", completion)
+	}
+}
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
index 2a1f9476fc..4ae50c2efc 100644
--- a/internal/flags/flags.go
+++ b/internal/flags/flags.go
@@ -38,6 +38,7 @@ var (
 	Version     bool
 	Help        bool
 	Init        bool
+	Completion  string
 	List        bool
 	ListAll     bool
 	ListJson    bool
@@ -80,6 +81,7 @@ func init() {
 	pflag.BoolVar(&Version, "version", false, "Show Task version.")
 	pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
 	pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
+	pflag.StringVar(&Completion, "completion", "", "Generates shell completion script.")
 	pflag.BoolVarP(&List, "list", "l", false, "Lists tasks with description of current Taskfile.")
 	pflag.BoolVarP(&ListAll, "list-all", "a", false, "Lists tasks with or without a description.")
 	pflag.BoolVarP(&ListJson, "json", "j", false, "Formats task list as JSON.")
diff --git a/website/docs/deprecations/completion_scripts.mdx b/website/docs/deprecations/completion_scripts.mdx
new file mode 100644
index 0000000000..fef7408bca
--- /dev/null
+++ b/website/docs/deprecations/completion_scripts.mdx
@@ -0,0 +1,25 @@
+---
+slug: /deprecations/completion-scripts/
+---
+
+# Completion Scripts
+
+:::warning
+
+This deprecation breaks the following functionality:
+
+- Any direct references to the completion scripts in the Task git repository
+
+:::
+
+Direct use of the completion scripts in the `completion/*` directory of the
+[github.com/go-task/task][task] Git repository is deprecated. Any shell
+configuration that directly refers to these scripts will potentially break in
+the future as the scripts may be moved or deleted entirely. Any configuration
+should be updated to use the [new method for generating shell
+completions][completions] instead.
+
+{/* prettier-ignore-start */}
+[completions]: ../installation.mdx#setup-completions
+[task]: https://github.com/go-task/task
+{/* prettier-ignore-end */}
diff --git a/website/docs/installation.mdx b/website/docs/installation.mdx
index 0e75035dfa..c2b20d18d1 100644
--- a/website/docs/installation.mdx
+++ b/website/docs/installation.mdx
@@ -3,6 +3,9 @@ slug: /installation/
 sidebar_position: 2
 ---
 
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
 # Installation
 
 Task offers many installation methods. Check out the available methods below.
@@ -247,65 +250,76 @@ released binary.
 
 ## Setup completions
 
-Download the autocompletion file corresponding to your shell.
-
-[All completions are available on the Task repository](https://github.com/go-task/task/tree/main/completion).
+Some installation methods will automatically install completions too, but if
+this isn't working for you or your chosen method doesn't include them, you can
+run `task --completion <shell>` to output a completion script for any supported
+shell. There are a couple of ways these completions can be added to your shell
+config:
 
-### Bash
+### Option 1. Load the completions in your shell's startup config (Recommended)
 
-First, ensure that you installed bash-completion using your package manager.
+This method loads the completion script from the currently installed version of
+task every time you create a new shell. This ensures that your completions are
+always up-to-date.
 
-Make the completion file executable:
+<Tabs values={[ {label: 'bash', value: '1'}, {label: 'zsh', value: '2'},
+    {label: 'fish', value: '3'},
+    {label: 'powershell', value: '4'}
+  ]}>
 
-```shell
-chmod +x path/to/task.bash
+<TabItem value="1">
+```shell title="~/.bashrc"
+eval "$(task --completion bash)"
 ```
+</TabItem>
 
-After, add this to your `~/.bash_profile`:
-
-```shell
-source path/to/task.bash
+<TabItem value="2">
+```shell title="~/.zshrc"
+eval "$(task --completion zsh)"
 ```
+</TabItem>
 
-### ZSH
-
-Put the `_task` file somewhere in your `$FPATH`:
-
-```shell
-mv path/to/_task /usr/local/share/zsh/site-functions/_task
+<TabItem value="3">
+```shell title="~/.config/fish/config.fish"
+task --completion fish | source
 ```
+</TabItem>
 
-Ensure that the following is present in your `~/.zshrc`:
-
-```shell
-autoload -U compinit
-compinit -i
+<TabItem value="4">
+```powershell title="$PROFILE\Microsoft.PowerShell_profile.ps1"
+Invoke-Expression (&task --completion powershell)
 ```
+</TabItem></Tabs>
 
-ZSH version 5.7 or later is recommended.
+### Option 2. Copy the script to your shell's completions directory
 
-### Fish
+This method requires you to manually update the completions whenever Task is
+updated. However, it is useful if you want to modify the completions yourself.
 
-Move the `task.fish` completion script:
+<Tabs
+  values={[
+    {label: 'bash', value: '1'},
+    {label: 'zsh', value: '2'},
+    {label: 'fish', value: '3'}
+  ]}>
 
+<TabItem value="1">
 ```shell
-mv path/to/task.fish ~/.config/fish/completions/task.fish
+task --completion bash > /etc/bash_completion.d/task
 ```
+</TabItem>
 
-### PowerShell
-
-Open your profile script with:
-
-```powershell
-mkdir -Path (Split-Path -Parent $profile) -ErrorAction SilentlyContinue
-notepad $profile
+<TabItem value="2">
+```shell
+task --completion zsh  > /usr/local/share/zsh/site-functions/_task
 ```
+</TabItem>
 
-Add the line and save the file:
-
+<TabItem value="3">
 ```shell
-Invoke-Expression -Command path/to/task.ps1
+task --completion fish > ~/.config/fish/completions/task.fish
 ```
+</TabItem></Tabs>
 
 {/* prettier-ignore-start */}
 [go]: https://golang.org/