aboutsummaryrefslogtreecommitdiffstats
path: root/plugin/50-diff_orig.lua
blob: 8208e06ee6c1aab958ac206f5d8f1187a65b28e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
--[[ 50-diff_orig.lua — diff the current buffer against the file it was loaded from.

Copies the file on disk to a temp file and diffs it against the buffer in a scratch split,
so the unsaved changes in the buffer show as a regular diff.
Re-running the command replaces the previous scratch with a fresh snapshot of the file.

User commands:
  `DiffOrig[!]`: show the diff between the current buffer and its file; with `!` split vertically
]]

--[[ Close the scratch window left by a previous `DiffOrig` on the current buffer, if any.
The window id is kept in the buffer-local `diff_orig_win`; closing the window wipes the scratch
(`bufhidden=wipe`) and drops the buffer out of diff mode. ]]
local function close_previous_scratch()
	local prev = vim.b.diff_orig_win
	if prev and vim.api.nvim_win_is_valid(prev) then
		vim.api.nvim_win_close(prev, true)
	end
end

--[[ Open `file` in a scratch diff split — vertical when `vertical` is true — and diff it.
The scratch takes `filetype` so the diff keeps the original's syntax highlighting. ]]
local function open_scratch_diff(file, vertical, filetype)
	vim.cmd.split({ file, mods = { vertical = vertical, keepalt = true } })
	vim.cmd.diffthis()
	vim.opt_local.bufhidden = "wipe"
	vim.opt_local.buftype = "nofile"
	vim.opt_local.swapfile = false
	vim.opt_local.readonly = true
	if filetype ~= "" then
		vim.opt_local.filetype = filetype
	end
end

-- Show the diff between the current buffer and the file it was loaded from
local function show_buf_diff_orig(opts)
	local bname = vim.api.nvim_buf_get_name(0)
	if bname == "" then
		vim.notify("Cannot diff with original because the buffer has no file", vim.log.levels.WARN)
		return
	end
	if not vim.uv.fs_stat(bname) then
		vim.notify(
			"Cannot diff with original because " .. bname .. " doesn't exist",
			vim.log.levels.WARN
		)
		return
	end
	local temp = vim.fn.tempname()
	local ok, err = vim.uv.fs_copyfile(bname, temp)
	if not ok then
		vim.notify("Could not copy file: " .. err, vim.log.levels.ERROR)
		return
	end
	close_previous_scratch()
	local cwin = vim.api.nvim_get_current_win()
	local filetype = vim.bo.filetype
	vim.cmd.diffthis()
	open_scratch_diff(temp, opts.bang, filetype)
	local swin = vim.api.nvim_get_current_win()
	vim.api.nvim_set_current_win(cwin)
	vim.b.diff_orig_win = swin
end

----------------------------------------------------------------------------------------------------

vim.api.nvim_create_user_command(
	"DiffOrig",
	show_buf_diff_orig,
	{ bang = true, desc = "Show diff between current buffer and file (! splits vertically)" }
)