Skip to content
Vim Advanced — Registers, Macros, Marks, Quickfix, Vimscript, Plugin Development

Vim Advanced — Registers, Macros, Marks, Quickfix, Vimscript, Plugin Development

DodaTech Updated Jun 20, 2026 10 min read

Vim is the ultimate text editor for speed and precision. This advanced guide covers the features that separate Vim novices from Vim masters — registers, macros, marks, the quickfix system, Vimscript programming, and creating your own plugins.

What You’ll Learn

You’ll use registers to store and replay text, record and edit macros for complex repetitive edits, navigate code with marks and jumps, manage build errors with quickfix lists, write Vimscript functions and commands, and create your own plugins. DodaZIP’s developers use Vim macros for batch configuration file editing across hundreds of servers.

Why Advanced Vim Matters

Vim’s modal editing is already the fastest way to edit text. But its real power comes from the advanced features: macros that replay complex edits, registers that store multiple pieces of text, marks that let you jump across files, and Vimscript that turns Vim into a fully programmable editing environment.

Learning Path

    flowchart LR
  A[Vim Basics] --> B[Vim Advanced<br/>You are here]
  B --> C[Vimscript Programming]
  C --> D[Plugin Development]
  style B fill:#f90,color:#fff
  

Registers

Registers are named storage locations for text and operations:

Register Types

RegisterIdentifierPurpose
Unnamed""Last yank, delete, or change
Numbered"0 to "9"0 = last yank, "1-"9 = last 9 deletes
Small delete"-Deletes < 1 line
Named"a to "zUser-assigned storage
Read-only":, "., "%Last command, last inserted text, current file
Expression"=Evaluate expression
Black hole"_Discard deleted text (like /dev/null)
Selection"*, "+System clipboard

Using Registers

" Yank into register a
"ayw

" Paste from register a
"ap

" Append to register a (uppercase)
"Ayw

" View all registers
:reg
:reg a b c    " Specific registers

" Paste from system clipboard
"+p
" Or from primary selection
"*p

" Execute register as commands (on command-line)
" Ctrl+R a (inserts contents of register a into command line)

Practical Register Examples

" Swap two words using registers
" Delete first word into register a: "adiw
" Delete second word into register b: "bdiw
" Paste a over second word:   "bP (since buffer is gone)
" Paste b over first word:    "ap

" Collect matching lines
:g/pattern/yank A    " Append all matching lines to register a
"ap                   " Paste all collected lines

" Numbered registers — recover deleted text
dd                   " Delete current line (goes to "1)
dd                   " Delete another (shifts to "2)
"1p                  " Paste the first deletion

Macros

Macros record a sequence of commands into a register for replay:

Recording and Playing

" Start recording into register a
qa

" Perform your edit sequence (any Vim commands)
Iprefix_<Esc>       " Insert prefix at line start
A_suffix<Esc>       " Append suffix at line end
j                   " Move to next line

" Stop recording
q

" Play the macro once
@a

" Play the macro from register a, 10 times
10@a

" Play the last recorded macro
@@

Advanced Macro Techniques

" Repeat until end of file
99@a    " Play macro 99 times — stop at EOF with error

" Better: recursive macro
" Record macro q: execute commands then @q
" Stop when line is empty or pattern doesn't match

" Edit a macro
" Open register a
:put a
" Edit the commands as text
" Yank back into register
"ayy

" Normalize whitespace with a macro
qa
:s/\s\+/ /g<CR>    " Collapse whitespace
:g/^$/d<CR>         " Delete empty lines
%s/foo/bar/g<CR>   " Replace
q

" Run macro on visual selection lines
" Select lines, then:
:normal @a

Example: CSV Transformation Macro

" Input:
name,email,role
alice,alice@example.com,admin
bob,bob@example.com,user

" Macro in register c:
qa
0f,                 " Jump to first comma
r;                  " Replace with semicolon
f,                  " Jump to next comma
r;                  " Replace with semicolon
f,                  " Jump to next comma
r;                  " Replace with semicolon
j                   " Next line
q

" Run on all lines
:1,$normal @a

" Result:
name;email;role
alice;alice@example.com;admin
bob;bob@example.com;user

Marks

Marks let you save positions and jump across files:

Mark Types

MarkScopeUsage
a-zFile-localWithin current file
A-ZGlobalAcross files (file + position)
0-9Read-only.viminfo positions from previous sessions
`` `AutomaticLast jump position

Using Marks

" Set mark a at current position
ma

" Jump to mark a (first non-blank in line)
'a

" Jump to mark a (exact line and column)
`a

