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
16 changes: 2 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,8 @@ My one slight annoyance is the manual set up required for a new host/account whi

### Bash

```bash
eval "$(roam completion bash)"
```

Maybe somehow also use,

```bash
-o bashdefault -o default -o nospace
```
Run `roam completion bash --help` for instructions.

### Zsh

```
roam completion zsh
```

This writes `_roam` to `~/.local/share/zsh/site-functions`. Use `--out` to choose another directory and `--force` to overwrite an existing file.
Run `roam completion zsh --help` for instructions.
68 changes: 2 additions & 66 deletions completion.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
package main

import (
_ "embed"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"
)

//go:embed _roam
var zshCompletion []byte

func (a *app) defaultZshOut() string {
return filepath.Join(a.home, ".local/share/zsh/site-functions")
}

func (a *app) expandOut(out string) string {
if out == "~" {
return a.home
}
if rest, ok := strings.CutPrefix(out, "~/"); ok {
return filepath.Join(a.home, rest)
}
return out
}

func (a *app) completionCommand() *cobra.Command {
func completionCommand(a *app) *cobra.Command {
cmd := &cobra.Command{
Use: "completion",
Short: "Generate the autocompletion script for the specified shell",
Expand All @@ -44,48 +22,6 @@ func (a *app) completionCommand() *cobra.Command {
return cmd.Help()
},
}
var force bool
out := a.defaultZshOut()
bash := &cobra.Command{
Use: "bash",
Short: "Generate the autocompletion script for bash",
Args: cobra.NoArgs,
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, _ []string) error {
_, err := fmt.Fprintln(cmd.OutOrStdout(), "complete -F _git roam")
return err
},
}
zsh := &cobra.Command{
Use: "zsh",
Short: "Generate the autocompletion script for zsh",
Args: cobra.NoArgs,
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return a.completionZsh(force, a.expandOut(out))
},
}
zsh.Flags().BoolVarP(&force, "force", "f", false, "Overwrite existing generated files")
zsh.Flags().StringVarP(&out, "out", "o", out, "Output directory for generated files (~/.local/share/zsh/site-functions)")
cmd.AddCommand(bash, zsh)
cmd.AddCommand(completionBashCommand(), completionZshCommand(a))
return cmd
}

func (a *app) completionZsh(force bool, out string) error {
if err := os.MkdirAll(out, 0o755); err != nil {
return err
}
path := filepath.Join(out, "_roam")
_, err := os.Stat(path)
switch {
case err == nil:
if !force {
return fmt.Errorf("%s already exists; use --force to overwrite", path)
}
case !errors.Is(err, os.ErrNotExist):
return err
}
return os.WriteFile(path, zshCompletion, 0o644)
}
43 changes: 43 additions & 0 deletions completion_bash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"fmt"

"github.com/spf13/cobra"
)

