Scripting

At some point, mappings and settings aren't enough. You want Vim to do something truly custom — create a backup with a timestamp, open all git-modified files, or build a mini-plugin for your team. That's when you reach for scripting.

Vim has its own scripting language, Vimscript, which has been around since the early days. It's quirky but powerful, and a lot of existing plugins and configurations use it. Neovim adds first-class support for Lua — a fast, clean language that makes writing plugins and complex configurations genuinely enjoyable.

This chapter teaches both. We start with Vimscript fundamentals and useful patterns, then transition to Lua scripting with Neovim's rich API. By the end, you'll be able to write your own functions, commands, and even simple plugins.

What you'll learn in this chapter:

  • Write Vimscript functions using variables, conditionals, loops, and the Preserve pattern

  • Create custom commands with arguments and range support

  • Use Lua to script Neovim with its buffer, window, and autocommand APIs

  • Build a simple Neovim plugin from scratch using the Lua module pattern

  • Leverage floating windows, user commands, and notifications in Neovim's API

Vimscript Basics

Variables

let g:my_var = 'global variable'     " Global
let b:my_var = 'buffer-local'        " Buffer-local
let w:my_var = 'window-local'        " Window-local
let s:my_var = 'script-local'        " Script-local
let l:my_var = 'function-local'      " Function-local
let &option = 'value'                " Set an option
let @a = 'register content'          " Set a register

Functions

Conditionals

Loops

The Preserve Pattern

A common pattern for functions that should not disturb the user's context:

Tip: The Preserve pattern is especially useful in autocommands and mappings (see Mappings) where you want to run a command without disrupting the user's cursor position or search history.

Guards

Prevent functions from being defined multiple times:

Useful Function: WriteBackup

Create a timestamped backup of the current file without changing the modified state:

This saves a copy like myfile.20260228-1430.py while keeping the original file's modified state unchanged.

Useful Function: StripTrailingWhitespace

Remove trailing whitespace without moving the cursor (using the Preserve pattern):

Useful Function: OpenChangedFiles

Open all files that have been modified in git (useful after git stash pop or merges). This pairs well with the multi-file workflows in (see Advanced Patterns & Workflows):

Useful Pattern: Custom Commands with Arguments

Lua Scripting for Neovim

For an introduction to Lua in the context of Neovim configuration, see (see Neovim and Lua). This section goes deeper into the language itself.

Variables and Types

Functions

Tables

Tables are Lua's only data structure — they serve as arrays, dictionaries, objects, and more:

String Operations

Writing a Simple Neovim Plugin

Plugin Structure

A Lua plugin for Neovim follows this structure:

Example: Word Counter Plugin

Use it in your config:

Neovim API Highlights

Buffer Operations

Window Operations

Floating Windows

Autocommands

User Commands

Notifications

Tip: Install nvim-notifyarrow-up-right for beautiful notification popups.

Summary

Vimscript and Lua are complementary tools for extending your editor beyond what mappings and settings alone can achieve. Vimscript remains essential for understanding existing plugins and writing portable configurations, while Lua unlocks Neovim's full potential with a cleaner syntax, better performance, and access to the rich vim.api surface. Whether you write a one-off function or a full plugin, scripting is what turns Vim from a text editor into your personal development environment.

Key takeaways:

  • Vimscript variables use scope prefixes (g:, b:, s:, l:) that make their visibility explicit and prevent naming collisions.

  • The Preserve pattern and guard checks are essential idioms for writing robust Vimscript functions that behave well in any context.

  • Lua tables serve as arrays, dictionaries, and objects, making them the single data structure you need to master for Neovim scripting.

  • Neovim's vim.api provides programmatic access to buffers, windows, autocommands, and user commands -- everything you need to build plugins.

Exercises

  1. Write a Vimscript function — Create a function called InsertDate() that inserts the current date at the cursor position. Map it to <leader>id (see Mappings).

  2. Create a custom command with arguments — Write a Vimscript command :Wrap that wraps the current visual selection with a given HTML tag. For example, :Wrap div should wrap the selection with <div> and </div>.

  3. Lua: Create a floating window — Write a Lua function that opens a centered floating window displaying the contents of the current buffer's filename and line count.

  4. Lua: Write a word counter plugin — Extend the word counter example from this chapter to also count characters and display both counts in a notification. Add a setup function that creates a :Stats command.

  5. Autocommand in Lua — Write a Lua autocommand that automatically strips trailing whitespace from .py and .lua files on save, without moving the cursor. Use the Preserve pattern concept.

Last updated

Was this helpful?