diff --git a/README.md b/README.md index 39cc43c..2db5e41 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ brew install tea Distribution packages exist for: **alpinelinux ([tea](https://pkgs.alpinelinux.org/packages?name=tea&branch=edge))** and **archlinux ([gitea-tea](https://aur.archlinux.org/packages/gitea-tea))** + +Shell completion can be added via `tea autocomplete --install`. + ## Usage First of all, you have to create a token on your `personal settings -> application` page of your gitea instance. diff --git a/cmd/autocomplete.go b/cmd/autocomplete.go new file mode 100644 index 0000000..ca28d38 --- /dev/null +++ b/cmd/autocomplete.go @@ -0,0 +1,105 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package cmd + +import ( + "fmt" + "io" + "net/http" + "os" + "os/exec" + + "github.com/adrg/xdg" + "github.com/urfave/cli/v2" +) + +// CmdAutocomplete manages autocompletion +var CmdAutocomplete = cli.Command{ + Name: "shellcompletion", + Aliases: []string{"autocomplete"}, + Usage: "Install shell completion for tea", + Description: "Install shell completion for tea", + ArgsUsage: " (bash, zsh, powershell)", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "install", + Usage: "Persist in shell config instead of printing commands", + }, + }, + Action: runAutocompleteAdd, +} + +func runAutocompleteAdd(ctx *cli.Context) error { + var remoteFile, localFile, cmds string + shell := ctx.Args().First() + + switch shell { + case "zsh": + remoteFile = "contrib/autocomplete.zsh" + localFile = "autocomplete.zsh" + cmds = "echo 'PROG=tea _CLI_ZSH_AUTOCOMPLETE_HACK=1 source %s' >> ~/.zshrc && source ~/.zshrc" + + case "bash": + remoteFile = "contrib/autocomplete.sh" + localFile = "autocomplete.sh" + cmds = "echo 'PROG=tea source %s' >> ~/.bashrc && source ~/.bashrc" + + case "powershell": + remoteFile = "contrib/autocomplete.ps1" + localFile = "tea.ps1" + cmds = "\"& %s\" >> $profile" + + default: + return fmt.Errorf("Must specify valid shell type") + } + + localPath, err := xdg.ConfigFile("tea/" + localFile) + if err != nil { + return err + } + + cmds = fmt.Sprintf(cmds, localPath) + + if err := saveAutoCompleteFile(remoteFile, localPath); err != nil { + return err + } + + if ctx.Bool("install") { + fmt.Println("Installing in your shellrc") + installer := exec.Command(shell, "-c", cmds) + if shell == "powershell" { + installer = exec.Command("powershell.exe", "-Command", cmds) + } + out, err := installer.CombinedOutput() + if err != nil { + return fmt.Errorf("Couldn't run the commands: %s %s", err, out) + } + } else { + fmt.Println("\n# Run the following commands to install autocompletion (or use --install)") + fmt.Println(cmds) + } + + return nil +} + +func saveAutoCompleteFile(file, destPath string) error { + url := fmt.Sprintf("https://gitea.com/gitea/tea/raw/branch/master/%s", file) + fmt.Println("Fetching " + url) + + res, err := http.Get(url) + if err != nil { + return err + } + defer res.Body.Close() + + writer, err := os.Create(destPath) + if err != nil { + return err + } + defer writer.Close() + + _, err = io.Copy(writer, res.Body) + return err +} diff --git a/contrib/autocomplete.ps1 b/contrib/autocomplete.ps1 new file mode 100644 index 0000000..81812a6 --- /dev/null +++ b/contrib/autocomplete.ps1 @@ -0,0 +1,9 @@ +$fn = $($MyInvocation.MyCommand.Name) +$name = $fn -replace "(.*)\.ps1$", '$1' +Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { + param($commandName, $wordToComplete, $cursorPosition) + $other = "$wordToComplete --generate-bash-completion" + Invoke-Expression $other | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + } \ No newline at end of file diff --git a/contrib/autocomplete.sh b/contrib/autocomplete.sh new file mode 100644 index 0000000..f0f6241 --- /dev/null +++ b/contrib/autocomplete.sh @@ -0,0 +1,21 @@ +#! /bin/bash + +: ${PROG:=$(basename ${BASH_SOURCE})} + +_cli_bash_autocomplete() { + if [[ "${COMP_WORDS[0]}" != "source" ]]; then + local cur opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + if [[ "$cur" == "-"* ]]; then + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion ) + else + opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) + fi + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi +} + +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete $PROG +unset PROG diff --git a/contrib/autocomplete.zsh b/contrib/autocomplete.zsh new file mode 100644 index 0000000..cf39c88 --- /dev/null +++ b/contrib/autocomplete.zsh @@ -0,0 +1,23 @@ +#compdef $PROG + +_cli_zsh_autocomplete() { + + local -a opts + local cur + cur=${words[-1]} + if [[ "$cur" == "-"* ]]; then + opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}") + else + opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}") + fi + + if [[ "${opts[1]}" != "" ]]; then + _describe 'values' opts + else + _files + fi + + return +} + +compdef _cli_zsh_autocomplete $PROG diff --git a/main.go b/main.go index f1253d8..a4b82af 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,7 @@ func main() { &cmd.CmdNotifications, &cmd.CmdMilestones, &cmd.CmdOrgs, + &cmd.CmdAutocomplete, } app.EnableBashCompletion = true err := app.Run(os.Args)