summaryrefslogtreecommitdiffstats
path: root/CONTRIBUTING.md
blob: 9c8292c1a3820a5822a57fc6086a5594ad90a975 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# Contributing to bash-completion

Contributions to the bash completion project are more than
welcome. Fixes, clean-ups and improvements of existing code are much
appreciated, as are completion functions for new commands.

However, before submitting a completion to us, first consider submitting it to
the project that ships the commands your completion is for. Having the
completion shipped along with the command opens up some liberties we don't have
if the completion is included with bash-completion. For example, we generally
do not want to hardcode lists of available command options and their
completions, because they quite probably vary between versions of the completed
command, and therefore resort to scraping --help output and the like. While we
do fairly well there, depending on the command, this can be fragile or
expensive, or just not possible. If the completion is shipped alongside the
command, they can be kept in sync and use more hardcoding etc. They are also
more likely to be maintained and/or watched by people intimately familiar with
the completed commands. See instructions in README.md how to install completion
files from other projects so they are automatically enabled and dynamically
loaded by bash-completion.

On the other hand, we do have a pretty nice test suite and a bunch of helper
functions that you may find useful. And a whole slew of completions in one
package. Our functions can be used from "external" completions as well, just
make sure you test for their existence and/or fail gracefully if you intend
your completion to be usable without having bash-completion installed.

It's nowhere near clear cut always what is the best place for the completion,
upstream project or us. Even if it would seem to be upstream, not all upstreams
are interested in shipping completions, or their install systems might not
easily support installing completion files properly. But give it some thought,
and ask if unsure.

If you wish to contribute code to us, volunteering for long term maintainership
of your code within bash-completion is welcome. When exactly you will be asked
to do that depends on the case; don't be disappointed if it does or doesn't
happen instantly.

Also, please bare the following coding guidelines in mind:

- Do not use Perl, Ruby, Python etc. to do text processing unless the
  command for which you are writing the completion code implies the
  presence of one of those languages.

  For example, if you were writing completion code for perldoc(1), the
  use of Perl to achieve your goal would be acceptable. irb(1)
  completion would similarly make the use of Ruby acceptable.

  Even so, please consider alternatives to these large and slow to
  start interpreters. Use lightweight programs such as grep(1), awk(1)
  and sed(1).

- Use the full power of bash >= 4.2. We no longer support earlier bash
  versions, so you may as well use all the features of that version of
  bash to optimise your code. However, be careful when using features
  added since bash 4.2, since not everyone will be able to use them.

  For example, extended globs often enable you to avoid the use of
  external programs, which are expensive to fork and execute, so do
  make full use of those:

  `?(pattern-list)` - match zero or one occurrences of patterns
  `*(pattern-list)` - match zero or more occurrences of patterns
  `+(pattern-list)` - match one or more occurrences of patterns
  `@(pattern-list)` - match exactly one of the given patterns
  `!(pattern-list)` - match anything except one of the given patterns

- Following on from the last point, be sparing with the use of
  external processes whenever you can. Completion functions need to be
  fast, so sacrificing some code legibility for speed is acceptable.

  For example, judicious use of sed(1) can save you from having to
  call grep(1) and pipe the output to cut(1), which saves a fork(2)
  and exec(3).

  Sometimes you don't even need sed(1) or other external programs at
  all, though. Use of constructs such as `${parameter#word}`,
  `${parameter%word}` and `${parameter/pattern/string}` can provide
  you a lot of power without having to leave the shell.

  For example, if `$foo` contains the path to an executable,
  `${foo##*/}` will give you the basename of the program, without
  having to call basename(1). Similarly, `${foo%/*}` will give you the
  dirname, without having to call dirname(1).

  As another example,

  ```shell
  bar=$(echo $foo | command sed -e 's/bar/baz/g')
  ```

  can be replaced by:

  ```shell
  bar=${foo//bar/baz}
  ```

  These forms of parameter substitutions can also be used on arrays,
  which makes them very powerful (if a little slow).

