Skip to content

Instantly share code, notes, and snippets.

@robin-a-meade
Last active February 15, 2025 06:50
Show Gist options
  • Save robin-a-meade/b83dd635076993c4c25b7115d0b5da32 to your computer and use it in GitHub Desktop.
Save robin-a-meade/b83dd635076993c4c25b7115d0b5da32 to your computer and use it in GitHub Desktop.
Minimal config for shell and vim

Quick minimal config for shell and vim

For when a full dotfiles setup is overkill.

Installation Instructions

curl https://gist.githubusercontent.com/robin-a-meade/b83dd635076993c4c25b7115d0b5da32/raw/install.sh | bash

Time-stamped backup files are made of all replaced files.

Once satisfied, you may interactively delete these backup files with:

cd
find . -regextype posix-extended -regex '^.*[0-9]{8}T[0-9]{6}$' -ok rm {} \;
# Compare to Fedora's baseline:
# https://src.fedoraproject.org/rpms/bash/blob/rawhide/f/dot-bash_profile
# Our changes:
# - We do our PATH customizations here instead of .bashrc.
# - - I disagree with Fedora's decision to move PATH customizations from ~/.bash_profile to ~/.bashrc
# https://src.fedoraproject.org/rpms/bash/c/739b272e5f5d10cf27a847a44d09eb7f4b6ec89b?branch=rawhide
# - - We utilize the pathmunge function from /etc/profile,
# https://pagure.io/setup/blob/master/f/profile,
# but refine it for bash:
# - - - Utilize modern test
# - - - Remove superfluous quoting
# - We guard the sourcing of .bashrc with a test for interactive shell. Login shells can
# be interactive or non-interactive, but ~/.bashrc is supposed to be for interactive
# shells only. Testing for interactive shell before sourcing ~/.bashrc from ~/.bash_profile is
# recommended here: https://superuser.com/a/183980
pathmunge () {
case :${PATH}: in
*:$1:*)
;;
*)
if [[ $2 == after ]]; then
pathvar=$pathvar:$1
else
pathvar=$1:$pathvar
fi
esac
}
pathmunge PATH "$HOME"/.local/bin
pathmunge PATH "$HOME"/bin
unset -f pathmunge
# Tame the sort order of ls so that important files like Makefile and README
# appear prominently near the beginning of a directory listing
# https://unix.stackexchange.com/a/469431
# https://www.gnu.org/software/make/manual/html_node/Makefile-Names.html
#export LC_COLLATE=C.UTF-8
export LC_COLLATE=C # Equivalent (UTF-8 has the property that byte order is same as codepoint order!)
# Add my ~/.terminfo directory to TERMINFO_DIRS, if not already present
if [[ ! :$TERMINFO_DIRS: = *:$HOME/.terminfo:* ]]; then
export TERMINFO_DIRS=$HOME/.terminfo:${TERMINFO_DIRS:-/usr/share/terminfo}
fi
# Interactive login shells should source ~/.bashrc
if [[ $- == *i* && -r ~/.bashrc ]]; then
source ~/.bashrc
fi
# Return immediately if not an interactive shell
# ==============================================
#
# The ~/.bashrc startup file is supposed to be for interactive shells only.
#
# In this file, I call commands that assume we are in an interactive shell. These
# commands can cause errors if this condition is not met. For example, `stty -ixon`
# will cause the following error if called from a non-interactive shell.
#
# $ ssh myserver 'cat file.txt'
# stty: 'standard input': Inappropriate ioctl for device
#
# For some strange reason, bash will source ~/.bashrc,
# even though it is non-interactive, if it detects that its standard input is connected to a
# network connection.
#
# [Why is bash sourcing .bashrc in non-interactive mode when working via ssh?](
# https://unix.stackexchange.com/q/587207
# )
#
# That's why we added the following check for interactive shell.
#
# Update: Apparently many linux distributions provide `/etc/skel/.bashrc` that do this check.
#
# [Why does bashrc check whether the current shell is interactive?](
# https://unix.stackexchange.com/q/257571
# )
#
# Not Fedora/RedHat though. (Todo: file bug)
if [[ $- != *i* ]]; then return; fi
# Based on Fedora's default .bashrc
# =================================
# https://src.fedoraproject.org/rpms/bash/blob/rawhide/f/dot-bashrc
# Except I moved PATH customizations out of this file back to where they used to be in ~/.bash_profile
# I disagree with Fedora's move of PATH customizations from ~/.bash_profile to ~/.bashrc
# https://src.fedoraproject.org/rpms/bash/c/739b272e5f5d10cf27a847a44d09eb7f4b6ec89b?branch=rawhide
# Doing PATH customizations in ~/.bash_profile is the preferred best practice.
# https://unix.stackexchange.com/a/26059
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# Enable extended glob patterns
shopt -s extglob
# Disable History Expansion
# http://superuser.com/a/133782
# https://unix.stackexchange.com/q/436505
# Short equivalent: set +H
set +o histexpand
# Support multi-line history entries well
# https://stackoverflow.com/a/17194155
# https://unix.stackexchange.com/a/109035
shopt -s lithist
shopt -s cmdhist
# Disable XON/XOFF flow control to free up Ctrl-S for forward-history-search
#
# The default readline binding for forward-search-history is Ctrl+S:
#
# $ bind -P | grep -E '^forward-search-history'
# forward-search-history can be found on "\C-s".
#
# This, however, collides with the terminal's use of Ctrl+S for XON/XOFF flow control.
#
# Never have I had needed to hit Ctrl+S to stop the sender from transmitting,
# or Ctrl+Q to resume transmitting. These key strokes were useful in the age
# of teletypes, early terminals, and primitive terminal emulators with
# no scrollback buffer on slow modem connection. At today's 1Mbps+ network speeds,
# trying to control the sender's transmission flow by pressing Ctrl+S and Ctrl+Q
# is futile. Modern terminal emulators have large scrollback buffers anyway, so
# the need no longer exists. Or use a pager, like 'more' (or 'less').
#
# We therefore disable XOFF/XON flow control so that readline's keybinding
# for Ctrl+S will work. As a bonus, it frees up Ctrl+Q too. Feel free to bind
# that to something useful.
#
# There's talk in some linux distributions of disabling this by default.
# https://bugs.launchpad.net/ubuntu/+source/bash/+bug/80635/comments/12
#
# Note: Disabling XOFF/XON flow control can cause 'Inappropriate ioctl for device' error
# when set in a non-interactive shell. See https://stackoverflow.com/q/24623021 .
# We're good though because we follow the best practice of testing for interactive
# shell at the top of .bashrc. See https://superuser.com/a/183980
stty -ixon
# When you press TAB after a variable holding a directory, expand it.
# http://tiswww.case.edu/php/chet/bash/NEWS
# http://askubuntu.com/questions/70750/how-to-get-bash-to-stop-escaping-during-tab-completion
# http://stackoverflow.com/questions/6418493/bash-variable-expansion-on-tab-complete
# https://bugs.launchpad.net/ubuntu/+source/bash/+bug/778627
# http://lists.gnu.org/archive/html/bug-bash/2011-02/msg00274.html
# http://nelsonslog.wordpress.com/2012/01/29/bash-4-2-variable-expansion-bug/
# https://unix.stackexchange.com/q/378625
shopt -s direxpand
# Make cd smarter
# So you never regret that you didn't use pushd
cd() {
if [[ $# == 0 ]]; then
pushd "$HOME" >/dev/null
elif [[ $1 == - ]]; then
pushd >/dev/null
else
pushd "$@" >/dev/null
fi
}
alias bk='popd >/dev/null'
# Source user-specific bash completion scripts
# ============================================
# Needed only on older systems. Modern bash-completion does this for us.
if [[ ! $BASH_COMPLETION_VERSINFO ]]; then
# We must be on an older system
# We'll need to source our user-specific bash completion scripts ourselves
if [[ -r ~/.local/share/bash-completion/completions ]]; then
for rc in ~/.local/share/bash-completion/completions/*; do
if [[ -f $rc ]]; then
. "$rc"
fi
done
fi
fi
# Disable beeping during TAB completion
# https://unix.stackexchange.com/a/593495
bind 'set bell-style none'
# It's better to put these keybindings in .inputrc so that they will apply to all
# programs that use the readline library, not just bash.
# I'm putting them here, too, in case you decide not to edit .inputrc.
bind 'TAB:menu-complete'
bind '"\e[A": history-search-backward'
bind '"\e[B": history-search-forward'
export PS1="\[$(tput setaf 104)\]\u\[$(tput setaf 59)\]@\[$(tput setaf 104)\]\h \[$(tput setaf 81)\]\w \[$(tput sgr0)\]$ "
# ls
alias ls='ls -l --classify --group-directories-first --color=auto'
# I also tame the ls sort order by setting the LC_COLLATE environment variable
# to C in my .bash_profile
if [[ -r ~/.dircolors ]]; then
eval "$(dircolors ~/.dircolors)"
fi
# Include system-wide settings
$include /etc/inputrc
# Enable prefix history search using UP and DOWN arrow keys
"\e[A": history-search-backward
"\e[B": history-search-forward
# Configure TAB key to cycle through completion candidates
# https://lists.gnu.org/archive/html/help-bash/2014-06/msg00002.html
TAB: menu-complete
" Source the example vimrc file
" - It has sensible defaults
" - It, in turn, sources $VIMRUNTIME/defaults.vim, which contains "defaults that most users want".
" This approach is inline with the default behavior on Windows: vim on Windows will
" source $VIMRUNTIME/vimrc_example.vim if the user did not define their own vimrc, as
" explained here: https://github.com/vim/vim/issues/1468
source $VIMRUNTIME/vimrc_example.vim
" Automatic installation of the Plug plugin manager
" https://github.com/junegunn/vim-plug/wiki/tips#automatic-installation
let data_dir = has('nvim') ? stdpath('data') . '/site' : '~/.vim'
if empty(glob(data_dir . '/autoload/plug.vim'))
silent execute '!curl -fLo '.data_dir.'/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
endif
" Install some good plugins
call plug#begin('~/.vim/plugged')
Plug 'ghifarit53/tokyonight-vim' " colorscheme
Plug 'dracula/vim', {'as': 'dracula'} " colorscheme
Plug 'tpope/vim-sensible'
Plug 'tpope/vim-commentary'
Plug 'tpope/vim-surround'
Plug 'itchyny/lightline.vim'
Plug 'ntpeters/vim-better-whitespace'
Plug 'editorconfig/editorconfig-vim'
call plug#end()
" Load additional sensible defaults from sensible.vim.
" As suggested in the documentation, I include sensible.vim at top of my
" .vimrc so that I can override settings.
" (https://github.com/tpope/vim-sensible)
runtime! plugin/sensible.vim
" MAKE Y SENSIBLE TOO: Y => y$
" This mapping sets up Y to be consistent with the C and D operators, which
" act from the cursor to the end of the line. The default behavior of Y is to
" yank the whole line, but we can do that with `yy`.
" http://vim.wikia.com/wiki/Short_mappings_for_common_tasks
" VIM HELP suggests it:
" If you like "Y" to work from the
" cursor to the end of line (which is more logical,
" but not Vi-compatible) use ":map Y y$".
" http://vimdoc.sourceforge.net/htmldoc/change.html#Y
" Vim Tips Wiki suggests it:
" https://vim.fandom.com/wiki/Short_mappings_for_common_tasks#Copy
" Tim Pope reluctantly removed it from sensible.vim
" https://github.com/tpope/vim-sensible/issues/47
" Nvim added it to their defaults in 2021:
" https://stackoverflow.com/q/14385998#comment127889635_19523143
" https://github.com/neovim/neovim/pull/13268
nnoremap Y y$
" Indent by four spaces
set expandtab
set shiftwidth=4
set softtabstop=-1 " Make 'softtabstop' follow 'shiftwidth'
set autoindent
" When pasting, it is necessary to temporarily disable autoindent
" Assign F2 to toggle autoindent
" Alternatively, you could use the ex commands:
" :set paste
" :set nopaste
" http://vim.wikia.com/wiki/Toggle_auto-indenting_for_code_paste
set pastetoggle=<F2>
" Press F7 to reindent entire file
" http://vim.wikia.com/wiki/Fix_indentation
" Explanation:
" gg Go to first line of file
" =G Reindent (=) from current line to end of file (G)
" <C-o> Move cursor back to previous position
" <C-o> One more time
map <F7> gg=G<C-o><C-o>
if has('mouse')
set mouse=a
endif
" Disable beep
" https://vim.fandom.com/wiki/Disable_beeping
set noerrorbells visualbell t_vb=
autocmd GUIEnter * set visualbell t_vb=
" Set the directory to use for swap files
" (This must be a pre-existing directory. Our 'install.sh' script ensures this is the case.)
" The double trailing slashes (//) at the end tells vim to use the absolute path of the file
" when forming the swap file name so that there's no chance collisions between files of the
" same name from different directories.
" https://vi.stackexchange.com/questions/177/what-is-the-purpose-of-swap-files
set directory=~/.vimswp//
" Set a central directory to use for backup files
" This is much better than littering
" (This must be a pre-existing directory. Our 'install.sh' script ensures this is the case.)
" The double trailing slashes (//) at the end tells vim to use the absolute path of the file
" when forming the backup file name so that there's no chance collisions between files of the
" same name from different directories.
" Support for this was committed on Aug 7, 2018
" https://github.com/vim/vim/commit/b782ba475a3f8f2b0be99dda164ba4545347f60f
" (Patch 8.1.0251)
" Associated issue: https://github.com/vim/vim/issues/179
set backupdir=~/.vimbackup//
" If we don't have that patch, use Victor Schröder's workound:
" https://stackoverflow.com/a/38479550
if ! has('patch-8.1-0251')
" This is a copy and paste of Victor Schröder's workound, except I already set backupdir above
" === BACKUP SETTINGS ===
" turn backup OFF
" Normally we would want to have it turned on. See bug and workaround below.
" OBS: It's a known-bug that backupdir is not supporting
" the correct double slash filename expansion
" see: https://code.google.com/p/vim/issues/detail?id=179
set nobackup
" set a centralized backup directory
"set backupdir=~/.vim/backup// <== I already set this above -Robin
" This is the workaround for the backup filename expansion problem.
autocmd BufWritePre * :call SaveBackups()
function! SaveBackups()
if expand('%:p') =~ &backupskip | return | endif
" If this is a newly created file, don't try to create a backup
if !filereadable(@%) | return | endif
for l:backupdir in split(&backupdir, ',')
:call SaveBackup(l:backupdir)
endfor
endfunction
function! SaveBackup(backupdir)
let l:filename = expand('%:p')
if a:backupdir =~ '//$'
let l:backup = escape(substitute(l:filename, '/', '%', 'g') . &backupext, '%')
else
let l:backup = escape(expand('%') . &backupext, '%')
endif
let l:backup_path = a:backupdir . l:backup
:silent! execute '!cp ' . resolve(l:filename) . ' ' . l:backup_path
endfunction
endif
" Write with sudo
" This variation works smoother than others I tried.
" Reportedly works in nvim too.
" https://stackoverflow.com/q/2600783#comment123636916_2600783
command! Wsudo :execute ':silent w !sudo tee > /dev/null "%"' | :edit!
" Persistent undo
"
" Keep undo history across sessions by storing it in a file
" http://stackoverflow.com/questions/5700389/using-vims-persistent-undo
if has('persistent_undo')
set undodir=~/.vim/undodir
set undofile
endif
" Make search more forgiving by ignoring case
set ignorecase
" colorscheme
" ===========
" Set colorscheme variables
let g:tokyonight_style = "night"
let g:tokyonight_disable_italic_comment = "1"
let g:dracula_bold = 1
let g:dracula_italic = 0
let g:dracula_underline = 1
let g:dracula_undercurl = 1
let g:dracula_inverse = 1
let g:dracula_colorterm = 0
let g:dracula_strikethrough = 0
" If the colorscheme has a variant for dark background, choose that
set background=dark
" This is a TAB
set list
set listchars=tab:>-
" My modifications to colorschemes
" ================================
" If a color scheme is almost right, you can add modifications...
" https://vimhelp.org/syntax.txt.html#%3Acolorscheme
augroup my_colorscheme_modifications
" If a color scheme is almost right, you can add modifications...
" https://vimhelp.org/syntax.txt.html#%3Acolorscheme
au!
"au Colorscheme tokyonight
" \ hi Normal guibg=#000000
" \ | hi Comment guifg=#777777
"\ | hi SpecialKey ctermfg=DarkGreen guifg=DarkRed
"\ hi Normal ctermbg=none guibg=#000000
" I wanted dracula's background color to be darker
"au Colorscheme dracula
" \ highlight Normal guibg=#171722
augroup END
" Set a colorscheme, if desired
"colorscheme desert
"colorscheme pablo
"colorscheme peachpuff
"colorscheme darkblue
"colorscheme elflord
"colorscheme murphy
"colorscheme ron
"colorscheme slate
"colorscheme torte
"colorscheme dracula
colorscheme tokyonight
" Lightline
" =========
" Lightline shows the mode in the status line
" It is no longer necessary to to display '-- INSERT --'
set noshowmode
" See available lightline colorschemes here:
" https://github.com/itchyny/lightline.vim/blob/master/colorscheme.md
"let g:lightline = { 'colorscheme': 'wombat' }
let g:lightline = { 'colorscheme': 'tokyonight' }
"let g:lightline = { 'colorscheme': 'Tomorrow_Night_Blue' }
"let g:lightline = { 'colorscheme': 'darcula' }
" OSC52 clipboard support for vim 7.4
" ========================================
"
" Yanking in visual mode will send the yanked text to the system clipboard, if
" your terminal supports the OSC 52 escape sequence.
"
" I first tried the ojroques/vim-oscyank plugin, but it wasn't
" compatible with vim 7.4
"
" I then hacked together this solution
"
" I'd prefer to stay consistent with the key bindings suggested by the
" ojroques/vim-oscyank plugin but that's not trivial. I decided to keep it
" simple and restrict OSC52 to the visual mapping
" Function to send yanked text to OSC52 sequence
function! OSC52()
let yanked = @"
call system("base64 -w0 | xargs -0 printf '\\e]52;c;%s\\a' >/dev/tty", yanked)
endfunction
" Map visual y to the OSC52 function as a side effect
" Maybe I should add more yank mappings, but I decided to keep it simple
" Remember, this is merely a workaround for vim-oscyank not being compatible
" with vim 7.4.
vnoremap <silent> y y:call OSC52()<CR>
" Jump to the last known position when reopening a file
" https://vimdoc.sourceforge.net/htmldoc/eval.html#last-position-jump
if has("autocmd")
au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$")
\| exe "normal! g`\"" | endif
endif
" Make split behave more intuitively
" Open file in right split, not default of left split
" https://stackoverflow.com/q/22614280
" This is for when you do :vsp filename.txt
set splitbelow
set splitright
" Trim trailing white space
" The ntpeters/vim-better-whitespace plugin supplies StripWhitespace
" Let's map it to <leader>w
nnoremap <leader>w :StripWhitespace<CR>
# Save the current working directory
saved_pwd=$(pwd)
cd # Ensure we are in home directory
# Create the directories needed by vim
# The -p option ensures there is no error for case of the directory already existing
mkdir -p ~/.vimswp
mkdir -p ~/.vimbackup
files=( .bash_profile .bashrc .vimrc .inputrc )
for f in "${files[@]}"; do
echo "Processing $f..."
if [[ -r "$f" ]]; then
cp -p "$f" "$f.$(date +'%Y%m%dT%H%M%S')"
if [[ $? != 0 ]]; then
echo "An error occurred while backing up $f" >&2
return
fi
fi
done
PRE=https://gist.githubusercontent.com/robin-a-meade/b83dd635076993c4c25b7115d0b5da32/raw
for f in "${files[@]}"; do
escaped="${f//\//:}" # Replace / with :
if ! mkdir -p ./"$(dirname "$f")"; then
echo "An error occurred while creating directory $(dirname "$f")" >&2
return
fi
# Cache-Control to bust the cache. I haven't tested yet. Seen here:
# https://gist.github.com/atenni/5604615#gistcomment-3063801
# Others recommend adding a fake query string parameter to bust the cache.
echo curl -sS -o "$f" -H 'Cache-Control: no-cache' "$PRE/$escaped"
if ! curl -sS -o "$f" -H 'Cache-Control: no-cache' "$PRE/$escaped"; then
echo "An error occurred while downloading $PRE/$escaped" >&2
return
fi
cd # Return to home directory
done
# Bash history won't persist to disk if the file referenced by $HISTFILE
# does not actually exist. Bash won't automatically create it.
if [[ $HISTFILE && ! -e $HISTFILE ]]; then
echo "History file '$HISTFILE' didn't exist. Creating it."
touch "$HISTFILE"
fi
# Return to the saved working directory
cd "$saved_pwd"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment