Skip to content

Devel §1 Design widgets

Koichi Murase edited this page Jul 12, 2026 · 1 revision

[ 日本語 | English ] ≫ Manual [§1 Design widgets | §2 Completion

Note

The information described here may change in the future.

1. Basics of widgets

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.

1.1 Widget definition and naming convetion

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.

1.2 Exit status of widgets

The exit status of widgets is used to determine an associated action of the line editor.

  • Status 0 implies that the requested operation is accomplished successfully.
  • Status 27 implies 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 148 implies 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 125 implies 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 1 implies that the operation cannot be accomplished for other reasons.

1.3 Variables available inside widgets

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

1.4 Cursor movement

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.

1.5 Changing the command-line contents

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]]

1.6 Receiving inputs from the user

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)
}

2. Widgets in vi/vim-mode

There are further complicated requiremts for widgets used in vi/vim-mode (except for vi_cmap).

2.1 ARG, FLAG, REG

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.

2.2 Mainitaining marks `[`] (the previous edit range)

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-area

Within 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.

2.3 Maintaining repetition by the . command

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:

  1. For a command for edit, when ble/keymap:vi/mark/set-previous-edit-area or ble/keymap:vi/mark/end-edit-area is called, it usually implies that one unit of the edit processing is completed. Thus, the call of ble/keymap:vi/repeat/record can usually be accompanied to those function calls.

  2. For a command that enters the insert mode, ble/keymap:vi/repeat/record or ble/keymap:vi/repeat/clear-insert can be called after ble/widget/vi_nmap/.insert-mode is called. The function ble/keymap:vi/repeat/record is used to record and repeat the command that lead to the insert mode. It should be noted that the function ble/keymap:vi/repeat/record needs to be called after ble/keymap:vi/vi_nmap/.insert-mode is called because the function references the current keymap. The function ble/keymap:vi/repeat/clear-insert can 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 in vi_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.

2.4 Adjustments after a command in vi_nmap

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-o in the insert mode), when a command is executed, the single-command normal mode is cleared.

2.5 Defining motion

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 opts
  • ble/widget/vi-command/inclusive-goto.impl index flag reg opts
  • ble/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.

2.5.1 Jump command

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
}

2.6 (Internal details)

  • ble/keymap:vi/repeat/record should not be called inside __before_widget__. ble/keymap:vi/repeat/record tries to identify the command that lead to the present change using the variable WIDGET. However, when the widget is called by __before_widget__, it implies that the widget is not called through WIDGET, and ble/keymap:vi/repeat/record fails to correctly identify WIDGET.

  • WIDGET should not be modified inside the widget because it would break the information for the repetition by .. However, it is valid to change WIDGET inside __before_widget__ to rewrite the widget to call.

3. Operators in vi/vim-mode

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, a and b represents the target range. When context=line, a and b are 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.
  • context contains the type of the context where the operator is called. This contains one of the strings, char, line, and block.
  • count is the number argument for the operator. The argument may be specified to an operator in vi_xmap.
  • reg contains 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.

3.1 Operator and mark `[`]

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.

3.2 Operator and repetition .

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

Clone this wiki locally