Building a Modern VIM Configuration from Scratch – Part 1: Solid Foundations

Understanding every setting, building incrementally, making it yours.

In my previous article, we set up a modern Unix environment on z/OS. Let’s build a VIM configuration that actually makes sense.

No copying mysterious snippets from the internet. No cargo-culting settings you don’t understand. We’ll build this step by step, and I’ll explain why each setting exists.

By the end, you’ll have a solid ~150 line configuration that’s comfortable, efficient, and one you can modify with confidence.

Who is this for?

This guide is for anyone who wants to understand their VIM configuration rather than just copy it.

Maybe you:

  • Just followed my previous article and have just installed VIM and want a good starting point
  • Have a .vimrc copied from somewhere, but don’t know what half of it does
  • Want to customize VIM but don’t know where to start

Important: This configuration reflects my current preferences. That’s the whole point. I’ll explain each setting so you can decide whether you want it. Think of this as a guided tour, not a rulebook.

This is Part 1 of a series:

  • Part 1 (this article): Core configuration, no plugins
  • Part 2: Essential plugins
  • Part 3: LSP with CoC and clangd for IDE features

Prerequisites

You’ll need:

  • VIM installed from Z Open Community Tools or IBM Open Enterprise Foundation for z/OS 
  • Basic VIM knowledge (you know what insert mode is, how to save/quit)

That’s it. No root access needed, no plugins yet.

The philosophy: No cargo-culting

Here’s the problem with copying someone’s 500-line .vimrc from GitHub:

  • You don’t know what half of it does
  • Settings conflict with each other
  • When something breaks, you have no idea where to look
  • You miss the chance to actually learn VIM

Our approach is different: every single setting gets explained.

You’ll understand why it’s there, what it does, and when you might want to change it.

The result? A configuration that’s actually yours.

Where we’re starting

Last time, we ended with this basic .vimrc:

set t_Co=256
syntax on
set number
set tabstop=4
set shiftwidth=4
set expandtab
set autoindent
set smartindent
set background=dark
colorscheme blue

It’s functional, but bare-bones. No organization, lots of missing features, and honestly? Not much better than default VIM.

Let’s fix that.

Organization first

Before we add features, let’s organize what we have.

A good .vimrc is structured into sections. Makes it easy to find things, easy to add features, easy to understand six months from now.

Here’s how we’ll structure ours, as an example:

" ============================================
" Basic Settings
" ============================================

" ============================================
" Colors & Scheme
" ============================================

" ============================================
" UI & Visuals
" ============================================

" ============================================
" Indentation & Formatting
" ============================================

" ============================================
" Search
" ============================================

" ============================================
" Behavior
" ============================================

" ============================================
" Completion
" ============================================

" ============================================
" Splits
" ============================================

" ============================================
" Backup & Swap
" ============================================

" ============================================
" Performance
" ============================================

" ============================================
" Key Mappings
" ============================================

" ============================================
" Auto Commands
" ============================================

" ============================================
" Clipboard Integration
" ============================================

Simple. Clean. Maintainable.

Now let’s fill these sections.

Basic Settings

" ============================================
" Basic Settings
" ============================================
set nocompatible              " Use VIM, not Vi
syntax on                     " Enable syntax highlighting
filetype plugin indent on     " Enable filetype detection

set nocompatible – Disables Vi compatibility mode. Vi is from 1976. VIM has hundreds of features Vi doesn’t. This unlocks them all. (It’s automatically set when you have a .vimrc, but being explicit doesn’t hurt.)

syntax on – Colors your code based on file type. Makes everything more readable. VIM knows 500+ languages out of the box.

filetype plugin indent on – Three things in one:

  • Detects file types (.c, .py, etc.)
  • Loads filetype-specific plugins
  • Loads filetype-specific indentation rules

This makes VIM language-aware. C files indent differently than Python files. Makes sense, right?

Colors & Scheme

