-
-
Notifications
You must be signed in to change notification settings - Fork 142
Devel §1 Design widgets
[ 日本語 | English ] ≫ Manual [§1 Design widgets | §2 Completion
Note
The information described here may change in the future.
Widget is a shell function that can be bound to a key seuqnece using
ble-bind -f and defines a behavior of the line editor in response to the key
sequence. It is a ble.sh-native version of a widget in Zsh's ZLE, or a
bindable function in Readline. ble.sh also supports binding Readline
bindable functions by bind, but the native widget allow designing the
line-editor behavior more flexibly. Here, we describe how to define a native
widget. The way to define a user-defined widget, which can be bound by
ble-bind -x, is not described here.
A widget named MyWidget can be defined as a shell function with the name
ble/widget/MyWidget, so that it can be bound to a key sequence by ble-bind -f kspecs MyWidget. To avoid conflicts, the name of the widget is preferred
to be in the form of namespace/name, where namespace is recommended to be
the package name providing the widget or the name of the author. The widget
name MyWidget cannot contain a period, as such a name is reserved for
hidden/private widgets or helper functions for widgets.
The exit status of widgets is used to determine an associated action of the line editor.
- Status
0implies that the requested operation is accomplished successfully. - Status
27implies that the operation was canceled by the user. For example, this happens with a widget receiving additional inputs from the user, where the user inputs a specific key to cancel the processign. - Status
148implies that the processing is suspended to receive inputs asynchronously. In this case, a certain callback performing the subsequent processing is expected to be registered. - Status
125implies that the widget further attempted to dispatch the event to another widget or registered handler, but the corresponding handler is not found. This is used for the widget to which key__defchar__is bound, and affects the determination of the fallback behavior. - Status
1implies that the operation cannot be accomplished for other reasons.
The following variables are read-only unless otherwise specified; Avoid
modifying those variables, though they are not marked with the -r attribute
so that ble.sh can manipulate it.
| Variable | Description |
|---|---|
WIDGET |
The shell function name of the widget triggered by the key input |
KEYS (array) |
The key codes that triggered the call of WIDGET
|
KEYMAP |
The keymap when WIDGET is called |
_ble_decode_keymap |
The current keymap. It should be noted that the keymap may change during the processing. |
_ble_edit_str |
The command-line contents |
_ble_edit_ind |
The cursor position |
_ble_edit_mark |
The mark position, i.e., the previous point or the other end of the current selection |
_ble_edit_mark_active |
The state of the selection |
_ble_edit_overwrite_mode |
Whether the overwriting mode is enabled |
The current position of the cursor _ble_edit_ind and the mark position
_ble_edit_mark can be directly modified. The values of those two variables
must be greater or equal to 0 and less than or equal to
${#_ble_edit_str[*]}.
Note
In the past, _ble_edit_ind were required to be modified through the shell
function ble/widget/.goto-char index, but this function was deleted and the
cursor position is now assigned to _ble_edit_ind directly.
The variable _ble_edit_str should not be directly modified because ble.sh
needs to track the dirty range (i.e., roughly the range in the command-line
string that was changed after the previous rendering to the terminal display).
The command-line string must be changed through the following function: In
addition, if necessary, _ble_edit_mark and _ble_edit_ind also need to be
updated to be in the valid range.
ble-edit/content/replace beg end [ins [reason]]ble-edit/content/reset [ins [reason]]ble-edit/content/reset-and-check-dirty [ins [reason]]
The widget must not read user inputs directly from the standard input because
ble.sh might have already read bytes and keys from the standard input and
might keep unprocessed bytes, characters, and keys. To read user inputs, the
widget must receive inputs from ble.sh asynchronously with the following
setup.
function ble/widget/MyWidget {
... # processing 1
_ble_decode_char__hook=ble/widget/MyWidget.hook1 # To receive a "character"
return 148
}
function ble/widget/MyWidget.hook1 {
local char=$1 # the code of the received character
... # processing 2 (subsequent processing)
_ble_decode_key__hook=ble/widget/MyWidget.hook2 # To receive a "key"
return 148
}
function ble/widget/MyWidget.hook2 {
local key=$1 # the code of the received key
... # processing 3 (subsequent processing)
}There are further complicated requiremts for widgets used in vi/vim-mode
(except for vi_cmap).
To reference arguments (i.e., the integers that can be input before each
command), registers (which can be specified by "x), and operators (which
is set before entering vi_omap), one can call the shell function
ble/keymap:vi/get-arg. This is needed, for example, in implementing motion
(i.e., the widgets to move the cursor in the normal mode), and edit (i.e.,
the widgets to apply changes to the string).
local ARG FLAG REG; ble/keymap:vi/get-arg '<default-value-for-ARG>'The variable ARG is set to be the argument by the function
ble/keymap:vi/get-arg. When the argument is not set, ARG is set to
'<default-value-for-ARG>', the first argument specified to
ble/keymap:vi/get-arg. The variable FLAG is set to be the operator name
(which corresponds to MyOperator described later). The variable REG is set
to be the index of the register (i.e., the character code of the character
associated with the register). When ble/keymap:vi/get-arg is called, the
information of the argument, the register and the operator is cleared, so that
they are not used again by the subsequent calls of the widgets.
Widgets that causes changes in the string should update the previous edit range
by calling ble/keymap:vi/mark/set-previous-edit-area beg end, where beg and
end are the starting and ending positions of the edit range. It is noted
that end indicates the ending boundary of the last character, but not the
index of the last character in the range.
If the identification of the edit range is non-trivial because of complicated
modifications, instead of manually specifying the edit range by
ble/keymap:vi/mark/set-previous-edit-area, the code section performing the
change can be enclosed within the calls of ble/keymap:vi/mark/start-edit-area
and ble/keymap:vi/mark/end-edit-area. As long as the string is properly
modified through dedicated functions, ble.sh tracks the changes on
_ble_edit_str and automatically determines the edit range. If the enclosed
code calls other widgets inside it, the local variable
_ble_keymap_vi_mark_suppress_edit should be set to a non-empty value, to
avoid the interference in calculating the edit range.
ble/keymap:vi/mark/start-edit-area
local _ble_keymap_vi_mark_suppress_edit=1
... # modifications to the command-line string
unset -v _ble_keymap_vi_mark_suppress_edit
ble/keymap:vi/mark/end-edit-areaWithin the code enclosed within ble/keymap:vi/mark/{start,end}-edit-area, to
manually add extra regions (which is not associated with the actual changes to
the string) to the edit range, ble/keymap:vi/mark/commit-edit-area beg end
can be called.
There are two types of commands for the target of the repetition. One is
edit, which is the command applying changes to the string, and the other is
the command to enter the insert mode. When such a command succeeds, a
repetition should be registered. This is basically accomplished by calling
ble/keymap:vi/repeat/record. To find the appropriate position of the call of
ble/keymap:vi/repeat/record, one should keep in mind the following points:
-
For a command for edit, when
ble/keymap:vi/mark/set-previous-edit-areaorble/keymap:vi/mark/end-edit-areais called, it usually implies that one unit of the edit processing is completed. Thus, the call ofble/keymap:vi/repeat/recordcan usually be accompanied to those function calls. -
For a command that enters the insert mode,
ble/keymap:vi/repeat/recordorble/keymap:vi/repeat/clear-insertcan be called afterble/widget/vi_nmap/.insert-modeis called. The functionble/keymap:vi/repeat/recordis used to record and repeat the command that lead to the insert mode. It should be noted that the functionble/keymap:vi/repeat/recordneeds to be called afterble/keymap:vi/vi_nmap/.insert-modeis called because the function references the current keymap. The functionble/keymap:vi/repeat/clear-insertcan be used to clear the insertion operations in the midle of the insert mode. For example, this function is used by<C-o>, motion, and unix-command execution invi_imap.
As an exception, those functions ble/keymap:vi/repeat/* should not be called
inside an operator because ble/keymap:vi/repeat/record is supposed to be
already called by the upper layer automatically. For example, operator:c
calls ble/keymap:vi/vi_nmap/.insert-mode but does not call
ble/keymap:vi/repeat/record itself. However, when the present processing is
the continuation of an operator asynchronously reading the user input, the
continuation processing needs to call the repetition recording function as well
as ble/keymap:vi/mark/set-previous-edit.
Calling ble/keymap:vi/repeat/record once is enough even for the command that
performs changes to the string and then enters the insert mode. Even in that
case, ble/keymap:vi/repeat/record needs to be called after calling
ble/keymap:vi/vi_nmap/.insert-mode for the aforementioned reason related to
the keymap.
The values of KEYMAP, KEYS, WIDGET, ARG, FLAG, REG are recorded,
and they are restored when the command is called for repetition. If the target
command of the repetition changes the behavior depending on other states, and
if the command wants to reproduce the behavior, those additional states need to
explicitly recorded and reproduced. This can be achieved by modifying the
array _ble_keymap_vi_repeat (or, if the command enters the insert mode, the
array _ble_keymap_vi_irepeat_repeat) after calling
ble/keymap:vi/repeat/record. For details about the array modifications,
please check the actual implementations in keymap.vi.sh.
When defining motion and a widget performed in vi_nmap or vi_omap, one
needs to ensure ble/keymap:vi/adjust-command-mode be called finally. If one
implements the widget by simply calling other widgets,
ble/widget/vi-command/* and ble/widget/vi_nmap/*, one does not need to
explicitly call ble/keymap:vi/adjust-command-mode because those existing
widgets internally calls it. However, if one does not rely on exiting widgets,
one needs to explicitly call ble/keymap:vi/adjust-command-mode at the end of
the processing of the widget.
ble/keymap:vi/adjust-command-mode performs the following adjustments.
- In the rectangle visual mode, when selection until the end of lines is
enabled by pressing
$, motion clears the selection until the end of the lines and turns it back to the normal rectangle selection. - In the operator pending mode, the operator pending mode is cleared after execution of motion or an operator.
- When a match by
/or?is selected and highlighted, if another command (including motion and any other commands) is performed, the hilighting will be cleared. - In the single-command normal mode (which can be entered by
C-oin the insert mode), when a command is executed, the single-command normal mode is cleared.
Depending on the nature of the motion (exclusive, inclusive, or
linewise), one can call the following function. The edit range `[`]
and the repetition . are automatically set up in those functions, so one does
not need to handle them explicitly. However, it should be noted that in the
continuation after asynchronous reading, one needs to explicitly handle them.
ble/widget/vi-command/exclusive-goto.impl index flag reg optsble/widget/vi-command/inclusive-goto.impl index flag reg optsble/widget/vi-command/linewise-goto.impl index flag reg opts
In the above call, index is the destination cursor point. The operator name
shall be passed to flag in the operator pending mode. reg is the index of
the register, if it is set. See the code document of the function in
keymap.vi.sh for the details of opts. The followings are the templates of
custom motion.
function ble/widget/MyExclusiveMotion {
local ARG FLAG REG; ble/keymap:vi/get-arg 1
local index='<destination>'
ble/widget/vi-command/exclusive-goto.impl "$index" "$FLAG" "$REG" nobell
}
function ble/widget/MyInclusiveMotion {
local ARG FLAG REG; ble/keymap:vi/get-arg 1
local index='<destination>'
ble/widget/vi-command/inclusive-goto.impl "$index" "$FLAG" "$REG" nobell
}
function ble/widget/MyLinewiseMotion {
local ARG FLAG REG; ble/keymap:vi/get-arg 1
local index='<destination>'
ble/widget/vi-command/linewise-goto.impl "$index" "$FLAG" "$REG"
}Backward compatibility: In ble-0.2, the fourth argument to
ble/widget/vi-command/{ex,in}clusive-goto.impl is an integer, and it
suppresses the bell on error when it is non-zero. In ble-0.3 or higher, the
fourth argument is a colon-separated list of options. When the option nobell
is contained, the bell on error is suppressed.
A jump command must set mark `` (i.e., the position before performing
the jump) by calling ble/keymap:vi/mark/set-jump before moving the current
position. However, when an operator is active (i.e., when $FLAG is not an
empty string), ble/keymap:vi/mark/set-jump should not be called because the
jump does not actually happen. Except for that, a jump command should be
implemented in the same way as motion. The implementation of a jump command
typically has the following form.
function ble/widget/MyJump {
local ARG FLAG REG; ble/keymap:vi/get-arg 1
[[ $FLAG ]] || ble/keymap:vi/mark/set-jump
# ... processing
}-
ble/keymap:vi/repeat/recordshould not be called inside__before_widget__.ble/keymap:vi/repeat/recordtries to identify the command that lead to the present change using the variableWIDGET. However, when the widget is called by__before_widget__, it implies that the widget is not called throughWIDGET, andble/keymap:vi/repeat/recordfails to correctly identifyWIDGET. -
WIDGETshould not be modified inside the widget because it would break the information for the repetition by.. However, it is valid to changeWIDGETinside__before_widget__to rewrite the widget to call.
An operator named MyOperator can be implemented as a shell function named
ble/keymap:vi/operator:MyOperator. The operator name MyOperator consists
of characters valid for function names, but should not contain :, ., and
/. To use the operator, keybindings of vi-command/operator can be set up
with the argument being the operator name as follows:
ble-bind -m vi_nmap -f kspecs 'vi-command/operator MyOperator'
ble-bind -m vi_omap -f kspecs 'vi-command/operator MyOperator'
ble-bind -m vi_xmap -f kspecs 'vi-command/operator MyOperator'The operator function receives the following arguments:
function ble/keymap:vi/operator:MyOperator {
local a=$1 b=$2 context=$3 count=$4 reg=$5
... # processing
}- Here,
aandbrepresents the target range. Whencontext=line,aandbare ensured to be at the beginning and end of lines, respectively. The newline at the end of a line will be included in the range. -
contextcontains the type of the context where the operator is called. This contains one of the strings,char,line, andblock. -
countis the number argument for the operator. The argument may be specified to an operator invi_xmap. -
regcontains the index of the register specified by"x.
The following variables are available from inside the operator function:
| Variable | Description |
|---|---|
beg, end
|
The target range. The same values as the arguments a and b. |
ble_keymap_vi_operator_index |
An empty string is specified. The operator can assign the destination cursor position. The default position is beg. |
ble_keymap_vi_mark_active |
The value of _ble_edit_mark_active before the call of the operator. It should be noted that _ble_edit_mark_active is cleared when the operator is called from vi_xmap. |
ble_keymap_vi_opmode |
The type of the context of the call of the operator. This becomes vi_line, vi_block, vi_char, or an empty string. |
sub_ranges (array) |
This is set for context=block and contains the information of each line consisting of the rectangle. |
sub_x1, sub_x2
|
This is set for context=block and keeps the column numbers of the left and right edges of the rectangle region. |
When the actual target range of the operator is expanded by the operator, it
can be notified to the caller by changing the variables beg and end. The
cursor position is typically set to beg after the call of the operator.
The exit status of the operator function is used by the caller. Exit status 0
implies the operator completed successfully. Status 1 implies that the
operator processing has failed for some reason. Status 148 instructs the
caller to skip the subsequent adjustments. This status is used to skip the
following processing in the middle of asynchronous reading of user inputs. In
asynchronous processing, the following functions need to be manually called.
-
ble/keymap:vi/mark/set-previous-edit-area(for recording the previous edit range`[`]) -
ble/keymap:vi/repeat/record(for recording the repetition information for.) -
ble/keymap:vi/adjust-command-mode(miscellaneous other state transitions)
The other exit statuses implie that the processing of the operator failed for a reason corresponding to the number, which is defined in advance by the author of the operator.
The edit range is automatically detected for the call of the operator, so
ble/keymap:vi/mark/{set-previous,end}-edit-area does not need to be
explicitly called for an operator basically. However, when the operator does
not change the string yet wants to set the edit range,
ble/keymap:vi/commit-edit-area needs to be explicitly called. For example,
ble/keymap:vi/operator:y sets the edit range explicitly.
By default, the operator command is subject to the repetition by .. If one
does not want to register the operator for the target of the repetition, an
empty function named ble/keymap:vi/operator:MyOperator.record can be defined:
function ble/keymap:vi/operator:MyOperator.record { return 0; }[ 日本語 | English ] ≫ Manual [§1 Design widgets | §2 Completion