Minimalistic development environment
Stack used
- vim
- fzf
- ag
- wezterm
- tmux
- mpv
- git
- zsh
- vale
- languagetool
shell
I used zsh, mostly because of ohmyzsh, but I could change that. I use the following alias
# If you come from bash you might have to change your $PATH.
# export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH
# Path to your Oh My Zsh installation.
export ZSH="$HOME/.oh-my-zsh"
ZSH_THEME="clean"
plugins=(git)
source $ZSH/oh-my-zsh.sh
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# blog
alias blog='cd ~/Projects/personal-site/org'
ff() {
local sel file line
sel=$(rg -n . | fzf --multi) || return
file=${sel%%:*}
line=${sel#*:}
line=${line%%:*}
vim +"$line" "$file"
}
nn() {
cd ~/Projects/personal-site/org
fzf --print-query | head -1 | xargs -o -I{} vi $(date +%Y%m%d-%H%M)-{}.md
}
gdf() {
local base=${1:-origin/main}
git diff --name-only "$base"...HEAD | \
fzf --multi --preview "git diff $base..HEAD -- {}" \
--bind 'enter:execute(less -R {+}),ctrl-e:become(vim {+})'
}
. "$HOME/.atuin/bin/env"
# -------------------------------
# ZSH: Vim mode
# -------------------------------
bindkey -v
# Better cursor shape (optional, works in most terminals)
function zle-keymap-select {
if [[ $KEYMAP == vicmd ]] || [[ $1 = 'block' ]]; then
printf '\e[2 q' # block cursor
else
printf '\e[6 q' # beam cursor
fi
}
zle -N zle-keymap-select
# -------------------------------
# Enable Ctrl-S (disable flow control)
# -------------------------------
stty -ixon
# -------------------------------
# Atuin init
# -------------------------------
eval "$(atuin init zsh)"
# Replace default reverse search
bindkey '^R' atuin-search
# Forward search (Ctrl-S)
atuin-forward-search() {
BUFFER=$(atuin search --shell-up-key-binding --interactive)
CURSOR=${#BUFFER}
}
zle -N atuin-forward-search
bindkey '^S' atuin-forward-search
# -------------------------------
# Project (cwd) scoped search
# Alt-h
# -------------------------------
atuin-cwd-search() {
BUFFER=$(atuin search --cwd --interactive)
CURSOR=${#BUFFER}
}
zle -N atuin-cwd-search
bindkey '^[h' atuin-cwd-search
# -------------------------------
# Session scoped search
# Alt-s
# -------------------------------
atuin-session-search() {
BUFFER=$(atuin search --session --interactive)
CURSOR=${#BUFFER}
}
zle -N atuin-session-search
bindkey '^[s' atuin-session-search
# =====================================================
# ZSH PAPER MODE CONFIG
# Minimal. Calm. Book-like.
# =====================================================
# -----------------------------------------------------
# Basics
# -----------------------------------------------------
export TERM="xterm-256color"
setopt AUTO_CD
setopt INTERACTIVE_COMMENTS
setopt HIST_IGNORE_DUPS
setopt HIST_REDUCE_BLANKS
setopt SHARE_HISTORY
setopt EXTENDED_HISTORY
HISTSIZE=50000
SAVEHIST=50000
HISTFILE=~/.zsh_history
# -----------------------------------------------------
# Completion (quiet + clean)
# -----------------------------------------------------
autoload -Uz compinit
compinit
zstyle ':completion:*' menu no
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Za-z}'
zstyle ':completion:*' list-colors ''
# -----------------------------------------------------
# Paper Color Palette (Grayscale 256)
# -----------------------------------------------------
autoload -U colors && colors
setopt PROMPT_SUBST
GRAY_DARKEST="%F{236}"
GRAY_DARK="%F{238}"
GRAY_MED="%F{242}"
GRAY_LIGHT="%F{245}"
RESET="%f"
# -----------------------------------------------------
# Git branch (subtle, soft)
# -----------------------------------------------------
git_branch() {
local branch
branch=$(git symbolic-ref --short HEAD 2>/dev/null)
if [[ -n $branch ]]; then
echo " ${GRAY_LIGHT}($branch)${RESET}"
fi
}
# -----------------------------------------------------
# Exit status (only if non-zero)
# -----------------------------------------------------
exit_status() {
local code=$?
if [[ $code -ne 0 ]]; then
echo " ${GRAY_DARK}[exit:$code]${RESET}"
fi
}
# -----------------------------------------------------
# Prompt (Two-line, Book Layout)
# -----------------------------------------------------
PROMPT='${GRAY_MED}%~${RESET}$(git_branch)$(exit_status)
${GRAY_DARK}%# ${RESET}'
# -----------------------------------------------------
# No visual noise
# -----------------------------------------------------
unsetopt PROMPT_CR
unsetopt PROMPT_SP
# No bold in completions
zstyle ':completion:*' format "${GRAY_MED}%d${RESET}"
# -----------------------------------------------------
# Vi Mode (optional but calm)
# -----------------------------------------------------
bindkey -v
KEYTIMEOUT=1
# -----------------------------------------------------
# No terminal title changes
# -----------------------------------------------------
DISABLE_AUTO_TITLE="true"
# -----------------------------------------------------
# Aliases (clean and minimal)
# -----------------------------------------------------
alias ll='ls -lh'
alias la='ls -lha'
alias l='ls -lh'
alias g='git'
alias v='vim'
alias e='emacs'
# -----------------------------------------------------
# Silence greeting
# -----------------------------------------------------
unsetopt BEEPff uses fzf to select files and then opens vim nn is to create new notes in markdown gdf to see what files were changed and review them.
.vimrc
Here is my .vimrc
let mapleader = " "
set nocompatible " Must come first because it changes other options.
syntax enable " Turn on syntax highlighting.
filetype plugin on " Turn on file type detection.
set showcmd " Display incomplete commands.
set showmode " Display the mode you're in.
set backspace=indent,eol,start " Intuitive backspacing.
set hidden " Handle multiple buffers better.
set wildmenu " Enhanced command line completion.
set wildmode=list:longest " Complete files like a shell.
set incsearch " Highlight matches as you type.
set hlsearch " Highlight matches.
set wrap " Turn on line wrapping.
set modeline " Allow per file config
set autoindent
set noexpandtab
set tabstop=4
set shiftwidth=4
au FileType cpp silent! cs add cscope.out
au FileType js silent! cs add cscope.out
set clipboard=unnamed
set ruler
"set colorcolumn=80
syntax off
"highlight ColorColumn ctermbg=000 guibg=lightgrey
set laststatus=2
set statusline=%f\ %m\ [%{&filetype}]\ %l:%c
set guicursor=n-v-c:block,i-ci-ve:ver25
set grepprg=rg\ --vimgrep\ --smart-case
set grepformat=%f:%l:%c:%m
set nowrap
set sidescroll=5
set sidescrolloff=8
nnoremap <Leader>cs :cs find s <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>cg :cs find g <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>cc :cs find c <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>ct :cs find t <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>ce :cs find e <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>cf :cs find f <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>ci :cs find i <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>cd :cs find d <C-R>=expand("<cword>")<CR><CR>
nnoremap <Leader>ca :cs find a <C-R>=expand("<cword>")<CR><CR>
nnoremap <leader>f :!vi "$(fzf)"<CR>
nnoremap <Leader>r :source ~/.vimrc<CR>
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l
" --- fzf + ripgrep: jump to definition / references for word under cursor ---
function! s:rg_fzf_jump(pattern)
" Uses: rg --vimgrep -> file:line:col:match
let l:cmd = 'rg --vimgrep --smart-case ' . shellescape(a:pattern) . ' . | fzf'
let l:out = system(l:cmd)
if v:shell_error != 0 || empty(l:out)
return
endif
let l:parts = split(l:out, ':', 4)
if len(l:parts) < 3
return
endif
execute 'edit ' . fnameescape(l:parts[0])
call cursor(str2nr(l:parts[1]), str2nr(l:parts[2]))
normal! zz
endfunction
function! FzfReferencesCword()
let l:w = expand('<cword>')
" word-boundary-ish: avoid partial matches
call s:rg_fzf_jump('\b' . l:w . '\b')
endfunction
function! FzfDefinitionsCword()
let l:w = expand('<cword>')
" Heuristic definition patterns (works across many languages)
" - anchored near line start, allows indentation
" - tries common constructs: function/type/class/struct/enum/interface/const/var/let
" - plus Go: func/type
" - plus Rust: fn/struct/enum/trait/impl
" - plus TS/JS: function/class/interface/type/const/let
let l:pat =
\ '(^\s*(export\s+)?(async\s+)?(function|class|interface|type|struct|enum|trait|impl|const|let|var)\s+'
\ . l:w . '(\b|\s|<|\(|\{|:)'
\ . '|^\s*(def|fn)\s+' . l:w . '\b'
\ . '|^\s*func\s+(\([^)]+\)\s*)?' . l:w . '\b'
\ . '|^\s*type\s+' . l:w . '\b'
call s:rg_fzf_jump(l:pat)
endfunction
" Mappings:
nnoremap <silent> <leader>r :call FzfReferencesCword()<CR>
nnoremap <silent> <leader>d :call FzfDefinitionsCword()<CR>
" ---------- Reading / Book mode (no plugins) ----------
let g:book_mode = 0
function! ToggleBookMode()
if g:book_mode
" OFF: restore normal coding UI
let g:book_mode = 0
set nowrap
set nocursorline
set colorcolumn=
set laststatus=2
set showmode
set ruler
set signcolumn=auto
else
" ON: reading-friendly UI
let g:book_mode = 1
set wrap
set linebreak
set breakindent
set showbreak=↳\
set cursorline
set laststatus=0
set noshowmode
set noruler
set signcolumn=no
" Pick your "page width" target:
set textwidth=88
" Visual guide at textwidth (subtle)
execute "set colorcolumn=" . ( &textwidth + 1 )
" Keep line numbers if you want; for pure reading, you can turn them off:
set nonumber
set norelativenumber
endif
endfunction
nnoremap <silent> <leader>b :call ToggleBookMode()<CR>
" Make the colorcolumn subtle (paper-like)
highlight ColorColumn ctermbg=NONE cterm=NONE guibg=#d8caa6
" ========== Writing setup ==========
" Enable spell automatically for writing
augroup writing
autocmd!
autocmd FileType markdown,gitcommit,text setlocal spell spelllang=en_us
augroup END
" Toggle spell manually
nnoremap <leader>s :setlocal spell!<CR>
" ---------- LanguageTool (grammar) ----------
function! LT_Check()
write
setlocal errorformat=%f:%l:%c:\ %m
cexpr system('languagetool --language en-US ' . shellescape(expand('%')))
if len(getqflist()) > 0
copen
else
cclose
echo "LanguageTool: clean."
endif
endfunction
command! Grammar call LT_Check()
nnoremap <leader>g :Grammar<CR>
" ========= Vale runner into quickfix =========
function! Vale_Check() abort
write
" Parse: file:line:col:message (Vale --output=line)
setlocal errorformat=%f:%l:%c:%m
cexpr system('vale --config /Users/carlosneira/.vale.ini --output=line ' . shellescape(expand('%')))
if len(getqflist()) > 0
copen
else
cclose
call s:ValeClearHighlights()
echo "Vale: clean."
endif
endfunction
command! Style call Vale_Check()
nnoremap <leader>v :Style<CR>
" ---------- Vale (style) ----------
function! Vale_Check()
write
setlocal errorformat=%f:%l:%c:%m
cexpr system('vale --config /Users/carlosneira/.vale.ini --output=line ' . shellescape(expand('%')))
if len(getqflist()) > 0
copen
else
cclose
echo "Vale: clean."
endif
endfunction
command! Style call Vale_Check()
nnoremap <leader>v :Style<CR>magit diff-range
I used a lot magit diff-range, but this is just enough for me
:!git diff main..somebranch -- %spottify ?
I just use
mpv --no-audio-display ~/Music .wezterm.lua
local wezterm = require("wezterm")
local act = wezterm.action
local is_reader = false
local function is_night()
local hour = tonumber(os.date("%H"))
return hour >= 21 or hour < 10
end
local function appearance_mode()
if wezterm.gui then
local appearance = wezterm.gui.get_appearance()
if appearance:find("Dark") then
return "dark"
end
end
return "light"
end
wezterm.on("update-right-status", function(window, pane)
local tab = window:active_tab()
local tab_index = tab.tab_index + 1
local tab_count = #window:mux_window():tabs()
local title = tab.active_pane.title
window:set_right_status(
wezterm.format({
{ Foreground = { Color = "#a89f91" } },
{ Text = " " .. tab_index .. "/" .. tab_count .. " " .. title .. " " },
})
)
end)
wezterm.on("toggle-reader-mode", function(window, pane)
is_reader = not is_reader
local overrides = window:get_config_overrides() or {}
if is_reader then
-- Reader / “book page” mode
overrides.window_padding = { left = 140, right = 140, top = 32, bottom = 32 }
overrides.initial_cols = 84
overrides.line_height = 1.30
overrides.cell_width = 1.02
-- Optional: hide tab bar for maximum “page” vibe
overrides.enable_tab_bar = false
else
-- Normal mode (your defaults)
overrides.window_padding = nil
overrides.initial_cols = nil
overrides.line_height = nil
overrides.cell_width = nil
overrides.enable_tab_bar = nil
end
window:set_config_overrides(overrides)
end)
local function paper_palette(mode)
if mode == "dark" then
return {
--foreground = "#c8c1b6",
--background = "#1e1b18",
background = "#23201c",
foreground = "#ddd6c8",
cursor_bg = "#c8c1b6",
cursor_fg = "#1e1b18",
cursor_border = "#c8c1b6",
selection_bg = "#3a332d",
selection_fg = "#c8c1b6",
ansi = {
"#3b3a36",
"#b85450",
"#6a9955",
"#b58900",
"#4f6fad",
"#875f9a",
"#3a7f7a",
"#c8c1b6",
},
brights = {
"#5c5b57",
"#c06060",
"#7aa65a",
"#c59f2f",
"#5f7fbe",
"#9a6fbf",
"#4a9f9a",
"#e0d8cc",
},
}
else
return {
foreground = "#3a352a", -- slightly softer than #2f2e2a for “paper”
-- background = "#f4ecd8",
background = "#fbf7ed",
cursor_bg = "#3a352a",
cursor_fg = "#f4ecd8",
cursor_border = "#3a352a",
selection_bg = "#d8caa6",
selection_fg = "#3a352a",
ansi = {
"#3b3a36",
"#b85450",
"#6a9955",
"#b58900",
"#4f6fad",
"#875f9a",
"#3a7f7a",
"#5c5b57",
},
brights = {
"#5c5b57",
"#c06060",
"#7aa65a",
"#c59f2f",
"#5f7fbe",
"#9a6fbf",
"#4a9f9a",
"#3a352a",
},
}
end
end
-- -----------------------------------------------------
-- Main
-- -----------------------------------------------------
local mode = appearance_mode()
local colors = paper_palette(mode)
return {
---------------------------------------------------------
-- Kindle feel / typography
---------------------------------------------------------
bold_brightens_ansi_colors = false,
font = wezterm.font("Iosevka Term", { weight = "Regular" }),
font_size = is_night() and 16.3 or 16.0,
line_height = 1.28, -- slightly more “book”
cell_width = 1.02, -- subtle breathing room for code
---------------------------------------------------------
-- Rendering (Smooth Text)
---------------------------------------------------------
freetype_load_target = "Light",
freetype_render_target = "HorizontalLcd",
front_end = "WebGpu",
---------------------------------------------------------
-- Window
---------------------------------------------------------
window_decorations = "RESIZE",
native_macos_fullscreen_mode = true,
-- keep layout stable when toggling font sizes
adjust_window_size_when_changing_font_size = false,
-- Paper / softness (removed duplicate opacity line)
window_background_opacity = is_night() and 0.96 or 1.0,
text_background_opacity = 1.0,
macos_window_background_blur = is_night() and 18 or 0,
colors = colors,
---------------------------------------------------------
-- Padding (Like Margins in a Book)
---------------------------------------------------------
window_padding = {
left = 60,
right = 60,
top = 28,
bottom = 28,
},
initial_cols = 88,
initial_rows = 42,
---------------------------------------------------------
-- Tabs (kept, but calmer)
---------------------------------------------------------
enable_tab_bar = true,
tab_bar_at_bottom = true,
use_fancy_tab_bar = false,
hide_tab_bar_if_only_one_tab = true,
---------------------------------------------------------
-- Scrolling / cursor / misc
---------------------------------------------------------
enable_scroll_bar = false,
scrollback_lines = 10000,
cursor_blink_rate = 0,
-- default_cursor_style = "SteadyBar",
animation_fps = 1,
pane_select_font_size = 36,
keys = {
-- book like mode
{
key = "b",
mods = "CMD|SHIFT",
action = act.EmitEvent("toggle-reader-mode"),
},
-- Increase density for fonts
{
key = "d",
mods = "CMD",
action = wezterm.action_callback(function(window, pane)
local overrides = window:get_config_overrides() or {}
if not overrides.font_size then
overrides.font_size = 15.5
overrides.line_height = 1.18
overrides.cell_width = 1.0
else
overrides.font_size = nil
overrides.line_height = nil
overrides.cell_width = nil
end
window:set_config_overrides(overrides)
end),
},
-- Pane resize key
{
key = "8",
mods = "CTRL|SHIFT",
action = wezterm.action.AdjustPaneSize({ "Left", 15 }),
},
-- Rename tab prompt
{
key = "E",
mods = "CTRL|SHIFT",
action = act.PromptInputLine({
description = "Enter new name for tab",
action = wezterm.action_callback(function(window, pane, line)
if line then
window:active_tab():set_title(line)
end
end),
}),
},
-- Emit event
{ key = "m", mods = "CTRL|SHIFT", action = wezterm.action({ EmitEvent = "update-right-status" }) },
},
mouse_bindings = {
-- ctrl-click link open
{
event = { Up = { streak = 1, button = "Left" } },
mods = "CTRL",
action = wezterm.action.OpenLinkAtMouseCursor,
},
},
}Atuin config.toml
keymap_mode = "vim"
enter_accept = false
style = "compact"
inline_height = 20
git log colors
git config --global color.ui auto
# Branches
git config --global color.branch.current "bold black"
git config --global color.branch.local "black"
git config --global color.branch.remote "bold blue"
# Log
git config --global color.log.commit "bold black"
git config --global color.log.author "blue"
git config --global color.log.date "dim black"
git config --global color.log.decorate "bold black"
# Diff
git config --global color.diff.meta "black"
git config --global color.diff.frag "magenta"
git config --global color.diff.old "red"
git config --global color.diff.new "green"
git config --global color.diff.commit "bold black"
# Status
git config --global color.status.added "green"
git config --global color.status.changed "blue"
git config --global color.status.untracked "red"
git config --global color.status.branch "bold black"
Vale
I have rules to check my grammar using William Zinsser “How to write well” book, they are included in my dotfiles repo.