" ============================================
" Colors & Scheme
" ============================================
set t_Co=256                 " Enable 256 colors
set background=dark          " Dark background
colorscheme blue             " Color scheme (will upgrade with plugins later)

t_Co=256 – Tells VIM your terminal supports 256 colors, even if TERM=xterm.

background=dark – You’re using a dark terminal background. VIM adjusts colors for better contrast. (If you use light backgrounds, set this to light.)

colorscheme blue – Built-in color scheme. It’s… okay. We’ll upgrade this with better themes in Part 2 (gruvbox, tokyonight, etc.).

UI & Visuals

This is where VIM starts feeling nice to use.

" ============================================
" UI & Visuals
" ============================================
set number                   " Show absolute line numbers
set relativenumber           " Show relative line numbers
set cursorline               " Highlight current line
set showmatch                " Highlight matching brackets
set ruler                    " Show line/column in status
set showcmd                  " Show command in status line
set showmode                 " Show current mode (INSERT, VISUAL, etc.)
set wildmenu                 " Enhanced command-line completion
set wildmode=longest:full,full " Complete longest common string, then full match
set laststatus=2             " Always show status line
set signcolumn=yes           " Always show sign column (for LSP/linting later)
set colorcolumn=72,80        " Visual guides at columns 72 and 80

Line Numbers: Why Both?

I use hybrid mode – both absolute and relative numbers.

It looks like this:

  3  int foo() {
  2      int x = 5;
  1      int y = 10;
42       return x + y;  ← Current line (absolute)
  1  }
  2

Why?

  • Current line shows absolute number – useful for error messages (“Error on line 42”)
  • Other lines show relative distance – perfect for VIM motions

Using relative numbers: See “3” above your cursor? Type d3k to delete 3 lines up. Or V2j to visually select 2 lines down. The number you see is the number you type.

(Don’t like relative numbers? Just remove set relativenumber. That’s the point of understanding your config!)

The Other Settings

cursorline – Highlights the line your cursor is on. Easy to track where you are.

showmatch – When you type a closing bracket, VIM briefly jumps to the matching opening bracket. Helps catch mismatched brackets early.

wildmenu + wildmode – Better command-line completion. Type :colorscheme <Tab> and see a menu of schemes. Much nicer than the default.

signcolumn=yes – A dedicated column for signs (marks, errors, warnings). Keeps it always visible so your text doesn’t jump around when signs appear. Essential for LSP in Part 3.

colorcolumn=72,80 – Vertical guides at columns 72 and 80. You should know why I have added those.  😑

Indentation & Formatting

The tabs vs spaces debate? We’re settling it: spaces.

" ============================================
" Indentation & Formatting
" ============================================
set tabstop=2                " Tab displays as 2 spaces
set shiftwidth=2             " Indent/outdent by 2 spaces
set softtabstop=2            " Backspace deletes 2 spaces
set expandtab                " Convert tabs to spaces
set autoindent               " Copy indent from previous line
set smartindent              " Smart indent for C-like languages
set nowrap                   " Don't wrap long lines visually
set linebreak                " Break at word boundaries (when wrap is on)
set textwidth=0              " Don't auto-insert line breaks

Understanding the Tab Settings

tabstop=2 – How many spaces a Tab character displays as. If a file has actual \t characters, they show as 2 spaces wide.

shiftwidth=2 – How many spaces >> (indent) and << (outdent) use.

softtabstop=2 – How many spaces Backspace deletes. Makes spaces feel like tabs when editing.

expandtab – Pressing <Tab> inserts spaces, not a tab character.

Why spaces? Consistent across all editors. Required by many projects. Avoids the holy war.

Why 2 spaces? Modern standard for JavaScript, Ruby, and many other projects. You prefer 4? Change all four settings to 4. That’s fine! Python folks often use 4.

Auto-Indentation

autoindent – New lines copy the indentation of the previous line. Simple.

smartindent – Smarter for programming. Indents after {, outdents on }. Works for C-like languages.

(Later, LSP will provide even better, language-specific indentation.)

Line Wrapping

nowrap – Long lines don’t wrap visually. I prefer horizontal scrolling. You like wrapping? Use set wrap.

linebreak – If wrapping is on, break at word boundaries (not mid-word).

textwidth=0 – Don’t auto-insert line breaks. You control where lines break, not VIM. (Some people like set textwidth=79 for automatic wrapping. Try it if you want!)

Search Settings

Make searching fast and smart.

" ============================================
" Search
" ============================================
set hlsearch                 " Highlight search results
set incsearch                " Incremental search (search as you type)
set ignorecase               " Case-insensitive search
set smartcase                " Case-sensitive if uppercase present

incsearch – See matches while you type. Type /hel and VIM highlights “hel” immediately. Adjust before pressing Enter.

hlsearch – Highlights all matches. Stays highlighted until you search for something else (or clear it with our <leader>/ keybinding coming later).

Smart Case Searching

ignorecase + smartcase work together:

  • /hello matches: hello, Hello, HELLO (case-insensitive)
  • /Hello matches: Hello only (case-sensitive because uppercase)

Best of both worlds. Fast when you don’t care, precise when you do.

Behavior Settings

How VIM responds to your actions.

" ============================================
" Behavior
" ============================================
set encoding=utf-8           " Use UTF-8 encoding (critical for z/OS!)
set backspace=indent,eol,start " Backspace over everything in insert mode
set hidden                   " Hide buffers instead of closing them
set history=1000             " Command history length
set undolevels=1000          " More undo steps
set mouse=a                  " Enable mouse support in all modes
set clipboard=unnamed        " Use system clipboard (when available)
set updatetime=300           " Faster completion (default 4000ms)
set timeoutlen=500           " Faster key sequence timeout (default 1000ms)
set modeline                 " Enable modeline support
set modelines=10             " Number of lines checked for modeline commands
set belloff=all              " Disable all bells (audio and visual)

The Critical Ones

encoding=utf-8Essential for z/OS! z/OS uses EBCDIC, modern tools expect UTF-8. This prevents encoding corruption. Also required for LSP servers (Part 3).

backspace=indent,eol,start – Without this, Backspace feels broken. You can’t delete indentation or join lines. With it, Backspace works like you expect.

hidden – Switch buffers without saving. Modern workflow essential. Without it, VIM forces you to save or abandon changes every time you open another file.

The Nice-to-Haves

history=1000 and undolevels=1000 – More history = more safety. Default is often 50-100.

mouse=a – Mouse support. Click to position cursor, scroll with wheel, select text. Doesn’t prevent keyboard workflow – just adds convenience.

clipboard=unnamed – Yank/paste uses system clipboard. y copies to clipboard, p pastes from clipboard.

(Hint: On z/OS over SSH, using the system clipboard often doesn’t work. We’ll fix this later.)

belloff=all – No more annoying beeps when you press <Esc> in normal mode.

Completion

" ============================================
" Completion
" ============================================
set completeopt=menu,menuone,noselect  " Better completion menu behavior

This controls how the auto-completion menu behaves. Will be important when we add LSP in Part 3. For now, just set it and forget it.

Split Behavior

" ============================================
" Splits
" ============================================
set splitbelow               " Horizontal splits open below
set splitright               " Vertical splits open to right

Default VIM might open splits above and to the left. Counterintuitive!

These settings make splits open below and to the right. Matches reading direction. Much more natural.

Backup & Swap Files

VIM creates backup and swap files by default. Should you keep them?

" ============================================
" Backup & Swap
" ============================================
set nobackup                 " Don't create backup files
set nowritebackup            " No backup before overwriting
set noswapfile               " Don't create swap files

My choice: Disable them.

Why:

  • Cleaner directories (no file~ or .file.swp everywhere)
  • No “swap file exists” warnings
  • Git handles versioning better than backup files

Trade-off: No crash recovery. If VIM crashes, you lose unsaved work.

