Skip to content

add basic implementation of tab completion #663

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 12, 2020
2 changes: 2 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/arduino/arduino-cli/cli/board"
"github.com/arduino/arduino-cli/cli/cache"
"github.com/arduino/arduino-cli/cli/compile"
"github.com/arduino/arduino-cli/cli/completion"
"github.com/arduino/arduino-cli/cli/config"
"github.com/arduino/arduino-cli/cli/core"
"github.com/arduino/arduino-cli/cli/daemon"
Expand Down Expand Up @@ -77,6 +78,7 @@ func createCliCommandTree(cmd *cobra.Command) {
cmd.AddCommand(board.NewCommand())
cmd.AddCommand(cache.NewCommand())
cmd.AddCommand(compile.NewCommand())
cmd.AddCommand(completion.NewCommand())
cmd.AddCommand(config.NewCommand())
cmd.AddCommand(core.NewCommand())
cmd.AddCommand(daemon.NewCommand())
Expand Down
76 changes: 76 additions & 0 deletions cli/completion/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package completion

import (
"bytes"
"os"
"strings"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/spf13/cobra"
)

var (
completionNoDesc bool //Disable completion description for shells that support it
)

// NewCommand created a new `version` command
func NewCommand() *cobra.Command {
command := &cobra.Command{
Use: "completion [bash|zsh|fish] [--no-descriptions]",
ValidArgs: []string{"bash", "zsh", "fish"},
Args: cobra.ExactArgs(1),
Short: "Generates completion scripts",
Long: "Generates completion scripts for various shells",
Example: " " + os.Args[0] + " completion bash > completion.sh\n" +
" " + "source completion.sh",
Run: run,
}
command.Flags().BoolVar(&completionNoDesc, "no-descriptions", false, "Disable completion description for shells that support it")

return command
}

func run(cmd *cobra.Command, args []string) {
if completionNoDesc && (args[0] == "bash" || args[0] == "zsh") {
feedback.Errorf("Error: command description is not supported by %v", args[0])
os.Exit(errorcodes.ErrGeneric)
}
switch args[0] {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
break
case "zsh":
buf := new(bytes.Buffer)
cmd.Root().GenZshCompletion(buf)
// Next 3 lines are Hack, we'll wait new version of cobra with ZshV2Completion https://github.com/spf13/cobra/pull/1070
//insert escaping before [ and ]
s := strings.ReplaceAll(buf.String(), "[", "\\[")
s = strings.ReplaceAll(s, "]", "\\]")
s = strings.ReplaceAll(s, "\\[1\\]", "[1]") // revert the case
os.Stdout.WriteString(s)
break
case "fish":
buf := new(bytes.Buffer)
cmd.Root().GenFishCompletion(buf, !completionNoDesc)
// Next 2 lines are Hack, fixed here https://github.com/spf13/cobra/pull/1122
s := strings.ReplaceAll(buf.String(), "arduino-cli_comp", "arduino_cli_comp") //required because fish does not support env variables with "-" in the name
os.Stdout.WriteString(s)
break
}
}
45 changes: 45 additions & 0 deletions docs/command-line-completion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
`arduino-cli` supports command-line completion (also known as *tab completion*) for basic commands.
Currently only `bash`, `zsh`, `fish` shells are supported

### Before you start
In order to generate the file required to make the completion work you have to [install](installation.md) Arduino CLI first.

### Generate the completion file
To generate the completion file you can use `arduino-cli completion [bash|zsh|fish] [--no-descriptions]`.
By default this command will print on the standard output (the shell window) the content of the completion file. To save to an actual file use the `>` redirect symbol.

### Bash
Use `arduino-cli completion bash > arduino-cli.sh` to generate the completion file.
At this point you can move that file in `/etc/bash_completion.d/` (root access is required) with `sudo mv arduino-cli.sh /etc/bash_completion.d/`.

A not recommended alternative is to source the completion file in `.bashrc`.

Remember to open a new shell to test the functionality

### Zsh
Use `arduino-cli completion zsh > _arduino-cli` to generate the completion file.
At this point you can place the file in a directory listed in your `fpath` if you have already created a directory to store your completion.

Or if you want you can create a directory, add it to your `fpath` and copy the file in it:

1. `mkdir ~/completion_zsh`
2. add `fpath=($HOME/completion_zsh $fpath)` at the beginning of your `.zshrc` file
3. `mv _arduino-cli ~/completion_zsh/`

Remember to open a new shell to test the functionality

*N.B.*
The Zsh completion is working with [Oh-My-Zsh](https://ohmyz.sh/) but not with [Prezto](https://github.com/sorin-ionescu/prezto) (the zsh completion system is working in a different way than classic zsh). But hopefully it will be fixed in the future

### Fish
Use `arduino-cli completion fish > arduino-cli.fish` to generate the completion file.
At this point you can place the file in `~/.config/fish/completions` as stated in the [official documentation](http://fishshell.com/docs/current/index.html#where-to-put-completions).
Remember to create the directory if it's not already there `mkdir -p ~/.config/fish/completions/` and then place the completion file in there with `mv arduino-cli.fish ~/.config/fish/completions/`

Remember to open a new shell to test the functionality

#### Disabling command and flag descriptions
By default fish completion has command and flag description enabled by default. If you want to disable this behaviour you can simply pass the `--no-descriptions` flag when calling `completion` command and the generated file will not have descriptions

*N.B.*
This flag is not compatible with bash or zsh
2 changes: 1 addition & 1 deletion docsgen/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ replace github.com/arduino/arduino-cli => ../

require (
github.com/arduino/arduino-cli v0.0.0
github.com/spf13/cobra v0.0.6
github.com/spf13/cobra v1.0.0
)
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/schollz/closestmatch v2.1.0+incompatible
github.com/segmentio/stats/v4 v4.5.3
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/spf13/cobra v1.0.0
github.com/spf13/jwalterweatherman v1.0.0
github.com/spf13/viper v1.6.2
github.com/stretchr/testify v1.4.0
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0=
github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
Expand Down Expand Up @@ -171,6 +173,8 @@ github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5/go.mod h1:GEXHk5H
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/segmentio/fasthash v0.0.0-20180216231524-a72b379d632e h1:uO75wNGioszjmIzcY/tvdDYKRLVvzggtAmmJkn9j4GQ=
Expand All @@ -179,6 +183,8 @@ github.com/segmentio/objconv v1.0.1 h1:QjfLzwriJj40JibCV3MGSEiAoXixbp4ybhwfTB8RX
github.com/segmentio/objconv v1.0.1/go.mod h1:auayaH5k3137Cl4SoXTgrzQcuQDmvuVtZgS0fb1Ahys=
github.com/segmentio/stats/v4 v4.5.3 h1:Y/DSUWZ4c8ICgqJ9rQohzKvGqGWbLPWad5zmxVoKN+Y=
github.com/segmentio/stats/v4 v4.5.3/go.mod h1:LsaahUJR7iiSs8mnkvQvdQ/RLHAS5adGLxuntg0ydGo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
Expand All @@ -194,12 +200,15 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ nav:
- Documentation Home: index.md
- installation.md
- getting-started.md
- command-line-completion.md
- CONTRIBUTING.md
- FAQ.md
- Command reference:
Expand All @@ -68,6 +69,7 @@ nav:
- cache: commands/arduino-cli_cache.md
- cache clean: commands/arduino-cli_cache_clean.md
- compile: commands/arduino-cli_compile.md
- completion: commands/arduino-cli_completion.md
- config: commands/arduino-cli_config.md
- config dump: commands/arduino-cli_config_dump.md
- config init: commands/arduino-cli_config_init.md
Expand Down
63 changes: 63 additions & 0 deletions test/test_completion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# This file is part of arduino-cli.
#
# Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
#
# This software is released under the GNU General Public License version 3,
# which covers the main part of arduino-cli.
# The terms of this license can be found at:
# https://www.gnu.org/licenses/gpl-3.0.en.html
#
# You can be released from the requirements of the above licenses by purchasing
# a commercial license. Buying such a license is mandatory if you want to modify or
# otherwise use the software for commercial activities involving the Arduino
# software without disclosing the source code of your own applications. To purchase
# a commercial license, send an email to [email protected].

import pytest

def test_completion_no_args(run_command):
result = run_command("completion")
assert not result.ok
assert "Error: accepts 1 arg(s), received 0" in result.stderr
assert result.stdout == ""

def test_completion_bash(run_command):
result = run_command("completion bash")
assert result.ok
assert result.stderr == ""
assert "_arduino-cli_root_command()" in result.stdout
assert "__start_arduino-cli()" in result.stdout

def test_completion_zsh(run_command):
result = run_command("completion zsh")
assert result.ok
assert result.stderr == ""
assert "#compdef _arduino-cli arduino-cli" in result.stdout
assert "function _arduino-cli" in result.stdout

def test_completion_fish(run_command):
result = run_command("completion fish")
assert result.ok
assert result.stderr == ""
assert "# fish completion for arduino-cli" in result.stdout
assert "function __arduino-cli_perform_completion" in result.stdout

def test_completion_bash_no_desc(run_command):
result = run_command("completion bash --no-descriptions")
assert not result.ok
assert result.stdout == ""
assert "Error: command description is not supported by bash" in result.stderr

def test_completion_zsh_no_desc(run_command):
result = run_command("completion zsh --no-descriptions")
assert not result.ok
assert result.stdout == ""
assert "Error: command description is not supported by zsh" in result.stderr

def test_completion_fish_no_desc(run_command):
result = run_command("completion fish --no-descriptions")
assert result.ok
assert result.stderr == ""
assert "# fish completion for arduino-cli" in result.stdout
assert "function __arduino-cli_perform_completion" in result.stdout
assert "__completeNoDesc" in result.stdout