avataralpha2phi

Summary

The web content provides a collection of Neovim configuration tips and hacks aimed at enhancing the modern development environment, including features like auto-indentation, LSP support, directory auto-creation, cursor shape customization, and version management with Bob.

Abstract

The article "Modern Neovim — Configuration Hacks" is part of the "Modern Neovim" series and offers insights into various configuration strategies to optimize Neovim for a modern development workflow. It covers practical hacks such as returning to the last location in a file, auto-indenting empty lines, leveraging signature help and documentation for Lua APIs, automatically creating intermediate directories when saving files, using the = operator for pretty-printing in command-line mode, managing Neovim versions with the Bob version manager, customizing cursor shapes for different modes, and configuring language servers for enhanced code analysis and features like go-to-definition and auto-completion. The article also touches on configuring the window bar for a more informative and visually appealing interface, with references to plugins like dropbar.nvim, barbecue.nvim, and lspsaga.nvim. The content is complemented by code snippets, screenshots, and GIFs demonstrating the functionality of the hacks, and it encourages readers to explore further Neovim configuration recipes and related articles for a comprehensive understanding of Neovim's capabilities.

Opinions

  • The author emphasizes the importance of a well-configured Neovim setup for an efficient and enjoyable development experience.
  • Neovim's extensibility and community-driven development are highlighted as key factors in its ability to provide a rich set of features for developers.
  • The use of neodev.nvim is recommended for better customization of Neovim with full signature help, docs, and completion for the Neovim Lua APIs.
  • The article suggests that using a version manager like Bob can simplify the process of trying out new Neovim features while maintaining a stable release for daily use.
  • The author's preference for the ruff-lsp language server for Python development is evident, citing its speed and extensive lint rule support.
  • For Markdown editing, the Marksman language server is endorsed for its completion, cross-references, diagnostics, and support for Zettelkasten-like note-taking.
  • The article implies that customizing the window bar can significantly enhance the user interface and user experience in Neovim.
  • The author encourages readers to become Medium members to support their work and gain unlimited access to all Medium articles, indicating a value placed on community support and knowledge sharing.

Modern Neovim — Configuration Hacks

Configuration tips for a modern development environment with Neovim.

Modern Neovim — Hacks

Let’s check out simple yet useful Neovim configuration hacks in this article!

This article is part of the Modern Neovim series.

The Neovim configuration files are available in this repository.

Getting Started

Neovim is designed to be a modern and maintainable editor, emphasizing improved performance, extensibility, and community-driven development. It provides a rich set of features and mechanisms for extending its functionality, making it a powerful tool for developers, writers, and other text editing enthusiasts.

The article will explore simple hacks and configuration recipes to make Neovim an ideal environment for everyone's needs.

Table of Content

· Getting Started · Table of Content · Last Location When Opening a File · Auto Indent the Current Empty Line · Signature Help, Documentation, and Completion for Lua APIs · Auto Create Intermediary Directories · = Operator in Command-Line Mode · Version Manager using Bob · Cursor Shape · Language ServersRuff LSP for PythonMarksman for Markdown · Configure Window Bar · References

Last Location When Opening a File

The following 0 and mark[1] <= lcount then pcall(vim.api.nvim_win_set_cursor, 0, mark) end end, })">auto command is designed to set the cursor position to the last used mark (if valid) after a buffer is read (loaded) in the current window.

The mark position is retrieved using the vim.api.nvim_buf_get_mark() function and the cursor is set using the vim.api.nvim_win_set_cursor() function.

-- Go to last location when opening a buffer
vim.api.nvim_create_autocmd("BufReadPost", {
  group = augroup "last_loc",
  callback = function()
    local mark = vim.api.nvim_buf_get_mark(0, '"')
    local lcount = vim.api.nvim_buf_line_count(0)
    if mark[1] > 0 and mark[1] <= lcount then
      pcall(vim.api.nvim_win_set_cursor, 0, mark)
    end
  end,
})
  • The function retrieves the current buffer’s mark position using vim.api.nvim_buf_get_mark(0, '"'), where 0 represents the current buffer and '"' specifies the mark to retrieve (in this case, the last used mark).
  • It also retrieves the total number of lines in the buffer using vim.api.nvim_buf_line_count(0).
  • If the mark position is valid (greater than 0 and less than or equal to the total line count), it then calls vim.api.nvim_win_set_cursor(0, mark) to set the cursor position in the current window to the mark position, using a protected call with pcall().

Auto Indent the Current Empty Line

In the screenshot below,

  • We insert a new line below the cursor
  • We go into Insert mode, and the cursor goes to the beginning of the line
  • We press i to go into Insert mode, and the cursor position is not indented properly
No Auto Indent

Using the following key mapping, we can fix this problem.