Alternative: Keep them, but centralized:

" Uncomment if you want crash recovery:
" set backupdir=~/.vim/backup//
" set directory=~/.vim/swap//
" set undodir=~/.vim/undo//
"
" Don't forget: mkdir -p ~/.vim/{backup,swap,undo}

The // at the end uses full paths in filenames, preventing name collisions.

Your choice. I disable them. You might prefer the safety net.

Performance

" ============================================
" Performance
" ============================================
set lazyredraw               " Don't redraw during macros
set ttyfast                  " Fast terminal connection

lazyredraw – Don’t redraw screen during macro execution. Makes macros faster, less visual noise.

ttyfast – Tells VIM the terminal connection is fast. Smoother scrolling and redraws. Always good on modern terminals/SSH.

Key Mappings – This is Where it Gets Good

Custom keybindings save thousands of keystrokes. This is where VIM becomes your editor.

" ============================================
" Key Mappings
" ============================================
let mapleader = " "          " Space as leader key

" Clear search highlighting
nnoremap <leader>/ :nohlsearch<CR>

" Buffer navigation
nnoremap <C-j> :bnext<CR>
nnoremap <C-k> :bprev<CR>
nnoremap <leader>bd :bdelete<CR>

" Window management
nnoremap <leader>wc <C-w>c   " Close window
nnoremap <leader>ws <C-w>s   " Split horizontal
nnoremap <leader>wv <C-w>v   " Split vertical
nnoremap <leader>wo <C-w>o   " Close others
nnoremap <leader>w= <C-w>=   " Equalize sizes

The Leader Key

let mapleader = ” “ – The leader key is a prefix for custom commands. Default is \ (backslash). I use Space – easy to reach, doesn’t conflict with VIM defaults.

Usage: <leader>w means Space then w.

Buffer Navigation

<C-j> and <C-k> (Ctrl+j, Ctrl+k) – Switch between open files (buffers).

Mnemonic: j=down/next, k=up/previous

Workflow:

:e file1.c    " Open file1
:e file2.c    " Open file2
<C-k>         " Back to file1
<C-j>         " Forward to file2
Space bd      " Close current file

Window Management

All window operations start with <leader>w:

  • Space ws – Split horizontal
  • Space wv – Split vertical
  • Space wc – Close window

Pattern makes it easy to remember.

Custom Functions – Line Number Toggle

Sometimes you need to quickly toggle features. Here’s one I use constantly:

" Toggle line numbers (useful for copy/paste)
function! ToggleNumbers()
  if &number || &relativenumber
    set nonumber
    set norelativenumber
  else
    set number
    set relativenumber
  endif
endfunction
nnoremap <leader>n :call ToggleNumbers()<CR>

The problem: When you select text with the mouse to copy, line numbers get included.

The solution:

  1. Space n – Numbers off
  2. Select with mouse, Cmd+C
  3. Space n – Numbers back on

Clean copy, no line numbers.

Auto Commands – Automation

Auto commands run automatically on events. Powerful for repetitive tasks.

" ============================================
" Auto Commands
" ============================================

" Remember cursor position between sessions
augroup remember_cursor
  autocmd!
  autocmd BufReadPost *
    \ if line("'\"") > 1 && line("'\"") <= line("$") |
    \   exe "normal! g`\"" |
    \ endif
augroup END

" Filetype detection
augroup custom_filetypes
  autocmd!
  autocmd BufNewFile,BufRead buildenv set filetype=sh
  autocmd BufNewFile,BufRead *.asm set filetype=hlasm
augroup END

Remember Cursor Position

Reopen a file → cursor returns to where you last were. Quality of life improvement.

Custom Filetypes

Examples:

  • buildenv files are shell scripts (zopen build system)
  • *.asm files are HLASM (z/OS assembler) – This will not work out of the box. We will fix this in a later article.

VIM doesn’t recognize these by default. Now it does.

The Pattern

Always wrap autocmds in groups:

