Advanced Techniques: Folds, Marks, Quickfix, Completion, Terminal
Folds
Folds collapse a region of text into a single line. Useful for navigating long files without scrolling past irrelevant code.
Fold methods
set foldmethod=indent " fold by indentation level
set foldmethod=syntax " fold by language syntax (requires support)
set foldmethod=marker " fold on explicit {{{ }}} markers
set foldmethod=manual " you create folds by hand
set foldmethod=expr " fold by a user-defined expression
For most code, syntax is the nicest if the language plugin supports it. indent works anywhere. marker is what people use for organising .vimrc files.
Fold commands
za toggle fold under the cursor
zo open fold
zc close fold
zR open all folds in the buffer
zM close all folds
zr reduce fold level by one
zm increase fold level by one
zj zk jump to next/previous fold
zf{motion} create a fold (manual method)
zd delete a fold (manual method)
[z ]z jump to start/end of the current fold
For a .vimrc with marker folds, add this at the end of the file:
" vim: foldmethod=marker
And wrap sections:
" Options {{{
set number
set relativenumber
" }}}
" Mappings {{{
nnoremap <leader>w :w<CR>
" }}}
Marks
Marks bookmark a position in a file. You saw them briefly in chapter 3. Now the full set.
Setting and jumping
m{a-z} set a lowercase mark (local to the file)
m{A-Z} set an uppercase mark (global across files)
'{mark} jump to the start of the line containing the mark
`{mark} jump to the exact position (column included)
Lowercase marks are per-buffer: mark a in one file doesn't affect mark a in another. Uppercase marks are global and include the filename: jumping to 'A opens the file if necessary.
Automatic marks
Vim maintains several marks without you setting them:
'. position of the last change
'^ position where insert mode was last left
'' position before the last jump
'[ start of the last yanked or changed text
'] end of the last yanked or changed text
'< start of the last visual selection
'> end of the last visual selection
'0 to '9 positions across recent sessions (see :help '0)
'' is the one to know. It sends you back to where you were before your last big jump.
gg jump to top of file
'' jump back to where you were
'0 opens the last file you were in before quitting Vim. Useful for resuming work.
Viewing marks
:marks show all marks
:delmarks a b delete marks a and b
:delmarks! delete all lowercase marks in the current buffer
Marks as text objects
With marks [ and ] set, you can use:
`[v`] visually select the last yanked or changed text
=`] reformat to the end of the last change
Plugins take this further. vim-textobj-user lets you define text objects that use mark positions.
Quickfix and Location Lists
The quickfix list is Vim's stash of file locations: errors, search results, greps, whatever. You jump between entries with a couple of keys.
Populating the list
:grep pattern path run system grep, load results
:vimgrep /pattern/ file Vim's built-in grep
:make run the program in 'makeprg', parse output
:cexpr expression populate from a Vim expression
:cbuffer populate from the current buffer
Navigating
:copen open the quickfix window
:cclose close it
:cnext :cn next entry
:cprevious :cp previous
:cfirst :clast first, last
:cc {n} jump to entry n
Example workflow. You want to find every usage of calculateTotal:
:vimgrep /calculateTotal/ **/*.js
:copen
The quickfix window lists every match with filename and line. Press <Enter> on a line to jump there. Or use :cnext to step through.
Location list
Each window has its own location list, parallel to the global quickfix. Same commands with l prefix:
:lopen :lclose
:lnext :lprev
Plugins like ALE and language servers use the location list for buffer-local diagnostics, leaving quickfix for project-wide operations.
Completion
Vim has built-in completion in insert mode. It is not as smart as LSP but requires no setup.
<C-n> next completion candidate (keyword completion)
<C-p> previous
<C-x><C-l> line completion (complete a whole line)
<C-x><C-f> filename completion (paths)
<C-x><C-o> omni-completion (filetype-aware, if a handler is set)
<C-x><C-i> include-file completion
<C-x><C-d> macro definition completion
<C-x><C-]> tags completion (requires ctags)
<C-x>s spelling suggestions (requires :set spell)
<C-n> completes from words in open buffers. Cheap and surprisingly useful. Start typing, <C-n>, pick, done.
For smarter completion, install an LSP-based plugin (coc.nvim, or the Neovim built-in LSP plus nvim-cmp). Once that's in, completion suggestions include symbols defined elsewhere in your project, function signatures, and import paths.
Spell Check
set spell
set spelllang=en_us
Misspelled words get underlined (if your colorscheme supports it).
]s next misspelling
[s previous
z= suggest corrections
zg add the word to your dictionary
zw mark the word as wrong
Great for markdown, commit messages, and prose. Toggle per-filetype:
autocmd FileType markdown setlocal spell
autocmd FileType gitcommit setlocal spell
Terminal Mode
Vim 8+ and Neovim have built-in terminals.
:terminal open a terminal in a new window (:term also works)
:vert terminal terminal in a vertical split
You enter terminal mode, which is a new mode specifically for the embedded shell. Keys go through to the shell.
Leave terminal mode (return to normal mode in the terminal window):
<C-w>N (Vim)
<C-\><C-n> (Neovim, also works in Vim)
Now you can scroll the terminal buffer with hjkl, search it with /, yank text out of it. It is an ordinary buffer in normal mode.
Back to terminal mode:
i or a enter terminal mode again
Common uses
- Run a test command, read the output, copy an error message back into your code
- Have a Python REPL in a vertical split beside your script
- Use
:term gitkor similar without leaving Vim
For most users, a real terminal in a separate pane is simpler. But it's there when you want it.
:normal and :execute
:normal runs normal-mode commands from an ex command:
:normal dd delete a line as if you typed dd
:normal! dd delete without triggering mappings
Combined with :global, you can do genuinely surprising things:
:g/TODO/normal! ddP every line with "TODO": swap with line above
:%normal! gUiw uppercase the first word of every line
:execute runs a string as an ex command:
:execute "edit " . fnameescape(filename)
Useful inside custom functions.
Basic Scripting: Custom Commands
Define your own commands:
command! W w " :W behaves like :w
command! -nargs=1 Find :e **/*<args>* " :Find foo opens foo.js etc
command! -range=% Strip <line1>,<line2>s/\s\+$//
For a starter: a function that saves and runs the current file:
function! RunFile()
write
if &filetype ==# 'python'
!python3 %
elseif &filetype ==# 'go'
!go run %
endif
endfunction
nnoremap <leader>r :call RunFile()<CR>
Add to .vimrc. <leader>r now saves and runs the current buffer based on its filetype.
Sessions
:mksession ~/sessions/project.vim save the current session
:source ~/sessions/project.vim restore
vim -S ~/sessions/project.vim restore from the shell
A session captures open buffers, window layout, marks, and options. Handy for picking up a project exactly where you left it.
Common Pitfalls
"My folds keep re-closing when I open a file." set nofoldenable on startup, or set a fold level: set foldlevel=99. zR opens all folds in the current buffer.
"I set a global mark and it won't jump." Global marks (A-Z) are tied to file paths. If you moved the file, the mark is stale. :delmarks A and re-set.
"Quickfix opens in a weird window." It is its own split. :cclose closes it. Pin the height with :copen 10 or map it. Every plugin uses quickfix; get used to it.
"Completion shows the same word once per open buffer, messily." Try a filetype-aware source: set omnifunc=syntaxcomplete#Complete, then use <C-x><C-o>. For real completion, install an LSP plugin.
"Terminal mode stole my key." <C-w> is a window command in Vim but a shell shortcut (delete word) in most terminals. Vim prefers its own. To pass a <C-w> to the terminal, prefix with <C-w><C-w>.
Next Steps
Continue to 12-best-practices.md for the habits that separate fluent users from survivors.