Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(nav): add nav_hunk() #976

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 26 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Here is a suggested example:
require('gitsigns').setup{
...
on_attach = function(bufnr)
local gs = package.loaded.gitsigns
local gitsigns = require('gitsigns')

local function map(mode, l, r, opts)
opts = opts or {}
Expand All @@ -123,31 +123,35 @@ require('gitsigns').setup{

-- Navigation
map('n', ']c', function()
if vim.wo.diff then return ']c' end
vim.schedule(function() gs.next_hunk() end)
return '<Ignore>'
end, {expr=true})
if vim.wo.diff then
vim.cmd.normal({']c', bang = true})
else
gitsigns.nav_hunk('next')
end
end)

map('n', '[c', function()
if vim.wo.diff then return '[c' end
vim.schedule(function() gs.prev_hunk() end)
return '<Ignore>'
end, {expr=true})
if vim.wo.diff then
vim.cmd.normal({'[c', bang = true})
else
gitsigns.nav_hunk('prev')
end
end)

-- Actions
map('n', '<leader>hs', gs.stage_hunk)
map('n', '<leader>hr', gs.reset_hunk)
map('v', '<leader>hs', function() gs.stage_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('v', '<leader>hr', function() gs.reset_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('n', '<leader>hS', gs.stage_buffer)
map('n', '<leader>hu', gs.undo_stage_hunk)
map('n', '<leader>hR', gs.reset_buffer)
map('n', '<leader>hp', gs.preview_hunk)
map('n', '<leader>hb', function() gs.blame_line{full=true} end)
map('n', '<leader>tb', gs.toggle_current_line_blame)
map('n', '<leader>hd', gs.diffthis)
map('n', '<leader>hD', function() gs.diffthis('~') end)
map('n', '<leader>td', gs.toggle_deleted)
map('n', '<leader>hs', gitsigns.stage_hunk)
map('n', '<leader>hr', gitsigns.reset_hunk)
map('v', '<leader>hs', function() gitsigns.stage_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('v', '<leader>hr', function() gitsigns.reset_hunk {vim.fn.line('.'), vim.fn.line('v')} end)
map('n', '<leader>hS', gitsigns.stage_buffer)
map('n', '<leader>hu', gitsigns.undo_stage_hunk)
map('n', '<leader>hR', gitsigns.reset_buffer)
map('n', '<leader>hp', gitsigns.preview_hunk)
map('n', '<leader>hb', function() gitsigns.blame_line{full=true} end)
map('n', '<leader>tb', gitsigns.toggle_current_line_blame)
map('n', '<leader>hd', gitsigns.diffthis)
map('n', '<leader>hD', function() gitsigns.diffthis('~') end)
map('n', '<leader>td', gitsigns.toggle_deleted)

-- Text object
map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')
Expand Down
54 changes: 36 additions & 18 deletions doc/gitsigns.txt
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ preview_hunk() *gitsigns.preview_hunk()*
will cause the window to get focus.

prev_hunk({opts}, {callback?}) *gitsigns.prev_hunk()*
DEPRECATED: use |gitsigns.nav_hunk()|

Jump to the previous hunk in the current buffer. If a hunk preview
(popup or inline) was previously opened, it will be re-opened
at the previous hunk.
Expand All @@ -351,9 +353,11 @@ prev_hunk({opts}, {callback?}) *gitsigns.prev_hunk()*
{async}

Parameters: ~
See |gitsigns.next_hunk()|.
See |gitsigns.nav_hunk()|.

next_hunk({opts}, {callback?}) *gitsigns.next_hunk()*
DEPRECATED: use |gitsigns.nav_hunk()|

Jump to the next hunk in the current buffer. If a hunk preview
(popup or inline) was previously opened, it will be re-opened
at the next hunk.
Expand All @@ -362,23 +366,37 @@ next_hunk({opts}, {callback?}) *gitsigns.next_hunk()*
{async}

Parameters: ~
{opts} (table|nil): Configuration table. Keys:
• {wrap}: (boolean)
Whether to loop around file or not. Defaults
to the value 'wrapscan'
• {navigation_message}: (boolean)
Whether to show navigation messages or not.
Looks at 'shortmess' for default behaviour.
• {foldopen}: (boolean)
Expand folds when navigating to a hunk which is
inside a fold. Defaults to `true` if 'foldopen'
contains `search`.
• {preview}: (boolean)
Automatically open preview_hunk() upon navigating
to a hunk.
• {greedy}: (boolean)
Only navigate between non-contiguous hunks. Only useful if
'diff_opts' contains `linematch`. Defaults to `true`.
See |gitsigns.nav_hunk()|.

nav_hunk({direction}, {opts}, {callback?}) *gitsigns.nav_hunk()*
Jump to hunk in the current buffer. If a hunk preview
(popup or inline) was previously opened, it will be re-opened
at the next hunk.

Attributes: ~
{async}

Parameters: ~
{direction} ('first'|'last'|'next'|'prev'):
{opts} (table|nil): Configuration table. Keys:
• {wrap}: (boolean)
Whether to loop around file or not. Defaults
to the value 'wrapscan'
• {navigation_message}: (boolean)
Whether to show navigation messages or not.
Looks at 'shortmess' for default behaviour.
• {foldopen}: (boolean)
Expand folds when navigating to a hunk which is
inside a fold. Defaults to `true` if 'foldopen'
contains `search`.
• {preview}: (boolean)
Automatically open preview_hunk() upon navigating
to a hunk.
• {greedy}: (boolean)
Only navigate between non-contiguous hunks. Only useful if
'diff_opts' contains `linematch`. Defaults to `true`.
• {count}: (integer)
Number of times to advance. Defaults to |v:count1|.

reset_buffer_index() *gitsigns.reset_buffer_index()*
Unstage all hunks for current buffer in the index. Note:
Expand Down
22 changes: 19 additions & 3 deletions gen_help.lua
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,22 @@ end
--- @param block string[]
--- @param params {[1]: string, [2]: string, [3]: string[]}[]
--- @param returns {[1]: string, [2]: string, [3]: string[]}[]
--- @param deprecated string?
--- @return string[]?
local function render_block(header, block, params, returns)
local function render_block(header, block, params, returns, deprecated)
if vim.startswith(header, '_') then
return
end

local res = { header }

if deprecated then
list_extend(res, {
' DEPRECATED: '..deprecated,
''
})
end

list_extend(res, block)

-- filter arguments beginning with '_'
Expand Down Expand Up @@ -356,21 +365,28 @@ local function gen_functions_doc_from_file(path)
local desc = {} --- @type string[]
local params = {} --- @type {[1]: string, [2]: string, [3]: string[]}[]
local returns = {} --- @type {[1]: string, [2]: string, [3]: string[]}[]
local deprecated --- @type string?

for l in i do
local doc_comment = l:match('^%-%-%- ?(.*)') --- @type string?
if doc_comment then
state = process_doc_comment(state, doc_comment, desc, params, returns)
local depre = doc_comment:match('@deprecated ?(.*)')
if depre then
deprecated = depre
else
state = process_doc_comment(state, doc_comment, desc, params, returns)
end
elseif state ~= 'none' then
-- First line after block
local ok, header = pcall(parse_func_header, l)
if ok then
blocks[#blocks + 1] = render_block(header, desc, params, returns)
blocks[#blocks + 1] = render_block(header, desc, params, returns, deprecated)
end
state = 'none'
desc = {}
params = {}
returns = {}
deprecated = nil
end
end

Expand Down
120 changes: 78 additions & 42 deletions lua/gitsigns/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ end)
--- @field navigation_message boolean
--- @field greedy boolean
--- @field preview boolean
--- @field count integer

--- @param x string
--- @param word string
Expand Down Expand Up @@ -494,6 +495,10 @@ local function process_nav_opts(opts)
opts.greedy = true
end

if opts.count == nil then
opts.count = vim.v.count1
end

return opts
end

Expand All @@ -514,75 +519,85 @@ local function has_preview_inline(bufnr)
end

--- @async
--- @param direction 'first'|'last'|'next'|'prev'
--- @param opts? Gitsigns.NavOpts
--- @param forwards boolean
local function nav_hunk(opts, forwards)
local function nav_hunk(direction, opts)
opts = process_nav_opts(opts)
local bufnr = current_buf()
local bcache = cache[bufnr]
if not bcache then
return
end

local hunks = {}
vim.list_extend(hunks, get_hunks(bufnr, bcache, opts.greedy, false) or {})
local hunks = get_hunks(bufnr, bcache, opts.greedy, false) or {}
local hunks_head = get_hunks(bufnr, bcache, opts.greedy, true) or {}
vim.list_extend(hunks, Hunks.filter_common(hunks_head, bcache.hunks) or {})
vim.list_extend(hunks, Hunks.filter_common(hunks_head, hunks) or {})

if not hunks or vim.tbl_isempty(hunks) then
if opts.navigation_message then
api.nvim_echo({ { 'No hunks', 'WarningMsg' } }, false, {})
end
return
end

local line = api.nvim_win_get_cursor(0)[1]
local index --- @type integer?

local hunk, index = Hunks.find_nearest_hunk(line, hunks, forwards, opts.wrap)
local forwards = direction == 'next' or direction == 'last'

if hunk == nil then
if opts.navigation_message then
api.nvim_echo({ { 'No more hunks', 'WarningMsg' } }, false, {})
for _ = 1, opts.count do
index = Hunks.find_nearest_hunk(line, hunks, direction, opts.wrap)

if not index then
if opts.navigation_message then
api.nvim_echo({ { 'No more hunks', 'WarningMsg' } }, false, {})
end
local _, col = vim.fn.getline(line):find('^%s*')
api.nvim_win_set_cursor(0, { line, col })
return
end
return

line = forwards and hunks[index].added.start or hunks[index].vend
end

local row = forwards and hunk.added.start or hunk.vend
if row then
-- Handle topdelete
if row == 0 then
row = 1
end
vim.cmd([[ normal! m' ]]) -- add current cursor position to the jump list
api.nvim_win_set_cursor(0, { row, 0 })
if opts.foldopen then
vim.cmd('silent! foldopen!')
end
if opts.preview or popup.is_open('hunk') ~= nil then
-- Use defer so the cursor change can settle, otherwise the popup might
-- appear in the old position
defer(function()
-- Close the popup in case one is open which will cause it to focus the
-- popup
popup.close('hunk')
M.preview_hunk()
end)
elseif has_preview_inline(bufnr) then
defer(M.preview_hunk_inline)
end
-- Handle topdelete
line = math.max(line, 1)

if index ~= nil and opts.navigation_message then
api.nvim_echo({ { string.format('Hunk %d of %d', index, #hunks), 'None' } }, false, {})
end
vim.cmd([[ normal! m' ]]) -- add current cursor position to the jump list

local _, col = vim.fn.getline(line):find('^%s*')
api.nvim_win_set_cursor(0, { line, col })

if opts.foldopen then
vim.cmd('silent! foldopen!')
end

if opts.preview or popup.is_open('hunk') ~= nil then
-- Use defer so the cursor change can settle, otherwise the popup might
-- appear in the old position
defer(function()
-- Close the popup in case one is open which will cause it to focus the
-- popup
popup.close('hunk')
M.preview_hunk()
end)
elseif has_preview_inline(bufnr) then
defer(M.preview_hunk_inline)
end

if index and opts.navigation_message then
api.nvim_echo({ { string.format('Hunk %d of %d', index, #hunks), 'None' } }, false, {})
end
end

--- Jump to the next hunk in the current buffer. If a hunk preview
--- Jump to hunk in the current buffer. If a hunk preview
--- (popup or inline) was previously opened, it will be re-opened
--- at the next hunk.
---
--- Attributes: ~
--- {async}
---
--- @param direction 'first'|'last'|'next'|'prev'
--- @param opts table|nil Configuration table. Keys:
--- • {wrap}: (boolean)
--- Whether to loop around file or not. Defaults
Expand All @@ -600,14 +615,35 @@ end
--- • {greedy}: (boolean)
--- Only navigate between non-contiguous hunks. Only useful if
--- 'diff_opts' contains `linematch`. Defaults to `true`.
--- • {count}: (integer)
--- Number of times to advance. Defaults to |v:count1|.
M.nav_hunk = async.create(2, function(direction, opts)
nav_hunk(direction, opts)
end)

C.nav_hunk = function(args, _)
M.nav_hunk(args[1], args)
end

--- @deprecated use |gitsigns.nav_hunk()|
--- Jump to the next hunk in the current buffer. If a hunk preview
--- (popup or inline) was previously opened, it will be re-opened
--- at the next hunk.
---
--- Attributes: ~
--- {async}
---
--- Parameters: ~
--- See |gitsigns.nav_hunk()|.
M.next_hunk = async.create(1, function(opts)
nav_hunk(opts, true)
nav_hunk('next', opts)
end)

C.next_hunk = function(args, _)
M.next_hunk(args)
M.nav_hunk('next', args)
end

--- @deprecated use |gitsigns.nav_hunk()|
--- Jump to the previous hunk in the current buffer. If a hunk preview
--- (popup or inline) was previously opened, it will be re-opened
--- at the previous hunk.
Expand All @@ -616,13 +652,13 @@ end
--- {async}
---
--- Parameters: ~
--- See |gitsigns.next_hunk()|.
--- See |gitsigns.nav_hunk()|.
M.prev_hunk = async.create(1, function(opts)
nav_hunk(opts, false)
nav_hunk('prev', opts)
end)

C.prev_hunk = function(args, _)
M.prev_hunk(args)
M.nav_hunk('prev', args)
end

--- @param fmt Gitsigns.LineSpec
Expand Down
Loading
Loading