augroup group_name
  autocmd!  " Clear existing - prevents duplicates
  " ... your commands ...
augroup END

Without autocmd!, sourcing .vimrc multiple times creates duplicate commands. Functions run twice. Bad times.

The Complete Configuration

Here’s everything together. Copy this to ~/.vimrc:

" ============================================
" Basic Settings
" ============================================
set nocompatible              " Use VIM, not Vi
syntax on                     " Enable syntax highlighting
filetype plugin indent on     " Enable filetype detection

" ============================================
" Colors & Scheme
" ============================================
set t_Co=256                 " Enable 256 colors
set background=dark          " Dark background
colorscheme blue             " Color scheme (will upgrade with plugins later)

" ============================================
" UI & Visuals
" ============================================
set number                   " Show absolute line numbers
set relativenumber           " Show relative line numbers
set cursorline               " Highlight current line
set showmatch                " Highlight matching brackets
set ruler                    " Show line/column in status
set showcmd                  " Show command in status line
set showmode                 " Show current mode (INSERT, VISUAL, etc.)
set wildmenu                 " Enhanced command-line completion
set wildmode=longest:full,full " Complete longest common string, then full match
set laststatus=2             " Always show status line
set signcolumn=yes           " Always show sign column (for LSP/linting later)
set colorcolumn=72,80        " Visual guides at columns 72 and 80

" ============================================
" Indentation & Formatting
" ============================================
set tabstop=2                " Tab displays as 2 spaces
set shiftwidth=2             " Indent/outdent by 2 spaces
set softtabstop=2            " Backspace deletes 2 spaces
set expandtab                " Convert tabs to spaces
set autoindent               " Copy indent from previous line
set smartindent              " Smart indent for C-like languages
set nowrap                   " Don't wrap long lines visually
set linebreak                " Break at word boundaries (when wrap is on)
set textwidth=0              " Don't auto-insert line breaks

" ============================================
" Search
" ============================================
set hlsearch                 " Highlight search results
set incsearch                " Incremental search (search as you type)
set ignorecase               " Case-insensitive search
set smartcase                " Case-sensitive if uppercase present

" ============================================
" Behavior
" ============================================
set encoding=utf-8           " Use UTF-8 encoding (critical for z/OS!)
set backspace=indent,eol,start  " Backspace over everything in insert mode
set hidden                   " Hide buffers instead of closing them
set history=1000             " Command history length
set undolevels=1000          " More undo steps
set mouse=a                  " Enable mouse support in all modes
set clipboard=unnamed        " Use system clipboard (when available)
set updatetime=300           " Faster completion (default 4000ms)
set timeoutlen=500           " Faster key sequence timeout (default 1000ms)
set modeline                 " Enable modeline support
set modelines=10             " Number of lines checked for modeline commands
set belloff=all              " Disable all bells (audio and visual)

" ============================================
" Completion
" ============================================
set completeopt=menu,menuone,noselect  " Better completion menu behavior

" ============================================
" Splits
" ============================================
set splitbelow               " Horizontal splits open below
set splitright               " Vertical splits open to right

" ============================================
" Backup & Swap
" ============================================
set nobackup                 " Don't create backup files
set nowritebackup            " No backup before overwriting
set noswapfile               " Don't create swap files

" ============================================
" Performance
" ============================================
set lazyredraw               " Don't redraw during macros
set ttyfast                  " Fast terminal connection

" ============================================
" Key Mappings
" ============================================
let mapleader = " "          " Space as leader key

" Clear search highlighting
nnoremap <leader>/ :nohlsearch<CR>

" Buffer navigation
nnoremap <C-j> :bnext<CR>
nnoremap <C-k> :bprev<CR>
nnoremap <leader>bd :bdelete<CR>

" Window management
nnoremap <leader>wc <C-w>c   " Close window
nnoremap <leader>ws <C-w>s   " Split horizontal
nnoremap <leader>wv <C-w>v   " Split vertical
nnoremap <leader>wo <C-w>o   " Close others
nnoremap <leader>w= <C-w>=   " Equalize sizes