" Jump to last edit position
`.

" List all marks
:marks
:marks a b c

" Delete mark
:delm a
:delm A-Z         " Delete all global marks

Practical Mark Usage

" During code review:
" Set marks at key checkpoints
ma  " Top of function
mb  " Conditional logic
mc  " Return statement

" Jump between them
'a, 'b, 'c

" Global marks for project navigation
mG  " Mark important function in any file
mT  " Mark TODO location
'G  " Jump there from anywhere

Quickfix and Location Lists

Quickfix lists manage build errors, search results, and diagnostics:

Using Quickfix

" Fill quickfix from a command
:grep -r "TODO" src/

" Fill from vimgrep
:vimgrep /pattern/ src/**/*.js

" Fill from make output
:make

" Navigation
:copen              " Open quickfix window
:cclose             " Close quickfix window
:cnext              " Next item
:cprev              " Previous item
:cfirst             " First item
:clast              " Last item
:cnfile             " Next file
:cpfile             " Previous file

" Filter quickfix
:cexpr getqflist()  " Get as list
:call setqflist([])  " Clear

" Location list (buffer-local)
:lopen
:lnext / :lprev

Quickfix with grep

" Define a grepprg (use ripgrep if available)
:set grepprg=rg\ --vimgrep\ --no-heading
:set grepformat=%f:%l:%c:%m

:grep "function.*validate" --type js
:copen
" Now use :cn, :cp to navigate matches

Vimscript

Vimscript (VimL) is Vim’s scripting language for configuration and plugins:

Variables and Types

" Variable types
let g:global_var = "global"
let s:script_var = "script-local"
let l:local_var = "function-local"
let w:window_var = "window-local"
let b:buffer_var = "buffer-local"

" Data types
let string = "hello"
let number = 42
let float = 3.14
let list = ['a', 'b', 'c']
let dict = {'key': 'value', 'num': 42}

" String concatenation
let full = "Hello" . " " . "World"

" List operations
let first = list[0]
call add(list, 'd')
let len = len(list)

Functions

function! Greet(name) abort
    return "Hello, " . a:name . "!"
endfunction

" Call it
echo Greet("Alice")

" Function with default argument
function! Multiply(a, ...) abort
    let b = get(a:000, 0, 1)    " Default to 1
    return a:a * b
endfunction

" Range function
function! SumLines() range abort
    let total = 0
    for line in getline(a:firstline, a:lastline)
        let total += str2nr(line)
    endfor
    return total
endfunction

" Call on visual selection
:'<,'>call SumLines()

Commands

" Define a custom command
command! -nargs=1 Grep call GrepWithRipgrep(<q-args>)

command! -nargs=0 FormatCode call FormatCurrentFile()
function! FormatCurrentFile()
    write
    execute "!prettier --write " . expand('%')
    edit!
endfunction

" Command with completion
command! -nargs=? -complete=dir ListFiles
    \ echo globpath(<q-args> . '/', '*')

Autocommands

" Automatically run actions on events
augroup mygroup
    autocmd!

    " Auto-indent JSON files
    autocmd BufRead,BufNewFile *.json setlocal tabstop=2 shiftwidth=2

    " Strip trailing whitespace on save
    autocmd BufWritePre * :%s/\s\+$//e

    " Reload vimrc on save
    autocmd BufWritePost $MYVIMRC source $MYVIMRC
augroup END

Plugin Development

Minimal Plugin Structure

~/.vim/pack/myplugins/opt/myplugin/
├── plugin/
│   └── myplugin.vim     " Loaded on startup
├── autoload/
│   └── myplugin.vim     " Lazy-loaded functions
├── doc/
│   └── myplugin.txt     " Help file
└── syntax/
    └── myfiletype.vim   " Syntax highlighting

A Simple Plugin

" ~/.vim/pack/myplugins/opt/hello/plugin/hello.vim
if exists('g:loaded_hello')
    finish
endif
let g:loaded_hello = 1

command! Hello echo "Hello from my plugin!"

function! hello#greet(name)
    return "Hello, " . a:name . "!"
endfunction

Autoload Functions

" ~/.vim/pack/myplugins/opt/hello/autoload/hello.vim

" These functions are only loaded when called
function! hello#format_json()
    %!python3 -m json.tool
    setlocal filetype=json
endfunction

function! hello#timestamp()
    put =strftime('%Y-%m-%d %H:%M:%S')
endfunction

Common Advanced Vim Mistakes

1. Forgetting to Use Named Registers

Using ""p pastes the last yank or delete — which might be from a macro. Use "0p for the last explicit yank. Named registers ("a-"z) prevent accidental overwrites.

2. Recording Macros Without Error Handling

If a macro uses j (move down) and hits the last line, the rest fails silently. Add error handling: use :normal! @a with silent! or structure macros to stop at clear boundaries.

3. Overwriting "0 Register

Every yank or delete overwrites registers. Use uppercase registers ("A) to append. Use "0p to paste the last yank even after other operations.

4. Not Using Relative Line Numbers

set relativenumber shows distances from the cursor, enabling 5j (5 lines down) instead of counting absolute line numbers.

5. Ignoring the .vimrc

A default Vim installation is painful. Every Vim user should customize settings. Start with basic settings (line numbers, tabs as spaces, search highlight) and add features as needed.

6. Manual Repeated Edits

If you do the same edit on 20 lines, don’t repeat manually 20 times. Record a macro once and replay it 20 times. If you do a task more than 3 times, macro it.

7. Not Using Quickfix for Search

:vimgrep + quickfix is faster and more structured than :grep. It shows matches in a navigable list with file paths and line numbers, and you can jump to each match with :cn.

Practice Questions

1. What’s the difference between 'a and `a when jumping to a mark? 'a jumps to the first non-blank character of the line containing mark a. `a jumps to the exact line and column where the mark was set.

