File History in Neovim

Shaik Zahid
7 min readFeb 21, 2023

--

Undo-tree is a Neovim plugin completely written in lua. It shows the history of file changes made from the installation of the plugin. It is developed by Jiao ShiJie. It lists out all the changes you have made throughout the file. One can revert back to a particular command if required.

Requirements

Neovim ≥ 0.7

Installation

  • Add the github reference to the plugins.lua file and install the plugin using shift + i command.
-- plugins.lua

return {

-- Alpha (Dashboard)
{
"goolord/alpha-nvim",
lazy = true,
},


-- Auto Pairs
{
"windwp/nvim-autopairs"
},

-- Bufferline
{
'akinsho/bufferline.nvim',
dependencies = {
'nvim-tree/nvim-web-devicons'
},
},

-- Colorscheme
{
'folke/tokyonight.nvim',
},

-- Hop (Better Navigation)
{
"phaazon/hop.nvim",
lazy = true,
},


-- Lualine
{
'nvim-lualine/lualine.nvim',
dependencies = {
'nvim-tree/nvim-web-devicons'
},
},


-- Language Support
{
'VonHeikemen/lsp-zero.nvim',
lazy = true,
branch = 'v1.x',
dependencies = {
-- LSP Support
{'neovim/nvim-lspconfig'}, -- Required
{'williamboman/mason.nvim'}, -- Optional
{'williamboman/mason-lspconfig.nvim'}, -- Optional

-- Autocompletion
{'hrsh7th/nvim-cmp'}, -- Required
{'hrsh7th/cmp-nvim-lsp'}, -- Required
{'hrsh7th/cmp-buffer'}, -- Optional
{'hrsh7th/cmp-path'}, -- Optional
{'saadparwaiz1/cmp_luasnip'}, -- Optional
{'hrsh7th/cmp-nvim-lua'}, -- Optional

-- Snippets
{'L3MON4D3/LuaSnip'}, -- Required
{'rafamadriz/friendly-snippets'}, -- Optional
}
},


-- Nvim-tree (File Explorer)
{
'nvim-tree/nvim-tree.lua',
lazy = true,
dependencies = {
'nvim-tree/nvim-web-devicons',
},
},


-- Nvim-Surround (Manipulating Surroundings)
{
"kylechui/nvim-surround",
config = function()
require("nvim-surround").setup({
-- Configuration here, or leave empty to use defaults
})
end
},


-- Telescope (Fuzzy Finder)
{
'nvim-telescope/telescope.nvim',
lazy = true,
dependencies = {
{'nvim-lua/plenary.nvim'},
}
},


-- Treesitter
{
"nvim-treesitter/nvim-treesitter",
},


-- Toggle Term
{
'akinsho/toggleterm.nvim',
config = true
},

-- Undo-Tree
-- Added this plugin
{
"jiaoshijie/undotree",
dependencies = {
"nvim-lua/plenary.nvim",
},
},

-- Which-key
{
'folke/which-key.nvim',
lazy = true,
},

}

Configuration

  • Create a new file called undotree-config.lua in lua directory and add the following code. This code is taken from the documentation of the plugin’s github repository. One can find it from the usage section of the documentation.
local undotree = require('undotree')

undotree.setup({
float_diff = true, -- using float window previews diff, set this `true` will disable layout option
layout = "left_bottom", -- "left_bottom", "left_left_bottom"
ignore_filetype = { 'Undotree', 'UndotreeDiff', 'qf', 'TelescopePrompt', 'spectre_panel', 'tsplayground' },
window = {
winblend = 30,
},
keymaps = {
['j'] = "move_next",
['k'] = "move_prev",
['J'] = "move_change_next",
['K'] = "move_change_prev",
['<cr>'] = "action_enter",
['p'] = "enter_diffbuf",
['q'] = "quit",
},
})
  • Source above file to the main configuration init.lua file.
-- init.lua

require "options"
require "keymaps"
require "lazy-config"
require "alpha-config"
require "autopairs-config"
require "bufferline-config"
require "hop-config"
require "nvim-tree-config"
require "lualine-config"
require "lsp-config"
require "telescope-config"
require "toggleterm-config"
require "treesitter-config"
require "undotree-config"
require "whichkey"
  • Let’s add a keybinding to toggle undo-tree using which-key.
   ["u"] = { "<cmd>lua require('undotree').toggle()<CR>", "Undo-Tree" },
  • The update which key looks like this.
local status_ok, which_key = pcall(require, "which-key")
if not status_ok then
return
end

