bash/.local/share/blesh/doc/CONTRIBUTING.md
2024-08-18 03:40:38 +02:00

348 lines
20 KiB
Markdown

# Contribution Guide
## How to contribute
### Issues
You can freely create an issue using the following links:
- Report and fixes for bugs and performance issues [[Here]](https://github.com/akinomyoga/ble.sh/issues/new?template=bug_report.md)
- Questions on usage [[Here]](https://github.com/akinomyoga/ble.sh/issues/new?template=feature_request.md)
- Feature request [[Here]](https://github.com/akinomyoga/ble.sh/issues/new?template=help.md)
- Others (suggestions, projects, discussion, complaints, news, information or anything) [[Here]](https://github.com/akinomyoga/ble.sh/issues/new?template=free_style.md)
### Pull requests
We always welcome the following types of pull requests. Any changes will be considered to be provided under the BSD 3-Clause License.
If you do not know whether your changes would be appropriate for merge, please feel free to create a pull request and let us talk with each other!
- Better translation to English, typo fixes
- Fixes, optimization, test cases
- New features
- New color themes ... We accept new themes in [`contrib`](https://github.com/akinomyoga/blesh-contrib/pulls) repository.
- Others
### Wiki
You can freely edit [wiki pages](https://github.com/akinomyoga/ble.sh/wiki).
- Translations
- Typo fixes
- Create new pages
## For package maintainers
If you are a package maintainer for a repository of Linux distribution, etc.
you may provide a package-specific setting by preparing
a file `/path/to/blesh/lib/_package.sh` (e.g. `/usr/share/blesh/lib/_package.sh`)
which will be sourced after the load of `ble.sh` just before sourcing user's configuration (`~/.blerc`).
- In the file, the shell variable `_ble_base_package_type=TYPE` should be
set up to have a repository-specific name (such as `AUR`).
- The function named `ble/base/package:TYPE/update` (where `TYPE`
matches with a value assigned to `_ble_base_package_type`) may be
provided to define a custom updating procedure. The exit status of the function can be
- `0` ... when the update succeeded
- `6` ... when the update was skipped because the package was up to date.
- `125` ... when it wants to fall back to the built-in updating procedure of `ble.sh`.
- Other ... when the update failed
An example `lib/_package.sh` might be
```bash
_ble_base_package_type=apt
function ble/base/package:apt/update {
sudo apt upgrade blesh
}
```
You can also find a real example for AUR (Arch User Repository) [here](https://aur.archlinux.org/cgit/aur.git/tree/blesh-update.sh?h=blesh-git).
## Summary of codebase
The core script file `ble.sh` is generated by combining the following files:
- `ble.pp` ... Basic initialiation
- `src/def.sh` ... Prototype definitions
- `src/util.sh` ... Basic utility functions
- `src/decode.sh` ... User-input decoder and keybindings
- `src/color.sh` ... Terminal graphic attributes
- `src/canvas.sh` ... Terminal layout engine
- `src/canvas.emoji.sh` ... Emoji database
- `src/history.sh` ... Command history management
- `src/edit.sh` ... Line editor
- `src/benchmark.sh` ... Measure processing time
- `lib/core-completion.sh` ... Prototype definition for completions
- `lib/core-syntax.sh` ... Prototype definitions for syntax analyzer
Useful features are implemented in separate modules:
- `lib/keymap.vi.sh` ... Vim mode
- `lib/vim-arpeggio.sh` ... `vim-arpeggio`-like plugin
- `lib/vim-surround.sh` ... `vim-surround`-like plugin
- `lib/keymap.emacs.sh` ... Emacs mode
- `lib/core-syntax.sh` ... Shell parser and syntax highlighting
- `lib/core-syntax-ctx.def` ... Definition of parser states
- `lib/core-complete.sh` ... Completions including `menu-complete`, `auto-complete`, `menu-filter`, `dabbrev`, `sabbrev`, etc.
The following files are initialization scripts:
- `lib/init-term.sh` ... Initialize terminal escape sequences (host-to-terminal, i.e. control sequences)
- `lib/init-cmap.sh` ... Initialize terminal escape sequences (terminal-to-host, i.e. key sequences)
- `lib/init-bind.sh` ... Initialize readline attacher
- `lib/init-msys1.sh` ... Workaround for MSYS1 with broken pipes
- `lib/init-msys1-helper.c` ... Helper C program for the workaround of broken pipes
The contrib repository contains configurations built on top of the `ble.sh` framework
- `contrib/config/*.bash` are specific configurations whose details may depend on the user's preferences. These are expected to work just by `ble-import` but at the same time serve as examples for the user's personal versions.
- `contrib/sample/*.bash` are examples to explain interfaces for configurations. These are just explanation of the interface so would not actually function.
- `contrib/airline/*.bash` are the themes for `vim-airline`.
- `contrib/integration/*.bash` are codes needed for the adjustments with other framework.
- `contrib/*.bash` are other miscellaneous configurations. Usually one needs additional configuration after `ble-import`. These files may be moved to another directory in the future.
## Tests
Tests can be run by one of the following commands:
```bash
$ make check
$ make check-all
$ bash out/ble.sh --test
```
Currently, test coverage is very small
partly because the testing for interactive behavior and terminal rendering results is hard.
Nevertheless, the tests are defined in the following files:
- `lib/core-test.sh` ... Library for tests
- `lib/test-bash.sh`
- `lib/test-main.sh`
- `lib/test-util.sh`
- `lib/test-canvas.sh`
- `lib/test-edit.sh`
- `lib/test-complete.sh`
- `lib/test-syntax.sh`
## Coding styles
These styles are not required for the initial PR as these are too complicated.
The maintainer(s) will apply the styles before merging.
Nevertheless, it would be useful to follow the styles when you are going to submit PRs many times.
### Naming convention for functions
The function names roughly have the following structure, but the rule is ambiguous to some extent, and there are also many exceptions in the codebase.
```ebnf
function_name = (namespace '/')* function_basename ;
namespace = function_basename ;
function_basename = name ext? (* namespace function *)
| class '.' name ext? (* class method *)
| class '#' name ext? (* instance method *)
| class '#' class '::' name ext? (* instance method (override) *)
| type ':' name ext? ; (* customization point *)
name = hyphenated_name (* public *)
| '.' hyphenated_name ; (* private *)
ext = '.hook' (* hook functions for blehook *)
| '.draw' (* function operating on DRAW_BUFF *)
| '.fib' (* worker for ble/util/fiberchain *)
| '.proc' (* callback functions *)
| '.impl' ; (* internal implementation *)
(* ... (and more extensions) *)
class = name ;
type = hyphenated_name ;
hyphenated_name = (word '-')* word ;
word = /[_a-zA-Z0-9]+/ ;
```
The function names are basically hyphenated words (such as `foo-bar-baz` instead of `foo_bar_baz`, `fooBarBaz`, or `FooBarBaz`).
**Namespace**--The function names can be prefixed by namespaces of the form `<namespace>/`.
Except for the functions intended to be called from the command line or the user configuration, all the functions should be defined in a namespace.
The interface of the functions defined in a namespace is subject to change in the future.
A function name can also be used as the namespace name for the helper functions to implement the function.
For the functions defined in external modules, the following namespace should be used to avoid conflicts:
| Types | Namespace |
|:----------------------------------------------------|----------------------------------------|
| `contrib/*.bash` | `ble/contrib/<modulename>` |
| `contrib/config/*.bash` | `ble/contrib/config:<modulename>` |
| `contrib/integration/*.bash` | `ble/contrib/integration:<modulename>` |
| User's public config | e.g. `<user>` |
| User's private config | e.g. `<user>` or `my` |
| User's private config in Bash initialization file | e.g. `<user>`, `my` or `bashrc` |
| User's private config in ble.sh initialization file | e.g. `<user>`, `my` or `blerc` |
**Private**--The function basename started with `.` is considered private within the namespace. All the other functions are public.
**Method**--The function basename of the form `<class>#<name>` is considered an instance method.
The function basename of the form `<class>.<name>` is considered a class method.
The difference between the two is the latter assumes a single global state while the former assumes that an instance is specified by the first argument or by other means.
The way to specify the instance depends on the class.
For example, `ble/array#*` receives the variable name as the first argument while `ble/string#*` directly receives the string value as the first argument.
For `ble/textarea#*`, the target instance is selected by the shell variable `_ble_textarea_panel`.
The function basename of the form `<class1>#<class2>::<name>` is used to define a virtual function called by the base class `<class2>` at the side of the derived class `<class1>`.
**Customization point**--The function basename of the form `<type>:<name>` is used to provide the customization point for dynamic values.
This function is typically called as `<type>:"$var"` with `var` contains a value that depends on the context including the currently selected class, user configuration, etc.
**Extension**--The extension `<ext>` can be used to mark the specific types of functions.
### Naming convention for variables
The variable names roughly have the following structure.
```ebnf
local_variable_name = varname
| '__ble_' varname
| '_ble_local_' varname ;
global_variable_name = ('_' namespace)* '_' varname ;
namespace = word ;
varname = (word '_')* word ;
word = /[a-zA-Z0-9]+/ ;
```
The variable names are basically lowercase underscored words such as `foo_bar_baz` (instead of `fooBarBaz` or `FooBarBaz`).
Some global constants and the exported variables have all-uppercase names, but the lowercase should be basically used.
**Local variables**--The local variables that are only used in the declared function should be basically unprefixed by an underscore.
When the function reads/returns a value through an arbitrary name of <i>in</i>/<i>out</i> variables,
the local variable declared within the function should be prefixed by `__ble_` or `_ble_local_` to avoid the name conflicts between the local and in/out variables.
**Global variables**--All the global variables, except for the variables that are intended to be directly modified from the command line, should be prefixed by `_`.
Currently, there are no variables intended to be directly modified from the command line<sup><b>[Note 1]</b></sup>.
The variables defined in external modules should be namespaced by the module name.
The variables in modules in the `contrib` repository should have the prefix `_ble_contrib_<module_name>_`
- <sup><b>[Note 1]</b></sup> In the initial implementation of ble.sh, the ble.sh options are provided as shell variables `bleopt_*`.
As remnant or for backward compatibility, the values of ble.sh options are still stored in the variables of the name `bleopt_*`,
but these variables for the ble.sh options will be renamed in the future.
### Function interface
The success/failure states should be returned by the exit status.
**`ret`**--All the other values should be basically returned through shell variables.
This is because, if a function returned its result through the standard output,
the caller would need to capture the results by a command substitution which involves a fork of a subshell and thus is slow.
Here, efficiency is the primary interest, so there are exceptions where the standard output can be more efficient:
For example, when the output needs to be saved to a file for later use or to be passed to a different process,
or when the output needs to be processed by the external commands that read the standard input.
The variable name to return a value should be basically `ret` unless the purpose of the function is to store values.
When the function basename has the form `get-<name>`, one should consider returning the result through the shell variable named `<name>`.
When a function returning multiple values is called from many places, the function may receive an array name as the first argument and store the result in the specified array.
When the <i>out</i> variable names are specified in the arguments, they should come first in the list of the arguments.
**`opts`**--The argument position should be basically fixed (e.g., never shifted by optional arguments like `-f` or `-o name`).
The optional arguments can be specified by colon-separated fields (like `SHELLOPTS` or `BASHOPTS`)
instead of the Unix-style flags and options like `-f` and `-o optarg`.
This is because the parsing of the Unix-style arguments (with or without `getopts`) is usually slow.
The local variable that receives the colon-separated option typically has the name `opts` or `*opts` (e.g. `local opts=$2`).
A single option has the form `name` or `name=value`,
where `name` should be hyphenated words like `foo-bar-baz`.
The former form without the value is called a flag option.
One can test a flag option by `[[ :$opts: == *:flag-name:* ]]`.
An option of the form `name=value` can be matched by `ble/opts#extract-first-optarg`, `ble/opts#extract-last-optarg`, or `ble/opts#extract-all-optargs`.
The function `ble/opts#has` can be used to test if either `name` or `name=value` is contained
**`$*`**--The functions that receive a string should not allow a variable number of arguments that are joined inside the function.
A variable number of arguments should be used only when each argument is really processed separately.
If necessary, the joining should be performed at the caller side.
This is because `IFS` in the caller context affects the joining `$*`, so the handling of `IFS` should be properly done at the caller side.
Another reason is for the future extension, e.g., to add another optional argument.
### Variable declarations
Local variables should be declared by `local` (instead of `declare` or `typeset`).
Variables should be always initialized before referencing their values. This is because uninitialized variables can have random values when `shopt -s localvar_inherit` is turned on.
The declaration and the initialization of an array variable should be separated as far as the initializer values contain any expansions or spaces.
There is a bug in Bash 3.0 that the values are separated by whitespaces even when the values are quoted.
When the declaration and the initialization of an array variable are made in a single command, the option `-a` needs to be specified.
Without `-a`, the entire list `(...)` including the surrounding parens will be treated as a scalar string in Bash 3.0.
```bash
local v; v=("$var")
local -a v=()
# The following may not work as expected
# local v=() # Bash 3.0 will initialize a scalar with the value v='()' #D0184
# local -a v=("a b") # Bash 3.0 will split it into the two elements "a" and "b" #D0525
```
Defining local readonly variables should be avoided
because they will hide global variables from the utilities `ble/variable#is-global`, `ble/util/print-global-definitions`, etc.
Global readonly variables should never be defined.
The background is described in the Limitation section of README.
### Conditional commands `[[ ... ]]`
Do not use `[ ... ]` or `test` unless it is intended to be a POSIX script. Always use `[[ ... ]]`.
Use `[[ word ]]` and `[[ ! word ]]` instead of `[[ -n word ]]` and `[[ -z word ]]`.
Use `==` instead of the operator `=` inside conditional commands.
**Quoting**--The right-hand sides of `==` and `!=` inside conditional commands `[[ ... ]]` should be properly quoted.
The right-hand sides of `=~` inside the conditional commands `[[ ... ]]` should be specified by a single parameter expansion
as `[[ word =~ $rex ]]`, or one can use `ble/string#match 'word' "$rex"` instead.
In other contexts, the arguments of the conditional command should not be quoted
unless raw special characters ``[[:space:]|&;<>\`"$]`` are contained in the argument.
**No `-eq` etc**--Basically use arithmetic commands `((expr))`
instead of the operators `-eq`, `-ne`, `-gt`, `-ge`, `-lt`, and `-le` of the conditional command.
They might be used when the test is used as a part of complicated conditions in the conditional commands.
For example, `[[ -f $file || $word && word -eq 0 ]]` can be used instead of `[[ -f $file ]] || { [[ $word ]] && ((word == 0)); }` because sticking with the arithmetic command would unnecessarily complicate the code in this case.
### Other styles
**Function definition**--There are two forms of function definitions, the POSIX style `func()` and the ksh style `function func`, as well as the mixed form.
We always use the ksh style of the function definition.
The reasons are that it is easier to search by the keyword `function ` and that it is protected by the aliases of the same name as the function (unless the user defines an alias with the same name as the keyword `function`).
The function body is always the compound command `{ ... }` (instead of any other compound commands such as `( ... )`, `for`, `if`, etc.), and the opening brace `{` is placed on the first line.
```bash
function function-name {
: do_something
}
```
**`ble/util/print`**--Use `ble/util/print` instead of `echo`. Use `ble/util/put` instead of `echo -n`.
The control characters should be directly specified by the escape string literal `$'...'` instead of relying on the conversion by `echo -e`.
This is because the builtin `echo` can change its behavior depending on the shell option `shopt -s xpg_echo`, where the backslashes in the arguments might have special meaning.
`ble.sh` is intended to work under any possible user options.
**Quote/unquote**--The arguments where the word splitting and pathname expansions can take place need to be always quoted.
When the subject word of the `case` statement and the right-hand sides of variable assignments
(including those specified to assignment builtins `declare`, `typeset`, `local`, `export`, and `readonly`)
contains `$*`, `${arr[*]}`, or raw special characters ``[[:space:]|&;<>\`"$]``, they need to be quoted.
In other contexts, the subject word of `case` and the right-hand sides of variable assignments should not be quoted.
In these contexts, the word splitting and pathname expansions will never happen, so the quoting is unnecessary.
Array subscripts are arithmetic context, so the arithmetic expansions `$((expr))` inside the array subscripts are redundant. One should simply write it as `arr[expr]` instead of `arr[$((expr))]`.
**`ble/util/assign`**--Use `ble/util/assign`, `ble/util/assign-array`, and `ble/util/assign-words` instead of `var=$(cmd)`, `mapfile -t var <<< "$(cmd)"`, and `var=($(cmd))`, respectively.
The command substitutions `$(...)` are slow because they involve the forks of subshells.
**External commands**--Implement it using built-in Bash features as much as possible.
If the same result can be obtained by built-in Bash features efficiently, do not use the external commands.
When the external command is used, first freeze the path by calling `ble/bin#freeze-utility-path '<cmd>'` and call the commands through `ble/bin/<cmd>`.
Or save the path of the external command in a variable, and call the command through the variable as `"$_saved_cmd" args...`.
The POSIX commands should be basically used. If non-POSIX commands are used, a POSIX fallback should be always prepared.
Please combine multiple calls of external commands into a single call of the command as much as possible.
Usually, a single call of `sed` and `awk` is preferred over the chain of simple commands.