- We want our completions to work in `posix` and `nounset` modes.

  Unfortunately due to a bash < 5.1 bug, toggling POSIX mode interferes
  with keybindings and should not be done. This rules out use of
  process substitution which causes syntax errors in POSIX mode.

  Instead of toggling `nounset` mode, make sure to test whether
  variables are set (e.g. with `[[ -v varname ]]`) or use default
  expansion (e.g. `${varname-}`).

- Prefer `compgen -W '...' -- $cur` over embedding `$cur` in external
  command arguments (often e.g. sed, grep etc) unless there's a good
  reason to embed it. Embedding user input in command lines can result
  in syntax errors and other undesired behavior, or messy quoting
  requirements when the input contains unusual characters. Good
  reasons for embedding include functionality (if the thing does not
  sanely work otherwise) or performance (if it makes a big difference
  in speed), but all embedding cases should be documented with
  rationale in comments in the code.

- When completing available options, offer only the most descriptive
  ones as completion results if there are multiple options that do the
  same thing. Usually this means that long options should be preferred
  over the corresponding short ones. This way the user is more likely
  to find what she's looking for and there's not too much noise to
  choose from, and there are less situations where user choice would
  be needed in the first place. Note that this concerns only display
  of available completions; argument processing/completion for options
  that take an argument should be made to work with all known variants
  for the functionality at hand. For example if `-s`, `-S`, and
  `--something` do the same thing and require an argument, offer only
  `--something` as a completion when completing option names starting
  with a dash, but do implement required argument processing for all
  `-s`, `-S`, and `--something`.  Note that GNU versions of various
  standard commands tend to have long options while other userland
  implementations of the same commands may not have them, and it would
  be good to have the completions work for as many userlands as
  possible so things aren't always that simple.

- Do not write to the file-system under any circumstances. This can
  create race conditions, is inefficient, violates the principle of
  least surprise and lacks robustness.

- Use printf(1) instead of echo(1) for portability reasons, and be
  sure to invoke commands that are often found aliased (such as `ls`
  or `grep` etc) using the `command` (or `builtin`) command as
  appropriate.

- Make small, incremental commits that do one thing. Don't cram
  unrelated changes into a single commit.

- If your code was written for a particular platform, try to make it
  portable to other platforms, so that everyone may enjoy it. If your
  code works only with the version of a binary on a particular
  platform, ensure that it will not be loaded on other platforms that
  have a command with the same name.

  In particular, do not use GNU extensions to commands like sed and
  awk if you can write your code another way. If you really, REALLY must
  use them, do so if there's no other sane way to do what you're doing.
  The "Shell and Utilities" volume of the POSIX specification is a good
  starting reference for portable use of various utilities, see
  <https://pubs.opengroup.org/onlinepubs/9699919799/>.

- Use an editor that supports EditorConfig, see <https://editorconfig.org/>,
  and format source code according to our settings.

- Read the existing source code for examples of how to solve
  particular problems. Read the bash man page for details of all the
  programming tools available to you within the shell.

- Please test your code thoroughly before sending it to us. We don't
  have access to all the commands for which we are sent completion
  functions, so we are unable to test them all personally. If your
  code is accepted into the distribution, a lot of people will try it
  out, so try to do a thorough job of eradicating all the bugs before
  you send it to us. If at all practical, **add test cases** to our
  test suite (in the test/ dir) that verify that the code does what it
  is intended to do, fixes issues it intends to fix, etc.

- In addition to running the test suite, there are a few scripts in the test/
  dir that catch some common issues, see and use for example runLint.

- File bugs, enhancement, and pull requests at GitHub,
  <https://github.com/scop/bash-completion>.
  Sending them to the developers might work too, but is really strongly
  discouraged as bits are more likely to fall through the cracks that
  way compared to the tracker. Just use GitHub. If that's not an
  option for some reason and you want to use email to send patches,
  send them as attachments formatted by `git format-patch` or directly
  with `git send-email`.