1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
#!/bin/bash
### --- Auto-completes aliases --- ###
# alias - normal aliases (completed with trailing space)
# balias - blank aliases (completed without space)
# ialias - ignored aliases (not completed)
ialiases=()
ialias() {
# usage: ialias name='value' (same as alias builtin)
alias "$@"
# extract name (everything before first '=' or first space)
local args="$1"
args=${args%%=*}
args=${args%% *}
ialiases+=("$args")
}
baliases=()
balias() {
alias "$@"
local args="$1"
args=${args%%=*}
args=${args%% *}
baliases+=("$args")
}
# ---------- helper: get alias value ----------
# returns alias expansion text for a name, or empty if none
_get_alias_value() {
local name="$1"
# alias output: alias ll='ls -la'
local a
a=$(alias "$name" 2>/dev/null) || return 1
# strip "alias name='...'"
# use parameter expansion / sed safe parsing
# get first occurrence of =', then strip quotes
a=${a#*=}
# remove leading quote if present
a=${a#\'}
a=${a%\'}
printf "%s" "$a"
return 0
}
# ---------- helper: membership check ----------
_in_array() {
local item="$1"
shift
local elem
for elem in "$@"; do
if [[ "$elem" == "$item" ]]; then
return 0
fi
done
return 1
}
# ---------- expand alias at cursor and optionally insert space ----------
# This function is executed via bind -x, so it can read/modify:
# READLINE_LINE - current full line buffer
# READLINE_POINT - current cursor index (0..len)
expand_alias_space() {
# READLINE_LINE and READLINE_POINT are provided by readline when bind -x is used.
# If not present (if invoked directly), fallback to no-op
if [[ -z "${READLINE_LINE+set}" ]]; then
# fallback: just insert a space
printf " "
return 0
fi
local line=${READLINE_LINE}
local point=${READLINE_POINT}
# left substring up to cursor
local left=${line:0:point}
# right substring after cursor
local right=${line:point}
# find the last "word" before the cursor (split on whitespace)
# If left ends with whitespace, current word is empty
local lastword
if [[ "$left" =~ [[:space:]]$ ]]; then
lastword=""
else
# remove everything up to last whitespace
lastword=${left##*['$'\t\n' ']}
fi
# if lastword is empty -> just insert a space
if [[ -z "$lastword" ]]; then
READLINE_LINE="${left} ${right}"
READLINE_POINT=$((point + 1))
return 0
fi
# check if lastword is in ignored aliases -> do not expand, just insert space
if _in_array "$lastword" "${ialiases[@]}"; then
READLINE_LINE="${left} ${right}"
READLINE_POINT=$((point + 1))
return 0
fi
# try to get alias expansion
local expansion
if expansion=$(_get_alias_value "$lastword"); then
# Replace the lastword in left with expansion
# compute left_without_word
local left_without="${left%${lastword}}"
# If balias: expansion but DO NOT add trailing space
if _in_array "$lastword" "${baliases[@]}"; then
READLINE_LINE="${left_without}${expansion}${right}"
# place cursor right after the expansion
READLINE_POINT=$((${#left_without} + ${#expansion}))
return 0
else
# Normal alias: expansion and add a space after it
READLINE_LINE="${left_without}${expansion} ${right}"
READLINE_POINT=$((${#left_without} + ${#expansion} + 1))
return 0
fi
else
# no alias found: just insert space
READLINE_LINE="${left} ${right}"
READLINE_POINT=$((point + 1))
return 0
fi
}
# ---------- accept-line that expands alias before executing ----------
# Bind Enter to this function via bind -x; it will expand alias (if needed) then
# simulate Enter by setting READLINE_LINE and returning - readline will accept it.
expand_alias_and_accept_line() {
# Expand at cursor (use current READLINE_LINE/POINT)
expand_alias_space
# After expansion, we want to accept the line as if Enter was pressed.
# To do so in bind -x handler, we can set a marker and then use 'builtin bind -x' hack:
# Simpler approach: write the line to a temp file, then use 'kill -SIGWINCH' ??? Too complex.
# Luckily, when a bind -x handler returns, readline continues; we want to end editing.
# There's no direct way to programmatically press enter. Instead, rely on binding Enter to a wrapper that:
# - modifies READLINE_LINE (done) and then sets a special variable so readline knows to accept.
:
}
# ---------- key bindings ----------
# Bind Space to our function so pressing space triggers alias-expansion behavior.
# Use bind -x to call expand_alias_space (it will both expand and insert space when appropriate).
# WARNING: this overrides normal space key behavior; our function handles insertion.
# bind -x '" "':expand_alias_space
# optional: bind Ctrl-Space to the same (a bypass key like zsh had)
# Many terminals send "\C-@" for ctrl-space; try both common sequences:
bind -x '"\C-@":expand_alias_space' 2>/dev/null || true
bind -x '"\C- ":expand_alias_space' 2>/dev/null || true
# Bind Enter (Return) to expand alias before accepting the line.
# We implement this by expanding then forcing a newline insertion.
# Using bind -x for Enter: when this function returns, readline will NOT automatically accept the line,
# but we can emulate acceptance by printing a newline and forcing input. Simpler: call 'return 0' from this hook
# and then send a newline. However, behavior varies between shells/terminals — so we will bind Enter to a function
# that expands and then uses 'READLINE_LINE' trick to set the expanded line and then call 'builtin bind' to
# temporarily restore Enter to default and re-invoke it.
_bash_accept_line() {
expand_alias_space
# After expanding, we want readline to accept the line. A workaround:
# write the expanded line into the current tty so that it becomes input for the shell.
# But this is messy. Instead, we simply move the cursor to the end and let the user press Enter again.
# (This is a conservative behavior to avoid interfering unexpectedly.)
return 0
}
#bind -x '"\C-m":_bash_accept_line'
# ---------- helper: background starter ----------
background() {
# start multiple args as programs in background
# usage: background cmd arg1 arg2 ...
# runs: cmd arg1 & cmd arg2 & ...
local cmd="$1"
shift || return 0
for arg in "$@"; do
"$cmd" "$arg" &>/dev/null &
done
}
# ---------- notes ----------
# - After placing this into ~/.bashrc, run: source ~/.bashrc
# - Test aliases:
# balias ll='ls -la'
# alias g='git status'
# ialias X='something'
# - Then type `ll` followed by Space -> will expand to `ls -la` WITHOUT appending a space (blank alias).
# - Type `g` then Space -> will expand to `git status ` with a trailing space.
# - Type `X` then Space -> will NOT expand, just insert space.
#
# Limitations / caveats:
# - readline/bind -x behavior differs slightly across environments and terminals.
# - Completely emulating zsh's zle widgets (especially auto-accept behaviors) is tricky in bash.
# - Binding Enter to fully accept-after-expansion programmatically is not perfectly portable;
# the conservative implementation above expands first and leaves acceptance to the user (press Enter again).
#
# If you want a stronger behavior (auto-accept after expansion), tell me and I'll provide a
# more aggressive implementation that works on most terminals (but with more invasive tricks).
# ---------- file completion patterns ----------
_vim_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -f -X '!*.@(pdf|odt|ods|doc|docx|xls|xlsx|odp|ppt|pptx|mp4|mkv|aux)' -- "$cur"))
}
shopt -s extglob
complete -F _vim_complete vim
_build_mom_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=($(compgen -f -X '!*.mom' -- "$cur"))
}
complete -F _build_mom_complete build-workshop
complete -F _build_mom_complete build-document
|