From 56db06a6005da8903ca246d3c283153f0c721b00 Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Thu, 16 Mar 2023 21:28:54 +0100 Subject: Not yet --- ftplugin/diff_navigator.lua | 329 -------------------------------------- ftplugin/diff_navigator.lua.new | 331 +++++++++++++++++++++++++++++++++++++++ ftplugin/diff_navigator.vim | 310 ++++++++++++++++++++++++++++++++++++ ftplugin/diff_navigator.vim.orig | 317 ------------------------------------- 4 files changed, 641 insertions(+), 646 deletions(-) delete mode 100644 ftplugin/diff_navigator.lua create mode 100644 ftplugin/diff_navigator.lua.new create mode 100644 ftplugin/diff_navigator.vim delete mode 100644 ftplugin/diff_navigator.vim.orig diff --git a/ftplugin/diff_navigator.lua b/ftplugin/diff_navigator.lua deleted file mode 100644 index 910121c..0000000 --- a/ftplugin/diff_navigator.lua +++ /dev/null @@ -1,329 +0,0 @@ --- ============================================================================ --- File: diff_navigator.vim --- Description: Filetype plugin to ease navigation in (unified) diffs --- Maintainer: Petr Uzel , --- Matěj Cepl --- 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\"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', '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', 's', DiffSplitHunk) -vim.keymap.set('n', 's', DiffDeleteHunk) diff --git a/ftplugin/diff_navigator.lua.new b/ftplugin/diff_navigator.lua.new new file mode 100644 index 0000000..96f09da --- /dev/null +++ b/ftplugin/diff_navigator.lua.new @@ -0,0 +1,331 @@ +-- ============================================================================ +-- File: diff_navigator.vim +-- Description: Filetype plugin to ease navigation in (unified) diffs +-- Maintainer: Petr Uzel , +-- Matěj Cepl +-- 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 +-- ============================================================================ + +-- FIXME https://www.reddit.com/r/neovim/comments/unwvkw/github_hkuptyrunesnvim_lua_test_framework_for/ +-- FIXME https://github.com/hkupty/runes.nvim + +-- 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 pairs(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[1] + local diff_new = diff_lines[2] + + -- 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\"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[2] - count_lines[1] + 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', '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', 's', DiffSplitHunk) +vim.keymap.set('n', 's', DiffDeleteHunk) diff --git a/ftplugin/diff_navigator.vim b/ftplugin/diff_navigator.vim new file mode 100644 index 0000000..7e28b36 --- /dev/null +++ b/ftplugin/diff_navigator.vim @@ -0,0 +1,310 @@ +" ============================================================================ +" File: diff_navigator.vim +" Description: Filetype plugin to ease navigation in (unified) diffs +" Maintainer: Petr Uzel , +" Matěj Cepl +" 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 +" endif +" let b:did_ftplugin = 1 + +" Load this plugin only once +if exists("g:loaded_diff_navigator") + finish +endif +let g:loaded_diff_navigator = 1 + +function s:checkFilterDiff() + if !executable('filterdiff') + echohl WarningMsg + echo "You need to install filterdiff first (part of patchutils)" + echohl None + return 0 + endif + return 1 +endfunction + +" 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 s:parseHunkHeader(lineno) + let inline = getline(a:lineno) + " Thanks to somian from #vim IRC channel for this incredible RE + let hunk_nos = substitute(inline, + \ '\_^\S\S\s\+-\(\d\+\),\(\d\+\)\s\++\(\d\+\),\(\d\+\)\s\+\S\S\(\_.*\)', + \ "\\1,\\2,\\3,\\4,\\5","") + let result = split(hunk_nos, ",") + + return { + \ 'line': a:lineno, + \ 'oldFirst': result[0], + \ 'oldCount': result[1], + \ 'newFirst': result[2], + \ 'newCount': result[3], + \ 'remainderLine': join(result[4:], ",") + \ } +endfunction + +" Get hunk header from the hunk surrounding cursor +function s:getCurrentHunkHeader() + if getline(".") =~ '^+++ \|^--- ' + let lineno = search('^@@[ +-\,\d]*@@.*$', 'ncW') + else + let lineno = search('^@@[ +-\,\d]*@@.*$', 'bncW') + endif + + return s:parseHunkHeader(lineno) +endfunction + +" Generate hunk header line +function s:createHunkHeader(oldStart, oldLen, newStart, newLen, remaind) + return "@@ -" . a:oldStart . "," . a:oldLen . + \ " +" . a:newStart . "," . a:newLen . " @@" . a:remaind +endfunction + +" 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 s:countLines(start, end) + let context_lines = 0 + let old_lines = 0 + let new_lines = 0 + + for line in getline(a:start, a:end) + let first_char = strpart(line, 0, 1) + if first_char == ' ' + let context_lines = context_lines + 1 + elseif first_char == '-' + let old_lines = old_lines + 1 + elseif first_char == '+' + let new_lines = new_lines + 1 + else + endif + endfor + + return [context_lines + old_lines, context_lines + new_lines] +endfunction + +" ------------------------------------------------------------- + +" Annotate each hunk with it's number and name of the changed file +function s:DiffAnnotate() + if s:checkFilterDiff() + let l:cursorpos = winsaveview() + %!filterdiff --annotate + call winrestview(cursorpos) + endif +endfunction + +" Print annotation of current hunk +function s:DiffShowHunk() + " if the current line begins with '+++' or '---', then it makes + " sense to search forwards + let hunk_header = s:getCurrentHunkHeader() + let l:hunk_annotation = substitute(getline(hunk_header['line']), + \ '^@@[ +-\,\d]*@@\s*\(.*\)$', '\1', '') + echo l:hunk_annotation +endfunction + +" Skip to next hunk +function s:DiffNextHunk() + call search('^@@[ +-\,\d]*@@', 'sW') +endfunction + +" Skip to previous hunk +function s:DiffPrevHunk() + call search('^@@[ +-\,\d]*@@', 'bsW') +endfunction + +" Skip to next changed file +function s:DiffNextFile() + call search('^--- ', 'sW') +endfunction + +" Skip to previous changed file +function s:DiffPrevFile() + call search('^--- ', 'bsW') +endfunction + +function s:DiffSplitHunk() + let old_cur_header = s:getCurrentHunkHeader() + let cur_line_no = 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 + let diff_lines = s:countLines(old_cur_header['line'] + 1, + \ cur_line_no - 1) + let diff_old = diff_lines[0] + let 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 @@ + let new_start_del_start = old_cur_header['oldFirst'] + let new_start_del_len = diff_old + let new_start_add_start = old_cur_header['newFirst'] + let new_start_add_len = diff_new + let @x = s:createHunkHeader(new_start_del_start, new_start_del_len, + \ new_start_add_start, new_start_add_len, + \ old_cur_header['remainderLine']) + let window_state = winsaveview() + " write the new original header line + call setpos(".", [0, old_cur_header['line'], 1, 0]) + normal ^d$"xp + call winrestview(window_state) + + " IN THE NEW HUNK HEADER + " new lengths = original len - new len + " new starts = original start + (difference) + let new_pos_del_start = old_cur_header['oldFirst'] + diff_old + let new_pos_del_len = old_cur_header['oldCount'] - diff_old + let new_pos_add_start = old_cur_header['newFirst'] + diff_new + let new_pos_add_len = old_cur_header['newCount'] - diff_new + let @x = s:createHunkHeader(new_pos_del_start, new_pos_del_len, + \ new_pos_add_start, new_pos_add_len, "") + execute "normal! O\\"xP" +endfunction + +" Delete the hunk cursor is in. +function s:DiffDeleteHunk() + let last_hunk_in_file = 0 + + let start_hunk_line = search('^@@[ +-\,\d]*@@.*$', 'bncW') + " we are before the first hunk ... return + if start_hunk_line == 0 + return + endif + + " end of the hunk is start of next hunk or next file, whichever + " comes first + let next_hunk_line = search('^@@[ +-\,\d]*@@.*$', 'nW') + let start_of_next_file_line = search('^--- .*$', 'ncW') + if start_of_next_file_line > 0 && + \ start_of_next_file_line < next_hunk_line + let next_hunk_line = start_of_next_file_line + let last_hunk_in_file = 1 + endif + + " 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 + execute start_hunk_line . ",$" . "d" + " we are in the middle of the file ... it's a bit more complicated + else + let count_lines = s:countLines(start_hunk_line + 1, + \ next_hunk_line - 1) + let added_lines = count_lines[1] - count_lines[0] + call setpos(".", [0, next_hunk_line, 1, 0]) + execute start_hunk_line . "," . (next_hunk_line - 1) . "d" + + " record the line number of the new hunk header after delete + let new_header_line = line(".") + + " recalculate current hunk header + while ! last_hunk_in_file + let cur_header = s:parseHunkHeader(line(".")) + let old_line = cur_header['newFirst'] + let new_line = old_line - added_lines + let @x = substitute(getline("."), "+" . old_line . ",", + \ "+" . new_line . ",", "") + normal ^d$"xp + + " check the next hunk + let next_hunk_line = search('\_^@@[ +-\,\d]*@@\_.*\_$', 'nW') + let start_of_next_file_line = search('^--- .*$', 'ncW') + if start_of_next_file_line < next_hunk_line + let next_hunk_line = start_of_next_file_line + let last_hunk_in_file = 1 + endif + call setpos(".", [0, next_hunk_line, 1, 0]) + endwhile + + " jump to the line after the deleted hunk + call setpos(".", [0, new_header_line, 1, 0]) + endif +endfunction + +" Define new commands +command DiffAnnotate call s:DiffAnnotate() +command DiffShowHunk call s:DiffShowHunk() +command DiffNextHunk call s:DiffNextHunk() +command DiffPrevHunk call s:DiffPrevHunk() +command DiffNextFile call s:DiffNextFile() +command DiffPrevFile call s:DiffPrevFile() +command DiffSplitHunk call s:DiffSplitHunk() +command DiffDeleteHunk call s:DiffDeleteHunk() + + +" Default },{,(,) do not make much sense in diffs, so remap them to +" make something useful +nnoremap