vim.keymap.set("n", "i", function()
  if #vim.fn.getline "." == 0 then
    return [["_cc]]
  else
    return "i"
  end
end, { expr = true })

Here is what the function does:

  • It first checks the length of the line under the cursor using vim.fn.getline "." to get the current line ("." represents the current line in Neovim).
  • If the length of the line is 0 (empty line), it executes the command [["_cc]] to clear the current line and preserve the indent. We use the black hole register (“_) so that the deletion does not affect the normal registers.
  • If the line length is not 0 (non-empty line), it simply executes the insert command, which is the Normal mode command to enter Insert mode.

The { expr = true } argument passed to vim.keymap.set() specifies that the keymap should be evaluated as an expression, allowing the use of Lua code in the keymap definition.

With Auto Indent

Signature Help, Documentation, and Completion for Lua APIs

To better customize Neovim to fit our needs, we need at least a basic understanding of the Lua APIs.

neodev.nvim provides a Neovim setup for init.lua and plugin development with full signature help, docs, and completion for the Neovim Lua APIs.

We configure this plugin using lazy.nvim.

{
  "folke/neodev.nvim",
  opts = {
    library = { plugins = { "neotest", "nvim-dap-ui" }, types = true },
  },
},
neodev.nvim

Auto Create Intermediary Directories

The auto command below creates intermediary directories when saving a buffer.

vim.api.nvim_create_autocmd({ "BufWritePre" }, {
  group = augroup "auto_create_dir",
  callback = function(event)
    local file = vim.loop.fs_realpath(event.match) or event.match
    vim.fn.mkdir(vim.fn.fnamemodify(file, ":p:h"), "p")
  end,
})

The auto command is triggered before writing a buffer to disk, and the callback function creates the parent directory of the file being written to disk if it doesn’t already exist.

= Operator in Command-Line Mode

The = operator comes in handy when we want to pretty print a Lua table or variable from the command line.

For example, we can use the following command to print out the loaded packages

:lua = package.loaded
Pretty Print Loaded Packages

To redirect the output messages to the current buffer, we can use the put command.

For example, we can print the loaded packages to the current buffer using the command below.

:put = execute('lua = package.loaded')
Print Loaded Package to Current Buffer.

Alternatively, we can use the redir (:h redir) command to redirect the command output to a file, register, or variable.

For example, we redirect the command output to the register a using the commands below.

:redir @a
:lua = package.loaded
:redir END

Version Manager using Bob

Neovim is evolving fast. Trying out the new features, and having a stable release for daily use become challenging. Using a Neovim version manager like Bob helps.

Bob is a cross-platform and easy-to-use Neovim version manager, allowing for easy switching between versions right from the command line.

Bob is available for Unix/Linux, Windows, and MacOS.

We use the following command to install a specific version of Neovim or update the outdated nightly version.

bob install |nightly|stable|latest|<version-string>|<commit-hash>|

We use the following command to switch to a specific version. By default, this command will auto-invoke the install command if the version is not installed.

bob use |nightly|stable|latest|<version-string>|<commit-hash>|

We use the bob list command to list all installed versions and the current version in use.

Installed Neovim Versions

For more features, check out the GitHub repository.

Cursor Shape

To configure the desired cursor shape in different Neovim modes (Insert, Normal, Terminal, etc), check out the following article.

By default, Neovim changes the terminal cursor to a block cursor.

Neovim Terminal Cursor

Using the following auto command, we can change the cursor to be underlined.

vim.api.nvim_set_hl(0, "TerminalCursorShape", { underline = true })
vim.api.nvim_create_autocmd("TermEnter", {
  callback = function()
    vim.cmd [[setlocal winhighlight=TermCursor:TerminalCursorShape]]
  end,
})
Underlined Cursor in Terminal

When exiting Neovim, we can use the following auto command to configure or reset the cursor to be a vertical line cursor.

vim.api.nvim_create_autocmd("VimLeave", {
  callback = function()
    vim.cmd [[set guicursor=a:ver25]]
  end,
})
Vertical Line Cursor After Exiting Neovim

Language Servers

Neovim built-in support for Language Server Protocol (LSP) facilitates features like go-to-definition, find-references, hover, completion, rename, format, refactor, etc., using semantic whole-project analysis.

Let’s go through several lesser-known language servers.

Ruff LSP for Python

Previously we use null-ls.nvim to configure Ruff, which is an extremely fast Python linter, written in Rust.

Ruff supports over 500 lint rules, many of which are inspired by popular tools like Flake8, isort, pyupgrade, and others.

Instead of using null-ls.nvim, now we can use ruff-lsp which is an LSP implementation for Ruff.

A sample configuration using lazy.nvim and nvim-lspconfig is shown below. We configure both ruff-lsp and pyright.

Ruff LSP

For anyone doing Python development, you should make use of Ruff.

Marksman for Markdown

For Markdown, we can configure the Marksman language server.

Marksman is a Markdown language server providing completion, cross-references, diagnostics, and more. In addition to regular Markdown, it also supports wiki-link-style references that enable Zettelkasten-like note-taking.

A sample configuration using lazy.nvim is shown below.

return {
  -- correctly setup lspconfig
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        marksman = {},
      },
    },
  },
}

We can link to other pages easily using LSP.

Marksman Language Server

We can generate a table of content using code action.

Marksman Language Server

Configure Window Bar

There are excellent plugins that can save you the effort, as listed below.

Check out these articles if you want to customize the window bar!

For more Neovim configuration recipes, check out this article!

Check out other related articles!

Check out the article Learn Neovim The Practical Way for all the Vim/Neovim articles!

If you are yet to be a Medium member and want to become one, click here. You will gain unlimited access to all Medium articles and support my work directly.

References

Vim
Coding
Programming
Software Development
Software Engineering
Recommended from ReadMedium