local setup = {
plugins = {
marks = true, -- shows a list of your marks on ' and `
registers = true, -- shows your registers on " in NORMAL or <C-r> in INSERT mode
spelling = {
enabled = true, -- enabling this will show WhichKey when pressing z= to select spelling suggestions
suggestions = 20, -- how many suggestions should be shown in the list?
},
-- the presets plugin, adds help for a bunch of default keybindings in Neovim
-- No actual key bindings are created
presets = {
operators = false, -- adds help for operators like d, y, ... and registers them for motion / text object completion
motions = true, -- adds help for motions
text_objects = true, -- help for text objects triggered after entering an operator
windows = true, -- default bindings on <c-w>
nav = true, -- misc bindings to work with windows
z = true, -- bindings for folds, spelling and others prefixed with z
g = true, -- bindings for prefixed with g
},
},
-- add operators that will trigger motion and text object completion
-- to enable all native operators, set the preset / operators plugin above
-- operators = { gc = "Comments" },
key_labels = {
-- override the label used to display some keys. It doesn't effect WK in any other way.
-- For example:
-- ["<space>"] = "SPC",
-- ["<cr>"] = "RET",
-- ["<tab>"] = "TAB",
},
icons = {
breadcrumb = "»", -- symbol used in the command line area that shows your active key combo
separator = "➜", -- symbol used between a key and it's label
group = "+", -- symbol prepended to a group
},
popup_mappings = {
scroll_down = "<c-d>", -- binding to scroll down inside the popup
scroll_up = "<c-u>", -- binding to scroll up inside the popup
},
window = {
border = "rounded", -- none, single, double, shadow
position = "bottom", -- bottom, top
margin = { 1, 0, 1, 0 }, -- extra window margin [top, right, bottom, left]
padding = { 2, 2, 2, 2 }, -- extra window padding [top, right, bottom, left]
winblend = 0,
},
layout = {
height = { min = 4, max = 25 }, -- min and max height of the columns
width = { min = 20, max = 50 }, -- min and max width of the columns
spacing = 3, -- spacing between columns
align = "left", -- align columns left, center or right
},
ignore_missing = true, -- enable this to hide mappings for which you didn't specify a label
hidden = { "<silent>", "<cmd>", "<Cmd>", "<CR>", "call", "lua", "^:", "^ " }, -- hide mapping boilerplate
show_help = true, -- show help message on the command line when the popup is visible
triggers = "auto", -- automatically setup triggers
-- triggers = {"<leader>"} -- or specify a list manually
triggers_blacklist = {
-- list of mode / prefixes that should never be hooked by WhichKey
-- this is mostly relevant for key maps that start with a native binding
-- most people should not need to change this
i = { "j", "k" },
v = { "j", "k" },
},
}

local opts = {
mode = "n", -- NORMAL mode
prefix = "<leader>",
buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings
silent = true, -- use `silent` when creating keymaps
noremap = true, -- use `noremap` when creating keymaps
nowait = true, -- use `nowait` when creating keymaps
}

local mappings = {

["a"] = { "<cmd>Alpha<cr>", "Alpha" },
["e"] = { "<cmd>NvimTreeToggle<cr>", "Explorer" }, -- File Explorer
["k"] = { "<cmd>bdelete<CR>", "Kill Buffer" }, -- Close current file
["m"] = { "<cmd>Mason<CR>", "Mason" }, -- Mason UI for LSP Management
["p"] = { "<cmd>Lazy<CR>", "Plugin Manager" }, -- Invoking plugin manager
["q"] = { "<cmd>wqall!<CR>", "Quit" }, -- Quit Neovim after saving the file
["r"] = { "<cmd>lua vim.lsp.buf.format{async=true}<cr>", "Reformat Code" },
-- Added this line
["u"] = { "<cmd>lua require('undotree').toggle()<CR>", "Undo-Tree" }, -- Undo History
["w"] = { "<cmd>w!<CR>", "Save" }, -- Save current file

-- Language Support
l = {
name = "LSP",
a = { "<cmd>lua vim.lsp.buf.code_action()<cr>", "Code Action" },
i = { "<cmd>LspInfo<cr>", "Info" },
l = { "<cmd>lua vim.lsp.codelens.run()<cr>", "CodeLens Action" },
r = { "<cmd>lua vim.lsp.buf.rename()<cr>", "Rename" },
s = { "<cmd>Telescope lsp_document_symbols<cr>", "Document Symbols" },
S = {
"<cmd>Telescope lsp_dynamic_workspace_symbols<cr>",
"Workspace Symbols",
},
},

-- Telescope
f = {
name = "File Search",
c = { "<cmd>Telescope colorscheme<cr>", "Colorscheme" },
f = { "<cmd>lua require('telescope.builtin').find_files()<cr>", "Find files" },
t = { "<cmd>Telescope live_grep <cr>", "Find Text Pattern" },
r = { "<cmd>Telescope oldfiles<cr>", "Recent Files" },
},

s = {
name = "Search",
h = { "<cmd>Telescope help_tags<cr>", "Find Help" },
m = { "<cmd>Telescope man_pages<cr>", "Man Pages" },
r = { "<cmd>Telescope registers<cr>", "Registers" },
k = { "<cmd>Telescope keymaps<cr>", "Keymaps" },
c = { "<cmd>Telescope commands<cr>", "Commands" },
},

--ToggleTerm
t = {
name = "Terminal",
n = { "<cmd>lua _NODE_TOGGLE()<cr>", "Node" },
t = { "<cmd>lua _HTOP_TOGGLE()<cr>", "Htop" },
p = { "<cmd>lua _PYTHON_TOGGLE()<cr>", "Python" },
f = { "<cmd>ToggleTerm direction=float<cr>", "Float" },
h = { "<cmd>ToggleTerm size=10 direction=horizontal<cr>", "Horizontal" },
v = { "<cmd>ToggleTerm size=80 direction=vertical<cr>", "Vertical" },
},

}

which_key.setup(setup)
which_key.register(mappings, opts)

Keybindings

  • Space + u :- Toggle Undo-tree
  • Keys inside Undo-Tree

Alternatives

Neovim From Scratch

  • If you want a complete installation and configuration of Neovim from Scratch, then you can head over to my NEOVIM SERIES.
  • This series is updated regularly, with updates and inclusion of newer plugins which improves the wholesome IDE experience of Neovim.

--

--