.bimrc support added. Unlick traditional vim, it's not settings, but quick commands you can use in documents. README.md has details.
This commit is contained in:
@@ -43,11 +43,34 @@ mode and supports:
|
|||||||
`4x`, `5w`, `5dw`, `3rX`, and `2s`
|
`4x`, `5w`, `5dw`, `3rX`, and `2s`
|
||||||
- `Esc` return to command mode
|
- `Esc` return to command mode
|
||||||
- `Ctrl+G` show file, modified state, line, and column info
|
- `Ctrl+G` show file, modified state, line, and column info
|
||||||
|
- `,name` then `Enter` run a command defined in `~/.bimrc` and insert its output
|
||||||
- `:w`, `:w path`, `:q`, `:q!`, `:wq`, and `:wq path`
|
- `:w`, `:w path`, `:q`, `:q!`, `:wq`, and `:wq path`
|
||||||
|
|
||||||
There is no persistent ruler/status line. The bottom line is used only for
|
There is no persistent ruler/status line. The bottom line is used only for
|
||||||
commands, messages, and explicit `Ctrl+G` file info.
|
commands, messages, and explicit `Ctrl+G` file info.
|
||||||
|
|
||||||
|
## `~/.bimrc` commands
|
||||||
|
|
||||||
|
`bim` can load simple command shortcuts from `~/.bimrc`. Each non-empty,
|
||||||
|
non-comment line is a command name, an equals sign, and the shell command to run:
|
||||||
|
|
||||||
|
```text
|
||||||
|
date=date +'%A, %B %d, %Y'
|
||||||
|
uuid=uuidgen
|
||||||
|
```
|
||||||
|
|
||||||
|
In command mode, type `,date` then `Enter` to run the configured `date` command
|
||||||
|
and insert its standard output at the cursor. `Esc` cancels a pending command,
|
||||||
|
and Backspace edits it before it runs. `bim` appends one space after inserted
|
||||||
|
command output so short snippets are ready for continued typing.
|
||||||
|
|
||||||
|
Commands are not run when `bim` starts. They only run when invoked explicitly
|
||||||
|
with `,name`. `bim` does not source `~/.bimrc`; it parses command definitions as
|
||||||
|
data. For safety, `~/.bimrc` must be a regular file owned by the current user,
|
||||||
|
must not be a symlink, and must not be writable by group or other users.
|
||||||
|
Command names may contain letters, digits, underscores, and hyphens, and must
|
||||||
|
start with a letter or underscore.
|
||||||
|
|
||||||
Run it with:
|
Run it with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ renderedMessage=$'\001'
|
|||||||
pendingInput=""
|
pendingInput=""
|
||||||
pendingCommand=""
|
pendingCommand=""
|
||||||
commandCount=""
|
commandCount=""
|
||||||
|
bimCommandActive=0
|
||||||
|
bimCommandName=""
|
||||||
lastChangeKeys=""
|
lastChangeKeys=""
|
||||||
insertChangeKeys=""
|
insertChangeKeys=""
|
||||||
replayKeysRemaining=0
|
replayKeysRemaining=0
|
||||||
@@ -33,6 +35,58 @@ undoCursorCol=0
|
|||||||
undoTopRow=0
|
undoTopRow=0
|
||||||
undoDirty=0
|
undoDirty=0
|
||||||
insertUndoSaved=0
|
insertUndoSaved=0
|
||||||
|
startupMessage=""
|
||||||
|
declare -A bimCommands=()
|
||||||
|
|
||||||
|
trim_whitespace() {
|
||||||
|
local value="$1"
|
||||||
|
value="${value#"${value%%[![:space:]]*}"}"
|
||||||
|
value="${value%"${value##*[![:space:]]}"}"
|
||||||
|
printf '%s' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
load_bimrc() {
|
||||||
|
local bimrcPath="${HOME:-}/.bimrc"
|
||||||
|
local permissions line trimmed name commandText invalidCount=0
|
||||||
|
|
||||||
|
[[ -n "${HOME:-}" && -e "$bimrcPath" ]] || return
|
||||||
|
|
||||||
|
if [[ -L "$bimrcPath" || ! -f "$bimrcPath" || ! -O "$bimrcPath" ]]; then
|
||||||
|
startupMessage="Ignoring unsafe ~/.bimrc"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
permissions="$(stat -c %A "$bimrcPath" 2>/dev/null)" || {
|
||||||
|
startupMessage="Could not inspect ~/.bimrc"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if [[ "${permissions:5:1}" == "w" || "${permissions:8:1}" == "w" ]]; then
|
||||||
|
startupMessage="Ignoring group/world-writable ~/.bimrc"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
trimmed="$(trim_whitespace "$line")"
|
||||||
|
[[ -z "$trimmed" || "${trimmed:0:1}" == "#" ]] && continue
|
||||||
|
if [[ "$line" != *"="* ]]; then
|
||||||
|
((invalidCount++))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
name="$(trim_whitespace "${line%%=*}")"
|
||||||
|
commandText="$(trim_whitespace "${line#*=}")"
|
||||||
|
if [[ "$name" =~ ^[A-Za-z_][A-Za-z0-9_-]*$ && -n "$commandText" ]]; then
|
||||||
|
bimCommands["$name"]="$commandText"
|
||||||
|
else
|
||||||
|
((invalidCount++))
|
||||||
|
fi
|
||||||
|
done < "$bimrcPath"
|
||||||
|
|
||||||
|
if ((invalidCount > 0)); then
|
||||||
|
startupMessage="Ignored $invalidCount invalid ~/.bimrc line(s)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
load_bimrc
|
||||||
|
|
||||||
if [[ -n "$filePath" && -f "$filePath" ]]; then
|
if [[ -n "$filePath" && -f "$filePath" ]]; then
|
||||||
mapfile -t lines < "$filePath"
|
mapfile -t lines < "$filePath"
|
||||||
@@ -58,6 +112,7 @@ trap cleanup EXIT
|
|||||||
trap 'exit 130' INT TERM
|
trap 'exit 130' INT TERM
|
||||||
stty raw -echo
|
stty raw -echo
|
||||||
printf '\033[?1049h\033[?25l'
|
printf '\033[?1049h\033[?25l'
|
||||||
|
statusMessage="$startupMessage"
|
||||||
|
|
||||||
screen_size() {
|
screen_size() {
|
||||||
screenRows="$(tput lines 2>/dev/null || printf '24')"
|
screenRows="$(tput lines 2>/dev/null || printf '24')"
|
||||||
@@ -97,7 +152,9 @@ file_info() {
|
|||||||
|
|
||||||
draw_message() {
|
draw_message() {
|
||||||
local message="$statusMessage"
|
local message="$statusMessage"
|
||||||
if [[ -n "$pendingCommand" || -n "$commandCount" ]]; then
|
if ((bimCommandActive)); then
|
||||||
|
message=",$bimCommandName"
|
||||||
|
elif [[ -n "$pendingCommand" || -n "$commandCount" ]]; then
|
||||||
message="${commandCount}${pendingCommand}"
|
message="${commandCount}${pendingCommand}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -453,6 +510,82 @@ repeat_last_change() {
|
|||||||
statusMessage=""
|
statusMessage=""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bim_command_exists() {
|
||||||
|
local name="$1"
|
||||||
|
[[ -n "${bimCommands[$name]+set}" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
bim_command_has_prefix() {
|
||||||
|
local prefix="$1"
|
||||||
|
local name
|
||||||
|
for name in "${!bimCommands[@]}"; do
|
||||||
|
[[ "$name" == "$prefix"* ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run_bim_command() {
|
||||||
|
local name="$1"
|
||||||
|
local commandText="${bimCommands[$name]}"
|
||||||
|
local output exitStatus
|
||||||
|
|
||||||
|
output="$(bash -c "$commandText" </dev/null 2>/dev/null)"
|
||||||
|
exitStatus=$?
|
||||||
|
if ((exitStatus != 0)); then
|
||||||
|
statusMessage=",$name failed with status $exitStatus"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if [[ -z "$output" ]]; then
|
||||||
|
statusMessage=",$name produced no output"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
insert_text_at_cursor "${output} "
|
||||||
|
statusMessage="Inserted ,$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_bim_command_key() {
|
||||||
|
local key="$1"
|
||||||
|
|
||||||
|
case "$key" in
|
||||||
|
$'\033')
|
||||||
|
bimCommandActive=0
|
||||||
|
bimCommandName=""
|
||||||
|
statusMessage=""
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
$'\r'|$'\n')
|
||||||
|
if bim_command_exists "$bimCommandName"; then
|
||||||
|
run_bim_command "$bimCommandName"
|
||||||
|
else
|
||||||
|
statusMessage="Unknown command: ,$bimCommandName"
|
||||||
|
fi
|
||||||
|
bimCommandActive=0
|
||||||
|
bimCommandName=""
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
$'\177'|$'\b')
|
||||||
|
if ((${#bimCommandName} > 0)); then
|
||||||
|
bimCommandName="${bimCommandName:0:${#bimCommandName}-1}"
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ "$key" =~ ^[A-Za-z0-9_-]$ ]]; then
|
||||||
|
bimCommandName+="$key"
|
||||||
|
if ! bim_command_has_prefix "$bimCommandName"; then
|
||||||
|
statusMessage="Unknown command: ,$bimCommandName"
|
||||||
|
bimCommandActive=0
|
||||||
|
bimCommandName=""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
statusMessage="Invalid command name: ,$bimCommandName"
|
||||||
|
bimCommandActive=0
|
||||||
|
bimCommandName=""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
save_undo() {
|
save_undo() {
|
||||||
undoLines=("${lines[@]}")
|
undoLines=("${lines[@]}")
|
||||||
undoCursorRow="$cursorRow"
|
undoCursorRow="$cursorRow"
|
||||||
@@ -536,6 +669,42 @@ backspace_insert() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insert_text_at_cursor() {
|
||||||
|
local text="$1"
|
||||||
|
local line="${lines[cursorRow]}"
|
||||||
|
local prefix="${line:0:cursorCol}"
|
||||||
|
local suffix="${line:cursorCol}"
|
||||||
|
local rest="$text"
|
||||||
|
local segment
|
||||||
|
local -a parts=()
|
||||||
|
local i lastIndex
|
||||||
|
|
||||||
|
[[ -n "$text" ]] || return 1
|
||||||
|
|
||||||
|
while [[ "$rest" == *$'\n'* ]]; do
|
||||||
|
segment="${rest%%$'\n'*}"
|
||||||
|
parts+=("$segment")
|
||||||
|
rest="${rest#*$'\n'}"
|
||||||
|
done
|
||||||
|
parts+=("$rest")
|
||||||
|
|
||||||
|
save_undo
|
||||||
|
if ((${#parts[@]} == 1)); then
|
||||||
|
lines[cursorRow]="${prefix}${parts[0]}${suffix}"
|
||||||
|
cursorCol=$((cursorCol + ${#parts[0]}))
|
||||||
|
else
|
||||||
|
lastIndex=$((${#parts[@]} - 1))
|
||||||
|
parts[0]="${prefix}${parts[0]}"
|
||||||
|
parts[lastIndex]="${parts[lastIndex]}${suffix}"
|
||||||
|
lines=("${lines[@]:0:cursorRow}" "${parts[@]}" "${lines[@]:cursorRow+1}")
|
||||||
|
cursorRow=$((cursorRow + lastIndex))
|
||||||
|
cursorCol=$((${#parts[lastIndex]} - ${#suffix}))
|
||||||
|
fi
|
||||||
|
dirty=1
|
||||||
|
insertUndoSaved=0
|
||||||
|
clamp_cursor
|
||||||
|
}
|
||||||
|
|
||||||
delete_under_cursor() {
|
delete_under_cursor() {
|
||||||
local line="${lines[cursorRow]}"
|
local line="${lines[cursorRow]}"
|
||||||
if ((cursorCol < ${#line})); then
|
if ((cursorCol < ${#line})); then
|
||||||
@@ -1132,6 +1301,13 @@ prompt_command() {
|
|||||||
handle_command_key() {
|
handle_command_key() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
local count i
|
local count i
|
||||||
|
if ((bimCommandActive)); then
|
||||||
|
handle_bim_command_key "$key"
|
||||||
|
commandCount=""
|
||||||
|
pendingCommand=""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$pendingCommand" ]]; then
|
if [[ -n "$pendingCommand" ]]; then
|
||||||
count="$(command_count)"
|
count="$(command_count)"
|
||||||
if [[ "$pendingCommand" == "r" ]]; then
|
if [[ "$pendingCommand" == "r" ]]; then
|
||||||
@@ -1382,6 +1558,16 @@ handle_command_key() {
|
|||||||
commandCount=""
|
commandCount=""
|
||||||
prompt_command
|
prompt_command
|
||||||
;;
|
;;
|
||||||
|
,)
|
||||||
|
commandCount=""
|
||||||
|
if ((${#bimCommands[@]} == 0)); then
|
||||||
|
statusMessage="No ~/.bimrc commands"
|
||||||
|
else
|
||||||
|
bimCommandActive=1
|
||||||
|
bimCommandName=""
|
||||||
|
statusMessage=""
|
||||||
|
fi
|
||||||
|
;;
|
||||||
$'\007')
|
$'\007')
|
||||||
commandCount=""
|
commandCount=""
|
||||||
file_info
|
file_info
|
||||||
|
|||||||
Reference in New Issue
Block a user