Trucos y Técnicas Avanzadas de Vim 🥷
Los secretos mejor guardados de Vim. Técnicas ninja que transformarán tu productividad y te harán parecer un mago del terminal.
🎯 Filosofía de los trucos avanzados
Principios ninja
- Eficiencia máxima: Cada pulsación cuenta
- Automatización: Reduce acciones repetitivas
- Personalización extrema: Vim adaptado a ti
- Conocimiento profundo: Entender el núcleo de Vim
- Creatividad: Combinar comandos de formas innovadoras
🔮 Manipulación de texto avanzada
Text objects personalizados
" Crear text objects personalizados
" Seleccionar contenido entre comillas específicas
vnoremap a" :<C-u>silent! normal! 2i"<CR>
onoremap a" :normal va"<CR>
" Text object para funciones completas
vnoremap af :<C-u>silent! normal! [mf]M<CR>
onoremap af :normal vaf<CR>
" Seleccionar número entero bajo cursor
vnoremap an :<C-u>silent! normal! /\d\+<CR>n
onoremap an :normal van<CR>
" Text object para comentarios
function! CommentTextObject()
let comment_char = &commentstring[0]
execute 'normal! ?^' . comment_char . '\+?e'
normal! v
execute 'normal! /^' . comment_char . '\+/e'
endfunction
vnoremap ac :<C-u>call CommentTextObject()<CR>
onoremap ac :normal vac<CR>
Macros ultra-avanzadas
" Macro recursiva para procesar líneas hasta el final
" 1. Grabar: qq
" 2. Hacer operación en línea actual
" 3. Ir a siguiente línea: j
" 4. Verificar si hay más líneas: @q (si hay) o parar
" 5. Terminar grabación: q
" Macro para convertir snake_case a camelCase
:let @c = '0f_x~'
" Macro para duplicar línea y comentar original
:let @d = 'yyP0i// <Esc>j'
" Macro para wrap text en función
:let @w = 'I(<Esc>A)<Esc>'
" Ejecutar macro en rango visual
:'<,'>normal @q
" Aplicar macro a todas las líneas que coinciden con patrón
:g/pattern/normal @q
Manipulación de registros ninja
" Combinar registros
:let @a = @a . @b
" Limpiar registro
:let @a = ''
" Usar registros como calculadora
:let @c = string(eval(@a))
" Intercambiar contenido de registros
:let temp = @a | let @a = @b | let @b = temp
" Rotar contenido entre registros
function! RotateRegisters()
let temp = @a
let @a = @b
let @b = @c
let @c = temp
endfunction
nnoremap <leader>rot :call RotateRegisters()<CR>
🔍 Búsqueda y navegación extrema
Búsquedas multi-archivo avanzadas
" Buscar y reemplazar en múltiples archivos
:args **/*.js | argdo %s/old/new/gc | update
" Búsqueda con contexto
:grep -n "pattern" **/*.py | copen
" Búsqueda de funciones específicas
:vimgrep /^function.*name/ **/*.js
" Búsqueda con exclusiones
:grep --exclude-dir=node_modules -r "pattern" .
" Búsqueda inversa (líneas que NO contienen patrón)
:v/pattern/d
" Mostrar solo líneas únicas
:sort u
Navegación de código avanzada
" Saltar a definición inteligente
function! SmartGoToDefinition()
" Intentar LSP primero
try
call CocAction('jumpDefinition')
catch
" Fallback a ctags
execute "normal! \<C-]>"
endtry
endfunction
nnoremap gd :call SmartGoToDefinition()<CR>
" Navegación de errores circular
function! NextError()
try
cnext
catch /^Vim\%((\a\+)\)\=:E553/
cfirst
endtry
endfunction
nnoremap ]e :call NextError()<CR>
" Saltar a siguiente/anterior función
nnoremap ]f /^\s*\(function\|def\|class\)<CR>
nnoremap [f ?^\s*\(function\|def\|class\)<CR>
" Navegación inteligente de brackets
function! MatchingBracket()
let line = getline('.')
let col = col('.')
let char = line[col-1]
if char =~ '[\[\](){}]'
normal! %
else
" Buscar el próximo bracket
call search('[\[\](){}]')
normal! %
endif
endfunction
nnoremap <leader>% :call MatchingBracket()<CR>
Marks avanzados
" Auto-marks para posiciones importantes
augroup AutoMarks
autocmd!
" Mark 'L' en última línea editada
autocmd BufLeave * normal! mL
" Mark 'Q' al salir de quickfix
autocmd FileType qf autocmd BufLeave <buffer> normal! mQ
augroup END
" Marks con nombres descriptivos
nnoremap <leader>mm :marks<CR>
nnoremap <leader>md :delmarks!<CR>
" Saltar a mark y centrar
nnoremap 'm `'zz
" Ciclar entre marks
let g:mark_cycle = ['a', 'b', 'c', 'd', 'e']
let g:mark_index = 0
function! CycleMark()
let mark = g:mark_cycle[g:mark_index]
execute 'normal! `' . mark . 'zz'
let g:mark_index = (g:mark_index + 1) % len(g:mark_cycle)
endfunction
nnoremap <C-m> :call CycleMark()<CR>
⚡ Edición ultra-rápida
Operadores personalizados
" Operador para buscar y reemplazar en selección
function! SubstituteOperator(type)
let saved_reg = @"
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
let pattern = escape(@", '/\')
call inputsave()
let replacement = input('Replace ' . @" . ' with: ')
call inputrestore()
if replacement != ''
execute "'<,'>s/" . pattern . "/" . replacement . "/g"
endif
let @" = saved_reg
endfunction
nnoremap <leader>s :set operatorfunc=SubstituteOperator<CR>g@
vnoremap <leader>s :<C-u>call SubstituteOperator(visualmode())<CR>
" Operador para envolver con tags
function! WrapWithTag(type)
let saved_reg = @"
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
call inputsave()
let tag = input('Tag: ')
call inputrestore()
if tag != ''
normal! `<
execute "normal! i<" . tag . ">"
normal! `>
execute "normal! a</" . tag . ">"
endif
let @" = saved_reg
endfunction
nnoremap <leader>w :set operatorfunc=WrapWithTag<CR>g@
vnoremap <leader>w :<C-u>call WrapWithTag(visualmode())<CR>
Múltiples cursores simulados
" Simular múltiples cursores con sustitución
function! MultipleCursors()
let word = expand('<cword>')
call inputsave()
let replacement = input('Replace ' . word . ' with: ')
call inputrestore()
if replacement != ''
execute '%s/\<' . word . '\>/' . replacement . '/gc'
endif
endfunction
nnoremap <leader>mc :call MultipleCursors()<CR>
" Múltiples cursores en selección visual
function! VisualMultipleCursors() range
let pattern = escape(getline("'<")[getpos("'<")[2]-1:getpos("'>")[2]-1], '/\')
call inputsave()
let replacement = input('Replace "' . pattern . '" with: ')
call inputrestore()
if replacement != ''
execute a:firstline . ',' . a:lastline . 's/' . pattern . '/' . replacement . '/gc'
endif
endfunction
vnoremap <leader>mc :call VisualMultipleCursors()<CR>
Edición de columnas avanzada
" Insertar numeración automática
function! InsertNumbers() range
let start = input('Start number: ', '1')
let step = input('Step: ', '1')
for i in range(a:firstline, a:lastline)
let num = start + (i - a:firstline) * step
call setline(i, num . ' ' . getline(i))
endfor
endfunction
command! -range InsertNumbers <line1>,<line2>call InsertNumbers()
" Alinear por delimitador
function! AlignByDelimiter() range
call inputsave()
let delimiter = input('Delimiter: ')
call inputrestore()
if delimiter != ''
execute a:firstline . ',' . a:lastline . 's/' . delimiter . '/\=repeat(" ", 20-col(".")) . "' . delimiter . '"/g'
endif
endfunction
command! -range AlignBy <line1>,<line2>call AlignByDelimiter()
" Incrementar números en columna
function! IncrementColumn() range
let increment = input('Increment by: ', '1')
execute a:firstline . ',' . a:lastline . 's/\d\+/\=submatch(0) + ' . increment . '/g'
endfunction
command! -range IncCol <line1>,<line2>call IncrementColumn()
🧠 Automatización inteligente
Auto-comandos ninja
" Auto-crear directorios al guardar
augroup AutoCreateDir
autocmd!
autocmd BufWritePre * if !isdirectory(expand('<afile>:p:h')) |
\ call mkdir(expand('<afile>:p:h'), 'p') | endif
augroup END
" Auto-reload si archivo cambió externamente
augroup AutoReload
autocmd!
autocmd FocusGained,BufEnter * checktime
autocmd CursorHold,CursorHoldI * checktime
augroup END
" Auto-save cuando perdemos foco
augroup AutoSave
autocmd!
autocmd FocusLost * silent! wa
autocmd TextChanged,TextChangedI * if &buftype == '' | silent write | endif
augroup END
" Auto-posicionarse en última posición conocida
augroup RestorePosition
autocmd!
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") |
\ execute "normal! g`\"" | endif
augroup END
" Auto-highlighting de palabra bajo cursor
augroup AutoHighlight
autocmd!
autocmd CursorHold * let @/ = '\<' . expand('<cword>') . '\>'
autocmd CursorHold * set hlsearch
autocmd CursorMoved * set nohlsearch
augroup END
Funciones inteligentes
" Toggle entre diferentes estilos de comentarios
function! ToggleCommentStyle()
let &commentstring = (&commentstring == '// %s') ? '/* %s */' : '// %s'
echo 'Comment style: ' . &commentstring
endfunction
nnoremap <leader>tc :call ToggleCommentStyle()<CR>
" Función para intercambiar ventanas
function! SwapWindows()
if winnr('$') == 2
let curwin = winnr()
let bufnum1 = bufnr('%')
wincmd w
let bufnum2 = bufnr('%')
execute 'buffer ' . bufnum1
wincmd w
execute 'buffer ' . bufnum2
endif
endfunction
nnoremap <leader>sw :call SwapWindows()<CR>
" Función para rotar ventanas
function! RotateWindows()
let wincount = winnr('$')
if wincount > 1
let curwin = winnr()
wincmd J
for i in range(wincount - 1)
wincmd w
endfor
execute curwin . 'wincmd w'
endif
endfunction
nnoremap <leader>rw :call RotateWindows()<CR>
" Smart tab que ajusta comportamiento según contexto
function! SmartTab()
let col = col('.') - 1
if !col || getline('.')[col - 1] !~ '\k'
return "\<Tab>"
else
return "\<C-n>"
endif
endfunction
inoremap <Tab> <C-r>=SmartTab()<CR>
🎨 Customización visual extrema
Status line personalizada avanzada
" Status line con información detallada
function! GitBranch()
return system("git rev-parse --abbrev-ref HEAD 2>/dev/null | tr -d '\n'")
endfunction
function! StatuslineGit()
let l:branchname = GitBranch()
return strlen(l:branchname) > 0 ? ' '.l:branchname.' ' : ''
endfunction
function! LinterStatus() abort
let l:counts = ale#statusline#Count(bufnr(''))
let l:all_errors = l:counts.error + l:counts.style_error
let l:all_warnings = l:counts.warning + l:counts.style_warning
return l:counts.total == 0 ? '✓' : printf('×%d ⚠%d', all_errors, all_warnings)
endfunction
set statusline=
set statusline+=%#PmenuSel#
set statusline+=%{StatuslineGit()}
set statusline+=%#LineNr#
set statusline+=\ %f
set statusline+=%m
set statusline+=%=
set statusline+=%#CursorColumn#
set statusline+=\ %y
set statusline+=\ %{&fileencoding?&fileencoding:&encoding}
set statusline+=\[%{&fileformat}\]
set statusline+=\ %{LinterStatus()}
set statusline+=\ %p%%
set statusline+=\ %l:%c
set statusline+=\
Colores dinámicos
" Cambiar color scheme según hora del día
function! SetColorSchemeByTime()
let hour = strftime('%H')
if hour >= 6 && hour < 18
colorscheme morning
set background=light
else
colorscheme evening
set background=dark
endif
endfunction
augroup DynamicColors
autocmd!
autocmd VimEnter * call SetColorSchemeByTime()
autocmd CursorHold * call SetColorSchemeByTime()
augroup END
" Highlight de línea actual solo en ventana activa
augroup ActiveWindow
autocmd!
autocmd VimEnter,WinEnter,BufWinEnter * setlocal cursorline
autocmd WinLeave * setlocal nocursorline
augroup END
🔧 Integración sistema avanzada
Clipboard inteligente
" Historial de clipboard
let g:clipboard_history = []
let g:clipboard_max_history = 10
function! AddToClipboardHistory()
let text = @+
if len(text) > 0 && index(g:clipboard_history, text) == -1
call insert(g:clipboard_history, text, 0)
if len(g:clipboard_history) > g:clipboard_max_history
call remove(g:clipboard_history, g:clipboard_max_history, -1)
endif
endif
endfunction
function! ShowClipboardHistory()
call inputsave()
let choice = inputlist(['Select from clipboard history:'] +
\ map(copy(g:clipboard_history), 'v:key+1 . ". " . v:val[:50]'))
call inputrestore()
if choice > 0 && choice <= len(g:clipboard_history)
let @+ = g:clipboard_history[choice-1]
normal! "+p
endif
endfunction
nnoremap <leader>ch :call ShowClipboardHistory()<CR>
autocmd TextYankPost * call AddToClipboardHistory()
Integración con herramientas externas
" Abrir archivo en aplicación externa
function! OpenWith()
call inputsave()
let app = input('Open with: ', 'code')
call inputrestore()
if app != ''
execute '!' . app . ' ' . shellescape(expand('%'))
endif
endfunction
nnoremap <leader>ow :call OpenWith()<CR>
" Enviar texto a aplicación externa
function! SendToApp() range
let lines = getline(a:firstline, a:lastline)
let text = join(lines, "\n")
call inputsave()
let command = input('Command: ')
call inputrestore()
if command != ''
call system(command, text)
endif
endfunction
vnoremap <leader>send :call SendToApp()<CR>
" Screenshot de código (usando highlight + convert)
function! CodeScreenshot() range
let temp_file = tempname() . '.html'
execute a:firstline . ',' . a:lastline . 'TOhtml'
execute 'write ' . temp_file
bdelete
call system('wkhtmltoimage ' . temp_file . ' code_screenshot.png')
call delete(temp_file)
echo 'Screenshot saved as code_screenshot.png'
endfunction
command! -range CodeShot <line1>,<line2>call CodeScreenshot()
🚀 Performance y debugging
Profiling de Vim
" Medir tiempo de carga de plugins
function! ProfileStartup()
execute 'profile start profile.log'
execute 'profile func *'
execute 'profile file *'
echo 'Profiling started. Restart Vim to see results.'
endfunction
command! ProfileStartup call ProfileStartup()
" Benchmark de comandos
function! Benchmark(command)
let start_time = reltime()
execute a:command
let elapsed = reltimestr(reltime(start_time))
echo 'Command executed in ' . elapsed . ' seconds'
endfunction
command! -nargs=1 Benchmark call Benchmark(<q-args>)
" Debug de highlighting
function! ShowSyntaxInfo()
for id in synstack(line('.'), col('.'))
echo synIDattr(id, 'name') . ' -> ' . synIDattr(synIDtrans(id), 'name')
endfor
endfunction
nnoremap <leader>syn :call ShowSyntaxInfo()<CR>
🎯 Ejercicios ninja
Retos progresivos
- Operador personalizado: Crea un operador que convierta texto a mayúsculas/minúsculas alternadas
- Macro recursiva: Implementa una macro que procese todas las funciones de un archivo
- Navegación inteligente: Crea función que salte automáticamente a errores más relevantes
- Automatización: Implementa auto-formateo inteligente basado en contexto
- Integración: Conecta Vim con tu herramienta favorita externa
Proyecto final ninja
Crear un "modo productividad" que:
- Active configuraciones optimizadas
- Cargue atajos específicos para tu trabajo
- Configure automatizaciones inteligentes
- Integre herramientas externas
- Monitore y optimice tu workflow
¡Con estos trucos ninja serás imparable en Vim! 🥷⚡