348 lines
20 KiB
Markdown
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.
|