2. How do you run a macro on a visual selection of lines? Select the lines, then press :normal @a (the :'<,'> range is automatically inserted). This runs macro a on each selected line.

3. How do you copy text to the system clipboard from Vim? Use "+y to yank into the system clipboard register. Use "*y for the primary selection (middle-click paste on Linux).

4. What is the difference between quickfix and location lists? Quickfix lists are global — shared across all windows. Location lists are per-window. Use quickfix for project-wide results (compile errors, search), location lists for buffer-specific results.

5. Challenge: You need to rename a variable oldName to newName in 15 files, but only when oldName is used as a method receiver (not a local variable). Write a Vimscript function using quickfix to do this safely. Answer: Use :vimgrep with a pattern matching the specific context. Navigate quickfix with :cn, check each match, and use :s if correct. Or write a function that iterates the quickfix list and conditionally substitutes.

Mini Project: Build a Log Navigation Plugin

Create a Vim plugin that helps navigate structured log files:

" ~/.vim/pack/myplugins/opt/lognav/plugin/lognav.vim
if exists('g:loaded_lognav')
    finish
endif
let g:loaded_lognav = 1

" Command to open quickfix with all ERROR lines
command! -range=% LogErrors call lognav#find_errors(<line1>, <line2>)

" Command to navigate to next WARNING
command! LogNextWarning call lognav#next_warning()

" Command to show log summary
command! LogSummary call lognav#summary()
" ~/.vim/pack/myplugins/opt/lognav/autoload/lognav.vim
function! lognav#find_errors(line1, line2) range
    cexpr []
    silent execute a:line1 . ',' . a:line2 . 'g/ERROR/caddexpr expand("%") . ":" . line(".") . ": " . getline(".")'
    copen
    echo "Found " . len(getqflist()) . " errors"
endfunction

function! lognav#next_warning()
    if !exists('w:warning_pattern')
        let w:warning_pattern = 'WARNING'
    endif
    call search(w:warning_pattern)
endfunction

function! lognav#summary()
    let errors = 0
    let warnings = 0
    let infos = 0

    for line in getline(1, '$')
        if line =~ 'ERROR' | let errors += 1
        elseif line =~ 'WARNING' | let warnings += 1
        elseif line =~ 'INFO' | let infos += 1
        endif
    endfor

    echo printf("Log Summary: %d ERROR, %d WARNING, %d INFO", errors, warnings, infos)
endfunction

FAQ

How do I learn Vimscript?
Start by reading :help vim-script-intro. Read other people’s .vimrc files. The “Learn Vimscript the Hard Way” book by Steve Losh is excellent. Practice by writing small functions that automate your daily tasks.
How do I debug Vimscript?
Use :echom "message" to print debug messages (view with :messages). Use :debug before a command to step through execution. :call Log() works for runtime logging.
What’s the difference between :normal and :normal!?
:normal executes normal mode commands respecting user mappings. :normal! uses default Vim mappings, ignoring user overrides. Use :normal! in plugins to ensure consistent behavior.
How do I distribute my Vim plugin?
Publish on GitHub with a README.md, a plugin/ directory, and doc/ with help text. Use vim-plug, Vundle, or packpath for installation. Consider using vim-scripts.org for discovery.
Should I use Neovim instead?
Neovim is a fork of Vim with better Lua integration, async job control, and a modern plugin ecosystem. If starting fresh today, use Neovim. If you have a mature .vimrc, Vim 9 is still excellent.
How do I profile Vim performance?
:profile start profile.log then :profile func * and :profile file *. Run your commands, then :profile pause and review profile.log for slow functions.

What’s Next

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro