Comments (19)
Just to chime in, I also ran into this a while ago and ended up using option 1, but in my case the script is supposed to always be called from its own directory, so using a different relative path is not a case I needed to cover.
That being said, what about switching from checking that $COMP_LINE
starts with <script_name> <command>
to checking that ${COMP_WORDS[1]}
(i.e. the second word in $COMP_LINE
) matches <command>
?
Using complete -F _cli_completions cli
is already telling bash that this function should be used for cli
, even when referring to the script from a relative/absolute path, so by the time the function is called we're sure we're being called for cli
: there's no need to check for that again.
from bashly.
How about this? Will this work in all cases to your knowledge?
This is trying Jack's idea:
- Use
COMP_LINE
without its first word when matching - Match patterns changed to not include the command name
#!/usr/bin/env bash
# This bash completions script was generated by
# completely (https://github.com/dannyben/completely)
# Modifying it manually is not recommended
# shellcheck disable=SC2207
_mygit_completions() {
local cur=${COMP_WORDS[COMP_CWORD]}
local comp_line="${COMP_WORDS[*]:1}"
case "$comp_line" in
'status'*) COMPREPLY=($(compgen -W "forSTATUS1 forSTATUS2" -- "$cur")) ;;
'commit'*) COMPREPLY=($(compgen -W "forCOMMIT1 forCOMMIT2" -- "$cur")) ;;
*) COMPREPLY=($(compgen -W "status commit" -- "$cur")) ;;
esac
}
complete -F _mygit_completions mygit
This can be tested by saving and sourcing it, and then:
$ mygit s<tab> #=> mygit status
$ mygit c<tab> #=> mygit commit
$ mygit commit <tab> #=> mygit commit forCOMMIT1
$ ...
From what I can tell, this works with or without a leading path string (either ./
or even /longer/path/mygit
)
from bashly.
Well - the new implementation doesn't change anything in that regard does it?
Yeah exactly, it was just an FYI because I noticed it while researching. Thanks for being so quick, as usual!
from bashly.
Am I doing something wrong or is this a bug?
It seems like a little bit of both:
- You are doing it wrong
- Since the documentation or example did not help you enough, its a documentation bug
Steps I took...
The command bashly init
creates an initial src/bashly.yml
for you.
So the order of things you have done is incorrect, you should either run bashly init
, and then paste the example YAML in it, or, create the file src/bashly.yml
manually.
Better than both these options, you can take a look (and clone) the files in the same example folder on GitHub. The reason these examples are on GitHub, is that they will include the files needed.
Note that, unlike the example, I do not have a completions command
This probably means that you did not copy the YAML as is. It looks like you are showing the output of the commands example.
Notice that the first command in the YAML is completions
:
bashly/examples/completions/src/bashly.yml
Lines 5 to 9 in 7a425c1
Now, that the bashly add comp function
just adds a function that you can call from anywhere in your script. In this example, it is from a completions
command. So take a look in the src/completions_command.sh
file - since it calls this function:
The reason the completions feature is implemented like this, where it requires a little more involvement from you, is that I suspect that different users would want to use it differently:
- Some would want it in
their-cli completions
command - Others would want it in
their-cli --completions
flag - Others would want it as a standalone script, that they install in their user-level completions directory
- etc.
You can also take a look at the completions docs, hopefully they are helpful. If not, let me know what would help to clarify these instructions or example.
from bashly.
Thanks for the fast response!
Doing bashly init
first and then editing the yml
file it generates works as expected.
I would have also overlooked that I needed to manually call send_completions
in the compeltions_command.sh
. But it makes sense that I have to do that.
Now the bash completions work ... sort of.
I'm not familiar with bash completion scripts, but it looks to me like it only registers completions for commands that start with cli
. I.e., if I call the script as ./cli
, it won't work. I have to install it / put it on my $PATH
in order to be able to use the completion feature.
Is there a way of making it work without installing the bash script? (I guess this might be more of a https://github.com/DannyBen/completely issue)
Most of my use cases will be with scripts that live inside of some project repo, and I'd call them like ./tools/myscript
. I wouldn't want to have to put all of these tools/
directories on the path.
from bashly.
I would have also overlooked that I needed to manually call send_completions in the compeltions_command.sh
I will work on the examples a little, to clarify these tricky points.
it only registers completions for commands that start with cli. I.e., if I call the script as ./cli, it won't work.
Well, yes, this is by design.
Bashly is intended to design scripts that look and feel like any other command line tool.
Except for the obvious option to copy this file to your path, you have at least a couple of options now:
Option 1: Change name in bashly.yml
You can try to name the tool (in bashly.yml
) as name: ./cli
. Although I did not design it for such a use case, it might work without a problem. After doing this, run bashly add comp function && bashly g
again to regenerate the completions.
Option 2: Add current directory to PATH
globally
Add the current directory to the PATH
. This is normally present in your ~/.bashrc
or other init script:
$ export PATH=".:$PATH"
$ cli --version
0.1.0
Option 3: Add current directory to PATH
locally
If having the current directory in the path is not something you want configured globally, you can use a tool like direnv
(recommended not only for this problem) to temporarily and automatically add/update environment variables when you cd
into a directory.
Then, you just need to create a .envrc
file with this content:
export PATH=".:$PATH"
and it will only be in effect when you are inside this folder.
I guess this might be more of a completely issue
Not necessarily. Bashly sends the name of the application to the completely library, so as we have seen, changing the name, changes the generated script.
If none of the above seem like good options for you, I might be convinced to continue thinking about a solution.
from bashly.
Maybe it's not common to use bash completion on non-installed scripts, in which case perhaps it should not work the way I expected it to.
That being said, I can achieve the behavior I would like by modifying the send_completions()
function as follows:
send_completions() {
echo $'#!/usr/bin/env bash'
echo $''
echo $'# This bash completions script was generated by'
echo $'# completely (https://github.com/dannyben/completely)'
echo $'# Modifying it manually is not recommended'
echo $'_cli_completions() {'
echo $' local CMD_NAME=$(readlink -f "$1")'
echo $' local SCRIPT_PATH="'$(realpath $0)'"'
echo $' if [[ "$CMD_NAME" != "$SCRIPT_PATH" ]] ; then'
echo $' # not for us'
echo $' return'
echo $' fi'
echo $''
echo $' local cur=${COMP_WORDS[COMP_CWORD]}'
echo $' COMPREPLY=($(compgen -W "$CMD_NAME" -- "$cur"))'
echo $' case "$COMP_LINE" in'
echo $' *\'cli completions\'*) COMPREPLY=($(compgen -W "--help -h" -- "$cur")) ;;'
echo $' *\'cli download\'*) COMPREPLY=($(compgen -A file -W "--force --help -f -h" -- "$cur")) ;;'
echo $' *\'cli upload\'*) COMPREPLY=($(compgen -A directory -A user -W "--help --password --user -h -p -u" -- "$cur")) ;;'
echo $' *\'cli\'*) COMPREPLY=($(compgen -W "--help --version -h -v completions download upload" -- "$cur")) ;;'
echo $' esac'
echo $'}'
echo $''
echo $'complete -F _cli_completions cli'
}
The most important change is in the case
statement: Each case option is prefixed with *
, s.t. any invocation of cli
, whether it's relative or not, works (./cli
, cli
, /tmp/bashlytest/cli
all match).
complete -F _cli_completions cli
will cause our function to be called regardless of the command used, as described here.
At the top also added a check that the executable being completed is actually the one that we're defining the completion function for, just in case there are name collisions.
If you don't see any downsides, consider modifying completely to generate the completion function like this.
from bashly.
No...
First of all, modifying the completion script manually is totally not recommended.
It should be regenerated whenever you update bashly.yml
.
Did you try any of my proposed solutions?
from bashly.
The options you proposed do not solve my problem (or not as well as my solution, in my opinion).
- Option 1: I am still only able to call the function from one location. If I'm in the parent directory, completions stop working
- Option 2: This is a bit better, but still not as nice as what I'm proposing. It requires the user to do one additional step to get this working
- Option 3: I did not know about
direnv
, that is interesting. But now I have to ask people to install/enable an additional tool, which is not ideal. But this is probably the best of the three options.
My proposal allows calling the script from anywhere without needing extra steps/tools.
First of all, modifying the completion script manually is totally not recommended.
Yes I know. I just did this to test whether what I want to do actually works. The solution I'm proposing is to modify the code generation in completely
to produce this output.
from bashly.
Oh well.
I am not sure that prefixing the completion pattern with a wildcard is a good idea. I don't know what can be the side effects.
I feel that completions should be a part of scripts with fixed names that exist in the path.
Bashly is perfectly capable of generating scripts for "local consumption", but expecting these to also have bash completions (which is a global feature by nature), sits a bit awkwardly with me. What if you have more than one repo with the same name of the CLI, just in different folders?
At this point I am leaning towards not modifying anything. I might be convinced otherwise as time passes, or more information becomes available though.
I did not know about direnv, that is interesting. But now I have to ask people to install/enable an additional tool, which is not ideal.
Since we are dealing with completions, you will have to instruct users to add something to their ~/.bashrc
... just for having completions to a script that only works in this folder, they need to add something to their boot sequence - how is this better?
This is my advice.
- If the users of your repo that contains the generated script are co workers, your README should include whatever "setup" instructions they need to start developing
- Having a local "helper" script is a great option for developers. It does not need autocomplete.
- If it is complex, and you feel that autocomplete is mandatory, then either use
direnv
(I believe you can also runeval $(your-cli completions)
in it), or have your users add the script to the path, or the current directory to the path. Either way, there is no way to add completions without the end user running something, or you providing a setup script that does that for them.
I feel there are several solutions, and some ways to avoid the problem altogether.
from bashly.
Thanks @iamjackg,
Do you have an example completions script that operates as you mention, but still maintains the same principles of the completion script that bashly generates? (i.e. still matching patterns by longest first, and without a prefix *
).
Or even better, what changes would you propose in this template from the completely gem?
from bashly.
In the meantime, I have updated all the examples, including the completions example, for improved clarity.
from bashly.
Haha, you beat me to it! I was about to post an almost identical snippet! Looks good to me (obviously
from bashly.
This has the additional advantage of working even if people add extra spaces between the commands. E.g. this
script command subcommand
will still autocomplete.
It does not work with flags specified in the --flag=value
format because, by default, =
is part of $COMP_WORDBREAKS
, but Bashly doesn't support that format anyway, so we're good. In case you ever need it (or you're just curious), this is how git's autocomplete solves that issue!
from bashly.
Haha, you beat me to it! I was about to post an almost identical snippet!
Sorry - it bugged me, had to try something... :)
It does not work with flags specified in the --flag=value
Well - the new implementation doesn't change anything in that regard does it?
I mean, it didn't work before, and wont work in the new one.
Although bashly does not support the --flag=value
notation, if there is an easy way to at least have it work in the completely gem, it wouldn't hurt.
In any case, I will open a separate ticket for the new feature, and implement it in completely and then in bashly.
I will CC this ticket, so we have links.
from bashly.
Thank you both for this ticket.
You can now enjoy the fruits of your labor with version 0.6.5, which uses completely 0.2.0, which generates completion scripts that will work even when the script is prefixed by any path.
from bashly.
Thanks a lot to both of you! This is great.
Just to note, since this does not have the check I proposed, it is possible to get wrong autocomplete suggestions if e.g. you have a script called cli
on your $PATH
, but have eval
-ed the completions for another ./cli
.
local CMD_NAME=$(readlink -f "$1")
local SCRIPT_PATH="'$(realpath $0)'"
if [[ "$CMD_NAME" != "$SCRIPT_PATH" ]] ; then
# not for us
return
fi
But you're probably right in feeling like this:
Bashly is perfectly capable of generating scripts for "local consumption", but expecting these to also have bash completions (which is a global feature by nature), sits a bit awkwardly with me.
The change I proposed does address this concern though:
What if you have more than one repo with the same name of the CLI, just in different folders?
from bashly.
Autocomplete relies on the basename of the script.
If you have two scripts with the same name, you should avoid enabling autocomplete globally for them.
from bashly.
BTW @ndepal - as I understand your use case is to create little scripts for each of your projects, you might want to take a look at opcode. This is what I use for a per-project shortcuts.
This is of course not a full blown "script generator" like bashly, but if your scripts mainly run other commands, then opcode is perfect for this.
I then have op.conf
files committed to my git repos, and everybody can easily run the shortcuts.
from bashly.
Related Issues (20)
- Is there any security consideration for the "bash name"? HOT 4
- Provide a third state to `strict` which omits `set -e` HOT 10
- Support for Alternate Dependencies HOT 12
- bashly about “The goal is to generate a new file and fill it with many variables” HOT 2
- Do something when a root command's flag is set HOT 8
- The `initialize` file is not loaded when using `.bash` extension
- Treat `initialize.sh` like the `before` and `after` hooks
- Automatically execute bashly generate when file change HOT 3
- Support storing variable via yaml file HOT 4
- `allowed` incompatible with `required: false` HOT 6
- Flags should not be set to their default value if not explicitly used HOT 3
- Flags must have a long form if you specify a default value HOT 3
- Whitespace not respected in here documents HOT 1
- Decide whether to use `bashly-` prefix for YAML configs HOT 6
- `bashly` completions for Bash, Fish, Zsh HOT 17
- Private commands appear in completion suggestion
- Questions about generated scripts (set -e) HOT 2
- Color escape sequences are not correctly shown in terminal HOT 8
- Organize scripts in src dir in subdirectories HOT 8
- Pass bashly.yml through ERB HOT 8
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from bashly.