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
: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 gitk or 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.