20 KiB
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]
- Questions on usage [Here]
- Feature request [Here]
- Others (suggestions, projects, discussion, complaints, news, information or anything) [Here]
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
repository. - Others
Wiki
You can freely edit wiki pages.
- 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 asAUR
). -
The function named
ble/base/package:TYPE/update
(whereTYPE
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 be0
... when the update succeeded6
... 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 ofble.sh
.- Other ... when the update failed
An example lib/_package.sh
might be
_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.
Summary of codebase
The core script file ble.sh
is generated by combining the following files:
ble.pp
... Basic initialiationsrc/def.sh
... Prototype definitionssrc/util.sh
... Basic utility functionssrc/decode.sh
... User-input decoder and keybindingssrc/color.sh
... Terminal graphic attributessrc/canvas.sh
... Terminal layout enginesrc/canvas.emoji.sh
... Emoji database
src/history.sh
... Command history managementsrc/edit.sh
... Line editorsrc/benchmark.sh
... Measure processing timelib/core-completion.sh
... Prototype definition for completionslib/core-syntax.sh
... Prototype definitions for syntax analyzer
Useful features are implemented in separate modules:
lib/keymap.vi.sh
... Vim modelib/vim-arpeggio.sh
...vim-arpeggio
-like pluginlib/vim-surround.sh
...vim-surround
-like plugin
lib/keymap.emacs.sh
... Emacs modelib/core-syntax.sh
... Shell parser and syntax highlightinglib/core-syntax-ctx.def
... Definition of parser states
lib/core-complete.sh
... Completions includingmenu-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 attacherlib/init-msys1.sh
... Workaround for MSYS1 with broken pipeslib/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 byble-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 forvim-airline
.contrib/integration/*.bash
are codes needed for the adjustments with other framework.contrib/*.bash
are other miscellaneous configurations. Usually one needs additional configuration afterble-import
. These files may be moved to another directory in the future.
Tests
Tests can be run by one of the following commands:
$ 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 testslib/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.
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.
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 in/out 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[Note 1].
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>_
- [Note 1] 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 namebleopt_*
, 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 out 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.
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.
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.