func completionBashCommand() *cobra.Command {
return &cobra.Command{
Use: "bash",
Short: "Generate the autocompletion script for bash",
Long: `Generate the autocompletion script for the bash shell.

This aliases roam completion to git completion, since roam wraps git.
It needs git's bash completion, usually provided by the 'bash-completion'
package. If it is not installed already, you can install it via your OS's
package manager.

To load completions in your current shell session:

source <(roam completion bash)

To load completions for every new session, execute once:

#### Linux:

roam completion bash > /etc/bash_completion.d/roam

#### macOS:

roam completion bash > $(brew --prefix)/etc/bash_completion.d/roam

You will need to start a new shell for this setup to take effect.`,
Args: cobra.NoArgs,
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, _ []string) error {
_, err := fmt.Fprintln(cmd.OutOrStdout(), "complete -F _git roam")
return err
},
}
}
16 changes: 14 additions & 2 deletions completion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@ func TestCompletionZsh(t *testing.T) {

func TestCompletionZshDefaultOut(t *testing.T) {
app, _ := testApp(t)
var stdout bytes.Buffer
app.stdout = &stdout
require.NoError(t, app.run(t.Context(), []string{"completion", "zsh"}))
require.Equal(t, string(zshCompletion), stdout.String())
path := filepath.Join(app.home, ".local/share/zsh/site-functions", "_roam")
require.FileExists(t, path)
_, statErr := os.Stat(path)
require.ErrorIs(t, statErr, os.ErrNotExist)
}

func TestCompletionZshExistsWithoutForce(t *testing.T) {
Expand Down Expand Up @@ -59,13 +63,21 @@ func TestCompletionZshForce(t *testing.T) {
require.Equal(t, string(zshCompletion), string(got))
}

func TestCompletionBashHelp(t *testing.T) {
app, _ := testApp(t)
var stdout bytes.Buffer
app.stdout = &stdout
require.NoError(t, app.run(t.Context(), []string{"completion", "bash", "--help"}))
require.Contains(t, stdout.String(), "source <(roam completion bash)")
}

func TestCompletionZshHelp(t *testing.T) {
app, _ := testApp(t)
var stdout bytes.Buffer
app.stdout = &stdout
require.NoError(t, app.run(t.Context(), []string{"completion", "zsh", "--help"}))
help := stdout.String()
for _, want := range []string{"--force", "--out", "~/.local/share/zsh/site-functions"} {
for _, want := range []string{"--force", "--out", "~/.local/share/zsh/site-functions", "source <(roam completion zsh)"} {
require.Contains(t, help, want)
}
}
Expand Down
93 changes: 93 additions & 0 deletions completion_zsh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main

import (
_ "embed"
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"
)

//go:embed _roam
var zshCompletion []byte

func defaultZshOut(a *app) string {
return filepath.Join(a.home, ".local/share/zsh/site-functions")
}

func expandOut(a *app, out string) string {
if out == "~" {
return a.home
}
if rest, ok := strings.CutPrefix(out, "~/"); ok {
return filepath.Join(a.home, rest)
}
return out
}

func completionZshCommand(a *app) *cobra.Command {
var force bool
out := defaultZshOut(a)
zsh := &cobra.Command{
Use: "zsh",
Short: "Generate the autocompletion script for zsh",
Long: `Generate the autocompletion script for the zsh shell.

If shell completion is not already enabled in your environment you will need
to enable it. You can execute the following once:

echo "autoload -U compinit; compinit" >> ~/.zshrc

To load completions in your current shell session:

source <(roam completion zsh)

To load completions for every new session, execute once:

#### Linux:

roam completion zsh > "${fpath[1]}/_roam"

#### macOS:

roam completion zsh > $(brew --prefix)/share/zsh/site-functions/_roam

You will need to start a new shell for this setup to take effect.

Alternatively, use --out to write _roam to a directory directly,
with --force to overwrite an existing file.`,
Args: cobra.NoArgs,
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, _ []string) error {
if cmd.Flags().Changed("out") {
return completionZsh(force, expandOut(a, out))
}
_, err := cmd.OutOrStdout().Write(zshCompletion)
return err
},
}
zsh.Flags().BoolVarP(&force, "force", "f", false, "Overwrite existing generated files")
zsh.Flags().StringVarP(&out, "out", "o", out, "Output directory for generated files (~/.local/share/zsh/site-functions)")
return zsh
}

func completionZsh(force bool, out string) error {
if err := os.MkdirAll(out, 0o755); err != nil {
return err
}
path := filepath.Join(out, "_roam")
_, err := os.Stat(path)
switch {
case err == nil:
if !force {
return fmt.Errorf("%s already exists; use --force to overwrite", path)
}
case !errors.Is(err, os.ErrNotExist):
return err
}
return os.WriteFile(path, zshCompletion, 0o644)
}
2 changes: 1 addition & 1 deletion roam.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (a *app) command() *cobra.Command {
a.setupCommand(),
a.updateCommand(),
a.syncCommand(),
a.completionCommand(),
completionCommand(a),
)
return root
}
Expand Down
Loading