Beginner's Guide to Vimscript

  ·   15 min read

Introduction to Vimscript #

What is Vimscript? #

Vimscript is the scripting language built into the Vim text editor (and its improved version, Neovim). It allows you to extend Vim’s functionality far beyond its built-in commands. You can automate repetitive tasks, create custom commands, write plugins, and tailor Vim’s behavior to your exact workflow. Vimscript is an interpreted language, meaning your code is executed line by line without needing a separate compilation step. While it has its quirks, mastering Vimscript unlocks a powerful level of customization and efficiency within the Vim editor.

Why learn Vimscript? #

Learning Vimscript is beneficial for several reasons:

  • Automation: Automate tedious tasks like renaming files, formatting code, or managing projects. This saves time and reduces the chance of human error.
  • Customization: Tailor Vim to your specific needs and preferences. Configure keybindings, create custom menus, and integrate with external tools.
  • Plugin Development: Create your own Vim plugins and share them with the community, contributing to the vibrant Vim ecosystem.
  • Deeper Understanding of Vim: Learning Vimscript provides a deeper understanding of how Vim works internally, enhancing your overall Vim proficiency.
  • Improved Efficiency: By automating tasks and customizing your environment, you can significantly increase your editing efficiency.

Setting up your environment #

To start writing and running Vimscript, you essentially just need Vim! No special installations are typically required beyond having Vim installed on your system. However, for a more comfortable development experience, consider these optional additions:

  • Syntax Highlighting: Ensure Vim is configured to highlight Vimscript syntax. Many distributions include this by default, but you might need to install a relevant plugin or configure your ~/.vimrc (or init.vim for Neovim) accordingly. A simple way to check is to create a file named test.vim with some Vimscript code and see if the syntax is highlighted.
  • A Good Text Editor (for writing the scripts): While you can write Vimscript directly within Vim, many developers find it more convenient to use a separate text editor with better features for code writing (e.g., syntax highlighting, autocompletion, etc.) Then, you would save the file and run it from within Vim.

Running your first Vimscript #

The simplest way to run a Vimscript is to place your code directly into your ~/.vimrc (or init.vim for Neovim). This file is sourced every time Vim starts. However, for testing purposes, it’s better to create a separate .vim file. Let’s create a file named hello.vim with the following content:

echo "Hello, world! from Vimscript"

Save the file. Then, within Vim, execute the script using the following command:

:source hello.vim

Press Enter. The text “Hello, world! from Vimscript” should appear in the command-line area at the bottom of your Vim window. This confirms that your Vimscript code is running correctly. You can now move on to more complex scripts.

Basic Syntax and Data Types #

Comments and Whitespace #

Vimscript uses " to denote a comment. Anything after a " on a line is ignored by the interpreter. This is useful for explaining your code.

" This is a comment.  It's ignored by Vimscript.
let myVariable = 10  " This is a comment at the end of a line.

Whitespace (spaces, tabs, newlines) is generally insignificant in Vimscript, except when it’s part of a string literal or needed to separate tokens (like keywords or variable names). However, using consistent indentation improves code readability. A common style is to indent with two spaces.

Variables and Data Types (Numbers, Strings, Lists) #

Vimscript uses let to declare and assign values to variables. Variable names are case-sensitive. There are three primary data types:

  • Numbers: Integers and floating-point numbers.
let myNumber = 10
let myFloat = 3.14
  • Strings: Sequences of characters enclosed in double quotes (").
let myString = "Hello, Vimscript!"
  • Lists: Ordered collections of items. Lists are created using square brackets ([]) and items are separated by commas. Lists can contain mixed data types.
let myList = [1, "two", 3.0, "four"]

Operators (Arithmetic, Comparison, Logical) #

Vimscript supports various operators:

  • Arithmetic: +, -, *, /, % (modulo), ** (exponentiation)
let sum = 5 + 3    " sum will be 8
let difference = 10 - 2 " difference will be 8
  • Comparison: == (equal), != (not equal), >, <, >=, <=
if 5 > 2
  echo "5 is greater than 2"
endif
  • Logical: && (AND), || (OR), ! (NOT)
if (x > 0) && (y < 10)
  echo "Condition met"
endif

Basic Input and Output (echo, print) #

The simplest way to display output in Vimscript is using the echo command. It prints text to the Vim command-line area at the bottom of the window.

echo "This is displayed in the command-line area"

The print function (available in Vim 8 and later and recommended for newer scripts) offers more flexibility and is generally preferred for printing to the command line.

call print("This is also displayed in the command line")

For more sophisticated output (such as messages in a separate window), you would need to use more advanced Vim functions which are beyond the scope of a basic introduction.

Control Flow #

Conditional Statements (if, else, elseif) #

Vimscript uses if, else, and elseif for conditional execution. The syntax is straightforward:

let x = 10

if x > 5
  echo "x is greater than 5"
elseif x == 5
  echo "x is equal to 5"
else
  echo "x is less than 5"
endif

Note the use of endif to close the if block. elseif can appear multiple times. The conditions are evaluated sequentially; the first true condition’s block is executed, and the rest are skipped.

Loops (for, while) #

Vimscript offers for and while loops for iterative execution:

  • for loop: Iterates a specific number of times or over a list.
" Iterate 5 times
for i in range(1, 6)
  echo "Iteration: " . i
endfor

" Iterate over a list
let myList = ["apple", "banana", "cherry"]
for item in myList
  echo "Fruit: " . item
endfor
  • while loop: Repeats a block of code as long as a condition is true.
let counter = 0
while counter < 5
  echo "Counter: " . counter
  let counter += 1
endwhile

Note the use of endfor and endwhile to close the respective loop blocks.

Loop Control (break, continue) #

  • break: Immediately exits the current loop.
for i in range(1, 10)
  if i == 5
    break
  endif
  echo "Iteration: " . i
endfor
  • continue: Skips the rest of the current iteration and proceeds to the next iteration.
for i in range(1, 10)
  if i == 5
    continue
  endif
  echo "Iteration: " . i
endfor

Functions #

Functions in Vimscript are defined using the function keyword. They can accept arguments and return values.

" Function to add two numbers
function! Add(a, b)
  return a + b
endfunction

" Call the function and print the result
let sum = Add(5, 3)
echo "The sum is: " . sum

Note the use of ! after function – this is important for defining functions in Vimscript (it’s a way to prevent naming conflicts). The return statement specifies the value returned by the function. If no return statement is present, the function implicitly returns 0. Functions can be called using the call function or simply by their name (as shown above), followed by parentheses enclosing the arguments.

Working with Text #

Buffers and Windows #

In Vim, a buffer is an in-memory representation of a file. You can have multiple buffers open simultaneously, even if only one is displayed in a window. A window is a viewport into a buffer. Vimscript allows you to control buffers and windows programmatically. For instance, you can open new buffers, switch between them, and close buffers. Functions like bufnew(), buffer, win_gotoid(), and close() are crucial for buffer and window management.

Accessing Text (getline, setline) #

  • getline(lnum): Retrieves the text of a specific line (specified by its line number lnum). Returns a string.
let myLine = getline(1)  " Gets the text of the first line
echo myLine
  • setline(lnum, text): Replaces the text of a specific line (lnum) with the provided text.
call setline(1, "This is the new first line.")

Manipulating Text (substitute, delete, insert) #

  • substitute(expr, pattern, replacement, flags): Performs a substitution on a string (expr) using a regular expression (pattern). replacement is the text to replace the matched pattern. flags (optional) modify the behavior (e.g., ‘g’ for global replacement, ‘c’ for confirmation).
let myString = "This is a test string."
let myString = substitute(myString, "test", "sample", "g")  "Replaces all instances of "test"
echo myString
  • delete(lnum): Deletes the line at line number lnum.
call delete(2)  "Deletes the second line"
  • append(lnum, text): Appends text after the line at line number lnum. Use lnum=$. to append to the end. insert(lnum, text) inserts the text before the line at the given line number.
call append(1,"This line is added after line 1")

Regular Expressions #

Vimscript uses a powerful regular expression engine. Regular expressions are patterns that match specific sequences of characters in text. This is critical for tasks like searching, replacing, and validating text. Vimscript’s regular expressions are very similar to those used in other programming languages, with some Vim-specific extensions.

Here’s a basic example showing how to find all lines containing the word “example”:

/example

This is a Vim command, but you can integrate it within Vimscript using execute().

More complex examples would involve using metacharacters like . (matches any character), * (matches zero or more occurrences), + (matches one or more occurrences), [] (matches any character within the brackets), ^ (matches the beginning of a line), $ (matches the end of a line), etc. Vim’s help documentation provides comprehensive information on its regular expression syntax (:help pattern). Regular expressions are fundamental for robust text manipulation in Vimscript.

Remember to consult Vim’s extensive help documentation (help) for details on all the functions and their options. Many of the functions described above have more complex usages and parameters that allow for precise control over text manipulation.

Mappings and Abbreviations #

Creating Custom Key Mappings #

Key mappings allow you to reassign existing key combinations or create new ones to execute Vim commands or Vimscript functions. This is a powerful way to customize your Vim workflow. Mappings are defined using the :map command (or its variants).

  • map: Creates a global mapping (affects all modes).
  • nmap: Creates a normal mode mapping.
  • imap: Creates an insert mode mapping.
  • vmap: Creates a visual mode mapping.

Example: Let’s map the key combination <leader>w (where <leader> is usually backslash \ but can be customized) to save the current file:

nmap <leader>w :w<CR>

This creates a normal mode mapping. <CR> represents the Enter key, which is needed to execute the :w (write) command.

Example with a function: Let’s create a function to center the cursor and map a key to it:

function! CenterCursor()
  normal zz
endfunction

nmap <leader>c :call CenterCursor()<CR>

This maps <leader>c to call the CenterCursor() function, which uses the zz command to center the cursor.

Defining Abbreviations #

Abbreviations expand shorthand text into longer sequences when you type them in insert mode. They are defined using the :abbreviate command (or iab for insert mode).

Example: Let’s define an abbreviation so that typing “btw” expands to “by the way”:

iab btw by the way

Now, whenever you type “btw” in insert mode and press a space or punctuation mark, it will automatically be replaced with “by the way”.

Using Maps and Abbreviations Effectively #

  • Choose meaningful key combinations: Select key mappings that are easy to remember and don’t conflict with existing Vim commands.

  • Avoid overusing mappings: Too many mappings can make your Vim configuration difficult to manage and understand.

  • Use descriptive function names: If you’re using functions within mappings, give them clear and descriptive names.

  • Test thoroughly: After creating mappings or abbreviations, test them carefully to ensure they work as expected and don’t cause unintended side effects.

  • Document your mappings: Keep a record of your mappings and their purposes. This is particularly important if you share your vimrc or work on complex configurations. A well-commented vimrc makes it easier to maintain and understand your customizations over time.

By combining key mappings and abbreviations strategically, you can streamline your workflow considerably, making your Vim editing experience significantly more efficient and enjoyable. Remember that the <Leader> key is very useful for grouping similar commands together, avoiding accidental key conflicts, and making your Vim keybindings more intuitive.

Autocommands and Plugins #

Introduction to Autocommands #

Autocommands allow you to execute Vimscript commands automatically in response to specific events within Vim. These events can include opening, closing, or saving files, changing buffers, or entering or leaving different modes. Autocommands are defined using the :autocmd command. This is a powerful mechanism for automating tasks and customizing Vim’s behavior based on context.

A basic autocommand structure looks like this:

autocmd {event} {pattern} {command}
  • {event}: The event that triggers the command (e.g., BufRead, BufWritePost, FileType).
  • {pattern}: A pattern that specifies which files or buffers the autocommand applies to (e.g., “.txt”, “.py”). Can be omitted for global events.
  • {command}: The Vimscript command(s) to execute when the event occurs.

Common Autocommand Events #

Here are some frequently used autocommand events:

  • BufRead: Triggered when a buffer is read (a file is opened).
  • BufWritePost: Triggered after a buffer is written (a file is saved).
  • BufNewFile: Triggered when a new, empty buffer is created.
  • FileType: Triggered when a file of a specific type is opened (allows for filetype-specific settings).
  • VimEnter: Triggered when Vim starts.
  • VimLeave: Triggered when Vim exits.

Example: Let’s set up an autocommand to automatically set the tab width to 4 spaces whenever a Python file is opened:

autocmd FileType python set tabstop=4 shiftwidth=4 expandtab

Installing Plugins with Pathogen or vim-plug #

Plugins extend Vim’s functionality, adding features like syntax highlighting for new languages, code completion, or project management tools. Two popular plugin managers are Pathogen and vim-plug.

  • Pathogen: A simpler plugin manager. You clone plugins into your ~/.vim/bundle directory.

  • vim-plug: A more modern and efficient plugin manager. It uses a concise configuration file to manage plugins.

Using vim-plug (Recommended):

  1. Add vim-plug to your init.vim (or ~/.vimrc):
call plug#begin('~/.vim/plugged')
Plug 'tpope/vim-fugitive'  "Example plugin
Plug 'junegunn/fzf'      "Another example plugin
call plug#end()
  1. Install plugins: In Vim, run :PlugInstall.

Understanding Plugin Structure #

A typical Vim plugin consists of one or more files, often located in a single directory. These files contain Vimscript code that implements the plugin’s features. Key components include:

  • plugin/ directory: Contains the core plugin scripts.
  • autoload/ directory: Contains functions that are loaded only when needed (improves startup time).
  • doc/ directory: Contains help files for the plugin (usually in .txt format).
  • ftdetect/ directory: Contains files to detect filetypes automatically.

Understanding the plugin structure helps you to navigate, debug, and potentially modify plugins if needed. The README file of a plugin usually provides more details about its structure and how to use it effectively. Many plugins use a modular design, dividing their functionality into separate files for better organization and maintainability.

Advanced Topics #

Working with Options #

Vim has numerous options that control its behavior. You can access and modify these options using Vimscript. Options are accessed and modified using the &{option_name} syntax to get the value, and let &{option_name} = {value} to set it. The :set command can also be used in a similar manner within Vimscript.

Example: To get the current value of the number option (which displays line numbers):

let currentNumberOption = &number
echo "Number option is currently: " . currentNumberOption

To set the number option to true:

let &number = 1  " or let &number = v:true

Using External Commands #

Vimscript allows you to execute external commands using the system() function. This opens up possibilities for interacting with your operating system, running shell scripts, and using external tools within your Vim workflow. The system() function returns the output of the command as a string. Error handling should always be included.

Example: To list the files in the current directory:

let output = system('ls -l')
echo output

Important Note: Use caution when executing external commands, especially those that take user input, as this could pose a security risk if not handled carefully. Sanitize any user input before incorporating it into external commands. Always consider the potential for command injection vulnerabilities.

Creating Custom Menus and User Interfaces #

Vimscript allows for creation of custom menus and more sophisticated user interfaces, although this is a more advanced topic requiring a deeper understanding of Vim’s internal structures. It involves defining menu items, their actions, and potentially integrating them with custom dialog boxes or other UI elements. The creation of custom user interfaces often involves the use of popup windows, and extensive understanding of Vim’s GUI interaction. This typically uses the functions available for menu creation, like menu and various other commands to control the user interface. Full details are extensively available in Vim’s help system.

Debugging Vimscript #

Debugging Vimscript can be challenging, as it involves a combination of techniques for identifying and resolving issues in your code. Here are some useful approaches:

  • echo statements: Insert strategic echo statements throughout your code to display the values of variables and trace the flow of execution.

  • The :messages command: Examine the Vim message area for error messages and other information about the state of your scripts.

  • The :verbose command: Use :verbose {command} to get detailed information about a command’s execution, including what functions were called and their arguments.

  • External Debuggers: While not as common as in other languages, there are some plugins that provide rudimentary debugging capabilities for Vimscript, offering breakpoint functionality and variable inspection.

  • Logging to a file: Write information to a log file to inspect variables and track execution flow. This will not interfere with Vim’s normal output.

  • Divide and conquer: Break down complex scripts into smaller, more manageable modules. Test each module independently to pinpoint problem areas.

Effective debugging is crucial for developing robust and reliable Vimscript plugins and scripts. A systematic approach, combined with careful use of debugging tools, will expedite the process of fixing errors and improving code quality.