Line Editing

fortsh provides comprehensive line editing with both Emacs and Vi modes.

Source: src/io/readline.f90:42-62, 2042-2320

Editing Modes

Emacs Mode (Default)

Most users are familiar with Emacs-style keybindings:

set -o emacs   # Enable (default)

Vi Mode

For vi/vim users:

set -o vi      # Enable vi mode

Vi mode has two states:

  • Insert mode - Normal text input
  • Command mode - Navigation and editing (press ESC)

Emacs Keybindings

Cursor Movement

KeyAction
Ctrl+ABeginning of line
Ctrl+EEnd of line
Ctrl+BBack one character
Ctrl+FForward one character
Alt+BBack one word
Alt+FForward one word

Editing

KeyAction
Ctrl+DDelete character at cursor (or EOF if empty)
BackspaceDelete character before cursor
Ctrl+KKill (cut) to end of line
Ctrl+UKill entire line
Ctrl+WKill previous word
Alt+DKill next word
Ctrl+YYank (paste) killed text
Ctrl+TTranspose characters
Alt+TTranspose words

History

KeyAction
Up / Ctrl+PPrevious history entry
Down / Ctrl+NNext history entry
Ctrl+RReverse incremental search
Ctrl+SForward incremental search
Alt+<First history entry
Alt+>Last history entry

Control

KeyAction
Ctrl+CCancel current input
Ctrl+GAbort (cancel search/operation)
Ctrl+LClear screen
Ctrl+ZSuspend shell

fortsh-Specific

KeyAction
Ctrl+HFZF history browser
Ctrl+FFZF file browser
Ctrl+XProcess kill mode

Vi Mode

Switching Modes

  • ESC - Enter command mode
  • i, a, I, A, etc. - Enter insert mode

Insert Mode Commands

KeyAction
iInsert before cursor
aInsert after cursor
IInsert at beginning of line
AInsert at end of line
oOpen line below
OOpen line above

Command Mode Navigation

KeyAction
hMove left
lMove right
0Beginning of line
$End of line
wNext word
bPrevious word
eEnd of word
jHistory down
kHistory up

Command Mode Editing

KeyAction
xDelete character at cursor
XDelete character before cursor
dwDelete word
ddDelete entire line
d$Delete to end of line
d0Delete to beginning
cwChange word
ccChange entire line
rReplace character
RReplace mode

Vi Repeat Counts

Commands can be prefixed with a count:

3w     # Move 3 words forward
5x     # Delete 5 characters
2dd    # Delete 2 lines

Vi Marks

Set and jump to positions:

  • ma - Set mark 'a' at current position
  • `a - Jump to mark 'a'
  • 'a - Jump to beginning of line with mark 'a'

Vi Search

  • /pattern - Search forward
  • ?pattern - Search backward
  • n - Next match
  • N - Previous match

Incremental Search

Reverse Search (Ctrl+R)

Search backward through history:

(reverse-i-search)`git': git commit -m "fix bug"
  • Type characters to narrow search
  • Ctrl+R again: next match
  • Ctrl+S: switch to forward search
  • Enter: accept result
  • Ctrl+G: cancel

Forward Search (Ctrl+S)

Search forward through history:

(i-search)`make': make clean

Note: Ctrl+S may be intercepted by terminal flow control. Disable with:

stty -ixon

Kill Ring

Text cut with Ctrl+K, Ctrl+U, Ctrl+W, etc. is saved to the kill ring.

  • Ctrl+Y - Paste most recent kill
  • Alt+Y - Cycle through kill ring (after Ctrl+Y)

Word Movement

Word boundaries depend on character class:

  • Alphanumeric characters and underscores form words
  • Punctuation and whitespace separate words
# Cursor positions marked with |
path=/usr/local/bin
     |   |     |    # Word boundaries

Implementation Details

State Management

Line editing state is managed by input_state_t (readline.f90:112-199):

type :: input_state_t
  character(len=MAX_LINE_LEN) :: buffer
  integer :: cursor_pos           ! 0-based
  integer :: editing_mode         ! EMACS or VI
  integer :: vi_mode              ! INSERT or COMMAND
  character(len=64) :: vi_command_buffer
  integer :: vi_marks(26)         ! Marks a-z
  logical :: dirty                ! Needs redraw
end type

Mode Constants

integer, parameter :: EDITING_MODE_EMACS = 1
integer, parameter :: EDITING_MODE_VI = 2
integer, parameter :: VI_MODE_INSERT = 1
integer, parameter :: VI_MODE_COMMAND = 2

Source: readline.f90:93-97

Configuration

inputrc

Create ~/.inputrc for readline configuration:

# Vi mode by default
set editing-mode vi

# Show mode in prompt
set show-mode-in-prompt on

# Case-insensitive completion
set completion-ignore-case on

Shell Options

# Switch to vi mode
set -o vi

# Switch back to emacs mode
set -o emacs