" Toggle line numbers (useful for copy/paste)
function! ToggleNumbers()
  if &number || &relativenumber
    set nonumber
    set norelativenumber
  else
    set number
    set relativenumber
  endif
endfunction
nnoremap <leader>n :call ToggleNumbers()<CR>

" ============================================
" Auto Commands
" ============================================

" Remember cursor position between sessions
augroup remember_cursor
  autocmd!
  autocmd BufReadPost *
    \ if line("'\"") > 1 && line("'\"") <= line("$") |
    \   exe "normal! g`\"" |
    \ endif
augroup END

" Filetype detection
augroup custom_filetypes
  autocmd!
  autocmd BufNewFile,BufRead buildenv set filetype=sh
  autocmd BufNewFile,BufRead *.asm set filetype=hlasm
augroup END

One More Thing

You might have issues with the backspace and / or the delete key. In this case add following settings to your .vimrc.
(You will find the best matching category.)

set t_kb=^?                  " fix backspace key
set t_kD=^[[3~               " fix delete key

Testing Your Configuration

Installation

# Backup existing config
cp ~/.vimrc ~/.vimrc.backup

# Copy the new config
vim ~/.vimrc
# Paste the complete configuration
# Save and quit: :x

Verification

Open VIM and test:

" Check settings
:set number?             " Should show 'number'
:echo mapleader          " Should show ' ' (space)

" Test keybindings
Space n                  " Should toggle numbers

" Test splits
Space ws                 " Create split
Space wc                 " Close split

" Test search
/test                    " Search
Space /                  " Clear highlight

Cursor Position Memory Test

# Create test file
echo -e "line1\nline2\nline3\nline4\nline5" > test.txt

# Open, go to line 3, quit
vim test.txt
3G
:q

# Reopen - should be at line 3
vim test.txt

Make It Yours

This basic configuration reflects my preferences. That’s the point. Now you should understand each setting and be able to adapt it.

Don’t like relative numbers? Remove set relativenumber.

Prefer 4-space indents? Change tabstop, shiftwidth, softtabstop to 4.

Want visual line wrapping? Use set wrap instead of set nowrap.

Different leader key? Try let mapleader = ","

Experimentation workflow:

  1. Make change in .vimrc
  2. Save
  3. :source ~/.vimrc in VIM
  4. Test
  5. Keep or revert

That’s the beauty of understanding your config – you can confidently modify it.


What’s Next

You now have a solid foundation. ~150 lines you fully understand.

In Part 2, we’ll add plugins:

  • Plugin management with vim-plug
  • File navigation (NERDTree, fzf)
  • Git integration (fugitive, gitgutter)
  • Better themes (gruvbox, tokyonight)
  • Enhanced editing (auto-pairs, commentary)

In Part 3, IDE features with LSP:

  • CoC (Conquer of Completion)
  • Auto-completion
  • Go-to-definition
  • Real-time diagnostics
  • LSP (yeah we will have clangd on USS)

Key Takeaways

What we built:

  • ✅ ~150 lines of organized, documented configuration
  • ✅ Every setting explained and justified
  • ✅ Efficient keybindings for common tasks
  • ✅ Smart automation with auto-commands
  • ✅ Clipboard over SSH (OSC 52)
  • ✅ Foundation ready for plugins

Core principles:

  • Understanding beats copying
  • Organization enables maintenance
  • Every setting serves a purpose
  • Make it yours

You now have a VIM configuration you understand and can confidently modify.

In the next article, we’ll supercharge it with plugins.


Resources

VIM Documentation:

  • :help <setting> – Built-in help
  • vimhelp.org – Browsable online

Learning:

Related:


Questions or feedback? Drop a comment below!

Found this helpful? Share it with other z/OS VIM users!

3 thoughts on “Building a Modern VIM Configuration from Scratch – Part 1: Solid Foundations”

Leave a Comment