.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`
|
||||
- `Esc` return to command mode
|
||||
- `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`
|
||||
|
||||
There is no persistent ruler/status line. The bottom line is used only for
|
||||
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:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -15,6 +15,8 @@ renderedMessage=$'\001'
|
||||
pendingInput=""
|
||||
pendingCommand=""
|
||||
commandCount=""
|
||||
bimCommandActive=0
|
||||
bimCommandName=""
|
||||
lastChangeKeys=""
|
||||
insertChangeKeys=""
|
||||
replayKeysRemaining=0
|
||||
@@ -33,6 +35,58 @@ undoCursorCol=0
|
||||
undoTopRow=0
|
||||
undoDirty=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
|
||||
mapfile -t lines < "$filePath"
|
||||
@@ -58,6 +112,7 @@ trap cleanup EXIT
|
||||
trap 'exit 130' INT TERM
|
||||
stty raw -echo
|
||||
printf '\033[?1049h\033[?25l'
|
||||
statusMessage="$startupMessage"
|
||||
|
||||
screen_size() {
|
||||
screenRows="$(tput lines 2>/dev/null || printf '24')"
|
||||
@@ -97,7 +152,9 @@ file_info() {
|
||||
|
||||
draw_message() {
|
||||
local message="$statusMessage"
|
||||
if [[ -n "$pendingCommand" || -n "$commandCount" ]]; then
|
||||
if ((bimCommandActive)); then
|
||||
message=",$bimCommandName"
|
||||
elif [[ -n "$pendingCommand" || -n "$commandCount" ]]; then
|
||||
message="${commandCount}${pendingCommand}"
|
||||
fi
|
||||
|
||||
@@ -453,6 +510,82 @@ repeat_last_change() {
|
||||
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() {
|
||||
undoLines=("${lines[@]}")
|
||||
undoCursorRow="$cursorRow"
|
||||
@@ -536,6 +669,42 @@ backspace_insert() {
|
||||
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() {
|
||||
local line="${lines[cursorRow]}"
|
||||
if ((cursorCol < ${#line})); then
|
||||
@@ -1132,6 +1301,13 @@ prompt_command() {
|
||||
handle_command_key() {
|
||||
local key="$1"
|
||||
local count i
|
||||
if ((bimCommandActive)); then
|
||||
handle_bim_command_key "$key"
|
||||
commandCount=""
|
||||
pendingCommand=""
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "$pendingCommand" ]]; then
|
||||
count="$(command_count)"
|
||||
if [[ "$pendingCommand" == "r" ]]; then
|
||||
@@ -1382,6 +1558,16 @@ handle_command_key() {
|
||||
commandCount=""
|
||||
prompt_command
|
||||
;;
|
||||
,)
|
||||
commandCount=""
|
||||
if ((${#bimCommands[@]} == 0)); then
|
||||
statusMessage="No ~/.bimrc commands"
|
||||
else
|
||||
bimCommandActive=1
|
||||
bimCommandName=""
|
||||
statusMessage=""
|
||||
fi
|
||||
;;
|
||||
$'\007')
|
||||
commandCount=""
|
||||
file_info
|
||||
|
||||
Reference in New Issue
Block a user