Skip to content
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

Add autocompletion for target orgs #620

Open
gilgourevitch opened this issue Feb 12, 2024 · 8 comments
Open

Add autocompletion for target orgs #620

gilgourevitch opened this issue Feb 12, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@gilgourevitch
Copy link

As a Salesforce user on multiple orgs, with multiple Sandboxes, the target org aliases and usernames are case-sensitive, and when the CLI is authenticated locally in multiple orgs, it become more difficult to find the correct alias.
Adding autocompletion for this flag could ease the usage of the CLI.

@mdonnalley
Copy link
Contributor

@gilgourevitch This is an awesome idea 🏆

We definitely want to support this for sf but plugin-autocomplete isn't the right spot for it, since oclif shouldn't be aware of anything salesforce related.

Ideally, plugin-autocomplete would be able to merge together various autocomplete scripts. So, for instance, sf would ship logic for target-org autocompletion that plugin-autocomplete would merge into it's own generated scripts.

That feature, unfortunately, doesn't exist yet. I'm going to put this on our backlog so we can prioritize it

Copy link

git2gus bot commented Feb 12, 2024

This issue has been linked to a new work item: W-15022864

@gilgourevitch
Copy link
Author

Hello @mdonnalley , and thank you for your feedback. I understand the concept. Do you already have an idea of the future code structure and how the custom scripts could be passed to the autocomplete plugin ? Having this in mind, I could start developing a script for this use-case, or if I can help on the core code, do not hesitate.

@mdonnalley
Copy link
Contributor

@gilgourevitch I don't have anything in mind for how this would actually be implemented - just a high level idea of the feature.

@cristiand391 is our resident autocomplete guru so I would defer to him how to implement this

@cristiand391
Copy link
Member

Hey @gilgourevitch 👋🏼

I explored this a while ago but never fully implemented it, here's a few things that would be needed:

  1. Expose a command that returns completion candidates:
    This is the most common approach, we could add a hidden __complete that accepts some args and return the completion candidates. The shell completion functions would pass the current line to it and get back the values to suggest.

  2. How args/flags define dynamic completion logic
    The __complete cmd needs a way to access the completion logic for the current args/flags, to follow the oclif style this could be another prop in a flag.

For the --target-dev-hub flag in some sf commands, this would be added here like the defaultHelp prop:
https://github.com/salesforcecli/sf-plugins-core/blob/5413761097259b1d060831b0bcc82f2550fc7c89/src/flags/orgFlags.ts#L175-L189

With those 2 you could get dynamic completions, it would work like this:

sf org create scratch --target-org <TAB>

***
1) completion script passes current line to `__complete`:
sf __complete `sf org create scratch --target-org `

2) `__complete` resolves the command name and flag, access the completion func defined for the flag and executes it

3) return values in a standard format, then pass those down to each shell completion helpers

Alternative to __complete: call node with a script containing the arg/flag comp. logic

This would be a bit tricky because it requires extracting the logic from code and calling it outside the "oclif" context but would be way faster.

I did this in my fuzzy completion script for sf:
https://github.com/cristiand391/sf-plugin-fzf-cmp
https://github.com/cristiand391/sf-plugin-fzf-cmp/blob/main/src/comp-gen.ts

_fzf_complete_sf() {
  if [[ "$@" == "sf " || "$@" == "sf which " || "$@" == "sf help " ]]; then
    local commands_file preview_command

    commands_file="/Users/cdominguez/Library/Caches/sf/autocomplete/fzf-cmp/commands.json"
    preview_command="jq --arg cmd {} -r '. as \$commands | map(.id == \$cmd) | index(true) | \$commands[.].summary' $commands_file"

    _fzf_complete --reverse --preview "$preview_command" --preview-window wrap,down,1 --prompt="sf> " -- "$@" < <(
      jq -r '.[] | .id' $commands_file
    )
  else
    local comp=$(node /Users/cdominguez/.local/share/sf/node_modules/@cristiand391/sf-plugin-fzf-cmp/lib/shell-completion.js "$@")
    _fzf_complete --select-1 --ansi --prompt="sf> " -- "$@" < <(
      echo $comp
    )
  fi
}

_fzf_complete_sf_post() {
  cut -f 1
}

Considerations

  1. UX
    The hidden complete command approach works great for compiled languages like Go and Rust because it's fast to generate these values, but in JS this could a pain if each dynamic completion takes a more than a few seconds.
    NOTE: I would be mostly concerted about __complete load performance (it has to resolve the command) rather than each flag logic (these can always be improved later).

As an example you can try the npm completion, it's way slower and I try to never use it.

  1. Caching
    Kinda related to 1, some frameworks/extension implement some caching mechanism so that dynamic completions feel snappy. If __complete it's slow we can explore caching options.

Related:

  1. jayree's fork of plugin-autocomplete: https://github.com/jayree/sfdx-autocomplete-plugin

it does org username completion and caching

  1. carapace's cache: https://rsteube.github.io/blog/2022/a-pragmatic-approach-to-shell-completion.html#caching
    It's a completion for the cobra framework (Go), but it's a great blog post for learning about this stuff.

  2. spf13/cobra completion:
    https://github.com/spf13/cobra/blob/main/site/content/completions/_index.md

I'm happy to answer any questions if you wanna give this a try :)

@rsteube
Copy link

rsteube commented Feb 19, 2024

fyi https://github.com/rsteube/carapace-spec

@jbbarth
Copy link
Contributor

jbbarth commented Feb 24, 2024

I'm super interested in that as well, as we're building our own CLI at my current company on top of Oclif and not having dynamic completion reduces the value proposal of our CLI.

Thanks @cristiand391 for the extensive notes. I'm not sure I get that one though:

The hidden complete command approach works great for compiled languages like Go and Rust because it's fast to generate these values, but in JS this could a pain if each dynamic completion takes a more than a few seconds.

My own CLI takes 150ms to execute "--help" end-to-end, and the "heroku" CLI, which has arguably a lot of commands, take 0.8s. I think it's not unreasonable to consider a dynamic completion can go over 1s, but I don't think that will take "a few seconds".

I'm willing to spend some time on the topic, though my knowledge of oclif internals are limited. Let me know if there's some valuable work to start in that space.

@cristiand391
Copy link
Member

My own CLI takes 150ms to execute "--help" end-to-end, and the "heroku" CLI, which has arguably a lot of commands, take 0.8s. I think it's not unreasonable to consider a dynamic completion can go over 1s, but I don't think that will take "a few seconds".

I was thinking more about completion that requires doing some HTTP requests, reading local files and similar should be fine.
That said, I think caching can be implemented by developers rather having oclif provide a generic way to do it (maybe people want. to store it in json, yml, etc).

I saw the angular CLI puts a ... in the current line while fetching completions, never looked at how it works but it's a nice way to let users know what happens:
https://angular.io/generated/images/guide/cli/completion.gif

I'm willing to spend some time on the topic, though my knowledge of oclif internals are limited. Let me know if there's some valuable work to start in that space.

for a POC I think you can go with points 1 & 2 mentioned in my comment above, a __complete command that executes a flag's completion function and print the returned values.

After that you will have to pick either zsh or powershell completion to test it (there's bash support too but it's has a few bugs and I'm not very familiar with it, we may rewrite it in the future to fix some bugs/bring it to feature parity with others).

also check the resources linked in this PR:
#406

and also the GitHub CLI generated completion script, you can see there how it calls its own __complete command and passes the values to zsh utils.

@mdonnalley mdonnalley added enhancement New feature or request and removed enhancement labels Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

5 participants