aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatěj Cepl <mcepl@cepl.eu>2023-03-16 21:23:08 +0100
committerMatěj Cepl <mcepl@cepl.eu>2023-03-16 21:23:08 +0100
commit702a537e4d701a7fb9af8a2deb1fcd44d79f706f (patch)
tree6f96f019f3346b727b1318360070822ee482194c
parenteb7963bb951c8be77db72fe4f362208b9d1adb1e (diff)
downloadvim-diff_navigator-702a537e4d701a7fb9af8a2deb1fcd44d79f706f.tar.gz
Another first draft
-rw-r--r--ftdetect/diff.vim1
-rw-r--r--ftdetect/filetype.lua7
-rw-r--r--ftplugin/diff_navigator.lua329
-rw-r--r--ftplugin/diff_navigator.vim.orig (renamed from ftplugin/diff_navigator.vim)7
4 files changed, 343 insertions, 1 deletions
diff --git a/ftdetect/diff.vim b/ftdetect/diff.vim
deleted file mode 100644
index d6295da..0000000
--- a/ftdetect/diff.vim
+++ /dev/null
@@ -1 +0,0 @@
-au BufNewFile,BufRead *.diff,*.rej setf diff
diff --git a/ftdetect/filetype.lua b/ftdetect/filetype.lua
new file mode 100644
index 0000000..09c26d8
--- /dev/null
+++ b/ftdetect/filetype.lua
@@ -0,0 +1,7 @@
+vim.filetype.add({
+ extension = {
+ diff = "diff",
+ patch = "diff",
+ rej = "diff",
+ },
+})
diff --git a/ftplugin/diff_navigator.lua b/ftplugin/diff_navigator.lua
new file mode 100644
index 0000000..910121c
--- /dev/null
+++ b/ftplugin/diff_navigator.lua
@@ -0,0 +1,329 @@
+-- ============================================================================
+-- File: diff_navigator.vim
+-- Description: Filetype plugin to ease navigation in (unified) diffs
+-- Maintainer: Petr Uzel <petr.uzel -at- centrum.cz>,
+-- Matěj Cepl <mcepl -at- cepl dot eu>
+-- Version: 0.2
+-- Last Change: 10 Sep, 2013
+-- License: This program is free software. It comes without any warranty,
+-- to the extent permitted by applicable law. You can redistribute
+-- it and/or modify it under the terms of the Do What The Fuck You
+-- Want To Public License, Version 2, as published by Sam Hocevar.
+-- See http://sam.zoy.org/wtfpl/COPYING for more details.
+--
+-- Bugs: Send bugreports/patches directly to me via mail
+-- Dependencies: filterdiff (part of patchutils project)
+--
+--
+-- TODO: show current hunk in status line
+-- TODO: delete whole file diff -
+-- like http://www.vim.org/scripts/script.php?script_id=444)
+-- TODO: incorporate more patchutils functionality
+-- TODO: something like taglist for diff (shows all files/hunks in
+-- the diff)
+-- TODO: option for *Next|Prev* funtions to wrap around end of file
+-- ============================================================================
+
+
+-- Only do this when not done yet for this buffer
+-- Usually, not needed, just for the keeping normal API
+-- if exists("b:did_ftplugin")
+-- finish
+-- end
+-- local b:did_ftplugin = 1
+
+-- Load this plugin only once
+if vim.g.loaded_diff_navigator ~= nil then
+ return
+end
+vim.g.loaded_diff_navigator = 1
+
+-- Utility functions
+
+-- from http://lua-users.org/wiki/SplitJoin
+local split = function(self, sep)
+ local sep, fields = sep or ":", {}
+ local pattern = string.format("([^%s]+)", sep)
+ self:gsub(pattern, function(c) fields[#fields+1] = c end)
+ return fields
+end
+
+local function executable(cmd)
+ -- calling /bin/sh -c even for users with default shells
+ -- which are not POSIX compatible (squinting at fish)
+ return (os.execute("/bin/sh -c 'command -v " .. cmd .. " >/dev/null'") == 0)
+end
+
+local function checkFilterDiff()
+ if not executable('filterdiff') then
+ vim.notify('You need to install filterdiff first (part of patchutils)', vim.log.levels.WARN)
+ return false
+ end
+ return true
+end
+
+-- Given the linenumber of the hunk returned its parsed content
+--
+-- From http://www.clearchain.com/blog/posts/splitting-a-patch
+-- -----------------------------------------------------------
+--
+-- here's at least some info about the format of a patch file.
+--
+-- @@ -143,6 +143,13 @@
+--
+-- the first number is the starting line for this hunk in oldfile
+-- the second number is the number of original source lines in this
+-- hunk (this includes lines marked with "-")
+-- the third number is the starting line for this hunk in newfile
+-- the last number is the number of lines after the hunk has been applied.
+function parseHunkHeader(lineno)
+ local inline = vim.fn.getline(lineno)
+ -- Thanks to somian from #vim IRC channel for this incredible RE
+ local hunk_nos = vim.fn.substitute(inline,
+ '\\_^\\S\\S\\s\\+-\\(\\d\\+\\),\\(\\d\\+\\)\\s\\++\\(\\d\\+\\),\\(\\d\\+\\)\\s\\+\\S\\S\\(\\_.*\\)',
+ "\\\\1,\\\\2,\\\\3,\\\\4,\\\\5","")
+ local result = hunk_nos:split(",")
+
+ return {
+ ['line'] = lineno,
+ ['oldFirst'] = result[1],
+ ['oldCount'] = result[2],
+ ['newFirst'] = result[3],
+ ['newCount'] = result[4],
+ ['remainderLine'] = table.concat(result, ",", 5)
+ }
+end
+
+-- vim.api.nvim_eval('"README.md" =~ glob2regpat("/home/nvim/*.md")') ~= 0
+-- is equivalent of
+-- "README.md" =~ glob2regpat("/home/nvim/*.md")
+
+-- Get hunk header from the hunk surrounding cursor
+function getCurrentHunkHeader()
+ local lineno = 0
+ if (vim.api.nvim_eval('getline(".") =~ "^+++ \\|^--- "') ~= 0) then
+ lineno = vim.fn.search('^@@[ +-\\,\\d]*@@.*$', 'ncW')
+ else
+ lineno = vim.fn.search('^@@[ +-\\,\\d]*@@.*$', 'bncW')
+ end
+
+ return parseHunkHeader(lineno)
+end
+
+-- Generate hunk header line
+function createHunkHeader(oldStart, oldLen, newStart, newLen, remaind)
+ return "@@ -" .. oldStart .. "," .. oldLen ..
+ " +" .. newStart .. "," .. newLen .. " @@" .. remaind
+end
+
+-- Return number of lines in the range between start and end line (both
+-- inclusive) which are from the state before patch and the one after
+-- patch is applied.
+function countLines(start, endline)
+ local context_lines = 0
+ local old_lines = 0
+ local new_lines = 0
+
+ for line in vim.fn.getline(start, endline) do
+ local first_char = vim.fn.strpart(line, 0, 1)
+ if first_char == ' ' then
+ local context_lines = context_lines + 1
+ elseif first_char == '-' then
+ local old_lines = old_lines + 1
+ elseif first_char == '+' then
+ local new_lines = new_lines + 1
+ end
+ end
+
+ return {context_lines + old_lines, context_lines + new_lines}
+end
+
+-- -------------------------------------------------------------
+
+-- Annotate each hunk with it's number and name of the changed file
+function DiffAnnotate()
+ if checkFilterDiff() then
+ local cursorpos = vim.cmd.winsaveview()
+ vim.cmd('normal %!filterdiff --annotate')
+ vim.cmd.winrestview(cursorpos)
+ end
+end
+
+-- Print annotation of current hunk
+function DiffShowHunk()
+ -- if the current line begins with '+++' or '---', then it makes
+ -- sense to search forwards
+ local hunk_header = getCurrentHunkHeader()
+ local hunk_annotation = vim.fn.substitute(vim.fn.getline(hunk_header['line']),
+ '^@@[ +-\\,\\d]*@@\\s*\\(.*\\)$', '\\1', '')
+ print(hunk_annotation)
+end
+
+-- Skip to next hunk
+function DiffNextHunk()
+ vim.fn.search('^@@[ +-\\,\\d]*@@', 'sW')
+end
+
+-- Skip to previous hunk
+function DiffPrevHunk()
+ vim.fn.search('^@@[ +-\\,\\d]*@@', 'bsW')
+end
+
+-- Skip to next changed file
+function DiffNextFile()
+ vim.fn.search('^--- ', 'sW')
+end
+
+-- Skip to previous changed file
+function DiffPrevFile()
+ vim.fn.search('^--- ', 'bsW')
+end
+
+function DiffSplitHunk()
+ local old_cur_header = getCurrentHunkHeader()
+ local cur_line_no = vim.fn.line(".")
+
+ -- With this hunk:
+ --
+ -- @@ -20,8 +20,17 @@ Hunk #1, a/tests/test_ec_curves.py
+ --
+ -- import unittest
+ -- #import sha
+ -- -from M2Crypto import EC, Rand
+ -- -from test_ecdsa import ECDSATestCase as ECDSATest
+ -- +try:
+ -- + from M2Crypto import EC, Rand
+---- " + from test_ecdsa import ECDSATestCase as ECDSATest
+ -- +# AttributeError: 'module' object has no attribute 'ec_init'
+ -- +#except AttributeError:
+ -- +except:
+ -- + EC_Module_Available = False
+ -- + print("No EC modules available")
+ -- +else:
+ -- + EC_Module_Available = True
+ -- + print("EC modules are available")
+ --
+ -- creates above the line
+ -- @@ -25,3 +25,12 @@
+ --
+ -- and the original hunk line is now
+ -- @@ -20,5 +20,5 @@ Hunk #1, a/tests/test_ec_curves.py
+ --
+ --
+ -- Start line below header and stop one line above the current line
+ local diff_lines = countLines(old_cur_header['line'] + 1,
+ cur_line_no - 1)
+ local diff_old = diff_lines[0]
+ local diff_new = diff_lines[1]
+
+ -- IN THE NEW START HUNK HEADER
+ -- 1. length is number of lines above the current position which
+ -- are either context or deleted lines (-)
+ -- 2. length is number of lines above the current position which
+ -- are either context or added lines (+)
+ -- Start positions are same as well the stuff after the second @@
+ local new_start_del_start = old_cur_header['oldFirst']
+ local new_start_del_len = diff_old
+ local new_start_add_start = old_cur_header['newFirst']
+ local new_start_add_len = diff_new
+ vim.fn.setreg('x', createHunkHeader(new_start_del_start, new_start_del_len,
+ new_start_add_start, new_start_add_len,
+ old_cur_header['remainderLine']))
+ local window_state = vim.cmd.winsaveview()
+ -- write the new original header line
+ vim.fn.setpos(".", {0, old_cur_header['line'], 1, 0})
+ vim.cmd('normal ^d$"xp')
+ vim.cmd.winrestview(window_state)
+
+ -- IN THE NEW HUNK HEADER
+ -- new lengths = original len - new len
+ -- new starts = original start + (difference)
+ local new_pos_del_start = old_cur_header['oldFirst'] + diff_old
+ local new_pos_del_len = old_cur_header['oldCount'] - diff_old
+ local new_pos_add_start = old_cur_header['newFirst'] + diff_new
+ local new_pos_add_len = old_cur_header['newCount'] - diff_new
+ vim.fn.setreg('x', createHunkHeader(new_pos_del_start, new_pos_del_len,
+ new_pos_add_start, new_pos_add_len, ""))
+ vim.cmd('normal! O<Esc>\"xP')
+end
+
+-- Delete the hunk cursor is in.
+function DiffDeleteHunk()
+ local last_hunk_in_file = 0
+
+ local start_hunk_line = vim.fn.search('^@@[ +-\\,\\d]*@@.*$', 'bncW')
+ -- we are before the first hunk ... return
+ if start_hunk_line == 0 then
+ return
+ end
+
+ -- end of the hunk is start of next hunk or next file, whichever
+ -- comes first
+ local next_hunk_line = vim.fn.search('^@@[ +-\\,\\d]*@@.*$', 'nW')
+ local start_of_next_file_line = vim.fn.search('^--- .*$', 'ncW')
+ if start_of_next_file_line > 0 and
+ start_of_next_file_line < next_hunk_line then
+ local next_hunk_line = start_of_next_file_line
+ local last_hunk_in_file = 1
+ end
+
+ -- if this is the last hunk in the file ... just erase everything
+ -- from the start of the hunk (inclusive) to the end
+ if next_hunk_line == 0 then
+ vim.cmd("normal " .. start_hunk_line .. ",$" .. "d")
+ -- we are in the middle of the file ... it's a bit more complicated
+ else
+ local count_lines = countLines(start_hunk_line + 1,
+ next_hunk_line - 1)
+ local added_lines = count_lines[1] - count_lines[0]
+ vim.fn.setpos(".", {0, next_hunk_line, 1, 0})
+ vim.cmd("normal " .. start_hunk_line .. "," .. (next_hunk_line - 1) .. "d")
+
+ -- record the line number of the new hunk header after delete
+ local new_header_line = vim.fn.line(".")
+
+ -- recalculate current hunk header
+ while (not last_hunk_in_file) do
+ local cur_header = parseHunkHeader(vim.fn.line("."))
+ local old_line = cur_header['newFirst']
+ local new_line = old_line - added_lines
+ vim.fn.setreg('x', vim.fn.substitute(vim.fn.getline("."), "+" .. old_line .. ",",
+ "+" .. new_line .. ",", ""))
+ vim.cmd('normal ^d$"xp')
+
+ -- check the next hunk
+ local next_hunk_line = vim.fn.search('\\_^@@[ +-\\,\\d]*@@\\_.*\\_$', 'nW')
+ local start_of_next_file_line = vim.fn.search('^--- .*$', 'ncW')
+ if (start_of_next_file_line < next_hunk_line) then
+ local next_hunk_line = start_of_next_file_line
+ local last_hunk_in_file = 1
+ end
+ vim.fn.setpos(".", {0, next_hunk_line, 1, 0})
+ end
+
+ -- jump to the line after the deleted hunk
+ vim.fn.setpos(".", {0, new_header_line, 1, 0})
+ end
+end
+
+-- -- Define new commands
+vim.cmd('command DiffAnnotate DiffAnnotate()')
+vim.cmd('command DiffShowHunk DiffShowHunk()')
+vim.cmd('command DiffNextHunk DiffNextHunk()')
+vim.cmd('command DiffPrevHunk DiffPrevHunk()')
+vim.cmd('command DiffNextFile DiffNextFile()')
+vim.cmd('command DiffPrevFile DiffPrevFile()')
+vim.cmd('command DiffSplitHunk DiffSplitHunk()')
+vim.cmd('command DiffDeleteHunk DiffDeleteHunk()')
+--
+
+-- Default },{,(,) do not make much sense in diffs, so remap them to
+-- make something useful
+vim.keymap.set('n', '<Leader>ex3', vim.treesitter.start)
+vim.keymap.set('n', '}', DiffNextFile)
+vim.keymap.set('n', '{', DiffPrevFile)
+vim.keymap.set('n', ')', DiffNextHunk)
+vim.keymap.set('n', '(', DiffPrevHunk)
+vim.keymap.set('n', '!', DiffShowHunk)
+vim.keymap.set('n', '<leader>s', DiffSplitHunk)
+vim.keymap.set('n', '<leader>s', DiffDeleteHunk)
diff --git a/ftplugin/diff_navigator.vim b/ftplugin/diff_navigator.vim.orig
index 7e28b36..b09c8cf 100644
--- a/ftplugin/diff_navigator.vim
+++ b/ftplugin/diff_navigator.vim.orig
@@ -38,6 +38,13 @@ if exists("g:loaded_diff_navigator")
endif
let g:loaded_diff_navigator = 1
+lua << EOF
+ local tbl = {1, 2, 3}
+ for k, v in ipairs(tbl) do
+ print(v)
+ end
+EOF
+
function s:checkFilterDiff()
if !executable('filterdiff')
echohl WarningMsg